Browse Source

Implement external post processing

whitebalance
Martijn Braam 1 year ago
parent
commit
8863133fe2
  1. 21
      README.md
  2. 91
      main.c
  3. 4
      meson.build
  4. 54
      postprocess.sh

21
README.md

@ -48,3 +48,24 @@ These are the sections describing the sensors.
* `rate=15` the refresh rate in fps to use for the sensor
* `fmt=BGGR8` sets the pixel and bus formats used when capturing from the sensor, only BGGR8 is fully supported
* `rotate=90` the rotation angle to make the sensor match the screen
# Post processing
Megapixels only captures raw frames and stores .dng files. It captures a 5 frame burst and saves it to a temporary
location. Then the postprocessing script is run which will generate the final .jpg file and writes it into the
pictures directory. Megapixels looks for the post processing script in the following locations:
* ./postprocess.sh
* $XDG_CONFIG_DIR/megapixels/postprocess.sh
* ~/.config/megapixels/postprocess.sh
* /etc/megapixels/postprocess.sh
* /usr/share/megapixels/postprocess.sh
The bundled postprocess.sh script will copy the first frame of the burst into the picture directory as an DNG
file and if dcraw and imagemagick are installed it will generate a JPG and also write that to the picture
directory. It supports either the full dcraw or dcraw_emu from libraw.
It is possible to write your own post processing pipeline my providing your own `postprocess.sh` script at
one of the above locations. The first argument to the script is the directory containing the temporary
burst files and the second argument is the final path for the image without an extension. For more details
see postprocess.sh in this repository.

91
main.c

