[xiph-commits] r15580 - trunk/gimp-montypak

xiphmont at svn.xiph.org xiphmont at svn.xiph.org
Sun Dec 14 01:46:09 PST 2008


Author: xiphmont
Date: 2008-12-14 01:46:08 -0800 (Sun, 14 Dec 2008)
New Revision: 15580

Added:
   trunk/gimp-montypak/varmean.c
Modified:
   trunk/gimp-montypak/Makefile
   trunk/gimp-montypak/denoise.c
   trunk/gimp-montypak/wavelet.c
Log:
A few fixes to denoise, add the low-light DSLR tailored mode



Modified: trunk/gimp-montypak/Makefile
===================================================================
--- trunk/gimp-montypak/Makefile	2008-12-13 10:54:04 UTC (rev 15579)
+++ trunk/gimp-montypak/Makefile	2008-12-14 09:46:08 UTC (rev 15580)
@@ -49,7 +49,7 @@
 
 montypak: $(TARGETS)
 
-denoise.o: wavelet.c
+denoise.o: wavelet.c varmean.c
 
 denoise:  denoise.o
 	$(LD) $< $(CFLAGS) -o $@ $(LIBS) $(LDF)

Modified: trunk/gimp-montypak/denoise.c
===================================================================
--- trunk/gimp-montypak/denoise.c	2008-12-13 10:54:04 UTC (rev 15579)
+++ trunk/gimp-montypak/denoise.c	2008-12-14 09:46:08 UTC (rev 15580)
@@ -51,6 +51,7 @@
                        GimpParam       **returm_vals);
 
 static void     denoise        (GimpDrawable *drawable);
+static void     denoise_pre    (GimpDrawable *drawable);
 
 static gboolean denoise_dialog (GimpDrawable *drawable);
 static int      denoise_work(int w, int h, int bpp, guchar *buffer, int progress, int(*interrupt)(void));
@@ -78,24 +79,27 @@
   float f2;
   float f3;
   float f4;
-  int isomode;
+  int lowlight;
+  float lowlight_adj;
 } DenoiseParams;
 
 static DenoiseParams denoise_params =
 {
   20, 0, 0,
   0.,0.,0.,0.,
-  0
+  0,0.
 };
 
 static GtkWidget    *preview;
 static GtkObject    *madj[4];
+static GtkObject    *ladj[1];
 static guchar *preview_cache_buffer=NULL;
 static int     preview_cache_x;
 static int     preview_cache_y;
 static int     preview_cache_w;
 static int     preview_cache_h;
 
+static int     variance_median=0;
 
 MAIN ()
 
@@ -116,6 +120,9 @@
     { GIMP_PDB_FLOAT,    "f2",            "Detail adjust"                     },
     { GIMP_PDB_FLOAT,    "f3",            "Mid adjust"                        },
     { GIMP_PDB_FLOAT,    "f4",            "Coarse adjust"                     },
