[gimp] Bug 628817 - Optimized Despeckle plug-in



commit 6a99cf7ed4ae925e7dcbe8b26e73da892fa6b879
Author: KermiDT <KermiDT KermiDTs-MacBook local>
Date:   Sun Sep 5 15:38:44 2010 +0200

    Bug 628817 - Optimized Despeckle plug-in
    
    Included optimized version of despeckle plug-in.

 plug-ins/common/despeckle.c |  436 ++++++++++++++++++++++++++-----------------
 1 files changed, 264 insertions(+), 172 deletions(-)
---
diff --git a/plug-ins/common/despeckle.c b/plug-ins/common/despeckle.c
index 773d457..902f816 100644
--- a/plug-ins/common/despeckle.c
+++ b/plug-ins/common/despeckle.c
@@ -4,6 +4,7 @@
  * Despeckle (adaptive median) filter
  *
  * Copyright 1997-1998 Michael Sweet (mike easysw com)
+ * optimized in 2010 by Przemyslaw Zych (kermidt zed gmail com)
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -35,10 +36,10 @@
 
 #define PLUG_IN_PROC     "plug-in-despeckle"
 #define PLUG_IN_BINARY   "despeckle"
-#define PLUG_IN_VERSION  "May 1998"
+#define PLUG_IN_VERSION  "May 2010"
 #define SCALE_WIDTH      100
 #define ENTRY_WIDTH        3
-#define MAX_RADIUS        20
+#define MAX_RADIUS        30
 
 #define FILTER_ADAPTIVE  0x01
 #define FILTER_RECURSIVE 0x02
@@ -48,12 +49,100 @@
 #define black_level      (despeckle_vals[2])    /* Black level */
 #define white_level      (despeckle_vals[3])    /* White level */
 
-#define VALUE_SWAP(a,b)   \
-  { register gdouble t = (a); (a) = (b); (b) = t; }
-#define POINTER_SWAP(a,b) \
-  { register const guchar *t = (a); (a) = (b); (b) = t; }
+/* List that stores pixels falling in to the same luma bucket */
+#define MAX_LIST_ELEMS SQR(2 * MAX_RADIUS + 1)
+typedef struct 
+{
+  guchar* elems[MAX_LIST_ELEMS];
+  gint start;
+  gint count;
+} PixelsList;
+
+static inline 
+void list_add_elem (PixelsList* list, 
+    guchar* elem) 
+{
+  gint pos = list->start + list->count++;
+  list->elems[pos >= MAX_LIST_ELEMS ? pos - MAX_LIST_ELEMS : pos] = elem;
+}
+
+static inline 
+void list_del_elem (PixelsList* list) 
+{
+  list->count--;
+  list->start++;
+  if (list->start >= MAX_LIST_ELEMS)
+    list->start = 0;
+}
+
+static inline guchar* 
+list_get_random_elem (PixelsList* list) 
+{
+  gint pos = list->start + rand () % list->count;
+  if (pos >= MAX_LIST_ELEMS)
+    return list->elems[pos - MAX_LIST_ELEMS];
+  return list->elems[pos];
+}
+
+typedef struct 
+{
+  gint elems[256]; /* Number of pixels that fall into each luma bucket */
+  PixelsList origs[256]; /* Original pixels */
+  gint xmin, ymin, xmax, ymax; /* Source rect */
+} DespeckleHistogram;
+
+/* Number of pixels in actual histogram falling into each category */
+static gint hist0; /* Less than min treshold */
+static gint hist255; /* More than max treshold */
+static gint histrest; /* From min to max */
+DespeckleHistogram histogram;
+
+static inline void 
+histogram_add (DespeckleHistogram* hist, 
+    guchar val, 
+    guchar* orig) 
+{
+  hist->elems[val]++;
+  list_add_elem (&hist->origs[val], orig);
+}
+
+static inline void 
+histogram_remove (DespeckleHistogram* hist, 
+    guchar val) 
+{
+  hist->elems[val]--;
+  list_del_elem (&hist->origs[val]);
+}
+
+static inline void 
+histogram_clean (DespeckleHistogram* hist) 
+{
+  gint i = 0;
+  for (i = 0; i < 256; i++) 
+    {
+      hist->elems[i] = 0;
+      hist->origs[i].count = 0;
+    }
+}
 
