//////////////////////////////////////////////////////////////// // // Highlight reconstruction // // copyright (c) 2008-2011 Emil Martinec // // // code dated: June 16, 2011 // // hilite_recon.cc is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . // //////////////////////////////////////////////////////////////// //#include "rtengine.h" #include #include #include "curves.h" #include "array2D.h" #include "improcfun.h" #include "rawimagesource.h" //#include "stack1.h" #ifdef _OPENMP #include #endif #include "rt_math.h" #include "rawimagesource.h" #ifdef __SSE2__ #include "sleefsseavx.c" #endif // __SSE2__ #define FOREACHCOLOR for (int c=0; c < ColorCount; c++) //#include "RGBdefringe.cc" namespace rtengine { //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% #if defined( __SSE2__ ) && defined( WIN32 ) __attribute__((force_align_arg_pointer)) void RawImageSource::boxblur2(float** src, float** dst, int H, int W, int box ) #else void RawImageSource::boxblur2(float** src, float** dst, int H, int W, int box ) #endif { array2D temp(W,H); //box blur image channel; box size = 2*box+1 //horizontal blur #ifdef _OPENMP #pragma omp parallel for #endif for (int row = 0; row < H; row++) { int len = box + 1; temp[row][0] = src[row][0]/len; for (int j=1; j<=box; j++) { temp[row][0] += src[row][j]/len; } for (int col=1; col<=box; col++) { temp[row][col] = (temp[row][col-1]*len + src[row][col+box])/(len+1); len ++; } for (int col = box+1; col < W-box; col++) { temp[row][col] = temp[row][col-1] + (src[row][col+box] - src[row][col-box-1])/len; } for (int col=W-box; col temp((W/samp)+ ((W%samp)==0 ? 0 : 1),H); float maxtmp=0.0f; #ifdef _OPENMP #pragma omp parallel #endif { float maxtmpthr = 0; float tempval; #ifdef _OPENMP #pragma omp for #endif //box blur image channel; box size = 2*box+1 //horizontal blur for (int row = 0; row < H; row++) { int len = box + 1; tempval = src[row][0]/len; maxtmpthr = max(maxtmpthr,src[row][0]); for (int j=1; j<=box; j++) { tempval += src[row][j]/len; maxtmpthr = max(maxtmpthr,src[row][j]); } temp[row][0] = tempval; for (int col=1; col<=box; col++) { tempval = (tempval*len + src[row][col+box])/(len+1); if(col%samp == 0) temp[row][col/samp] = tempval; maxtmpthr = max(maxtmpthr,src[row][col]); len ++; } for (int col = box+1; col < W-box; col++) { tempval = tempval + (src[row][col+box] - src[row][col-box-1])/len; if(col%samp == 0) temp[row][col/samp] = tempval; maxtmpthr = max(maxtmpthr,src[row][col]); } for (int col=W-box; colsetProgressStr ("HL reconstruction..."); plistener->setProgress (progress); } int height = H; int width = W; const int range = 2; const int pitch = 4; int hfh = (height-(height%pitch))/pitch; int hfw = (width-(width%pitch))/pitch; static const int numdirs = 4; static const float threshpct = 0.25; static const float fixthreshpct = 0.7; static const float maxpct = 0.95; //%%%%%%%%%%%%%%%%%%%% //for blend algorithm: static const float blendthresh=1.0; const int ColorCount=3; // Transform matrixes rgb>lab and back static const float trans[2][ColorCount][ColorCount] = { { { 1,1,1 }, { 1.7320508,-1.7320508,0 }, { -1,-1,2 } }, { { 1,1,1 }, { 1,-1,1 }, { 1,1,-1 } } }; static const float itrans[2][ColorCount][ColorCount] = { { { 1,0.8660254,-0.5 }, { 1,-0.8660254,-0.5 }, { 1,0,1 } }, { { 1,1,1 }, { 1,-1,1 }, { 1,1,-1 } } }; //%%%%%%%%%%%%%%%%%%%% float max_f[3], thresh[3], fixthresh[3], norm[3]; //float red1, green1, blue1;//diagnostic float chmaxalt[4]={0,0,0,0};//diagnostic //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% //halfsize demosaic multi_array2D hfsize (hfw+1,hfh+1,ARRAY2D_CLEAR_DATA); boxblur_resamp(red,hfsize[0],chmaxalt[0],height,width,range,pitch); if(plistener){ progress += 0.05; plistener->setProgress(progress); } boxblur_resamp(green,hfsize[1],chmaxalt[1],height,width,range,pitch); if(plistener){ progress += 0.05; plistener->setProgress(progress); } boxblur_resamp(blue,hfsize[2],chmaxalt[2],height,width,range,pitch); if(plistener){ progress += 0.05; plistener->setProgress(progress); } //blur image //for (int m=0; m<3; m++) // boxblur2(hfsize[m],hfsizeblur[m],hfh,hfw,3); //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% for (int c=0; c<3; c++) { thresh[c] = chmax[c]*threshpct; fixthresh[c] = chmax[c]*fixthreshpct; max_f[c] = chmax[c]*maxpct;//min(chmax[0],chmax[1],chmax[2])*maxpct; norm[c] = 1.0/(max_f[c]-fixthresh[c]); } float whitept = max(max_f[0],max_f[1],max_f[2]); float clippt = min(max_f[0],max_f[1],max_f[2]); float medpt = max_f[0]+max_f[1]+max_f[2]-whitept-clippt; float blendpt = blendthresh*clippt; float camwb[4]; for (int c=0; c<4; c++) camwb[c]=ri->get_cam_mul(c); multi_array2D channelblur(width,height,ARRAY2D_CLEAR_DATA); multi_array2D hilite_full(width,height,ARRAY2D_CLEAR_DATA); if(plistener){ progress += 0.05; plistener->setProgress(progress); } // blur RGB channels boxblur2(red ,channelblur[0],height,width,4); if(plistener){ progress += 0.05; plistener->setProgress(progress); } boxblur2(green,channelblur[1],height,width,4); if(plistener){ progress += 0.05; plistener->setProgress(progress); } boxblur2(blue ,channelblur[2],height,width,4); if(plistener){ progress += 0.05; plistener->setProgress(progress); } float hipass_sum=0, hipass_norm=0.00; // set up which pixels are clipped or near clipping #ifdef _OPENMP #pragma omp parallel for reduction(+:hipass_sum,hipass_norm) #endif for (int i=0; ithresh[0] || green[i][j]>thresh[1] || blue[i][j]>thresh[2]) && (red[i][j]setProgress(progress); } //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% //blur highlight data boxblur2(hilite_full[4],hilite_full[4],height,width,1); if(plistener){ progress += 0.05; plistener->setProgress(progress); } #ifdef _OPENMP #pragma omp parallel for #endif for (int i=0; i 2*hipass_ave) { //too much variation hilite_full[0][i][j] = hilite_full[1][i][j] = hilite_full[2][i][j] = hilite_full[3][i][j] = 0; continue; } if (hilite_full[4][i][j]>0.00001 && hilite_full[4][i][j]<0.95) { //too near an edge, could risk using CA affected pixels, therefore omit hilite_full[0][i][j] = hilite_full[1][i][j] = hilite_full[2][i][j] = hilite_full[3][i][j] = 0; } } } for (int c=0; c<3; c++) channelblur[c](1,1);//free up some memory multi_array2D hilite(hfw+1,hfh+1,ARRAY2D_CLEAR_DATA); //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% // blur and resample highlight data; range=size of blur, pitch=sample spacing for (int m=0; m<4; m++) { boxblur_resamp(hilite_full[m],hilite[m],chmaxalt[m],height,width,range,pitch); if(plistener){ progress += 0.05; plistener->setProgress(progress); } } for (int c=0; c<5; c++) hilite_full[c](1,1);//free up some memory multi_array2D hilite_dir(hfw,hfh,ARRAY2D_CLEAR_DATA); //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% //blur highlights //for (int m=0; m<4; m++) // boxblur2(hilite[m],hilite[m],hfh,hfw,4); //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% if(plistener){ progress += 0.05; plistener->setProgress(progress); } LUTf invfn(0x10000); for (int i=0; i<0x10000; i++) invfn[i] = 1.0/(1.0+i); //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% //fill gaps in highlight map by directional extension //raster scan from four corners #ifdef _OPENMP #pragma omp parallel for #endif for (int j=1; j0.01) { for (int c=0; c<4; c++) { hilite_dir[c][i][j] = hilite[c][i][j]/hilite[3][i][j]; } } else { for (int c=0; c<4; c++) { hilite_dir[c][i][j] = 0.1*((hilite_dir[0+c][i-2][j-1]+hilite_dir[0+c][i-1][j-1]+hilite_dir[0+c][i][j-1]+hilite_dir[0+c][i+1][j-1]+hilite_dir[0+c][i+2][j-1])/ (hilite_dir[0+3][i-2][j-1]+hilite_dir[0+3][i-1][j-1]+hilite_dir[0+3][i][j-1]+hilite_dir[0+3][i+1][j-1]+hilite_dir[0+3][i+2][j-1]+0.00001)); hilite_dir[4+c][i][j+1] += hilite_dir[c][i][j]; hilite_dir[8+c][i-2][j] += hilite_dir[c][i][j]; hilite_dir[12+c][i+2][j] += hilite_dir[c][i][j]; } } } if(plistener){ progress += 0.05; plistener->setProgress(progress); } #ifdef _OPENMP #pragma omp parallel for #endif for (int j=hfw-2; j>0; j--) for (int i=2; i0.01) { for (int c=0; c<4; c++) { hilite_dir[4+c][i][j] = hilite[c][i][j]/hilite[3][i][j]; } } else { for (int c=0; c<4; c++) { hilite_dir[4+c][i][j] = 0.1*((hilite_dir[4+c][(i-2)][(j+1)]+hilite_dir[4+c][(i-1)][(j+1)]+hilite_dir[4+c][(i)][(j+1)]+hilite_dir[4+c][(i+1)][(j+1)]+hilite_dir[4+c][(i+2)][(j+1)])/ (hilite_dir[4+3][(i-2)][(j+1)]+hilite_dir[4+3][(i-1)][(j+1)]+hilite_dir[4+3][(i)][(j+1)]+hilite_dir[4+3][(i+1)][(j+1)]+hilite_dir[4+3][(i+2)][(j+1)]+0.00001)); hilite_dir[8+c][i-2][j] += hilite_dir[4+c][i][j]; hilite_dir[12+c][i+2][j] += hilite_dir[4+c][i][j]; } } } if(plistener){ progress += 0.05; plistener->setProgress(progress); } #ifdef _OPENMP #pragma omp parallel for #endif for (int i=1; i0.01) { for (int c=0; c<4; c++) { hilite_dir[8+c][i][j] = hilite[c][i][j]/hilite[3][i][j]; } } else { for (int c=0; c<4; c++) { hilite_dir[8+c][i][j] = 0.1*((hilite_dir[8+c][i-1][j-2]+hilite_dir[8+c][i-1][j-1]+hilite_dir[8+c][i-1][j]+hilite_dir[8+c][i-1][j+1]+hilite_dir[8+c][i-1][j+2])/ (hilite_dir[8+3][i-1][j-2]+hilite_dir[8+3][i-1][j-1]+hilite_dir[8+3][i-1][j]+hilite_dir[8+3][i-1][j+1]+hilite_dir[8+3][i-1][j+2]+0.00001)); hilite_dir[12+c][i+1][j] += hilite_dir[8+c][i][j]; } } } if(plistener){ progress += 0.05; plistener->setProgress(progress); } #ifdef _OPENMP #pragma omp parallel for #endif for (int i=hfh-2; i>0; i--) for (int j=2; j0.01) { for (int c=0; c<4; c++) { hilite_dir[12+c][i][j] = hilite[c][i][j]/hilite[3][i][j]; } } else { for (int c=0; c<4; c++) { hilite_dir[12+c][i][j] = 0.1*((hilite_dir[12+c][(i+1)][(j-2)]+hilite_dir[12+c][(i+1)][(j-1)]+hilite_dir[12+c][(i+1)][(j)]+hilite_dir[12+c][(i+1)][(j+1)]+hilite_dir[12+c][(i+1)][(j+2)])/ (hilite_dir[12+3][(i+1)][(j-2)]+hilite_dir[12+3][(i+1)][(j-1)]+hilite_dir[12+3][(i+1)][(j)]+hilite_dir[12+3][(i+1)][(j+1)]+hilite_dir[12+3][(i+1)][(j+2)]+0.00001)); } } } if(plistener){ progress += 0.05; plistener->setProgress(progress); } #ifdef _OPENMP #pragma omp parallel for #endif //fill in edges for (int dir=0; dirsetProgress(progress); } //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% /*for (int m=0; m<4*numdirs; m++) { boxblur2(hilite_dir[m],hilite_dir[m],hfh,hfw,4); }*/ //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% //now we have highlight data extended in various directions //next step is to build back local data by averaging //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% // now reconstruct clipped channels using color ratios //const float Yclip = 0.3333*(max[0] + max[1] + max[2]); //float sumwt=0, counts=0; #ifdef _OPENMP #pragma omp parallel for #endif for (int i=0; i blendpt) rgb_blend[0]= rfrac*rgb[0]+(1-rfrac)*pixel[0]; if (pixel[1] > blendpt) rgb_blend[1]= gfrac*rgb[1]+(1-gfrac)*pixel[1]; if (pixel[2] > blendpt) rgb_blend[2]= bfrac*rgb[2]+(1-bfrac)*pixel[2]; //end of HLRecovery_blend estimation //%%%%%%%%%%%%%%%%%%%%%%% //float pixref[3]={min(Yclip,hfsize[0][i1][j1]),min(Yclip,hfsize[1][i1][j1]),min(Yclip,hfsize[2][i1][j1])}; //there are clipped highlights //first, determine weighted average of unclipped extensions (weighting is by 'hue' proximity) float dirwt, factor, Y; float totwt=0;//0.0003; float clipfix[3]={0,0,0};//={totwt*rgb_blend[0],totwt*rgb_blend[1],totwt*rgb_blend[2]}; for (int dir=0; dir0.5) { dirwt = invfn[65535*(SQR(rgb_blend[0]/Y-hilite_dir[dir*4+0][i1][j1]/Yhi) + SQR(rgb_blend[1]/Y-hilite_dir[dir*4+1][i1][j1]/Yhi) + SQR(rgb_blend[2]/Y-hilite_dir[dir*4+2][i1][j1]/Yhi))]; totwt += dirwt; clipfix[0] += dirwt*hilite_dir[dir*4+0][i1][j1]/(hilite_dir[dir*4+3][i1][j1]+0.00001); clipfix[1] += dirwt*hilite_dir[dir*4+1][i1][j1]/(hilite_dir[dir*4+3][i1][j1]+0.00001); clipfix[2] += dirwt*hilite_dir[dir*4+2][i1][j1]/(hilite_dir[dir*4+3][i1][j1]+0.00001); } } clipfix[0] /= totwt; clipfix[1] /= totwt; clipfix[2] /= totwt; //sumwt += totwt; //counts ++; //now correct clipped channels if (pixel[0]>max_f[0] && pixel[1]>max_f[1] && pixel[2]>max_f[2]) { //all channels clipped float Y = (0.299*clipfix[0] + 0.587*clipfix[1] + 0.114*clipfix[2]); //float Y = (clipfix[0] + clipfix[1] + clipfix[2]); factor = whitept/Y; red[i][j] = clipfix[0]*factor; green[i][j] = clipfix[1]*factor; blue[i][j] = clipfix[2]*factor; } else {//some channels clipped int notclipped[3] = {pixel[0]0.01) { red[i][j] = (red[i][j] + hilite[0][i1][j1])/(1+hilite[3][i1][j1]); green[i][j] = (green[i][j]+ hilite[1][i1][j1])/(1+hilite[3][i1][j1]); blue[i][j] = (blue[i][j] + hilite[2][i1][j1])/(1+hilite[3][i1][j1]); }*/ Y = (0.299 * red[i][j] + 0.587 * green[i][j] + 0.114 * blue[i][j]); if (Y>whitept) { factor = whitept/Y; /*I = (0.596 * red[i][j] - 0.275 * green[i][j] - 0.321 * blue[i][j]); Q = (0.212 * red[i][j] - 0.523 * green[i][j] + 0.311 * blue[i][j]); Y *= factor; I *= factor;//max(0,min(1,(whitept-Y)/(whitept-clippt))); Q *= factor;//max(0,min(1,(whitept-Y)/(whitept-clippt))); red[i][j] = Y + 0.956*I + 0.621*Q; green[i][j] = Y - 0.272*I - 0.647*Q; blue[i][j] = Y - 1.105*I + 1.702*Q;*/ red[i][j] *= factor; green[i][j] *= factor; blue[i][j] *= factor; } } } if(plistener) plistener->setProgress(1.00); //printf("ave wt=%f\n",sumwt/counts); // diagnostic output /*for (int i=0; i