+    { GIMP_PDB_INT32,    "lowlight",      "Low light noise mode"              },
+    { GIMP_PDB_FLOAT,    "lowlight_adj",  "Low light threshold adj"           },
+
   };
 
   gimp_install_procedure (PLUG_IN_PROC,
@@ -142,9 +149,9 @@
      gint             *nreturn_vals,
      GimpParam       **return_vals)
 {
+  static GimpParam   values[1];    /* Return values */
   GimpRunMode        run_mode;  /* Current run mode */
   GimpPDBStatusType  status;    /* Return status */
-  GimpParam         *values;    /* Return values */
   GimpDrawable      *drawable;  /* Current image */
 
   /*
@@ -153,7 +160,6 @@
 
   status   = GIMP_PDB_SUCCESS;
   run_mode = param[0].data.d_int32;
-  values = g_new (GimpParam, 1);
 
   *nreturn_vals = 1;
   *return_vals  = values;
@@ -168,6 +174,9 @@
   drawable = gimp_drawable_get (param[2].data.d_drawable);
   gimp_tile_cache_ntiles (2 * drawable->ntile_cols);
 
+  /* math that has to be done ahead of time */
+  denoise_pre(drawable);
+
   /*
    * See how we will run
    */
@@ -191,7 +200,7 @@
       /*
        * Make sure all the arguments are present...
        */
-      if (nparams != 10)
+      if (nparams != 12)
         status = GIMP_PDB_CALLING_ERROR;
       else{
         denoise_params.filter = param[3].data.d_float;
@@ -201,6 +210,8 @@
         denoise_params.f2 = param[7].data.d_float;
         denoise_params.f3 = param[8].data.d_float;
         denoise_params.f4 = param[9].data.d_float;
+        denoise_params.lowlight = param[10].data.d_int32;
+        denoise_params.lowlight_adj = param[11].data.d_float;
       }
       break;
 
@@ -273,11 +284,6 @@
   bpp    = gimp_drawable_bpp (drawable->drawable_id);
 
   /*
-   * Let the user know what we're doing...
-   */
-  gimp_progress_init( "Denoising");
-
-  /*
    * Setup for filter...
    */
 
@@ -329,6 +335,14 @@
     gimp_preview_invalidate (GIMP_PREVIEW (preview));
 }
 
+static void dialog_lowlight_callback (GtkWidget *widget,
+				      gpointer   data)
+{
+  denoise_params.lowlight = (GTK_TOGGLE_BUTTON (widget)->active);
+  gimp_scale_entry_set_sensitive(ladj[0],denoise_params.lowlight);
+  gimp_preview_invalidate (GIMP_PREVIEW (preview));
+}
+
 static void dump_cache(GtkWidget *widget, gpointer dummy){
   if(!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (widget))){
     if(preview_cache_buffer)
@@ -415,6 +429,38 @@
                     G_CALLBACK (dialog_soft_callback),
                     NULL);
 
+  /* Low-light mode */
+  button = gtk_check_button_new_with_mnemonic ("_Low-light noise mode");
+  gtk_box_pack_start (GTK_BOX (main_vbox), button, FALSE, FALSE, 0);
+  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button),
+                                denoise_params.lowlight);
+  gtk_widget_show (button);
+  g_signal_connect (button, "toggled",
+                    G_CALLBACK (dialog_lowlight_callback),
+                    NULL);
+
+  /* Subadjustments for lowlight mode */
+  table = gtk_table_new (1, 4, FALSE);
+  gtk_table_set_col_spacings (GTK_TABLE (table), 6);
+  gtk_table_set_col_spacing (GTK_TABLE (table), 0, 20);
+  gtk_box_pack_start (GTK_BOX (main_vbox), table, FALSE, FALSE, 0);
+  gtk_widget_show (table);
+
+  /* low light threshold adj */
+  ladj[0] = adj = gimp_scale_entry_new (GTK_TABLE (table), 1, 0,
+				       "_Threshold adjust:", 300, 0,
+					denoise_params.lowlight_adj,
+					-100, +100, 1, 10, 0,
+					TRUE, 0, 0,
+					NULL, NULL);
+  g_signal_connect (adj, "value-changed",
+                    G_CALLBACK (gimp_float_adjustment_update),
+                    &denoise_params.lowlight_adj);
+  g_signal_connect_swapped (adj, "value-changed",
+                            G_CALLBACK (gimp_preview_invalidate),
+                            preview);
+  gimp_scale_entry_set_sensitive(ladj[0],denoise_params.lowlight);
+
   /* multiscale adjust select */
   button = gtk_check_button_new_with_mnemonic ("Multiscale _adjustment");
   gtk_box_pack_start (GTK_BOX (main_vbox), button, FALSE, FALSE, 0);
@@ -425,7 +471,7 @@
                     G_CALLBACK (dialog_multiscale_callback),
                     NULL);
 