+static inline guchar* 
+histogram_get_median (DespeckleHistogram* hist, 
+    guchar* _default) 
+{
+  gint count = histrest;
+  gint i;
+  gint sum = 0;
+
+  if (!count) 
+    return _default;
 
+  count = (count + 1) / 2;
+  i = 0;
+  while ((sum += hist->elems[i]) < count) 
+    i++;
+
+  return list_get_random_elem (&hist->origs[i]);
+}
 
 /*
  * Local functions...
@@ -84,10 +173,6 @@ static void      dialog_recursive_callback (GtkWidget     *widget,
 
 static void      preview_update            (GtkWidget     *preview);
 
-static gint      quick_median_select       (const guchar **p,
-                                            guchar        *i,
-                                            gint           n);
-
 /*
  * Globals...
  */
@@ -614,6 +699,134 @@ dialog_recursive_callback (GtkWidget *widget,
   gimp_preview_invalidate (GIMP_PREVIEW (preview));
 }
 
+static inline void 
+add_val (DespeckleHistogram* hist, 
+         guchar* src, 
+         gint width, 
+         gint bpp, 
+         gint x, 
+         gint y) 
+{
+  gint pos = (x + (y * width)) * bpp;
+  gint value = pixel_luminance (src + pos, bpp);
+
+  if (value > black_level && value < white_level)
+  {
+    histogram_add (hist, value, src + pos);
+    histrest++;
+  }
+  else
+  {
+    if (value <= black_level)
+      hist0++;
+
+    if (value >= white_level)
+      hist255++;
+  }
+}
+
+static inline void 
+del_val (DespeckleHistogram* hist, 
+         guchar* src, 
+         gint width, 
+         gint bpp, 
+         gint x, 
+         gint y) 
+{
+  gint pos = (x + (y * width)) * bpp;
+  gint value = pixel_luminance (src + pos, bpp);
+
+  if (value > black_level && value < white_level)
+  {
+    histogram_remove (hist, value);
+    histrest--;
+  }
+  else
+  {
+    if (value <= black_level)
+      hist0--;
+
+    if (value >= white_level)
+      hist255--;
+  }
+}
+
+static inline void 
+add_vals (DespeckleHistogram* hist, 
+          guchar* src, 
+          gint width, 
+          gint bpp, 
+          gint xmin, 
+          gint ymin, 
+          gint xmax, 
+          gint ymax) 
+{
+  gint x;
+  gint y;
+  if (xmin > xmax) return;
+
+  for (y = ymin; y <= ymax; y++) 
+    {
+      for (x = xmin; x <= xmax; x++) 
+        {
+          add_val (hist, src, width, bpp, x, y);
+        }
+    }
+}
+
+static inline void 
+del_vals (DespeckleHistogram* hist, 
+          guchar* src, 
+          gint width, 
+          gint bpp, 
+          gint xmin, 
+          gint ymin, 
+          gint xmax, 
+          gint ymax) 
+{
+  gint x;
+  gint y;
+  if (xmin > xmax) return;
+
+  for (y = ymin; y <= ymax; y++) 
+    {
+      for (x = xmin; x <= xmax; x++) 
+        {
+          del_val (hist, src, width, bpp, x, y);
+        }
+    }
+}
+
+static inline void 
+update_histogram (DespeckleHistogram* hist, 
+                  guchar *src, 
+                  gint width, 
+                  gint bpp,
+                  gint xmin, 
+                  gint ymin, 
+                  gint xmax, 
+                  gint ymax) 
+{
+  /* assuming that radious of the box can change no more than one pixel in each call */
+  /* assuming that box is moving either right or down */
+
+  del_vals (hist, src, width, bpp, hist->xmin, hist->ymin, xmin - 1, hist->ymax);
+//  del_vals (hist, src, width, bpp, xmax + 1, hist->ymin, hist->xmax, hist->ymax); // only when moving down (for x for y)
+  del_vals (hist, src, width, bpp, xmin, hist->ymin, xmax, ymin - 1);
+  del_vals (hist, src, width, bpp, xmin, ymax + 1, xmax, hist->ymax); // only when moving right (for y for x)
+
+//  add_vals (hist, src, width, bpp, xmin, ymin, hist->xmin - 1, ymax); // only when moving down (for x for y)
+  add_vals (hist, src, width, bpp, hist->xmax + 1, ymin, xmax, ymax);
+  add_vals (hist, src, width, bpp, xmin, ymin, hist->xmax, hist->ymin - 1); // only when moving right (for y for x)
+  add_vals (hist, src, width, bpp, hist->xmin, hist->ymax + 1, hist->xmax, ymax);
+
+  hist->xmin = xmin;
+  hist->ymin = ymin;
+  hist->xmax = xmax;
+  hist->ymax = ymax;
+}
+
+
 static void
 despeckle_median (guchar   *src,
                   guchar   *dst,
@@ -623,84 +836,62 @@ despeckle_median (guchar   *src,
                   gint      radius,
                   gboolean  preview)
 {
-  const guchar **buf;
-  guchar        *ibuf;
-  guint          progress;
-  guint          max_progress;
-  gint           x, y;
-  gint           u, v;
-  gint           diameter;
-  gint           box;
-  gint           pos;
-
+  guint  progress;
+  guint  max_progress;
+  gint   x, y;
+  gint   input_radius = radius;
+  gint   pos;
+  gint   ymin;
+  gint   ymax;
+  gint   xmin;
+  gint   xmax;
+
+  memset (&histogram, 0, sizeof(histogram));
   progress     = 0;
   max_progress = width * height;
 
-  diameter = (2 * radius) + 1;
-  box      = SQR (diameter);
-  buf      = g_new (const guchar *, box);
-  ibuf     = g_new (guchar, box);
-
   if (! preview)
     gimp_progress_init(_("Despeckle"));
 
 
   for (y = 0; y < height; y++)
     {
-      gint ymin = MAX (0, y - radius);
-      gint ymax = MIN (height - 1, y + radius);
+      x = 0;
+      ymin = MAX (0, y - radius);
+      ymax = MIN (height - 1, y + radius);
+      xmin = MAX (0, x - radius);
+      xmax = MIN (width - 1, x + radius);
+      hist0   = 0;
+      histrest = 0;
+      hist255 = 0;
+      histogram_clean (&histogram);
+      histogram.xmin = xmin;
+      histogram.ymin = ymin;
+      histogram.xmax = xmax;
+      histogram.ymax = ymax;
+      add_vals (&histogram, src, width, bpp, histogram.xmin, histogram.ymin, histogram.xmax, histogram.ymax);
 
       for (x = 0; x < width; x++)
         {
-          gint xmin    = MAX (0, x - radius);
-          gint xmax    = MIN (width - 1, x + radius);
-          gint hist0   = 0;
-          gint hist255 = 0;
-          gint med     = -1;
+          const guchar *pixel;
+          ymin = MAX (0, y - radius); /* update ymin, ymax when radius changed (FILTER_ADAPTIVE) */
+          ymax = MIN (height - 1, y + radius);
+          xmin = MAX (0, x - radius);
+          xmax = MIN (width - 1, x + radius);
 
-          for (v = ymin; v <= ymax; v++)
-            {
-              for (u = xmin; u <= xmax; u++)
-                {
-                  gint value;
-
-                  pos = (u + (v * width)) * bpp;
-                  value = pixel_luminance (src + pos, bpp);
-
-                  if (value > black_level && value < white_level)
-                    {
-                      med++;
-                      buf[med]  = src + pos;
-                      ibuf[med] = value;
-                    }
-                  else
-                    {
-                      if (value <= black_level)
-                        hist0++;
-
-                      if (value >= white_level)
-                        hist255++;
-                    }
-                }
-             }
+          update_histogram (&histogram, src, width, bpp, xmin, ymin, xmax, ymax);
 
-           pos = (x + (y * width)) * bpp;
+          pos = (x + (y * width)) * bpp;
+          pixel = histogram_get_median (&histogram, src + pos);
 
-           if (med < 1)
-             {
-               pixel_copy (dst + pos, src + pos, bpp);
-             }
-           else
-             {
-               const guchar *pixel;
-
-               pixel = buf[quick_median_select (buf, ibuf, med + 1)];
-
-                if (filter_type & FILTER_RECURSIVE)
-                  pixel_copy (src + pos, pixel, bpp);
+          if (filter_type & FILTER_RECURSIVE) 
+            {
+              del_val (&histogram, src, width, bpp, x, y);
+              pixel_copy (src + pos, pixel, bpp);
+              add_val (&histogram, src, width, bpp, x, y);
+            }
 
-               pixel_copy (dst + pos, pixel, bpp);
-             }
+          pixel_copy (dst + pos, pixel, bpp);
 
           /*
            * Check the histogram and adjust the diameter accordingly...
@@ -709,7 +900,7 @@ despeckle_median (guchar   *src,
             {
               if (hist0 >= radius || hist255 >= radius)
                 {
-                  if (radius < diameter / 2)
+                  if (radius < input_radius)
                     radius++;
                 }
               else if (radius > 1)
@@ -717,7 +908,6 @@ despeckle_median (guchar   *src,
                   radius--;
                 }
             }
-
         }
 
       progress += width;
@@ -728,102 +918,4 @@ despeckle_median (guchar   *src,
 
   if (! preview)
     gimp_progress_update (1.0);
-
-  g_free (ibuf);
-  g_free (buf);
-}
-
-/*
- * This Quickselect routine is based on the algorithm described in
- * "Numerical recipes in C", Second Edition,
- * Cambridge University Press, 1992, Section 8.5, ISBN 0-521-43108-5
- * This code by Nicolas Devillard - 1998. Public domain.
- *
- * Modified to swap pointers: swap is done by comparing luminance
- * value for the pointer to RGB.
- */
-static gint
-quick_median_select (const guchar **p,
-                     guchar        *i,
-                     gint           n)
-{
-  gint low    = 0;
-  gint high   = n - 1;
-  gint median = (low + high) / 2;
-
-  while (TRUE)
-    {
-      gint middle, ll, hh;
-
-      if (high <= low) /* One element only */
-        return median;
-
-      if (high == low + 1)
-        {
-          /* Two elements only */
-          if (i[low] > i[high])
-            {
-               VALUE_SWAP (i[low], i[high]);
-               POINTER_SWAP (p[low], p[high]);
-            }
-
-          return median;
-        }
-
-      /* Find median of low, middle and high items; swap into position low */
-      middle = (low + high) / 2;
-
-      if (i[middle] > i[high])
-        {
-           VALUE_SWAP (i[middle], i[high]);
-           POINTER_SWAP (p[middle], p[high]);
-        }
-
-      if (i[low] > i[high])
-        {
-           VALUE_SWAP (i[low], i[high]);
-           POINTER_SWAP (p[low], p[high]);
-        }
-
-      if (i[middle] > i[low])
-        {
-          VALUE_SWAP (i[middle], i[low]);
-          POINTER_SWAP (p[middle], p[low]);
-        }
-
-      /* Swap low item (now in position middle) into position (low+1) */
-      VALUE_SWAP (i[middle], i[low+1]);
-      POINTER_SWAP (p[middle], p[low+1]);
-
-      /* Nibble from each end towards middle, swapping items when stuck */
-      ll = low + 1;
-      hh = high;
-
-      while (TRUE)
-        {
-           do ll++;
-           while (i[low] > i[ll]);
-
-           do hh--;
-           while (i[hh]  > i[low]);
-
-           if (hh < ll)
-             break;
-
-           VALUE_SWAP (i[ll], i[hh]);
-           POINTER_SWAP (p[ll], p[hh]);
-        }
-
-      /* Swap middle item (in position low) back into correct position */
-      VALUE_SWAP (i[low], i[hh]);
-      POINTER_SWAP (p[low], p[hh]);
-
-      /* Re-set active partition */
-      if (hh <= median)
-        low = ll;
-
-      if (hh >= median)
-        high = hh - 1;
-    }
 }
-



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]