[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