-  /* Subadjustments for autoclean */
+  /* Subadjustments for multiscale */
   table = gtk_table_new (4, 4, FALSE);
   gtk_table_set_col_spacings (GTK_TABLE (table), 6);
   gtk_table_set_col_spacing (GTK_TABLE (table), 0, 20);
@@ -549,7 +595,8 @@
     
     gimp_preview_draw_buffer (GIMP_PREVIEW(preview), preview_cache_buffer, w*bpp);
   }else{
-    g_free(preview_cache_buffer);
+    if(preview_cache_buffer)
+      g_free(preview_cache_buffer);
     preview_cache_buffer=NULL;
   }
 
@@ -587,10 +634,132 @@
   denoise_active_interruptable = 0;
 }
 
+#include "varmean.c"
+#define clamp(x) ((x)<0?0:((x)>255?255:(x)))
+
+static void compute_luma(guchar *buffer, guchar *luma, int width, int height, int planes){
+  int i;
+  switch(planes){
+  case 1:
+    memcpy(luma,buffer,sizeof(*luma)*width*height);
+    break;
+  case 2: 
+    for(i=0;i<width*height;i++)
+      luma[i]=buffer[i<<1];
+    break;
+  case 3:
+    for(i=0;i<width*height;i++)
+      luma[i]=buffer[i*3]*.2126 + buffer[i*3+1]*.7152 + buffer[i*3+2]*.0722;
+    break;
+  case 4:
+    for(i=0;i<width*height;i++)
+      luma[i]=buffer[i*4]*.2126 + buffer[i*4+1]*.7152 + buffer[i*4+2]*.0722;
+    break;
+  }
+}
+
+static void denoise_pre(GimpDrawable *drawable){
+  /* find the variance median for the whole image, not just preview, not just the selection */
+
+  GimpPixelRgn  rgn;
+  gint          w = gimp_drawable_width(drawable->drawable_id);
+  gint          h = gimp_drawable_height(drawable->drawable_id);
+  gint          bpp = gimp_drawable_bpp (drawable->drawable_id);
+  guchar       *buffer;
+  guchar       *luma;
+  double       *v;
+  long          d[256];
+  int           i,a,a2,med;
+
+  gimp_pixel_rgn_init (&rgn, drawable,
+                       0, 0, w, h, FALSE, FALSE);
+  buffer = g_new (guchar, w * h * bpp);
+  luma = g_new (guchar, w * h);
+  gimp_pixel_rgn_get_rect (&rgn, buffer, 0, 0, w, h);
+  compute_luma(buffer,luma,w,h,bpp);
+  g_free(buffer);
+
+  /* collect var/mean on the luma plane */
+  v = g_new(double,w*h);
+  collect_var(luma, v, NULL, w, h, 5);
+  g_free(luma);
+
+  a=0,a2=0;
+  memset(d,0,sizeof(d));
+    
+  for(i=0;i<w*h;i++){
+    int val = clamp(sqrt(v[i]));
+    d[val]++;
+  }
+  g_free(v);
+
+  for(i=0;i<256;i++)
+    a+=d[i];
+    
+  variance_median=256;
+  for(i=0;i<256;i++){
+    a2+=d[i];
+    if(a2>=a*.5){
+      variance_median=i+1;
+      break;
+    }
+  }
+}
+
 static int denoise_work(int width, int height, int planes, guchar *buffer, int pr, int(*check)(void)){
   int i;
   double T[16];
+  guchar *mask=NULL;
 
+  if(denoise_params.lowlight){
+    double l = denoise_params.lowlight_adj*.01;
+    int med = variance_median*8;
+    if(pr)gimp_progress_init( "Masking luma...");
+
+    mask = g_new(guchar,width*height);
+    compute_luma(buffer,mask,width,height,planes);
+
+    if(l>0){
+      med += (255-med)*l;
+    }else{
+      med += med*l;
+    }
+
+    if(check && check()){
+      g_free(mask);
+      return 1;
+    } 
+
+    /* adjust luma into mask form */
+    for(i=0;i<width*height;i++){
+      if(mask[i]<med){
+	mask[i]=255;
+      }else if(mask[i]>=med*2){
+	mask[i]=0;
+      }else{
+	double del = (double)(mask[i]-med)/med;
+	mask[i]=255-255*del;
+      }
+    }
+
+    if(check && check()){
+      g_free(mask);
+      return 1;
+    } 
+
+    /* smooth the luma plane */
+    for(i=0;i<16;i++)
+      T[i]=10.;
+    if(wavelet_filter(width, height, 1, mask, NULL, pr, T, denoise_params.soft, check)){
+      g_free(mask);
+      return 1;
+    }
+
+    if(pr)gimp_progress_end();
+  }
+
+  if(pr)gimp_progress_init( "Denoising...");
+  
   for(i=0;i<16;i++)
     T[i]=denoise_params.filter*.2;
   
@@ -602,7 +771,14 @@
       T[i]*=(denoise_params.f4+100)*.01;
   }
   
