diff --git a/rtdata/languages/default b/rtdata/languages/default index f1b84ed4a..9ad5e57e3 100644 --- a/rtdata/languages/default +++ b/rtdata/languages/default @@ -476,11 +476,11 @@ MAIN_TOOLTIP_HIDEHP;Show/hide the left panel (including the history) l MAIN_TOOLTIP_INDCLIPPEDH;Clipped highlight indication (Key <) MAIN_TOOLTIP_INDCLIPPEDS;Clipped shadow indication (Key >) MAIN_TOOLTIP_PREFERENCES;Set preferences -MAIN_TOOLTIP_PREVIEWB;Preview Blue channel -MAIN_TOOLTIP_PREVIEWFOCUSMASK;Preview Focus Mask -MAIN_TOOLTIP_PREVIEWG;Preview Green channel -MAIN_TOOLTIP_PREVIEWL;Preview Luminosity\n0.299*R + 0.587*G + 0.114*B -MAIN_TOOLTIP_PREVIEWR;Preview Red channel +MAIN_TOOLTIP_PREVIEWB;Preview Blue channel\n-b +MAIN_TOOLTIP_PREVIEWFOCUSMASK;Preview Focus Mask (beta) Shift-F\nMore accurate on images with shallow depth of field, low noise and at higher zoom levels\nTo improve detection accuracy for higher noise images evaluate at smaller zoom 10-30% +MAIN_TOOLTIP_PREVIEWG;Preview Green channel\n-g +MAIN_TOOLTIP_PREVIEWL;Preview Luminosity\n0.299*R + 0.587*G + 0.114*B\n-v +MAIN_TOOLTIP_PREVIEWR;Preview Red channel\n-r MAIN_TOOLTIP_QINFO;Quick info on the image I MAIN_TOOLTIP_SAVEAS;Save image to a selected folder MAIN_TOOLTIP_SAVE;Save image to the default folder @@ -488,7 +488,7 @@ MAIN_TOOLTIP_SHOWHIDELP1;Show/hide the left panel l MAIN_TOOLTIP_SHOWHIDERP1;Show/hide the right panel Alt-l MAIN_TOOLTIP_SHOWHIDETP1;Show/hide the top panel Shift-l MAIN_TOOLTIP_THRESHOLD;Threshold -MAIN_TOOLTIP_TOGGLE;Toggle before/after view B +MAIN_TOOLTIP_TOGGLE;Toggle before/after view Shift-B NAVIGATOR_B_NA;B = n/a NAVIGATOR_B_VALUE;B = %1 NAVIGATOR_G_NA;G = n/a diff --git a/rtgui/cropwindow.cc b/rtgui/cropwindow.cc index 3875811d0..b426debd8 100644 --- a/rtgui/cropwindow.cc +++ b/rtgui/cropwindow.cc @@ -696,7 +696,7 @@ void CropWindow::expose (Cairo::RefPtr cr) { } #endif - if (showcs || showch || showR || showG || showB || showL /*|| showFocusMask*/) { + if (showcs || showch || showR || showG || showB || showL || showFocusMask) { Glib::RefPtr tmp = cropHandler.cropPixbuf->copy (); guint8* pix = tmp->get_pixels(); guint8* pixWrkSpace = cropHandler.cropPixbuftrue->get_pixels(); @@ -710,63 +710,156 @@ void CropWindow::expose (Cairo::RefPtr cr) { #ifdef _OPENMP #pragma omp parallel for #endif - for (int i=0; iget_height(); i++) { + for (int i=0; iget_height(); i++) { guint8* curr = pix + i*pixRowStride; guint8* currWS = pixWrkSpace + i*pixWSRowStride; int delta; bool changedHL; bool changedSH; for (int j=0; jget_width(); j++) { - // we must compare clippings in working space, since the cropPixbuf is in sRGB, with mon profile + + + if (showFocusMask){ // modulate preview to display focus mask - changedHL=false; - changedSH=false; + //************* + // 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_gradation, focus_coef; + float focus_thresh, 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 - if (showch) { - delta=0; changedHL=false; - if (currWS[0]>=options.highlightThreshold && showclippedR) { delta += 255-currWS[0]; changedHL=true; } - if (currWS[1]>=options.highlightThreshold && showclippedG) { delta += 255-currWS[1]; changedHL=true; } - if (currWS[2]>=options.highlightThreshold && showclippedB) { delta += 255-currWS[2]; changedHL=true; } + //TODO: dynamically determine appropriate values based on image analysis + blur_radius=4;; + focus_thresh=80; - if (changedHL) { - delta *= HighlightFac; - if (showclippedAll) curr[0]=curr[1]=curr[2]=delta; // indicate clipped highlights in gray - else {curr[0]=255; curr[1]=curr[2]=delta;} // indicate clipped highlights in red - } - } - if (showcs) { - delta=0; changedSH=false; + blur_radius2 = blur_radius/4; // Band2 + focus_thresh2 = focus_thresh/2; // Band 2 threshold - if (currWS[0]<=options.shadowThreshold && showclippedR) { delta += currWS[0]; changedSH=true; } - if (currWS[1]<=options.shadowThreshold && showclippedG) { delta += currWS[1]; changedSH=true; } - if (currWS[2]<=options.shadowThreshold && showclippedB) { delta += currWS[2]; changedSH=true; } - - if (changedSH) { - if (showclippedAll) { - delta = 255 - (delta * ShawdowFac); - curr[0]=curr[1]=curr[2]=delta; // indicate clipped shadows in gray + if (j>blur_radius && jget_width()-blur_radius + && i>blur_radius && iget_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 + + 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 += pow(curL,2); + + // Band2 @ blur_radius2 + if (kh>=-blur_radius2 && kh<=blur_radius2 && k>=-blur_radius2 && k<=blur_radius2){ + sum_L2 += curL; + sumsq_L2 += pow(curL,2); + } + } + } + //************* + // averages + kernel_size= pow(2*blur_radius+1,2); // consider -1: Bessel's correction for the sample standard deviation (tried, did not make any visible difference) + kernel_size2= pow(2*blur_radius2+1,2); + avg_L = sum_L/kernel_size; + avg_L2 = sum_L2/kernel_size2; + + + stdDev_L = sqrt(sumsq_L/kernel_size - pow(avg_L,2)); + stdDev_L2 = sqrt(sumsq_L2/kernel_size2 - pow(avg_L2,2)); + + //TODO: try to normalize by average L of the entire (preview) image + + //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 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 { - delta *= ShawdowFac; - curr[2]=255; curr[0]=curr[1]=delta; // indicate clipped shadows in blue - } - } - } //if (showcs) - - // modulate the preview of channels & L; - if (!changedHL && !changedSH){ //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 - if (showL) { //Luminosity - // see http://en.wikipedia.org/wiki/HSL_and_HSV#Lightness for more info - //int L = (int)(0.212671*curr[0]+0.715160*curr[1]+0.072169*curr[2]); - int L = (int)(0.299*curr[0]+0.587*curr[1]+0.114*curr[2]); //Lightness - this matches Luminosity mode in Photoshop CS5 - curr[0]=curr[1]=curr[2]=L; } } - //if (showFocusMask){}; TODO add display of focus mask here + + else { // !showFocusMask + + // we must compare clippings in working space, since the cropPixbuf is in sRGB, with mon profile + + changedHL=false; + changedSH=false; + + if (showch) { + delta=0; changedHL=false; + + if (currWS[0]>=options.highlightThreshold && showclippedR) { delta += 255-currWS[0]; changedHL=true; } + if (currWS[1]>=options.highlightThreshold && showclippedG) { delta += 255-currWS[1]; changedHL=true; } + if (currWS[2]>=options.highlightThreshold && showclippedB) { delta += 255-currWS[2]; changedHL=true; } + + if (changedHL) { + delta *= HighlightFac; + if (showclippedAll) curr[0]=curr[1]=curr[2]=delta; // indicate clipped highlights in gray + else {curr[0]=255; curr[1]=curr[2]=delta;} // indicate clipped highlights in red + } + } + if (showcs) { + delta=0; changedSH=false; + + if (currWS[0]<=options.shadowThreshold && showclippedR) { delta += currWS[0]; changedSH=true; } + if (currWS[1]<=options.shadowThreshold && showclippedG) { delta += currWS[1]; changedSH=true; } + if (currWS[2]<=options.shadowThreshold && showclippedB) { delta += currWS[2]; changedSH=true; } + + if (changedSH) { + if (showclippedAll) { + delta = 255 - (delta * ShawdowFac); + curr[0]=curr[1]=curr[2]=delta; // indicate clipped shadows in gray + } + else { + delta *= ShawdowFac; + curr[2]=255; curr[0]=curr[1]=delta; // indicate clipped shadows in blue + } + } + } //if (showcs) + + // modulate the preview of channels & L; + if (!changedHL && !changedSH){ //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 + if (showL) { //Luminosity + // see http://en.wikipedia.org/wiki/HSL_and_HSV#Lightness for more info + //int L = (int)(0.212671*curr[0]+0.715160*curr[1]+0.072169*curr[2]); + int L = (int)(0.299*curr[0]+0.587*curr[1]+0.114*curr[2]); //Lightness - this matches Luminosity mode in Photoshop CS5 + curr[0]=curr[1]=curr[2]=L; + } + } + } // else (!showFocusMask) + + /* if (showch && (currWS[0]>=options.highlightThreshold || currWS[1]>=options.highlightThreshold || currWS[2]>=options.highlightThreshold)) @@ -782,7 +875,7 @@ void CropWindow::expose (Cairo::RefPtr cr) { 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 diff --git a/rtgui/editorpanel.cc b/rtgui/editorpanel.cc index 34bb4fd8d..9611c06b5 100644 --- a/rtgui/editorpanel.cc +++ b/rtgui/editorpanel.cc @@ -827,7 +827,6 @@ bool EditorPanel::handleShortcutKey (GdkEventKey* event) { case GDK_I: info->set_active (!info->get_active()); return true; - case GDK_b: case GDK_B: beforeAfter->set_active (!beforeAfter->get_active()); return true; @@ -843,6 +842,22 @@ bool EditorPanel::handleShortcutKey (GdkEventKey* event) { iarea->imageArea->zoomPanel->zoom11Clicked(); return true; + case GDK_r: //preview mode Red + iarea->imageArea->previewModePanel->toggleR(); + return true; + case GDK_g: //preview mode Green + iarea->imageArea->previewModePanel->toggleG(); + return true; + case GDK_b: //preview mode Blue + iarea->imageArea->previewModePanel->toggleB(); + return true; + case GDK_v: //preview mode Luminosity + iarea->imageArea->previewModePanel->toggleL(); + return true; + case GDK_F: //preview mode Focus Mask + iarea->imageArea->previewModePanel->toggleFocusMask(); + return true; + case GDK_f: iarea->imageArea->zoomPanel->zoomFitClicked(); return true; @@ -852,7 +867,7 @@ bool EditorPanel::handleShortcutKey (GdkEventKey* event) { case GDK_greater: iarea->imageArea->indClippedPanel->toggleClipped(false); return true; -//TODO add shortcuts for previewModePanel buttons + case GDK_F5: openThm->openDefaultViewer(event->state & GDK_SHIFT_MASK ? 2 : 1); return true; diff --git a/rtgui/previewmodepanel.cc b/rtgui/previewmodepanel.cc index 2c90439c5..640de2e7f 100644 --- a/rtgui/previewmodepanel.cc +++ b/rtgui/previewmodepanel.cc @@ -42,7 +42,6 @@ PreviewModePanel::PreviewModePanel (ImageArea* ia) : imageArea(ia) { previewFocusMask = Gtk::manage (new Gtk::ToggleButton ("F")); previewFocusMask->set_relief(Gtk::RELIEF_NONE); previewFocusMask->set_tooltip_markup (M("MAIN_TOOLTIP_PREVIEWFOCUSMASK")); - previewFocusMask->hide();//TODO re-enable when Focus Mask is developed previewR->set_active (false); previewG->set_active (false); @@ -54,7 +53,7 @@ PreviewModePanel::PreviewModePanel (ImageArea* ia) : imageArea(ia) { pack_start (*previewG, Gtk::PACK_SHRINK, 0); pack_start (*previewB, Gtk::PACK_SHRINK, 0); pack_start (*previewL, Gtk::PACK_SHRINK, 0); - //pack_start (*previewFocusMask, Gtk::PACK_SHRINK, 0); //TODO re-enable when Focus Mask is developed + pack_start (*previewFocusMask, Gtk::PACK_SHRINK, 0); connR = previewR->signal_toggled().connect( sigc::bind(sigc::mem_fun(*this, &PreviewModePanel::buttonToggled),previewR) ); connG = previewG->signal_toggled().connect( sigc::bind(sigc::mem_fun(*this, &PreviewModePanel::buttonToggled),previewG) ); @@ -67,19 +66,19 @@ PreviewModePanel::PreviewModePanel (ImageArea* ia) : imageArea(ia) { //TODO: use functions below for shortcuts void PreviewModePanel::toggleR () { - + previewR->set_active(!previewR->get_active()); } void PreviewModePanel::toggleG () { - + previewG->set_active(!previewG->get_active()); } void PreviewModePanel::toggleB () { - + previewB->set_active(!previewB->get_active()); } void PreviewModePanel::toggleL () { - + previewL->set_active(!previewL->get_active()); } void PreviewModePanel::toggleFocusMask () { - + previewFocusMask->set_active(!previewFocusMask->get_active()); } void PreviewModePanel::buttonToggled (Gtk::ToggleButton* tbpreview) { diff --git a/rtgui/previewmodepanel.h b/rtgui/previewmodepanel.h index d7f10a1ba..cf908c787 100644 --- a/rtgui/previewmodepanel.h +++ b/rtgui/previewmodepanel.h @@ -19,6 +19,7 @@ #define _PREVIEWMODEPANEL_ #include +#include "adjuster.h"//dev class ImageArea; class PreviewModePanel : public Gtk::HBox {