On Mon, Nov 09, 2009 at 09:34:05PM -0600, Udi Fuchs wrote:
> > I've designed a despeckling/denoising algorithm to solve issues with
> > images taken under very difficult lighting conditions, deep sea diving
> > in my case. It may be useful in other situations too. The reason to post
> > it is that (after a number of incarnations which have been thrown away
> > by me) I think this one is going to stay.
> 
> I'm not sure about the algorithm, but the code seems almost perfect,
> so I applied the patch as is.
> 
> I'll have to dig up my underwater photos, so for now I only tried it
> with a normal picture. The (50, 0, 5) parameters that you suggest give
> me some artifact. I'm not sure that having window much larger than
> passes makes sense, it make the algorithm very sensitive to the x,y
> orientation of the photo. Or are you trying to get rid of sensor
> banding? Some more background on the algorithm would be interesting.

There's no speckle preselection yet (e.g. by comparing neighbouring
pixels) because there is a category of images which are so badly speckled
that it is a bit difficult to do this right. Before going into speckle
preselection I'd first like to see what chroma denoising can do for
more typical images which only have an unusual amount of noise.
Maybe despeckling will end up as the last option, only for very
problematic cases.

Sensor banding is not at issue. Large windows (over 100 pixels) can have
a positive effect but it depends on how severe the noise speckles are
and if the image/composition allows it: it's a balance. Issues I had
with the algorithm:

-       the algorithm should at least remove the worst part of visually
        offensive noise in very problematic images without losing the
        entire channel.
-       come up with a good idea. A lot of ideas did not work in practice.
-       it must be simple. It is easy to add lots of code or complexity
        obfuscating a tiny working part of the idea.
-       get rid of magical constants
-       minimize the number of controls
-       don't make it too slow.
-       eliminate strange banding artefacts, visible when dealing with
        pure noise.

The last issue was a flaw in the algorithm. The fix was to not advance the
window a constant amount of pixels (10-15 at most before banding occurs)
or any other linear variable amount but to reposition the window using
the minimum index of the corrected hot- and cold pixels. That way it
lingers on problematic parts of the image.

There is room for improvement but it's not easy: sometimes what looks
like an improvement works for exactly one (sub-category of) image(s)
and has a random uninteresting effect on others at best.

> 
> There is a small bug in the "View channel" implementation, since it
> modifies the live histogram. I think the correct place to implement
> this is in preview_draw_area(). On the other hand, this function is
> already a bit overloaded, and it has to be very fast, so I'm not sure.

That's indeed a better place, this pulls the corresponding code completely
into ufraw_preview.c. To avoid testing data->ChannelSelect for every pixel
I used an additional loop. The inner pixel loop in preview_draw_area()
as it currently stands should probably be split (there are better ways
for drawing a rectangle than comparing the coordinates of every pixel).

Changing the channel selection still invalidates the develop phase and
not the display phase because that doesn't work.

> 
> Another thing is that I think the "View channel" should be reset when
> switching out of the "despeckling page".

Fixed too.

-- 
Frank
diff --git a/ufraw.h b/ufraw.h
index bfe334e..cc03ab3 100644
--- a/ufraw.h
+++ b/ufraw.h
@@ -295,7 +295,6 @@ typedef struct ufraw_struct {
     int hotpixels;
     gboolean mark_hotpixels;
     unsigned raw_multiplier;
-    int channel_select;
 } ufraw_data;
 
 extern const conf_data conf_default;