@ -86,6 +86,9 @@ static int preview_height = -1;
static char *last_path = NULL;
static int auto_exposure = 1;
static int auto_gain = 1;
static int burst_length = 5;
static char burst_dir[20];
static char processing_script[512];
// Widgets
GtkWidget *preview;
@ -404,6 +407,8 @@ process_image(const int *p, int size)
struct tm tim;
uint8_t *pixels;
char fname[255];
char fname_target[255];
char command[1024];
char timestamp[30];
char uniquecameramodel[255];
GdkPixbuf *pixbuf;
@ -453,7 +458,9 @@ process_image(const int *p, int size)
tim = *(localtime(&rawtime));
strftime(timestamp, 30, "%Y%m%d%H%M%S", &tim);
strftime(datetime, 20, "%Y:%m:%d %H:%M:%S", &tim);
sprintf(fname, "%s/Pictures/IMG%s-%d.dng", getenv("HOME"), timestamp, capture);
sprintf(fname_target, "%s/Pictures/IMG%s", getenv("HOME"), timestamp);
sprintf(fname, "%s/%d.dng", burst_dir, burst_length - capture);
if(!(tif = TIFFOpen(fname, "w"))) {
printf("Could not open tiff\n");
@ -516,7 +523,7 @@ process_image(const int *p, int size)
TIFFSetField(tif, TIFFTAG_BLACKLEVEL, 1, &current.blacklevel);
}
TIFFCheckpointDirectory(tif);
printf("Writing frame\n");
printf("Writing frame to %s\n", fname);
unsigned char *pLine = (unsigned char*)malloc(current.width);
for(int row = 0; row < current.height; row++){
@ -551,8 +558,8 @@ process_image(const int *p, int size)
TIFFClose(tif);
// Update the thumbnail if this is the last frame
if (capture == 0) {
// Update the thumbnail if this is the last frame
pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, FALSE, 8, current.width / (skip*2), current.height / (skip*2));
pixels = gdk_pixbuf_get_pixels(pixbuf);
quick_debayer_bggr8((const uint8_t *)p, pixels, current.width, current.height, skip);
@ -573,6 +580,12 @@ process_image(const int *p, int size)
g_printerr("%s\n", error->message);
g_clear_error(&error);
}
// Start post-processing the captured burst
g_printerr("Post process %s to %s.ext\n", burst_dir, fname_target);
sprintf(command, "%s %s %s &", processing_script, burst_dir, fname_target);
system(command);
}
}
}
@ -959,7 +972,18 @@ on_open_directory_clicked(GtkWidget *widget, gpointer user_data)
void
on_shutter_clicked(GtkWidget *widget, gpointer user_data)
{
capture = 5;
char template[] = "/tmp/megapixels.XXXXXX";
char *tempdir;
tempdir = mkdtemp(template);
if (tempdir == NULL) {
g_printerr("Could not make capture directory %s\n", template);
exit (EXIT_FAILURE);
}
strcpy(burst_dir, tempdir);
capture = burst_length;
}
void
@ -1065,12 +1089,69 @@ find_config(char *conffile)
return -1;
}
int
find_processor(char *script)
{
char *xdg_config_home;
char filename[] = "postprocess.sh";
wordexp_t exp_result;
// Resolve XDG stuff
if ((xdg_config_home = getenv("XDG_CONFIG_HOME")) == NULL) {
xdg_config_home = "~/.config";
}
wordexp(xdg_config_home, &exp_result, 0);
xdg_config_home = strdup(exp_result.we_wordv[0]);
wordfree(&exp_result);
// Check postprocess.h in the current working directory
sprintf(script, "%s", filename);
if(access(script, F_OK) != -1) {
sprintf(script, "./%s", filename);
printf("Found postprocessor script at %s\n", script);
return 0;
}
// Check for a script in XDG_CONFIG_HOME
sprintf(script, "%s/megapixels/%s", xdg_config_home, filename);
if(access(script, F_OK) != -1) {
printf("Found postprocessor script at %s\n", script);
return 0;
}
// Check user overridden /etc/megapixels/postprocessor.sh
sprintf(script, "%s/megapixels/%s", SYSCONFDIR, filename);
if(access(script, F_OK) != -1) {
printf("Found postprocessor script at %s\n", script);
return 0;
}
// Check packaged /usr/share/megapixels/postprocessor.sh
sprintf(script, "%s/megapixels/%s", DATADIR, filename);
if(access(script, F_OK) != -1) {
printf("Found postprocessor script at %s\n", script);
return 0;
}
return -1;
}
int
main(int argc, char *argv[])
{
int ret;
char conffile[512];
find_config(conffile);
ret = find_config(conffile);
if (ret) {
g_printerr("Could not find any config file\n");
return ret;
}
ret = find_processor(processing_script);
if (ret) {
g_printerr("Could not find any post-process script\n");
return ret;
}
TIFFSetTagExtender(register_custom_tiff_tags);

4
meson.build

@ -31,3 +31,7 @@ install_data([
'config/pine64,pinetab.ini',
],
install_dir : get_option('datadir') / 'megapixels/config/')
install_data(['postprocess.sh'],
install_dir : get_option('datadir') / 'megapixels/',
install_mode: 'rwxr-xr-x')

54
postprocess.sh

@ -0,0 +1,54 @@
#!/bin/sh
# The post-processing script gets called after taking a burst of
# pictures into a temporary directory. The first argument is the
# directory containing the raw files in the burst. The contents
# are 1.dng, 2.dng.... up to the number of photos in the burst.
#
# The second argument is the filename for the final photo without
# the extension, like "/home/user/Pictures/IMG202104031234"
#
# The post-processing script is responsible for cleaning up
# temporary directory for the burst.
if [ "$#" -ne 2 ]; then
echo "Usage: $0 [burst-dir] [target-name]"
exit 2
fi
BURST_DIR="$1"
TARGET_NAME="$2"
# Copy the first frame of the burst as the raw photo
cp "$BURST_DIR"/1.dng "$TARGET_NAME.dng"
# Create a .jpg if raw processing tools are installed
DCRAW=""
if command -v "dcraw_emu" &> /dev/null
then
DCRAW=dcraw_emu
fi
if command -v "dcraw" &> /dev/null
then
DCRAW=dcraw
fi
if [ -n "$DCRAW" ]; then
# +M use embedded color matrix
# -H 4 Recover highlights by rebuilding them
# -o 1 Output in sRGB colorspace
# -q 3 Debayer with AHD algorithm
# -T Output TIFF
# -fbdd 1 Raw denoising with FBDD
$DCRAW +M -H 4 -o 1 -q 3 -T -fbdd 1 $BURST_DIR/1.dng
if command -v convert &> /dev/null
then
convert "$BURST_DIR"/1.dng.tiff "$TARGET_NAME.jpg"
else
cp "$BURST_DIR"/1.dng.tiff "$TARGET_NAME.tiff"
fi
fi
# Clean up the temp dir containing the burst
rm -rf "$BURST_DIR"
Loading…
Cancel
Save