Optimization for Focus Mask, Issue 2319

This commit is contained in:
Ingo
2014-04-02 00:02:31 +02:00
parent 8afc265d78
commit 367d951c5f

View File

@@ -878,10 +878,6 @@ void CropWindow::updateCursor (int x, int y) {
void CropWindow::expose (Cairo::RefPtr<Cairo::Context> cr) {
MyMutex::MyLock lock(cropHandler.cimg);
//MyTime t1, t2, t3, t4;
//t1.set ();
if (decorated)
drawDecoration (cr);
@@ -930,16 +926,14 @@ void CropWindow::expose (Cairo::RefPtr<Cairo::Context> cr) {
imgX = imgAreaX + (imgAreaW-imgW)/2;
imgY = imgAreaY + (imgAreaH-imgH)/2;
exposeVersion++;
// PERFORMANCE BOTTLENECK STARTS HERE
//t3.set ();
bool showcs = iarea->indClippedPanel->showClippedShadows();
bool showch = iarea->indClippedPanel->showClippedHighlights();
bool showR = iarea->previewModePanel->showR(); // will show clipping if R channel is clipped
bool showG = iarea->previewModePanel->showG(); // will show clipping if G channel is clipped
bool showB = iarea->previewModePanel->showB(); // will show clipping if B channel is clipped
bool showL = iarea->previewModePanel->showL(); // will show clipping if L value is clipped
bool showFocusMask = iarea->previewModePanel->showFocusMask();
bool showclippedAny = (!showR && !showG && !showB && !showL); // will show clipping if any of RGB chanels is clipped
const bool showR = iarea->previewModePanel->showR(); // will show clipping if R channel is clipped
const bool showG = iarea->previewModePanel->showG(); // will show clipping if G channel is clipped
const bool showB = iarea->previewModePanel->showB(); // will show clipping if B channel is clipped
const bool showL = iarea->previewModePanel->showL(); // will show clipping if L value is clipped
const bool showFocusMask = iarea->previewModePanel->showFocusMask();
// While the Right-side ALT is pressed, auto-enable highlight and shadow clipping indicators
// TODO: Add linux/MacOS specific functions for alternative
@@ -954,129 +948,243 @@ void CropWindow::expose (Cairo::RefPtr<Cairo::Context> cr) {
guint8* pix = tmp->get_pixels();
guint8* pixWrkSpace = cropHandler.cropPixbuftrue->get_pixels();
int pixRowStride = tmp->get_rowstride ();
int pixWSRowStride = cropHandler.cropPixbuftrue->get_rowstride ();
const int pixRowStride = tmp->get_rowstride ();
const int pixWSRowStride = cropHandler.cropPixbuftrue->get_rowstride ();
const float ShawdowFac = 64 / (options.shadowThreshold+1);
const float HighlightFac = 64 / (256-options.highlightThreshold);
const int bHeight = tmp->get_height();
const int bWidth = tmp->get_width();
#ifdef _OPENMP
#pragma omp parallel for
#endif
for (int i=0; i<tmp->get_height(); i++) {
guint8* curr = pix + i*pixRowStride;
guint8* currWS = pixWrkSpace + i*pixWSRowStride;
if (showFocusMask) { // modulate preview to display focus mask
const int blur_radius2 = 1; // radius of small kernel. 1 => 3x3 kernel
const int blur_dim2 = 2*blur_radius2+1; // dimension of small kernel
const int blur_radius = (blur_dim2*blur_dim2)/2; // radius of big kernel
const float kernel_size = SQR(2.f*blur_radius+1.f); // count of pixels in the big blur kernel
const float rkernel_size = 1.0f/kernel_size; // reciprocal of kernel_size to avoid divisions
const float kernel_size2 = SQR(2.f*blur_radius2+1.f); // count of pixels in the small blur kernel
const float rkernel_size2 = 1.0f/kernel_size2; // reciprocal of kernel_size to avoid divisions
int delta; bool changedHL; bool changedSH;
// aloocate buffer for precalculated Luminance
float* tmpL = (float*)malloc(bHeight * bWidth * sizeof(float) );
// aloocate buffers for sums and sums of squares of small kernel
float* tmpLsum = (float*)malloc((bHeight) * (bWidth) * sizeof(float) );
float* tmpLsumSq = (float*)malloc((bHeight) * (bWidth) * sizeof(float) );
float* tmpstdDev2 = (float*)malloc((bHeight) * (bWidth) * sizeof(float) );
float maxstdDev_L2 = 0.f;
for (int j=0; j<tmp->get_width(); j++) {
#ifdef _OPENMP
#pragma omp parallel
#endif
{
#ifdef _OPENMP
#pragma omp for
#endif
// precalculate Luminance
for(int i=0;i<bHeight;i++) {
guint8* currWS = pixWrkSpace + i*pixWSRowStride;
float* currL = tmpL + i*bWidth;
for(int j=0;j<bWidth;j++) {
*currL = 0.299f*(currWS)[0]+0.587f*(currWS)[1]+0.114f*(currWS)[2];
currL++;
currWS+=3;
}
}
if (showFocusMask){ // modulate preview to display focus mask
float maxthrstdDev_L2 = 0.f;
#ifdef _OPENMP
#pragma omp for nowait
#endif
// precalculate sum and sum of squares of small kernel
for(int i=blur_radius2;i<bHeight-blur_radius2;i++) {
for(int j=blur_radius2;j<bWidth-blur_radius2;j++) {
float sumL = 0.f;
float sumLSqu = 0.f;
for(int kh=-blur_radius2;kh<=blur_radius2;kh++) {
for(int kw=-blur_radius2;kw<=blur_radius2;kw++) {
float curL = tmpL[(i+kh)*bWidth + j + kw];
sumL += curL;
sumLSqu += SQR(curL);
}
}
tmpLsum[i*bWidth+j] = sumL;
tmpLsumSq[i*bWidth+j] = sumLSqu;
float stdDev_L2 = rkernel_size2 * sqrtf(sumLSqu * kernel_size2 - sumL * sumL);
if(stdDev_L2 > maxthrstdDev_L2)
maxthrstdDev_L2 = stdDev_L2;
tmpstdDev2[i*bWidth+j] = stdDev_L2;
}
}
#pragma omp critical
{
if(maxthrstdDev_L2 > maxstdDev_L2)
maxstdDev_L2 = maxthrstdDev_L2;
}
}
//*************
// Copyright (c) 2011 Michael Ezra michael@michaelezra.com
// determine if pixel is in the sharp area of the image using
// standard deviation analysis on two different scales
int blur_radius, blur_radius2;
float curL;
guint8* currIndex;
float avg_L, avg_L2;
float sum_L, sum_L2;
float sumsq_L, sumsq_L2; //sum of deviations squared
float stdDev_L, stdDev_L2;
float focus_thresh;
//float focus_thresh2;
int kernel_size, kernel_size2;// count of pixels in the blur kernel
//float opacity = 0.9;//TODO: implement opacity
//TODO: evaluate effects of altering sampling frequency
const float focus_thresh = 80.f;
maxstdDev_L2 = std::min(maxstdDev_L2,focus_thresh);
const float focus_threshby10 = focus_thresh / 10.f;
#ifdef _OPENMP
#pragma omp parallel for schedule(dynamic,16)
#endif
for (int i=blur_radius+1; i<bHeight-blur_radius; i++) {
guint8* curr = pix + i*pixRowStride + 3*(blur_radius+1);
guint8* currWs = pixWrkSpace + i*pixWSRowStride + 3*(blur_radius+1);
for (int j=blur_radius+1; j<bWidth-blur_radius; j++) {
//*************
// Copyright (c) 2011 Michael Ezra michael@michaelezra.com
// determine if pixel is in the sharp area of the image using
// standard deviation analysis on two different scales
//float focus_thresh2;
//float opacity = 0.9;//TODO: implement opacity
//TODO: evaluate effects of altering sampling frequency
//TODO: dynamically determine appropriate values based on image analysis
blur_radius=4;;
focus_thresh=80;
//TODO: dynamically determine appropriate values based on image analysis
blur_radius2 = blur_radius/4; // Band2
//focus_thresh2 = focus_thresh/2; // Band 2 threshold
// calculate average in +-blur_radius pixels area around the current pixel
// speed up: calculate sum of squares in the same loops
if (j>blur_radius && j<tmp->get_width()-blur_radius
&& i>blur_radius && i<tmp->get_height()-blur_radius){ //stay within image area
// calculate average in +-blur_radius pixels area around the current pixel
// speed up: calculate sum of squares in the same loops
float sum_L = 0.f;
float sumsq_L = 0.f;
// use precalculated values of small kernel to reduce number of iterations
for (int kh=-blur_radius+blur_radius2; kh<=blur_radius-blur_radius2;kh+=blur_dim2){
float* currLsum = &tmpLsum[(i+kh)*bWidth+j-blur_radius+1];
float* currLsumSqu = &tmpLsumSq[(i+kh)*bWidth+j-blur_radius+1];
for (int k=-blur_radius+blur_radius2; k<=blur_radius-blur_radius2;k+=blur_dim2,currLsum+=blur_dim2,currLsumSqu+=blur_dim2){
sum_L += *currLsum;
sumsq_L += *currLsumSqu;
}
}
float sum_L2 = tmpLsum[i*bWidth+j];
float sumsq_L2 = tmpLsumSq[i*bWidth+j];
//*************
// averages
// Optimized formulas to avoid divisions
float stdDev_L = rkernel_size * sqrtf(sumsq_L * kernel_size - sum_L * sum_L);
float stdDev_L2 = tmpstdDev2[i*bWidth+j];
// float stdDev_L2 = rkernel_size2 * sqrtf(sumsq_L2 * kernel_size2 - sum_L2 * sum_L2);
sum_L = 0; sum_L2=0;
sumsq_L = 0; sumsq_L2 = 0;
for (int kh=-blur_radius; kh<=blur_radius;kh++){
for (int k=-blur_radius; k<=blur_radius;k++){
//1 pixel step is equivalent to 3-bytes step
currIndex = currWS+3*k + kh*pixWSRowStride;
curL = 0.299*(currIndex)[0]+0.587*(currIndex)[1]+0.114*(currIndex)[2];
sum_L += curL;
sumsq_L += SQR(curL);
//TODO: try to normalize by average L of the entire (preview) image
// Band2 @ blur_radius2
if (kh>=-blur_radius2 && kh<=blur_radius2 && k>=-blur_radius2 && k<=blur_radius2){
sum_L2 += curL;
sumsq_L2 += SQR(curL);
}
}
}
//*************
// averages
kernel_size= SQR(2*blur_radius+1); // consider -1: Bessel's correction for the sample standard deviation (tried, did not make any visible difference)
kernel_size2= SQR(2*blur_radius2+1);
avg_L = sum_L/kernel_size;
avg_L2 = sum_L2/kernel_size2;
//detection method 1: detect focus in features
//there is no strict condition between stdDev_L and stdDev_L2 themselves
/* if (stdDev_L2>focus_thresh2
&& (stdDev_L <focus_thresh)){ // this excludes false positives due to high contrast edges
curr[1]=255;
curr[0]=0;
curr[2]=0;
stdDev_L = sqrt(sumsq_L/kernel_size - SQR(avg_L));
stdDev_L2 = sqrt(sumsq_L2/kernel_size2 - SQR(avg_L2));
}*/
//TODO: try to normalize by average L of the entire (preview) image
//detection method 2: detect focus in texture
// key point is std deviation on lower scale is higher than for the larger scale
// plus some boundary conditions
if (focus_thresh >= stdDev_L2 //TODO: could vary this to bypass noise better
&& stdDev_L2 > stdDev_L //this is the key to select fine detail within lower contrast on larger scale
&& stdDev_L > focus_threshby10 //options.highlightThreshold
){
// transpareny depends on sdtDev_L2 and maxstdDev_L2
float transparency = 1.f - std::min(stdDev_L2 / maxstdDev_L2, 1.0f) ;
// first row of circle
guint8* currtmp = &curr[0] + (-3*pixRowStride);
guint8* currtmpWS = &currWs[0] + (-3*pixWSRowStride);
for(int jj=-3;jj<=3;jj+=3) {
guint8* currtmpl=currtmp+jj;
guint8* currtmpWSl=currtmpWS+jj;
//transparent green
currtmpl[0] = transparency * currtmpWSl[0];
currtmpl[1] = transparency * currtmpWSl[1] + (1.f-transparency)*255.f;
currtmpl[2] = transparency * currtmpWSl[2];
}
// second row of circle
currtmp = &curr[0] + (-2*pixRowStride);
currtmpWS = &currWs[0] + (-2*pixWSRowStride);
for(int jj=-6;jj<=6;jj+=3) {
guint8* currtmpl=currtmp+jj;
guint8* currtmpWSl=currtmpWS+jj;
//transparent green
currtmpl[0] = transparency * currtmpWSl[0];
currtmpl[1] = transparency * currtmpWSl[1] + (1.f-transparency)*255.f;
currtmpl[2] = transparency * currtmpWSl[2];
}
// three middle row of circle
for(int ii=-1;ii<=1;ii++) {
currtmp = &curr[0] + (ii*pixRowStride);
currtmpWS = &currWs[0] + (ii*pixWSRowStride);
for(int jj=-9;jj<=9;jj+=3) {
guint8* currtmpl=currtmp+jj;
guint8* currtmpWSl=currtmpWS+jj;
//transparent green
currtmpl[0] = transparency * currtmpWSl[0];
currtmpl[1] = transparency * currtmpWSl[1] + (1.f-transparency)*255.f;
currtmpl[2] = transparency * currtmpWSl[2];
}
}
// second last row of circle
currtmp = &curr[0] + (2*pixRowStride);
currtmpWS = &currWs[0] + (2*pixWSRowStride);
for(int jj=-6;jj<=6;jj+=3) {
guint8* currtmpl=currtmp+jj;
guint8* currtmpWSl=currtmpWS+jj;
//transparent green
currtmpl[0] = transparency * currtmpWSl[0];
currtmpl[1] = transparency * currtmpWSl[1] + (1.f-transparency)*255.f;
currtmpl[2] = transparency * currtmpWSl[2];
}
// last row of circle
currtmp = &curr[0] + (3*pixRowStride);
currtmpWS = &currWs[0] + (3*pixWSRowStride);
for(int jj=-3;jj<=3;jj+=3) {
guint8* currtmpl=currtmp+jj;
guint8* currtmpWSl=currtmpWS+jj;
//transparent green
currtmpl[0] = transparency * currtmpWSl[0];
currtmpl[1] = transparency * currtmpWSl[1] + (1.f-transparency)*255.f;
currtmpl[2] = transparency * currtmpWSl[2];
}
}
curr+=3; currWs+=3;
}
}
free(tmpL);
free(tmpLsum);
free(tmpLsumSq);
free(tmpstdDev2);
} else { // !showFocusMask
const int hlThreshold = options.highlightThreshold;
const int shThreshold = options.shadowThreshold;
const float ShawdowFac = 64 / (options.shadowThreshold+1);
const float HighlightFac = 64 / (256-options.highlightThreshold);
const bool showclippedAny = (!showR && !showG && !showB && !showL); // will show clipping if any of RGB chanels is clipped
//detection method 1: detect focus in features
//there is no strict condition between stdDev_L and stdDev_L2 themselves
/* if (stdDev_L2>focus_thresh2
&& (stdDev_L <focus_thresh)){ // this excludes false positives due to high contrast edges
curr[1]=255;
curr[0]=0;
curr[2]=0;
}*/
//detection method 2: detect focus in texture
// key point is std deviation on lower scale is higher than for the larger scale
// plus some boundary conditions
if (focus_thresh > stdDev_L2 //TODO: could vary this to bypass noise better
&& stdDev_L2 > stdDev_L //this is the key to select fine detail within lower contrast on larger scale
&& stdDev_L > focus_thresh/10 //options.highlightThreshold
){
curr[0]=0;
curr[1]=255;
curr[2]=0;
}
}
}
else { // !showFocusMask
#ifdef _OPENMP
#pragma omp parallel for schedule(dynamic,16)
#endif
for (int i=0; i<bHeight; i++) {
guint8* curr = pix + i*pixRowStride;
guint8* currWS = pixWrkSpace + i*pixWSRowStride;
for (int j=0; j<bWidth; j++) {
// we must compare clippings in working space, since the cropPixbuf is in sRGB, with mon profile
changedHL=false;
changedSH=false;
bool changedHL = false;
bool changedSH = false;
int delta = 0;
// for efficiency, pre-calculate currWS_L as it may be needed in both
// if (showch) and if (showcs) branches
int currWS_L;
if (showL && (showch||showcs)) currWS_L = (int)(0.299*currWS[0]+0.587*currWS[1]+0.114*currWS[2]);
if (showL && (showch||showcs))
currWS_L = (int)(0.299f*currWS[0]+0.587f*currWS[1]+0.114f*currWS[2]);
if (showch) {
delta=0; changedHL=false;
if (currWS[0]>=options.highlightThreshold && (showR || showclippedAny)) { delta += 255-currWS[0]; changedHL=true; }
if (currWS[1]>=options.highlightThreshold && (showG || showclippedAny)) { delta += 255-currWS[1]; changedHL=true; }
if (currWS[2]>=options.highlightThreshold && (showB || showclippedAny)) { delta += 255-currWS[2]; changedHL=true; }
if (currWS_L>= options.highlightThreshold && showL) { delta += 255-currWS_L ; changedHL=true; }
if ((showclippedAny || showR) && currWS[0]>=hlThreshold ) { delta += 255-currWS[0]; changedHL=true; }
if ((showclippedAny || showG) && currWS[1]>=hlThreshold ) { delta += 255-currWS[1]; changedHL=true; }
if ((showclippedAny || showB) && currWS[2]>=hlThreshold ) { delta += 255-currWS[2]; changedHL=true; }
if (showL && currWS_L>= hlThreshold ) { delta += 255-currWS_L ; changedHL=true; }
if (changedHL) {
delta *= HighlightFac;
@@ -1085,12 +1193,10 @@ void CropWindow::expose (Cairo::RefPtr<Cairo::Context> cr) {
}
}
if (showcs) {
delta=0; changedSH=false;
if (currWS[0]<=options.shadowThreshold && (showR || showclippedAny)) { delta += currWS[0]; changedSH=true; }
if (currWS[1]<=options.shadowThreshold && (showG || showclippedAny)) { delta += currWS[1]; changedSH=true; }
if (currWS[2]<=options.shadowThreshold && (showB || showclippedAny)) { delta += currWS[2]; changedSH=true; }
if (currWS_L <=options.shadowThreshold && showL) { delta += currWS_L ; changedSH=true; }
if ((showclippedAny || showR) && currWS[0]<=shThreshold ) { delta += currWS[0]; changedSH=true; }
if ((showclippedAny || showG) && currWS[1]<=shThreshold ) { delta += currWS[1]; changedSH=true; }
if ((showclippedAny || showB) && currWS[2]<=shThreshold ) { delta += currWS[2]; changedSH=true; }
if (showL && currWS_L <=shThreshold ) { delta += currWS_L ; changedSH=true; }
if (changedSH) {
if (showclippedAny) {
@@ -1105,7 +1211,7 @@ void CropWindow::expose (Cairo::RefPtr<Cairo::Context> cr) {
} //if (showcs)
// modulate the preview of channels & L;
if (!changedHL && !changedSH){ //This condition allows clipping indicators for RGB channels to remain in color
if (!changedHL && !changedSH && !showclippedAny){ //This condition allows clipping indicators for RGB channels to remain in color
if (showR) curr[1]=curr[2]=curr[0]; //Red channel in grayscale
if (showG) curr[0]=curr[2]=curr[1]; //Green channel in grayscale
if (showB) curr[0]=curr[1]=curr[2]; //Blue channel in grayscale
@@ -1116,9 +1222,6 @@ void CropWindow::expose (Cairo::RefPtr<Cairo::Context> cr) {
curr[0]=curr[1]=curr[2]=L;
}
}
} // else (!showFocusMask)
/*
if (showch && (currWS[0]>=options.highlightThreshold || currWS[1]>=options.highlightThreshold || currWS[2]>=options.highlightThreshold))
@@ -1131,16 +1234,16 @@ void CropWindow::expose (Cairo::RefPtr<Cairo::Context> cr) {
// curr[0] = curr[1] = curr[2] = 255;
*/
curr+=3; currWS+=3;
}
curr+=3; currWS+=3;
}
}
}
//printf("zoomSteps[cropZoom].zoom=%d\n",zoomSteps[cropZoom].zoom);
iarea->get_window()->draw_pixbuf (iarea->get_style()->get_base_gc(Gtk::STATE_NORMAL), tmp, 0, 0, x+imgX, y+imgY, -1, -1, Gdk::RGB_DITHER_NONE, 0, 0);
}
else
iarea->get_window()->draw_pixbuf (iarea->get_style()->get_base_gc(Gtk::STATE_NORMAL), cropHandler.cropPixbuf, 0, 0, x+imgX, y+imgY, -1, -1, Gdk::RGB_DITHER_NONE, 0, 0);
//t4.set ();
// END OF BOTTLENECK
if (cropHandler.cropParams.enabled) {
int cropX, cropY;
cropHandler.getPosition (cropX, cropY);