diff --git a/ufraw_preview.c b/ufraw_preview.c
index 7421ae8..5e5feb1 100644
--- a/ufraw_preview.c
+++ b/ufraw_preview.c
@@ -590,6 +590,13 @@ static void preview_draw_area(preview_data *data,
        guint8 *p8 = pixies + yy*rowstride;
        memcpy(p8, displayPixies + yy*displayImage->rowstride,
                width*displayImage->depth);
+       if (data->ChannelSelect >= 0) {
+           guint8 *p = p8;
+           for (xx = 0; xx < width; xx++, p += 3) {
+               guint8 px = p[data->ChannelSelect];
+               p[0] = p[1] = p[2] = px;
+           }
+       }
        guint8 *p8working = workingPixies + yy*workingImage->rowstride;
        for (xx=x; xx<x+width; xx++, p8+=3, p8working+=workingImage->depth) {
            if ( data->SpotDraw &&
@@ -2726,20 +2733,20 @@ static void toggle_button_update(GtkToggleButton 
*button, gboolean *valuep)
            update_scales(data);
        }
     } else if (valuep==(void*)data->ChannelSelectButton) {
-       if (data->UF->channel_select >= -1) {
+       if (data->ChannelSelect >= -1) {
            int i, b = 0;
            while (data->ChannelSelectButton[b] != button)
                ++b;
            if (gtk_toggle_button_get_active(button)) {
                /* ignore generated events, for render_preview() */
-               data->UF->channel_select = -2;
+               data->ChannelSelect = -2;
                for (i = 0; i < data->UF->colors; ++i)
                    if (i != b)
                        gtk_toggle_button_set_active(
                                data->ChannelSelectButton[i], FALSE);
-               data->UF->channel_select = b;
+               data->ChannelSelect = b;
            } else {
-               data->UF->channel_select = -1;
+               data->ChannelSelect = -1;
            }
            ufraw_invalidate_layer(data->UF, ufraw_develop_phase);
            render_preview(data);
@@ -3924,6 +3931,12 @@ static void notebook_switch_page(GtkNotebook *notebook, 
GtkNotebookPage *page,
     preview_data *data = get_preview_data(notebook);
     if (data->FreezeDialog==TRUE) return;
 
+    if (page_num != data->PageNumGray && data->ChannelSelect >= 0) {
+       gtk_toggle_button_set_active(
+               data->ChannelSelectButton[data->ChannelSelect], FALSE);
+       ufraw_invalidate_layer(data->UF, ufraw_develop_phase);
+       render_preview(data);
+    }
     GtkWidget *event_box =
            gtk_widget_get_ancestor(data->PreviewWidget, GTK_TYPE_EVENT_BOX);
     if ( page_num==data->PageNumSpot ||
@@ -4552,6 +4565,7 @@ static void denoise_fill_interface(preview_data *data, 
GtkWidget *page)
                G_CALLBACK(toggle_button_update), data->ChannelSelectButton);
        gtk_table_attach(table, button, 6 + i, 6 + i + 1, 0, 1, 0, 0, 0, 0);
     }
+    data->ChannelSelect = -1;
 
     /* Parameters */
     label = gtk_label_new(_("Window size:"));
diff --git a/ufraw_ufraw.c b/ufraw_ufraw.c
index 2ef7ac5..7f34171 100644
--- a/ufraw_ufraw.c
+++ b/ufraw_ufraw.c
@@ -251,7 +251,6 @@ ufraw_data *ufraw_open(char *filename)
     uf->modifier = NULL;
     uf->lanczos_func = NULL;
 #endif
-    uf->channel_select = -1;
     ufraw_message(UFRAW_SET_LOG, "ufraw_open: w:%d h:%d curvesize:%d\n",
            raw->width, raw->height, raw->toneCurveSize);
 
@@ -735,7 +734,6 @@ void ufraw_developer_prepare(ufraw_data *uf, DeveloperMode 
mode)
 int ufraw_convert_image(ufraw_data *uf)
 {
     uf->mark_hotpixels = FALSE;
-    uf->channel_select = -1;
     ufraw_developer_prepare(uf, file_developer);
     ufraw_convert_image_raw(uf, ufraw_raw_phase);
     ufraw_convert_image_first(uf, ufraw_first_phase);
@@ -1588,14 +1586,6 @@ ufraw_image_data *ufraw_convert_image_area(ufraw_data 
*uf, unsigned saidx,
             for (yy = 0; yy < area.height; yy++, dest += out->rowstride,
                                src += in->rowstride) {
                 develop(dest, (void *)src, uf->developer, 8, area.width);
-               if (uf->channel_select >= 0) {
-                   int xx;
-                   guint8 *p = dest;
-                   for (xx = 0; xx < area.width; xx++, p += out->depth) {
-                       guint8 px = p[uf->channel_select];
-                       p[0] = p[1] = p[2] = px;
-                   }
-               }
             }
             break;
 
diff --git a/ufraw_ui.h b/ufraw_ui.h
index dd238ce..7446fa4 100644
--- a/ufraw_ui.h
+++ b/ufraw_ui.h
@@ -117,6 +117,7 @@ typedef struct {
     GtkAdjustment *DespecklePassesAdj[3];
     GtkToggleButton *DespeckleLockChannelsButton;
     GtkToggleButton *ChannelSelectButton[3];
+    int ChannelSelect;
 #ifdef HAVE_LENSFUN
     /* The GtkEntry with camera maker/model name */
     GtkWidget *CameraModel;
------------------------------------------------------------------------------
Let Crystal Reports handle the reporting - Free Crystal Reports 2008 30-Day 
trial. Simplify your report design, integration and deployment - and focus on 
what you do best, core application coding. Discover what's new with
Crystal Reports now.  http://p.sf.net/sfu/bobj-july
_______________________________________________
ufraw-devel mailing list
ufraw-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/ufraw-devel

Reply via email to