Implement external post processing
This commit is contained in:
parent
9cbebee116
commit
8863133fe2
21
README.md
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
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, ¤t.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);
|
||||
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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…
Reference in New Issue