-  return wavelet_filter(width, height, planes, buffer, pr, T, denoise_params.soft, check);
+  if(wavelet_filter(width, height, planes, buffer, mask, pr, T, denoise_params.soft, check)){
+    if(mask)g_free(mask);
+    return 1;
+  }
 
+  if(mask)
+    g_free(mask);
+ 
+  return 0;
 }
 

Added: trunk/gimp-montypak/varmean.c
===================================================================
--- trunk/gimp-montypak/varmean.c	                        (rev 0)
+++ trunk/gimp-montypak/varmean.c	2008-12-14 09:46:08 UTC (rev 15580)
@@ -0,0 +1,146 @@
+/*
+ *   Variance/mean collection helper for GIMP - The GNU Image Manipulation Program
+ *
+ *   Copyright 2008 Monty
+ *
+ *   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
+ *   the Free Software Foundation; either version 2 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, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+static int collect_add_row(guchar *b, int *s, int *ss, int w, int h, int row, int x1, int x2){
+  int i,j;
+  if(row<0||row>=h)return 0;
+  if(x1<0)x1=0;
+  if(x2>w)x2=w;
+  i=row*w+x1;
+  j=row*w+x2;
+  while(i<j){
+    *s+=b[i];
+    *ss+=b[i]*b[i];
+    i++;
+  }
+  return(x2-x1);
+}
+
+static int collect_sub_row(guchar *b, int *s, int *ss, int w, int h, int row, int x1, int x2){
+  int i,j;
+  if(row<0||row>=h)return 0;
+  if(x1<0)x1=0;
+  if(x2>w)x2=w;
+  i=row*w+x1;
+  j=row*w+x2;
+  while(i<j){
+    *s-=b[i];
+    *ss-=b[i]*b[i];
+    i++;
+  }
+  return(x1-x2);
+}
+
+static int collect_add_col(guchar *b, int *s, int *ss, int w, int h, int col, int y1, int y2){
+  int i,j;
+  if(col<0||col>=w)return 0;
+  if(y1<0)y1=0;
+  if(y2>h)y2=h;
+  i=y1*w+col;
+  j=y2*w+col;
+  while(i<j){
+    *s+=b[i];
+    *ss+=b[i]*b[i];
+    i+=w;
+  }
+  return(y2-y1);
+}
+
+static int collect_sub_col(guchar *b, int *s, int *ss, int w, int h, int col, int y1, int y2){
+  int i,j;
+  if(col<0||col>=w)return 0;
+  if(y1<0)y1=0;
+  if(y2>h)y2=h;
+  i=y1*w+col;
+  j=y2*w+col;
+  while(i<j){
+    *s-=b[i];
+    *ss-=b[i]*b[i];
+    i+=w;
+  }
+  return(y1-y2);
+}
+
+static inline void collect_var(guchar *b, double *v, guchar *m, int w, int h, int n){
+  int x,y;
+  int sum=0;
+  int ssum=0;
+  int acc=0;
+  double d = 1;
+
+  /* prime */
+  for(y=0;y<=n;y++)
+    acc+=collect_add_row(b,&sum,&ssum,w,h,y,0,n+1);
+  d=1./acc;
+
+  for(x=0;x<w;){
+    /* even x == increasing y */
+
+    int mean = sum*d;
+    if(m) m[x] = mean;
+    v[x] = ssum*d - mean*mean;
+
+    for(y=1;y<h;y++){
+      int nn=collect_add_row(b,&sum,&ssum,w,h,y+n,x-n,x+n+1) +
+	collect_sub_row(b,&sum,&ssum,w,h,y-n-1,x-n,x+n+1);
+      if(nn){
+	acc += nn;
+	d = 1./acc;
+      }
+      
+      mean = sum*d;
+      if(m) m[y*w+x] = mean;
+      v[y*w+x] = ssum*d - mean*mean;
+
+    }
+
+    x++;
+    if(x>=w)break;
+
+    acc+=collect_add_col(b,&sum,&ssum,w,h,x+n,h-n-1,h);
+    acc+=collect_sub_col(b,&sum,&ssum,w,h,x-n-1,h-n-1,h);
+    d=1./acc;
+
+    /* odd x == decreasing y */
+    mean = sum*d;
+    if(m) m[(h-1)*w+x] = mean;
+    v[(h-1)*w+x] = ssum*d - mean*mean;
+
+    for(y=h-2;y>=0;y--){
+      int nn=collect_add_row(b,&sum,&ssum,w,h,y-n,x-n,x+n+1) +
+	collect_sub_row(b,&sum,&ssum,w,h,y+n+1,x-n,x+n+1);
+      if(nn){
+	acc += nn;
+	d = 1./acc;
+      }
+      
+      mean = sum*d;
+      if(m) m[y*w+x] = mean;
+      v[y*w+x] = ssum*d - mean*mean;
+    }
+    x++;
+    acc+=collect_add_col(b,&sum,&ssum,w,h,x+n,0,n+1);
+    acc+=collect_sub_col(b,&sum,&ssum,w,h,x-n-1,0,n+1);
+    d=1./acc;
+
+  }
+
+}

