Hi.
Here is a patch for DCRaw 8.99 which equalizes the green channels before
interpolation. This should get rid of (some of) the maze effect caused by
un-balanced green channels. The code is from the perfectRAW source and I have
not cleaned it up yet.
There are two levels of equalization. Global (-l 1) and global+local (-l 2). I
have seen some highlight artifacts from the global+local equalization, but I
have kept it in the code for you to play with.
Please test the code and let me know how it works for your problem files. If
and when it works I will add it to UFRaw.
Regards,
Niels Kristian
_________________________________________________________________
--- dcraw.c~ 2009-12-25 19:51:25.000000000 +0100
+++ dcraw.c 2010-03-31 06:34:27.758722227 +0200
@@ -126,7 +126,7 @@
int half_size=0, four_color_rgb=0, document_mode=0, highlight=0;
int verbose=0, use_auto_wb=0, use_camera_wb=0, use_camera_matrix=-1;
int output_color=1, output_bps=8, output_tiff=0, med_passes=0;
-int no_auto_bright=0;
+int no_auto_bright=0, level_greens=0, level_cell=8;
unsigned greybox[4] = { 0, 0, UINT_MAX, UINT_MAX };
float cam_mul[4], pre_mul[4], cmatrix[3][4], rgb_cam[3][4];
const double xyz_rgb[3][3] = { /* XYZ from RGB */
@@ -3779,6 +3779,388 @@
if (half_size) filters = 0;
}
+int median(int *elements, int N)
+{
+ int i,j,min;
+ int temp;
+
+ // Order elements (only half of them)
+ for (i = 0; i < (N >> 1) + 1; ++i)
+ {
+ // Find position of minimum element
+ min = i;
+ for (j = i + 1; j < N; ++j)
+ if (elements[j] < elements[min])
+ min = j;
+ // Put found minimum element in its place
+ temp = elements[i];
+ elements[i] = elements[min];
+ elements[min] = temp;
+ }
+ // Get result - the middle element
+ return elements[N >> 1];
+}
+
+void shrink_image(void)
+{
+ ushort (*img)[4];
+ int row, col, c;
+
+ if (!shrink) {
+ shrink = filters && (half_size || threshold || aber[0] != 1 || aber[2] != 1);
+ iheight = (height + shrink) >> shrink;
+ iwidth = (width + shrink) >> shrink;
+ img = (ushort (*)[4]) calloc (iheight*iwidth, sizeof *img);
+ merror (img, "shrink_image()");
+ for (row=0; row < height; row++)
+ for (col=0; col < width; col++) {
+ c = FC(row,col);
+ img[(row >> 1)*iwidth+(col >> 1)][c] = image[row*width+col][c];
+ }
+ free (image);
+ image = img;
+ }
+}
+
+void unshrink_image(void)
+{
+ ushort (*img)[4];
+ int row, col, c;
+
+ if ((shrink)&&(!half_size)) {
+ img = (ushort (*)[4]) calloc (height*width, sizeof *img);
+ merror (img, "unshrink_image()");
+ for (row=0; row < height; row++)
+ for (col=0; col < width; col++) {
+ c = fc(row,col);
+ img[row*width+col][c] = image[(row >> 1)*iwidth+(col >> 1)][c];
+ }
+ free (image);
+ image = img;
+ shrink = 0;
+ iheight = height;
+ iwidth = width;
+ }
+}
+
+#define DIFF_IN_LIMIT(a,b,c,d) ((abs(c-a)<dif_limit)&&(abs(d-b)<dif_limit))
+
+void CLASS eq_greens_old()
+{
+ int row, col, c;
+ ushort sat_max,sat_min;
+ double dif_limit;
+ double val[2];
+ int cnt;
+ ushort v[2],v2[4];
+ double f1,f2;
+ int ival[2];
+ int pwidth, pheight;
+ int x[4],y[4];
+ ushort *bufferG1, *bufferG2, pix;
+ int i,j,offset,offset2;
+ int S=level_cell;
+ char sD[1000];
+ int shrinked;
+
+ if (half_size) return;
+ shrinked=0;
+ if (shrink){
+ shrinked=shrink;
+ unshrink_image();
+ }
+
+ // Make width and height of image be pair numbers
+ pwidth=((width >> 1) << 1);
+ pheight=((height >> 1) << 1);
+
+ // Get channel sub-coordinates inside 2x2 RAW pixels
+ // This makes code more readeable, at least for me,
+ // maybe just a bit slower
+
+ for(c=0;c<4;c++){
+ y[c]=x[c]=0;
+ if(FC(0,0)==c){y[c]=0;x[c]=0;}
+ if(FC(0,1)==c){y[c]=0;x[c]=1;}
+ if(FC(1,1)==c){y[c]=1;x[c]=1;}
+ if(FC(1,0)==c){y[c]=1;x[c]=0;}
+ }
+
+ sat_max=maximum-125; // Do not equilibre if any of the channels is saturated
+ dif_limit=((double)maximum)/2.0; // Maximum pixel different
+ //sprintf(sD,"%f",dif_limit);
+ //MessageBox(0,sD,"debug",0);
+
+ // Green channels global equilibration
+ if (verbose) fprintf (stderr,_("Equilibrating green channels globally\n"));
+ for (cnt=val[0]=val[1]=row=0; row < pheight; row+=2)
+ for (col=0; col < pwidth; col+=2){
+ v[0]=BAYER(row+y[1],col+x[1]);
+ v[1]=BAYER(row+y[3],col+x[3]);
+ if((v[0]<sat_max)&&(v[1]<sat_max)) {val[0]+=v[0];val[1]+=v[1];cnt++;}
+ }
+
+ if((cnt!=0)&&(val[0]!=0)&&(val[1]!=0)){
+ v[0]=val[0]/cnt;
+ v[1]=val[1]/cnt;
+
+ f1=(v[0]+v[1])/2;
+ f2=f1/v[1];
+ f1/=v[0];
+
+ for (row=0; row < pheight; row+=2)
+ for (col=0; col < pwidth; col+=2) {
+ if((BAYER(row+y[1],col+x[1])<sat_max)) BAYER(row+y[1],col+x[1])*=f1;
+ if((BAYER(row+y[3],col+x[3])<sat_max)) BAYER(row+y[3],col+x[3])*=f2;
+ }
+ }
+
+ // Green channels local equilibration
+ if(level_greens>1){
+ if (verbose) fprintf (stderr,_("Equilibrating green channels locally\n"));
+
+ bufferG1=(ushort *)calloc(width*height,sizeof *bufferG1);
+ bufferG2=(ushort *)calloc(width*height,sizeof *bufferG2);
+
+ // Bilinear interpolate G1 and G2 in two passes
+ for(row=0;row<pheight-2;row+=2)
+ for(col=0;col<pwidth-2;col+=2){
+ // G1, right, then down
+ offset=(row+y[1])*width+col+x[1];
+ pix=BAYER(row+y[1],col+x[1]);
+ bufferG1[offset]=pix;
+ bufferG1[offset+1]=(pix + BAYER(row+y[1],col+x[1]+2)) >> 1;
+ bufferG1[offset+width]=(pix + BAYER(row+y[1]+2,col+x[1])) >> 1;
+
+ // G2, right, then down
+ offset=(row+y[3])*width+col+x[3];
+ pix=BAYER(row+y[3],col+x[3]);
+ bufferG2[offset]=pix;
+ bufferG2[offset+1]=(pix + BAYER(row+y[3],col+x[3]+2)) >> 1;
+ bufferG2[offset+width]=(pix + BAYER(row+y[3]+2,col+x[3])) >> 1;
+ }
+ for(row=0;row<pheight-2;row+=2)
+ for(col=0;col<pwidth-2;col+=2){
+ // G1, right and down
+ bufferG1[(row+y[1]+1)*width+col+x[1]+1]=(bufferG1[(row+y[1])*width+col+x[1]+1] + bufferG1[(row+y[1]+1)*width+col+x[1]] + bufferG1[(row+y[1])*width+col+x[1]+2] + bufferG1[(row+y[1]+2)*width+col+x[1]]) >> 2;
+
+ // G2, right and down
+ bufferG2[(row+y[3]+1)*width+col+x[3]+1]=(bufferG2[(row+y[3])*width+col+x[3]+1] + bufferG2[(row+y[3]+1)*width+col+x[3]] + bufferG2[(row+y[3])*width+col+x[3]+2] + bufferG2[(row+y[3]+2)*width+col+x[3]]) >> 2;
+ }
+
+ // Perform local equilibration with mean green levels in S*S square cells around each pixel
+ for(row=S+2;row<pheight-S-2;row+=2){
+ // Calculate G1 and G2 mean value around pixel
+ for(col=S+2;col<pwidth-S-2;col+=2){
+ v2[2]=v2[0];
+ v2[3]=v2[1];
+ v2[0]=BAYER(row+y[1],col+x[1]);
+ v2[1]=BAYER(row+y[3],col+x[3]);
+
+ if(col==S+2){
+ // First column, full calculation
+ for(cnt=ival[0]=ival[1]=0,i=row-S;i<row+S;i++){
+ for(offset=i*width+col-S;offset<i*width+col+S;offset++){
+ v[0]=bufferG1[offset];
+ v[1]=bufferG2[offset];
+ if((v[0]<sat_max)&&(v[1]<sat_max)&&DIFF_IN_LIMIT(v[0],v[1],v2[0],v2[1])){
+ cnt++;
+ ival[0]+=v[0];
+ ival[1]+=v[1];
+ }
+ }
+ }
+ }else{
+ // Other column, incremental calculation
+ for(offset=(row-S)*width+col;offset<(row+S)*width+col;offset+=width){
+ // Subtract left column
+ offset2=offset-S-2;
+ v[0]=bufferG1[offset2];
+ v[1]=bufferG2[offset2];
+ if((v[0]<sat_max)&&(v[1]<sat_max)&&DIFF_IN_LIMIT(v[0],v[1],v2[2],v2[3])){
+ cnt--;
+ ival[0]-=v[0];
+ ival[1]-=v[1];
+ }
+ offset2=offset-S-1;
+ v[0]=bufferG1[offset2];
+ v[1]=bufferG2[offset2];
+ if((v[0]<sat_max)&&(v[1]<sat_max)&&DIFF_IN_LIMIT(v[0],v[1],v2[2],v2[3])){
+ cnt--;
+ ival[0]-=v[0];
+ ival[1]-=v[1];
+ }
+ // Add right column
+ offset2=offset+S-2;
+ v[0]=bufferG1[offset2];
+ v[1]=bufferG2[offset2];
+ if((v[0]<sat_max)&&(v[1]<sat_max)&&DIFF_IN_LIMIT(v[0],v[1],v2[0],v2[1])){
+ cnt++;
+ ival[0]+=v[0];
+ ival[1]+=v[1];
+ }
+ offset2=offset+S-1;
+ v[0]=bufferG1[offset2];
+ v[1]=bufferG2[offset2];
+ if((v[0]<sat_max)&&(v[1]<sat_max)&&DIFF_IN_LIMIT(v[0],v[1],v2[0],v2[1])){
+ cnt++;
+ ival[0]+=v[0];
+ ival[1]+=v[1];
+ }
+ }
+ }
+
+ // Check if green channels are saturated for this raw pixel and limit green difference to avoid artifacts
+ // (detected by oluv).
+ if((v2[0]<sat_max)&&(v2[1]<sat_max)){
+ val[0]=(double)ival[0];
+ val[1]=(double)ival[1];
+ if((cnt>1)&&(val[0]>0)&&(val[1]>0)){
+ val[0]/=cnt;
+ val[1]/=cnt;
+ //if((val[0]<sat_max)&&(val[1]<sat_max)&&(abs(val[1]-val[0])<(dif_limit*4))){
+ f1=(val[0]+val[1])/2;
+ f2=f1/val[1];
+ f1/=val[0];
+ BAYER(row+y[1],col+x[1])=(ushort)CLIP((double)BAYER(row+y[1],col+x[1])*f1);
+ BAYER(row+y[3],col+x[3])=(ushort)CLIP((double)BAYER(row+y[3],col+x[3])*f2);
+ //}
+ }
+ }
+ }
+ }
+ if (bufferG1) free (bufferG1);
+ if (bufferG2) free (bufferG2);
+ }
+
+ if (shrinked) {
+ shrink=shrinked;
+ shrink_image();
+ }
+}
+
+void CLASS eq_greens()
+{
+ int row,col,i,c,offset,shrinked;
+ int S=4; // Must be an even number
+ int pix,pixB;
+ ushort *GBuffer;
+ int o[20],p[16];
+ double mA,mB;
+ int nA,nB;
+ int limit,sat;
+ int v[9];
+
+ if (half_size) return;
+ shrinked=0;
+ if (shrink){
+ shrinked=shrink;
+ unshrink_image();
+ }
+
+ o[0]=-width*4-2;
+ o[1]=-width*4;
+ o[2]=-width*4+2;
+ o[3]=-width*2-4;
+ o[4]=-width*2-2;
+ o[5]=-width*2;
+ o[6]=-width*2+2;
+ o[7]=-width+2+4;
+ o[8]=-4;
+ o[9]=-2;
+ o[10]=2;
+ o[11]=4;
+ o[12]=width*2-4;
+ o[13]=width*2-2;
+ o[14]=width*2;
+ o[15]=width*2+2;
+ o[16]=width*2+4;
+ o[17]=width*4-2;
+ o[18]=width*4;
+ o[19]=width*4+2;
+
+ p[0]=-width*3-3;
+ p[1]=-width*3-1;
+ p[2]=-width*3+1;
+ p[3]=-width*3-3;
+ p[4]=-width-3;
+ p[5]=-width-1;
+ p[6]=-width+1;
+ p[7]=-width+3;
+ p[8]=width-3;
+ p[9]=width-1;
+ p[10]=width+1;
+ p[11]=width+3;
+ p[12]=width*3-3;
+ p[13]=width*3-1;
+ p[14]=width*3+1;
+ p[15]=width*3+3;
+
+ limit=maximum/(4*(18-level_cell));
+ sat=maximum - 125;
+
+ // Global equilibration
+ eq_greens_old();
+
+ GBuffer=calloc(width*height,sizeof *GBuffer);
+ merror(GBuffer,"eq_greens");
+
+ // Copy green values into buffer
+ for(row=0;row<height;row++)
+ for(col=1-(FC(row,0) & 1),c=FC(row,col),offset=row*width+col;col<width;col+=2,offset+=2){
+ GBuffer[offset]=image[offset][c];
+ }
+
+ // Median filter green buffer
+ for(row=2;row<height-2;row++)
+ for(col=3-(FC(row,0) & 1),c=FC(row,col),offset=row*width+col;col<width-2;col+=2,offset+=2){
+ v[0]=GBuffer[offset];
+ v[1]=GBuffer[offset+o[4]];
+ v[2]=GBuffer[offset+o[5]];
+ v[3]=GBuffer[offset+o[6]];
+ v[4]=GBuffer[offset+o[9]];
+ v[5]=GBuffer[offset+o[10]];
+ v[6]=GBuffer[offset+o[13]];
+ v[7]=GBuffer[offset+o[14]];
+ v[8]=GBuffer[offset+o[15]];
+ GBuffer[offset]=(ushort)CLIP(median(v,9));
+ }
+
+ // Local equilibration
+ for(row=4;row<height-4;row++)
+ for(col=5-(FC(row,0) & 1),c=FC(row,col),offset=row*width+col;col<width-4;col+=2,offset+=2){
+ pix=(int)image[offset][c];
+
+ // Get mean G value of the same channel
+ for(i=nA=mA=0;i<20;i++){
+ pixB=(int)GBuffer[offset+o[i]];
+ if((ABS(pix-pixB)<limit)&&(pixB<sat)){
+ nA++;
+ mA+=(double)pixB;
+ }
+ }
+ if(nA>0) mA/=(double)nA;
+
+ // Get mean G value of the other channel
+ for(i=nB=mB=0;i<16;i++){
+ pixB=(int)GBuffer[offset+p[i]];
+ if((ABS(pix-pixB)<limit)&&(pixB<sat)){
+ nB++;
+ mB+=(double)pixB;
+ }
+ }
+ if(nB>0) mB/=(double)nB;
+
+ if((nA>0)&&(nB>0)&&(mA>0)&&(mB>0)) image[offset][c]=(ushort)CLIP((double)image[offset][c]*(mA+mB)/(2.0*mA));
+ }
+
+ free (GBuffer);
+ if (shrinked) {
+ shrink=shrinked;
+ shrink_image();
+ }
+}
+
void CLASS border_interpolate (int border)
{
unsigned row, col, y, x, f, c, sum[8];
@@ -8464,6 +8846,7 @@
puts(_("-D Document mode without scaling (totally raw)"));
puts(_("-j Don't stretch or rotate raw pixels"));
puts(_("-W Don't automatically brighten the image"));
+ puts(_("-l [0-2] Equalize green channels (0=none, 1=global, 2=global+local)"));
puts(_("-b <num> Adjust brightness (default = 1.0)"));
puts(_("-g <p ts> Set custom gamma curve (default = 2.222 4.5)"));
puts(_("-q [0-3] Set the interpolation quality"));
@@ -8480,8 +8863,8 @@
argv[argc] = "";
for (arg=1; (((opm = argv[arg][0]) - 2) | 2) == '+'; ) {
opt = argv[arg++][1];
- if ((cp = (char *) strchr (sp="nbrkStqmHACg", opt)))
- for (i=0; i < "114111111422"[cp-sp]-'0'; i++)
+ if ((cp = (char *) strchr (sp="nbrkStqmHACgl", opt)))
+ for (i=0; i < "1141111114221"[cp-sp]-'0'; i++)
if (!isdigit(argv[arg+i][0])) {
fprintf (stderr,_("Non-numeric argument to \"-%c\"\n"), opt);
return 1;
@@ -8532,6 +8915,7 @@
case 'd': document_mode = 1 + (opt == 'D');
case 'j': use_fuji_rotate = 0; break;
case 'W': no_auto_bright = 1; break;
+ case 'l': level_greens = atoi(argv[arg++]); break;
case 'T': output_tiff = 1; break;
case '4': gamm[0] = gamm[1] =
no_auto_bright = 1;
@@ -8714,6 +9098,7 @@
#ifdef COLORCHECK
colorcheck();
#endif
+ if (level_greens && colors < 4 && !is_foveon) eq_greens();
if (is_foveon && !document_mode) foveon_interpolate();
if (!is_foveon && document_mode < 2) scale_colors();
pre_interpolate();
------------------------------------------------------------------------------
Download Intel® Parallel Studio Eval
Try the new software tools for yourself. Speed compiling, find bugs
proactively, and fine-tune applications for parallel performance.
See why Intel Parallel Studio got high marks during beta.
http://p.sf.net/sfu/intel-sw-dev
_______________________________________________
ufraw-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/ufraw-devel