/* * postprocess.c * * Here are the decompression function for the compressed photos and the * postprocessing for uncompressed photos. * * Copyright (c) 2005 and 2007 Theodore Kilgore <kilgota@auburn.edu> * Camera library support under libgphoto2.1.1 for camera(s) * with chipset from Service & Quality Technologies, Taiwan. * The chip supported by this driver is presumed to be the SQ905, * * Licensed under GNU Lesser General Public License, as part of Gphoto * camera support project. For a copy of the license, see the file * COPYING in the main source tree of libgphoto2. */ #include <config.h> #include <stdlib.h> #include <stdio.h> #include <errno.h> #include <fcntl.h> #include <string.h> #include <math.h> #ifdef OS2 #include <db.h> #endif #include <gphoto2/gphoto2.h> #include <gphoto2/gphoto2-port.h> #include <gamma.h> #include "digigr8.h" #define GP_MODULE "digigr8" static int digi_first_decompress (unsigned char *output, unsigned char *input, unsigned int outputsize) { unsigned char parity = 0; unsigned char nibble_to_keep[2]; unsigned char temp1 = 0, temp2 = 0; unsigned char input_byte; unsigned char lookup = 0; unsigned int i = 0; unsigned int bytes_used = 0; unsigned int bytes_done = 0; unsigned int bit_counter = 8; unsigned int cycles = 0; int table[9] = { -1, 0, 2, 6, 0x0e, 0x0e, 0x0e, 0x0e, 0xfb}; unsigned char lookup_table[16] ={0, 2, 6, 0x0e, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb}; unsigned char translator[16] = {8,7,9,6,10,11,12,13,14,15,5,4,3,2,1,0}; GP_DEBUG ("Running first_decompress.\n"); nibble_to_keep[0] = 0; nibble_to_keep[1] = 0; while (bytes_done < outputsize) { while (parity < 2 ) { while ( lookup > table[cycles]) { if (bit_counter == 8) { input_byte = input[bytes_used]; bytes_used ++; temp1 = input_byte; bit_counter = 0; } input_byte = temp1; temp2 = (temp2 << 1) & 0xFF; input_byte = input_byte >> 7; temp2 = temp2 | input_byte; temp1 = (temp1 <<1)&0xFF; bit_counter ++ ; cycles ++ ; if (cycles > 9) { GP_DEBUG ("Too many cycles?\n"); return GP_ERROR; } lookup = temp2 & 0xff; } temp2 = 0; for (i=0; i < 17; i++ ) { if (lookup == lookup_table[i] ) { nibble_to_keep[parity] = translator[i]; break; } if (i == 16) { GP_DEBUG ("Illegal lookup value during decomp\n"); return GP_ERROR; } } cycles = 0; parity ++ ; } output[bytes_done] = (nibble_to_keep[0]<<4)|nibble_to_keep[1]; bytes_done ++ ; parity = 0; } GP_DEBUG ("bytes_used = 0x%x = %i\n", bytes_used, bytes_used); return GP_OK; } static int digi_second_decompress (unsigned char *uncomp, unsigned char *in, int width, int height) { int diff = 0; int tempval = 0; int i, m, parity; unsigned char delta_left = 0; unsigned char delta_right = 0; int input_counter = 0; int delta_table[] = {-144, -110, -77, -53, -35, -21, -11, -3, 2, 10, 20, 34, 52, 76, 110, 144}; unsigned char *templine_red; unsigned char *templine_green; unsigned char *templine_blue; templine_red = malloc(width); if (!templine_red) { free(templine_red); return GP_ERROR; } for(i=0; i < width; i++){ templine_red[i] = 0x80; } templine_green = malloc(width); if (!templine_green) { free(templine_green); return GP_ERROR; } for(i=0; i < width; i++){ templine_green[i] = 0x80; } templine_blue = malloc(width); if (!templine_blue) { free(templine_blue); return GP_ERROR; } for(i=0; i < width; i++){ templine_blue[i] = 0x80; } GP_DEBUG ("Running second_decompress.\n"); for (m=0; m < height/2; m++) { /* First we do an even-numbered line */ for (i=0; i< width/2; i++) { parity = i&1; delta_right = in[input_counter] &0x0f; delta_left = (in[input_counter]>>4)&0xff; input_counter ++; /* left pixel (red) */ diff = delta_table[delta_left]; if (!i) tempval = templine_red[0] + diff; else tempval = (templine_red[i] + uncomp[2*m*width+2*i-2])/2 + diff; tempval = MIN(tempval, 0xff); tempval = MAX(tempval, 0); uncomp[2*m*width+2*i] = tempval; templine_red[i] = tempval; /* right pixel (green) */ diff = delta_table[delta_right]; if (!i) tempval = templine_green[1] + diff; else if (2*i == width - 2 ) tempval = (templine_green[i] + uncomp[2*m*width+2*i-1])/2 + diff; else tempval = (templine_green[i+1] + uncomp[2*m*width+2*i-1])/2 + diff; tempval = MIN(tempval, 0xff); tempval = MAX(tempval, 0); uncomp[2*m*width+2*i+1] = tempval; templine_green[i] = tempval; } /* then an odd-numbered line */ for (i=0; i< width/2; i++) { delta_right = in[input_counter] &0x0f; delta_left = (in[input_counter]>>4)&0xff; input_counter ++; /* left pixel (green) */ diff = delta_table[delta_left]; if (!i) tempval = templine_green[0] + diff; else tempval = (templine_green[i] + uncomp[(2*m+1)*width+2*i-2])/2 + diff; tempval = MIN(tempval, 0xff); tempval = MAX(tempval, 0); uncomp[(2*m+1)*width+2*i] = tempval; templine_green[i] = tempval; /* right pixel (blue) */ diff = delta_table[delta_right]; if (!i) tempval = templine_blue[0] + diff; else tempval = (templine_blue[i] + uncomp[(2*m+1)*width+2*i-1])/2 + diff; tempval = MIN(tempval, 0xff); tempval = MAX(tempval, 0); uncomp[(2*m+1)*width+2*i+1] = tempval; templine_blue[i] = tempval; } } free(templine_green); free(templine_red); free(templine_blue); return GP_OK; } int digi_decompress (unsigned char *out_data, unsigned char *data, int w, int h) { int size; unsigned char *temp_data; size = w*h/2; temp_data = malloc(size); if (!temp_data) return(GP_ERROR_NO_MEMORY); digi_first_decompress (temp_data, data, size); GP_DEBUG("Stage one done\n"); digi_second_decompress (out_data, temp_data, w, h); GP_DEBUG("Stage two done\n"); free(temp_data); return(GP_OK); } /* Brightness correction routine adapted from * camlibs/polaroid/jd350e.c, copyright © 2001 Michael Trawny * <trawny99@users.sourceforge.net> */ #define RED(p,x,y,w) *((p)+3*((y)*(w)+(x)) ) #define GREEN(p,x,y,w) *((p)+3*((y)*(w)+(x))+1) #define BLUE(p,x,y,w) *((p)+3*((y)*(w)+(x))+2) #define MINMAX(a,min,max) { (min)=MIN(min,a); (max)=MAX(max,a); } #ifndef MAX # define MAX(a, b) ((a) > (b) ? (a) : (b)) #endif #ifndef MIN # define MIN(a, b) ((a) < (b) ? (a) : (b)) #endif #ifndef CLAMP #define CLAMP(x) ((x)<0?0:((x)>255)?255:(x)) #endif int digi_postprocess(int width, int height, unsigned char* rgb) { int x,y, red_min=255, red_max=0, blue_min=255, blue_max=0, green_min=255, green_max=0; double min, max, amplify; /* determine min and max per color... */ for( y=0; y<height; y++){ for( x=0; x<width; x++ ){ MINMAX( RED(rgb,x,y,width), red_min, red_max ); MINMAX( GREEN(rgb,x,y,width), green_min, green_max); MINMAX( BLUE(rgb,x,y,width), blue_min, blue_max ); } } /* determine min and max per color... */ for( y=0; y<height; y++){ for( x=0; x<width; x++ ){ MINMAX( RED(rgb,x,y,width), red_min, red_max ); MINMAX( GREEN(rgb,x,y,width), green_min, green_max); MINMAX( BLUE(rgb,x,y,width), blue_min, blue_max ); } } /* Normalize brightness ... */ max = MAX( MAX( red_max, green_max ), blue_max); min = MIN( MIN( red_min, green_min ), blue_min); amplify = 255.0/(max-min); for( y=0; y<height; y++){ for( x=0; x<width; x++ ){ RED(rgb,x,y,width)= MIN(amplify*(double)(RED(rgb,x,y,width)-min),255); GREEN(rgb,x,y,width)= MIN(amplify*(double)(GREEN(rgb,x,y,width)-min),255); BLUE(rgb,x,y,width)= MIN(amplify*(double)(BLUE(rgb,x,y,width)-min),255); } } return GP_OK; } /* ========= White Balance / Color Enhance / Gamma adjust (experimental) ========== Get histogram for each color plane Expand to reach 0.5% of white dots in image Get new histogram for each color plane Expand to reach 0.5% of black dots in image Get new histogram Calculate and apply gamma correction if not a dark image: For each dot, increases color separation ================================================================================== */ static int histogram (unsigned char *data, unsigned int size, int *htable_r, int *htable_g, int *htable_b) { int x; /* Initializations */ for (x = 0; x < 0x100; x++) { htable_r[x] = 0; htable_g[x] = 0; htable_b[x] = 0; } /* Building the histograms */ for (x = 0; x < (size * 3); x += 3) { htable_r[data[x+0]]++; /* red histogram */ htable_g[data[x+1]]++; /* green histogram */ htable_b[data[x+2]]++; /* blue histogram */ } return GP_OK; } int white_balance (unsigned char *data, unsigned int size, float saturation) { int x, r, g, b, max, d; double r_factor, g_factor, b_factor, max_factor; int htable_r[0x100], htable_g[0x100], htable_b[0x100]; unsigned char gtable[0x100]; double new_gamma, gamma=1.0; /* ------------------- GAMMA CORRECTION ------------------- */ histogram(data, size, htable_r, htable_g, htable_b); x = 1; for (r = 64; r < 192; r++) { x += htable_r[r]; x += htable_g[r]; x += htable_b[r]; } new_gamma = sqrt((double) (x * 1.5) / (double) (size * 3)); GP_DEBUG("Provisional gamma correction = %1.2f\n", new_gamma); /* Recalculate saturation factor for later use. */ saturation=saturation*new_gamma*new_gamma; GP_DEBUG("saturation = %1.2f\n", saturation); gamma = new_gamma; if (new_gamma < .70) gamma = 0.70; if (new_gamma > 1.2) gamma = 1.2; GP_DEBUG("Gamma correction = %1.2f\n", gamma); gp_gamma_fill_table(gtable, gamma); gp_gamma_correct_single(gtable,data,size); if (saturation < .5 ) /* If so, exit now. */ return GP_OK; /* ---------------- BRIGHT DOTS ------------------- */ max = size / 200; histogram(data, size, htable_r, htable_g, htable_b); for (r=0xfe, x=0; (r > 32) && (x < max); r--) x += htable_r[r]; for (g=0xfe, x=0; (g > 32) && (x < max); g--) x += htable_g[g]; for (b=0xfe, x=0; (b > 32) && (x < max); b--) x += htable_b[b]; r_factor = (double) 0xfd / r; g_factor = (double) 0xfd / g; b_factor = (double) 0xfd / b; max_factor = r_factor; if (g_factor > max_factor) max_factor = g_factor; if (b_factor > max_factor) max_factor = b_factor; if (max_factor >= 4.0) { /* We need a little bit of control, here. If max_factor > 5 the photo * was very dark, after all. */ if (2.0*b_factor < max_factor) b_factor = max_factor/2.; if (2.0*r_factor < max_factor) r_factor = max_factor/2.; if (2.0*g_factor < max_factor) g_factor = max_factor/2.; r_factor = (r_factor / max_factor) * 4.0; g_factor = (g_factor / max_factor) * 4.0; b_factor = (b_factor / max_factor) * 4.0; } if (max_factor > 1.5) saturation = 0; GP_DEBUG("White balance (bright): r=%1d, g=%1d, b=%1d, fr=%1.3f, fg=%1.3f, fb=%1.3f\n", r, g, b, r_factor, g_factor, b_factor); if (max_factor <= 1.4) { for (x = 0; x < (size * 3); x += 3) { d = (data[x+0]<<8) * r_factor+8; d >>=8; if (d > 0xff) { d = 0xff; } data[x+0] = d; d = (data[x+1]<<8) * g_factor+8; d >>=8; if (d > 0xff) { d = 0xff; } data[x+1] = d; d = (data[x+2]<<8) * b_factor+8; d >>=8; if (d > 0xff) { d = 0xff; } data[x+2] = d; } } /* ---------------- DARK DOTS ------------------- */ max = size / 200; /* 1/200 = 0.5% */ histogram(data, size, htable_r, htable_g, htable_b); for (r=0, x=0; (r < 96) && (x < max); r++) x += htable_r[r]; for (g=0, x=0; (g < 96) && (x < max); g++) x += htable_g[g]; for (b=0, x=0; (b < 96) && (x < max); b++) x += htable_b[b]; r_factor = (double) 0xfe / (0xff-r); g_factor = (double) 0xfe / (0xff-g); b_factor = (double) 0xfe / (0xff-b); GP_DEBUG( "White balance (dark): r=%1d, g=%1d, b=%1d, fr=%1.3f, fg=%1.3f, fb=%1.3f\n", r, g, b, r_factor, g_factor, b_factor); for (x = 0; x < (size * 3); x += 3) { d = (int) 0xff08-(((0xff-data[x+0])<<8) * r_factor); d >>= 8; if (d < 0) { d = 0; } data[x+0] = d; d = (int) 0xff08-(((0xff-data[x+1])<<8) * g_factor); d >>= 8; if (d < 0) { d = 0; } data[x+1] = d; d = (int) 0xff08-(((0xff-data[x+2])<<8) * b_factor); d >>= 8; if (d < 0) { d = 0; } data[x+2] = d; } /* ------------------ COLOR ENHANCE ------------------ */ if(saturation > 0.0) { for (x = 0; x < (size * 3); x += 3) { r = data[x+0]; g = data[x+1]; b = data[x+2]; d = (int) (r + g + b) / 3.; if ( r > d ) r = r + (int) ((r - d) * (0xff-r)/(0x100-d) * saturation); else r = r + (int) ((r - d) * (0xff-d)/(0x100-r) * saturation); if (g > d) g = g + (int) ((g - d) * (0xff-g)/(0x100-d) * saturation); else g = g + (int) ((g - d) * (0xff-d)/(0x100-g) * saturation); if (b > d) b = b + (int) ((b - d) * (0xff-b)/(0x100-d) * saturation); else b = b + (int) ((b - d) * (0xff-d)/(0x100-b) * saturation); data[x+0] = CLAMP(r); data[x+1] = CLAMP(g); data[x+2] = CLAMP(b); } } return GP_OK; }