Modified: trunk/gimp-montypak/wavelet.c
===================================================================
--- trunk/gimp-montypak/wavelet.c	2008-12-13 10:54:04 UTC (rev 15579)
+++ trunk/gimp-montypak/wavelet.c	2008-12-14 09:46:08 UTC (rev 15580)
@@ -716,6 +716,7 @@
     if(check && check())goto cleanup;
     deconvolve_transpose_padded(temp2[i],tempw[i],sf[i&1][2], pt, pc, check);
     if(check && check())goto cleanup;
+    free_m2D(tempw+i);
     tempw[i] = convolve_padded(temp[i], af[i&1][1], pt, pc, check); /* w6 */
     if(check && check())goto cleanup;
   }
@@ -725,6 +726,7 @@
   for(i=0;i<4;i++){
     deconvolve_transpose_padded(temp2[i],tempw[i],sf[i&1][1], pt, pc, check);
     if(check && check())goto cleanup;
+    free_m2D(tempw+i);
     tempw[i] = convolve_padded(temp[i], af[i&1][0], pt, pc, check); /* w5 */
     if(check && check())goto cleanup;
     free_m2D(temp+i);
@@ -739,8 +741,10 @@
     if(check && check())goto cleanup;
     temp[i]   = convolve_transpose_padded(x[i], af[i>>1][1], pt, pc, check);
     if(check && check())goto cleanup;
+    free_m2D(temp2+i);
     temp2[i]  = alloc_m2D(c*2 - FSZ*3 + 2, r);
     if(check && check())goto cleanup;
+    free_m2D(tempw+i);
     tempw[i]  = convolve_padded(temp[i], af[i&1][2], pt, pc, check); /* w4 */
     if(check && check())goto cleanup;
   }
@@ -750,6 +754,7 @@
   for(i=0;i<4;i++){
     deconvolve_transpose_padded(temp2[i],tempw[i],sf[i&1][2], pt, pc, check);
     if(check && check())goto cleanup;
+    free_m2D(tempw+i);
     tempw[i] = convolve_padded(temp[i], af[i&1][1], pt, pc, check); /* w3 */
     if(check && check())goto cleanup;
   }
@@ -759,6 +764,7 @@
   for(i=0;i<4;i++){
     deconvolve_transpose_padded(temp2[i],tempw[i],sf[i&1][1], pt, pc, check);
     if(check && check())goto cleanup;
+    free_m2D(tempw+i);
     tempw[i] = convolve_padded(temp[i], af[i&1][0], pt, pc, check); /* w2 */
     if(check && check())goto cleanup;
     free_m2D(temp+i);
@@ -774,8 +780,10 @@
     temp[i]  = convolve_transpose_padded(x[i], af[i>>1][0], pt, pc, check);
     if(check && check())goto cleanup;
     if(free_input) free_m2D(x+i);
+    free_m2D(temp2+i);
     temp2[i] = alloc_m2D(c*2 - FSZ*3 + 2, r);
     if(check && check())goto cleanup;
+    free_m2D(tempw+i);
     tempw[i] = convolve_padded(temp[i], af[i&1][2], pt, pc, check); /* w1 */
     if(check && check())goto cleanup;
   }
@@ -785,6 +793,7 @@
   for(i=0;i<4;i++){
     deconvolve_transpose_padded(temp2[i],tempw[i],sf[i&1][2], pt, pc, check);
     if(check && check())goto cleanup;
+    free_m2D(tempw+i);
     tempw[i] = convolve_padded(temp[i], af[i&1][1], pt, pc, check); /* w0 */
     if(check && check())goto cleanup;
   }
@@ -802,7 +811,6 @@
     if(check && check())goto cleanup;
   }
 
-  return;
  cleanup:
   for(i=0;i<4;i++){
     if(free_input)free_m2D(x+i);
@@ -885,8 +893,9 @@
   
 }
 
-static int wavelet_filter(int width, int height, int planes, guchar *buffer, int pr, double T[16],int soft,
-			  int (*check)(void)){
+static int wavelet_filter(int width, int height, int planes, guchar *buffer, guchar *mask,
+			  int pr, double T[16],int soft, int (*check)(void)){
+
   int J=4;
   int i,j,p;
   m2D xc={NULL,0,0};
@@ -956,14 +965,28 @@
     if(check && check())goto abort;
 
     /* pull filtered values back out of padded matrix */
-    ptr = buffer+p; 
-    for(i=0;i<height;i++){
-      double *row = yc.x + (i+FSZ-1)*yc.cols + FSZ-1;
-      for(j=0;j<width;j++){
-	int v = row[j]*.5; 
-	if(v>255)v=255;if(v<0)v=0;
-	*ptr = v;
-	ptr+=planes;
+    {
+      int k=0;
+      ptr = buffer+p; 
+      for(i=0;i<height;i++){
+	double *row = yc.x + (i+FSZ-1)*yc.cols + FSZ-1;
+
+	if(mask){
+	  for(j=0;j<width;j++,k++){
+	    double del = mask[k]*.0039215686;
+	    int v = rint(del*row[j]*.5 + (1.-del)* *ptr);
+	    if(v>255)v=255;if(v<0)v=0;
+	    *ptr = v;
+	    ptr+=planes;
+	  }
+	}else{
+	  for(j=0;j<width;j++){
+	    int v = rint(row[j]*.5);
+	    if(v>255)v=255;if(v<0)v=0;
+	    *ptr = v;
+	    ptr+=planes;
+	  }
+	}
       }
     }
     
@@ -975,6 +998,6 @@
  abort:
   free_m2D(&yc);
   free_m2D(&xc);
-  return check();
+  return (check && check());
 }
 



More information about the commits mailing list