Add Russian translation provided by Валерий Крувялис <valkru@mail.ru>
[xiph-mirror.git] / gimp-montypak / denoise.c
blobc909514510fd48725e4ff364ccd3691256cc15a3
1 /*
2 * Wavelet Denoise filter for GIMP - The GNU Image Manipulation Program
4 * Copyright (C) 2008 Monty
5 * Code based on research by Crystal Wagner and Prof. Ivan Selesnik,
6 * Polytechnic University, Brooklyn, NY
7 * See: http://taco.poly.edu/selesi/DoubleSoftware/
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
26 #include <string.h>
27 #include <stdlib.h>
29 #include <gtk/gtk.h>
30 #include <libgimp/gimp.h>
31 #include <libgimp/gimpui.h>
34 * Constants...
37 #define PLUG_IN_PROC "plug-in-simple-denoise"
38 #define PLUG_IN_PROC2 "plug-in-selective-denoise"
39 #define PLUG_IN_BINARY "denoise"
40 #define PLUG_IN_VERSION "17 Dec 2008"
43 * Local functions...
46 static void query (void);
47 static void run (const gchar *name,
48 gint nparams,
49 const GimpParam *param,
50 gint *nreturn_vals,
51 GimpParam **returm_vals);
53 static void denoise (GimpDrawable *drawable);
54 static void denoise_pre (GimpDrawable *drawable);
56 static gboolean denoise_dialog (GimpDrawable *drawable);
57 static gboolean denoise_dialog_simple (GimpDrawable *drawable);
58 static void denoise_work(int w, int h, int bpp, guchar *buffer);
60 static void preview_update (GtkWidget *preview, GtkWidget *dialog);
63 * Globals...
66 const GimpPlugInInfo PLUG_IN_INFO =
68 NULL, /* init_proc */
69 NULL, /* quit_proc */
70 query, /* query_proc */
71 run /* run_proc */
74 typedef struct
76 float filterY;
77 float filterC;
78 int soft;
79 int multiscale;
80 float f1;
81 float f2;
82 float f3;
83 float f4;
84 int lowlight;
85 float lowlight_adj;
86 } DenoiseParams;
88 static DenoiseParams denoise_params =
90 20,20, 0,0,
91 0.,0.,0.,0.,
92 0,0.
95 static GtkWidget *preview;
96 static GtkToggleButton *preview_toggle=NULL;
97 static GtkObject *madj[4];
98 static GtkObject *ladj[1];
99 static guchar *preview_cache_blit=NULL;
100 static float *preview_cache_luma=NULL;
101 static float *preview_cache_Pb=NULL;
102 static float *preview_cache_Pr=NULL;
103 static int preview_cache_x;
104 static int preview_cache_y;
105 static int preview_cache_w;
106 static int preview_cache_h;
108 static int variance_median=0;
109 static int pre_run=0;
111 MAIN ()
113 #include "wavelet.c"
115 static void
116 query (void)
118 static const GimpParamDef args[] =
120 { GIMP_PDB_INT32, "run-mode", "Interactive, non-interactive" },
121 { GIMP_PDB_IMAGE, "image", "Input image" },
122 { GIMP_PDB_DRAWABLE, "drawable", "Input drawable" },
123 { GIMP_PDB_FLOAT, "filter", "Denoise filter strength" },
126 static const GimpParamDef args2[] =
128 { GIMP_PDB_INT32, "run-mode", "Interactive, non-interactive" },
129 { GIMP_PDB_IMAGE, "image", "Input image" },
130 { GIMP_PDB_DRAWABLE, "drawable", "Input drawable" },
131 { GIMP_PDB_FLOAT, "filterY", "Denoise filter luma strength" },
132 { GIMP_PDB_FLOAT, "filterC", "Denoise filter chroma strength" },
133 { GIMP_PDB_INT32, "soft", "Use soft thresholding" },
134 { GIMP_PDB_INT32, "multiscale", "Enable multiscale adjustment" },
135 { GIMP_PDB_FLOAT, "f1", "Fine detail adjust" },
136 { GIMP_PDB_FLOAT, "f2", "Detail adjust" },
137 { GIMP_PDB_FLOAT, "f3", "Mid adjust" },
138 { GIMP_PDB_FLOAT, "f4", "Coarse adjust" },
139 { GIMP_PDB_INT32, "lowlight", "Low light noise mode" },
140 { GIMP_PDB_FLOAT, "lowlight_adj", "Low light threshold adj" },
143 gimp_install_procedure (PLUG_IN_PROC,
144 "Simple denoise filter",
145 "This plugin removes random noise at all scales from "
146 "an image; at an extreme produces an airbrush-like"
147 "effect.",
148 "Monty <monty@xiph.org>",
149 "Copyright 2008 by Monty",
150 PLUG_IN_VERSION,
151 "_Denoise...",
152 "RGB*, GRAY*",
153 GIMP_PLUGIN,
154 G_N_ELEMENTS (args), 0,
155 args, NULL);
157 gimp_install_procedure (PLUG_IN_PROC2,
158 "Selective denoise filter",
159 "This plugin selectively removes remove random "
160 "noise from an image; at an extreme produces an airbrush-like"
161 "effect.",
162 "Monty <monty@xiph.org>",
163 "Copyright 2008 by Monty",
164 PLUG_IN_VERSION,
165 "Selecti_ve Denoise...",
166 "RGB*, GRAY*",
167 GIMP_PLUGIN,
168 G_N_ELEMENTS (args2), 0,
169 args2, NULL);
171 gimp_plugin_menu_register (PLUG_IN_PROC, "<Image>/Filters/Montypak");
172 gimp_plugin_menu_register (PLUG_IN_PROC2, "<Image>/Filters/Montypak");
175 static void
176 run (const gchar *name,
177 gint nparams,
178 const GimpParam *param,
179 gint *nreturn_vals,
180 GimpParam **return_vals)
182 static GimpParam values[1]; /* Return values */
183 GimpRunMode run_mode; /* Current run mode */
184 GimpPDBStatusType status; /* Return status */
185 GimpDrawable *drawable; /* Current image */
188 * Initialize parameter data...
191 status = GIMP_PDB_SUCCESS;
192 run_mode = param[0].data.d_int32;
194 *nreturn_vals = 1;
195 *return_vals = values;
197 values[0].type = GIMP_PDB_STATUS;
198 values[0].data.d_status = status;
201 * Get drawable information...
204 drawable = gimp_drawable_get (param[2].data.d_drawable);
205 gimp_tile_cache_ntiles (2 * drawable->ntile_cols);
208 * See how we will run
211 switch (run_mode)
213 case GIMP_RUN_INTERACTIVE:
215 * Possibly retrieve data...
217 gimp_get_data (PLUG_IN_PROC, &denoise_params);
220 * Get information from the dialog...
222 if (strcmp (name, PLUG_IN_PROC) == 0){
223 if (!denoise_dialog_simple (drawable))
224 return;
225 }else{
226 if (!denoise_dialog (drawable))
227 return;
229 break;
231 case GIMP_RUN_NONINTERACTIVE:
233 * Make sure all the arguments are present...
235 if (strcmp (name, PLUG_IN_PROC) == 0){
236 if (nparams != 4)
237 status = GIMP_PDB_CALLING_ERROR;
238 else{
239 memset(&denoise_params,0,sizeof(denoise_params));
240 denoise_params.filterC = denoise_params.filterY = param[3].data.d_float;
242 }else{
243 if (nparams != 13)
244 status = GIMP_PDB_CALLING_ERROR;
245 else{
246 denoise_params.filterY = param[3].data.d_float;
247 denoise_params.filterC = param[4].data.d_float;
248 denoise_params.soft = param[5].data.d_int32;
249 denoise_params.multiscale = param[6].data.d_int32;
250 denoise_params.f1 = param[7].data.d_float;
251 denoise_params.f2 = param[8].data.d_float;
252 denoise_params.f3 = param[9].data.d_float;
253 denoise_params.f4 = param[10].data.d_float;
254 denoise_params.lowlight = param[11].data.d_int32;
255 denoise_params.lowlight_adj = param[12].data.d_float;
258 break;
260 case GIMP_RUN_WITH_LAST_VALS:
262 * Possibly retrieve data...
264 gimp_get_data (PLUG_IN_PROC, &denoise_params);
265 break;
267 default:
268 status = GIMP_PDB_CALLING_ERROR;
269 break;
272 if (status == GIMP_PDB_SUCCESS)
274 if ((gimp_drawable_is_rgb (drawable->drawable_id) ||
275 gimp_drawable_is_gray (drawable->drawable_id)))
278 * Run!
280 denoise (drawable);
283 * If run mode is interactive, flush displays...
285 if (run_mode != GIMP_RUN_NONINTERACTIVE)
286 gimp_displays_flush ();
289 * Store data...
291 if (run_mode == GIMP_RUN_INTERACTIVE)
292 gimp_set_data (PLUG_IN_PROC,
293 &denoise_params, sizeof (DenoiseParams));
295 else
296 status = GIMP_PDB_EXECUTION_ERROR;
300 * Reset the current run status...
302 values[0].data.d_status = status;
305 * Detach from the drawable...
307 gimp_drawable_detach (drawable);
310 static void denoise (GimpDrawable *drawable){
312 GimpPixelRgn src_rgn; /* Source image region */
313 GimpPixelRgn dst_rgn; /* Destination image region */
314 gint x1,x2;
315 gint y1,y2;
316 gint w;
317 gint h;
318 gint bpp;
319 guchar *buffer;
321 gimp_drawable_mask_bounds (drawable->drawable_id,
322 &x1, &y1, &x2, &y2);
324 w = x2 - x1;
325 h = y2 - y1;
326 bpp = gimp_drawable_bpp (drawable->drawable_id);
329 * Setup for filter...
332 gimp_pixel_rgn_init (&src_rgn, drawable,
333 x1, y1, w, h, FALSE, FALSE);
334 gimp_pixel_rgn_init (&dst_rgn, drawable,
335 x1, y1, w, h, TRUE, TRUE);
337 /***************************************/
338 buffer = g_new (guchar, w * h * bpp);
339 gimp_pixel_rgn_get_rect (&src_rgn, buffer, x1, y1, w, h);
340 if(!pre_run) denoise_pre(drawable);
341 denoise_work(w,h,bpp,buffer);
342 gimp_pixel_rgn_set_rect (&dst_rgn, buffer, x1, y1, w, h);
343 /**************************************/
347 * Update the screen...
350 gimp_drawable_flush (drawable);
351 gimp_drawable_merge_shadow (drawable->drawable_id, TRUE);
352 gimp_drawable_update (drawable->drawable_id, x1, y1, w, h);
354 g_free(buffer);
357 static void dialog_filterB_callback (GtkWidget *widget,
358 gpointer data)
360 if(preview_cache_luma)
361 g_free(preview_cache_luma);
362 preview_cache_luma=NULL;
363 if(preview_cache_Pb)
364 g_free(preview_cache_Pb);
365 preview_cache_Pb=NULL;
366 if(preview_cache_Pr)
367 g_free(preview_cache_Pr);
368 preview_cache_Pr=NULL;
369 if(!preview_toggle->active){
370 if(preview_cache_blit)
371 g_free(preview_cache_blit);
372 preview_cache_blit=NULL;
376 static void dialog_filterY_callback (GtkWidget *widget,
377 gpointer data)
379 if(preview_cache_luma)
380 g_free(preview_cache_luma);
381 preview_cache_luma=NULL;
382 if(!preview_toggle->active){
383 if(preview_cache_blit)
384 g_free(preview_cache_blit);
385 preview_cache_blit=NULL;
389 static void dialog_filterC_callback (GtkWidget *widget,
390 gpointer data)
392 if(preview_cache_Pb)
393 g_free(preview_cache_Pb);
394 preview_cache_Pb=NULL;
395 if(preview_cache_Pr)
396 g_free(preview_cache_Pr);
397 preview_cache_Pr=NULL;
398 if(!preview_toggle->active){
399 if(preview_cache_blit)
400 g_free(preview_cache_blit);
401 preview_cache_blit=NULL;
405 static void dialog_soft_callback (GtkWidget *widget,
406 gpointer data)
408 denoise_params.soft = (GTK_TOGGLE_BUTTON (widget)->active);
409 if(denoise_params.filterY>0.)
410 dialog_filterY_callback(widget,data);
411 if(denoise_params.filterC>0.)
412 dialog_filterC_callback(widget,data);
413 if(denoise_params.filterY>0. || denoise_params.filterC>0.)
414 gimp_preview_invalidate (GIMP_PREVIEW (preview));
417 static void dialog_multiscale_callback (GtkWidget *widget,
418 gpointer data)
420 denoise_params.multiscale = (GTK_TOGGLE_BUTTON (widget)->active);
421 gimp_scale_entry_set_sensitive(madj[0],denoise_params.multiscale);
422 gimp_scale_entry_set_sensitive(madj[1],denoise_params.multiscale);
423 gimp_scale_entry_set_sensitive(madj[2],denoise_params.multiscale);
424 gimp_scale_entry_set_sensitive(madj[3],denoise_params.multiscale);
425 if(denoise_params.f1!=0. ||
426 denoise_params.f2!=0. ||
427 denoise_params.f3!=0. ||
428 denoise_params.f4!=0.){
429 if(denoise_params.filterY>0.)
430 dialog_filterY_callback(widget,data);
431 if(denoise_params.filterC>0.)
432 dialog_filterC_callback(widget,data);
433 if(denoise_params.filterY>0. || denoise_params.filterC>0.)
434 gimp_preview_invalidate (GIMP_PREVIEW (preview));
438 static void dialog_multiadj_callback (GtkWidget *widget,
439 gpointer data){
440 if(denoise_params.filterY>0.)
441 dialog_filterY_callback(widget,data);
442 if(denoise_params.filterC>0.)
443 dialog_filterC_callback(widget,data);
444 if(denoise_params.filterY>0. || denoise_params.filterC>0.)
445 gimp_preview_invalidate (GIMP_PREVIEW (preview));
448 static void dialog_mask_callback (GtkWidget *widget,
449 gpointer data)
451 if(!preview_toggle->active){
452 if(preview_cache_blit)
453 g_free(preview_cache_blit);
454 preview_cache_blit=NULL;
458 static void dialog_lowlight_callback (GtkWidget *widget,
459 gpointer data)
461 denoise_params.lowlight = (GTK_TOGGLE_BUTTON (widget)->active);
462 gimp_scale_entry_set_sensitive(ladj[0],denoise_params.lowlight);
463 dialog_mask_callback(widget,data);
464 gimp_preview_invalidate (GIMP_PREVIEW (preview));
467 static void find_preview_toggle(GtkWidget *widget, gpointer dummy){
468 if(!preview_toggle)
469 preview_toggle = GTK_TOGGLE_BUTTON(widget);
472 static void set_busy(GtkWidget *preview, GtkWidget *dialog){
473 GdkDisplay *display = gtk_widget_get_display (dialog);
474 GdkCursor *cursor = gdk_cursor_new_for_display (display, GDK_WATCH);
475 gdk_window_set_cursor(preview->window, cursor);
476 gdk_window_set_cursor(gimp_preview_get_area(GIMP_PREVIEW(preview))->window, cursor);
477 gdk_window_set_cursor(dialog->window, cursor);
478 gdk_cursor_unref(cursor);
481 static gboolean denoise_dialog_simple (GimpDrawable *drawable){
482 GtkWidget *dialog;
483 GtkWidget *main_vbox;
484 GtkWidget *table;
485 GtkWidget *button;
486 GtkObject *adj;
487 gboolean run;
488 gint planes = gimp_drawable_bpp (drawable->drawable_id);
489 gimp_ui_init (PLUG_IN_BINARY, TRUE);
491 dialog = gimp_dialog_new ("Simple Denoise", PLUG_IN_BINARY,
492 NULL, 0,
493 gimp_standard_help_func, PLUG_IN_PROC,
495 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
496 GTK_STOCK_OK, GTK_RESPONSE_OK,
498 NULL);
500 gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
501 GTK_RESPONSE_OK,
502 GTK_RESPONSE_CANCEL,
503 -1);
505 gimp_window_set_transient (GTK_WINDOW (dialog));
507 main_vbox = gtk_vbox_new (FALSE, 12);
508 gtk_container_set_border_width (GTK_CONTAINER (main_vbox), 12);
509 gtk_container_add (GTK_CONTAINER (GTK_DIALOG (dialog)->vbox), main_vbox);
510 gtk_widget_show (main_vbox);
512 preview = gimp_drawable_preview_new (drawable, NULL);
513 gtk_box_pack_start (GTK_BOX (main_vbox), preview, TRUE, TRUE, 0);
514 gtk_widget_show (preview);
516 g_signal_connect (preview, "invalidated",
517 G_CALLBACK (preview_update),
518 dialog);
519 gtk_container_foreach(GTK_CONTAINER(gimp_preview_get_controls(GIMP_PREVIEW(preview))),
520 find_preview_toggle,NULL);
522 /* Filter strength adjust */
523 table = gtk_table_new (1+(planes>2), 3, FALSE);
524 gtk_table_set_col_spacings (GTK_TABLE (table), 6);
525 gtk_box_pack_start (GTK_BOX (main_vbox), table, FALSE, FALSE, 0);
526 gtk_widget_show (table);
528 /* denoise */
529 adj = gimp_scale_entry_new (GTK_TABLE (table), 0, 0,
530 "_Denoise",
531 300, 0,
532 denoise_params.filterY,
533 0, 100, 1, 10, 0,
534 TRUE, 0, 0,
535 NULL, NULL);
536 g_signal_connect (adj, "value-changed",
537 G_CALLBACK (gimp_float_adjustment_update),
538 &denoise_params.filterY);
539 g_signal_connect (adj, "value-changed",
540 G_CALLBACK (gimp_float_adjustment_update),
541 &denoise_params.filterC);
542 g_signal_connect_swapped (adj, "value-changed",
543 G_CALLBACK (gimp_preview_invalidate),
544 preview);
545 g_signal_connect (adj, "value-changed",G_CALLBACK (dialog_filterB_callback),NULL);
547 gtk_widget_show (dialog);
548 run = (gimp_dialog_run (GIMP_DIALOG (dialog)) == GTK_RESPONSE_OK);
550 gtk_widget_destroy (dialog);
552 if(preview_cache_blit)
553 g_free(preview_cache_blit);
554 preview_cache_blit=NULL;
556 if(preview_cache_luma)
557 g_free(preview_cache_luma);
558 preview_cache_luma=NULL;
559 if(preview_cache_Pb)
560 g_free(preview_cache_Pb);
561 preview_cache_Pb=NULL;
562 if(preview_cache_Pr)
563 g_free(preview_cache_Pr);
564 preview_cache_Pr=NULL;
566 return run;
569 static gboolean denoise_dialog (GimpDrawable *drawable){
570 GtkWidget *dialog;
571 GtkWidget *main_vbox;
572 GtkWidget *table;
573 GtkWidget *button;
574 GtkObject *adj;
575 gboolean run;
576 gint planes = gimp_drawable_bpp (drawable->drawable_id);
577 gimp_ui_init (PLUG_IN_BINARY, TRUE);
579 dialog = gimp_dialog_new ("Selective Denoise", PLUG_IN_BINARY,
580 NULL, 0,
581 gimp_standard_help_func, PLUG_IN_PROC,
583 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
584 GTK_STOCK_OK, GTK_RESPONSE_OK,
586 NULL);
588 gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
589 GTK_RESPONSE_OK,
590 GTK_RESPONSE_CANCEL,
591 -1);
593 gimp_window_set_transient (GTK_WINDOW (dialog));
595 main_vbox = gtk_vbox_new (FALSE, 12);
596 gtk_container_set_border_width (GTK_CONTAINER (main_vbox), 12);
597 gtk_container_add (GTK_CONTAINER (GTK_DIALOG (dialog)->vbox), main_vbox);
598 gtk_widget_show (main_vbox);
600 preview = gimp_drawable_preview_new (drawable, NULL);
601 gtk_box_pack_start (GTK_BOX (main_vbox), preview, TRUE, TRUE, 0);
602 gtk_widget_show (preview);
604 g_signal_connect (preview, "invalidated",
605 G_CALLBACK (preview_update),
606 dialog);
607 gtk_container_foreach(GTK_CONTAINER(gimp_preview_get_controls(GIMP_PREVIEW(preview))),
608 find_preview_toggle,NULL);
610 /* Filter strength adjust */
611 table = gtk_table_new (1+(planes>2), 3, FALSE);
612 gtk_table_set_col_spacings (GTK_TABLE (table), 6);
613 gtk_box_pack_start (GTK_BOX (main_vbox), table, FALSE, FALSE, 0);
614 gtk_widget_show (table);
616 /* Luma denoise */
617 adj = gimp_scale_entry_new (GTK_TABLE (table), 0, 0,
618 (planes>2?"Luma _Denoise":"_Denoise"),
619 300, 0,
620 denoise_params.filterY,
621 0, 100, 1, 10, 0,
622 TRUE, 0, 0,
623 NULL, NULL);
624 g_signal_connect (adj, "value-changed",
625 G_CALLBACK (gimp_float_adjustment_update),
626 &denoise_params.filterY);
627 g_signal_connect_swapped (adj, "value-changed",
628 G_CALLBACK (gimp_preview_invalidate),
629 preview);
630 g_signal_connect (adj, "value-changed",G_CALLBACK (dialog_filterY_callback),NULL);
633 /* Chroma filter strength adjust */
634 if(planes>2){
635 adj = gimp_scale_entry_new (GTK_TABLE (table), 0, 1,
636 "_Chroma Denoise", 300, 0,
637 denoise_params.filterC,
638 0, 100, 1, 10, 0,
639 TRUE, 0, 0,
640 NULL, NULL);
641 g_signal_connect (adj, "value-changed",
642 G_CALLBACK (gimp_float_adjustment_update),
643 &denoise_params.filterC);
644 g_signal_connect_swapped (adj, "value-changed",
645 G_CALLBACK (gimp_preview_invalidate),
646 preview);
647 g_signal_connect (adj, "value-changed",G_CALLBACK (dialog_filterC_callback),NULL);
650 /* Threshold shape */
651 button = gtk_check_button_new_with_mnemonic ("So_ft thresholding");
652 gtk_box_pack_start (GTK_BOX (main_vbox), button, FALSE, FALSE, 0);
653 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button),
654 denoise_params.soft);
655 gtk_widget_show (button);
656 g_signal_connect (button, "toggled", G_CALLBACK (dialog_soft_callback), NULL);
658 /* Low-light mode */
659 button = gtk_check_button_new_with_mnemonic ("_Low-light noise mode");
660 gtk_box_pack_start (GTK_BOX (main_vbox), button, FALSE, FALSE, 0);
661 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button),
662 denoise_params.lowlight);
663 gtk_widget_show (button);
664 g_signal_connect (button, "toggled", G_CALLBACK (dialog_lowlight_callback),NULL);
666 /* Subadjustments for lowlight mode */
667 table = gtk_table_new (1, 4, FALSE);
668 gtk_table_set_col_spacings (GTK_TABLE (table), 6);
669 gtk_table_set_col_spacing (GTK_TABLE (table), 0, 20);
670 gtk_box_pack_start (GTK_BOX (main_vbox), table, FALSE, FALSE, 0);
671 gtk_widget_show (table);
673 /* low light threshold adj */
674 ladj[0] = adj = gimp_scale_entry_new (GTK_TABLE (table), 1, 0,
675 "_Threshold adjust:", 300, 0,
676 denoise_params.lowlight_adj,
677 -100, +100, 1, 10, 0,
678 TRUE, 0, 0,
679 NULL, NULL);
680 g_signal_connect (adj, "value-changed",
681 G_CALLBACK (gimp_float_adjustment_update),
682 &denoise_params.lowlight_adj);
683 g_signal_connect_swapped (adj, "value-changed",
684 G_CALLBACK (gimp_preview_invalidate),
685 preview);
686 g_signal_connect (adj, "value-changed",G_CALLBACK (dialog_mask_callback),NULL);
687 gimp_scale_entry_set_sensitive(ladj[0],denoise_params.lowlight);
689 /* multiscale adjust select */
690 button = gtk_check_button_new_with_mnemonic ("Multiscale _adjustment");
691 gtk_box_pack_start (GTK_BOX (main_vbox), button, FALSE, FALSE, 0);
692 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button),
693 denoise_params.multiscale);
694 gtk_widget_show (button);
695 g_signal_connect (button, "toggled",
696 G_CALLBACK (dialog_multiscale_callback),
697 NULL);
699 /* Subadjustments for multiscale */
700 table = gtk_table_new (4, 4, FALSE);
701 gtk_table_set_col_spacings (GTK_TABLE (table), 6);
702 gtk_table_set_col_spacing (GTK_TABLE (table), 0, 20);
703 gtk_box_pack_start (GTK_BOX (main_vbox), table, FALSE, FALSE, 0);
704 gtk_widget_show (table);
706 /* fine detail adjust */
707 madj[0] = adj = gimp_scale_entry_new (GTK_TABLE (table), 1, 0,
708 "_Very fine denoise:", 300, 0,
709 denoise_params.f1,
710 -100, +100, 1, 10, 0,
711 TRUE, 0, 0,
712 NULL, NULL);
713 g_signal_connect (adj, "value-changed",
714 G_CALLBACK (gimp_float_adjustment_update),
715 &denoise_params.f1);
716 g_signal_connect (adj,"value-changed",G_CALLBACK(dialog_multiadj_callback),NULL);
718 /* detail adjust */
719 madj[1] = adj = gimp_scale_entry_new (GTK_TABLE (table), 1, 1,
720 "_Fine denoise:", 300, 0,
721 denoise_params.f2,
722 -100, 100, 1, 10, 0,
723 TRUE, 0, 0,
724 NULL, NULL);
725 g_signal_connect (adj, "value-changed",
726 G_CALLBACK (gimp_float_adjustment_update),
727 &denoise_params.f2);
728 g_signal_connect (adj,"value-changed",G_CALLBACK(dialog_multiadj_callback),NULL);
730 /* mid adjust */
731 madj[2] = adj = gimp_scale_entry_new (GTK_TABLE (table), 1, 2,
732 "_Mid denoise:", 300, 0,
733 denoise_params.f3,
734 -100, 100, 1, 10, 0,
735 TRUE, 0, 0,
736 NULL, NULL);
737 g_signal_connect (adj, "value-changed",
738 G_CALLBACK (gimp_float_adjustment_update),
739 &denoise_params.f3);
740 g_signal_connect (adj,"value-changed",G_CALLBACK(dialog_multiadj_callback),NULL);
742 /* Coarse adjust */
743 madj[3] = adj = gimp_scale_entry_new (GTK_TABLE (table), 1, 3,
744 "_Coarse denoise:", 300, 0,
745 denoise_params.f4,
746 -100, 100, 1, 10, 0,
747 TRUE, 0, 0,
748 NULL, NULL);
749 g_signal_connect (adj, "value-changed",
750 G_CALLBACK (gimp_float_adjustment_update),
751 &denoise_params.f4);
752 g_signal_connect (adj,"value-changed",G_CALLBACK(dialog_multiadj_callback),NULL);
754 gimp_scale_entry_set_sensitive(madj[0],denoise_params.multiscale);
755 gimp_scale_entry_set_sensitive(madj[1],denoise_params.multiscale);
756 gimp_scale_entry_set_sensitive(madj[2],denoise_params.multiscale);
757 gimp_scale_entry_set_sensitive(madj[3],denoise_params.multiscale);
759 gtk_widget_show (dialog);
760 run = (gimp_dialog_run (GIMP_DIALOG (dialog)) == GTK_RESPONSE_OK);
762 gtk_widget_destroy (dialog);
764 if(preview_cache_blit)
765 g_free(preview_cache_blit);
766 preview_cache_blit=NULL;
768 if(preview_cache_luma)
769 g_free(preview_cache_luma);
770 preview_cache_luma=NULL;
771 if(preview_cache_Pb)
772 g_free(preview_cache_Pb);
773 preview_cache_Pb=NULL;
774 if(preview_cache_Pr)
775 g_free(preview_cache_Pr);
776 preview_cache_Pr=NULL;
778 return run;
781 static int denoise_active_interruptable;
782 static int denoise_active_interrupt;
784 static int check_recompute(){
785 while(gtk_events_pending())
786 gtk_main_iteration ();
787 return denoise_active_interrupt;
790 #include "varmean.c"
791 #define clamp(x) ((x)<0?0:((x)>255?255:(x)))
793 #define Kr 0.2126f
794 #define Kb 0.0722f
795 #define Kg (1.f-Kr-Kb)
796 #define Ir (2.f*Kr-2.f)
797 #define Ib (2.f*Kb-2.f)
799 static void compute_luma(guchar *buffer, guchar *luma, int width, int height, int planes){
800 int i;
801 switch(planes){
802 case 1:
803 for(i=0;i<width*height;i++)
804 luma[i]=buffer[i];
805 break;
806 case 2:
807 for(i=0;i<width*height;i++)
808 luma[i]=buffer[i<<1];
809 break;
810 case 3:
811 for(i=0;i<width*height;i++)
812 luma[i]=clamp(rint(buffer[i*3]*Kr +
813 buffer[i*3+1]*Kg +
814 buffer[i*3+2]*Kb));
815 break;
816 case 4:
817 for(i=0;i<width*height;i++)
818 luma[i]=clamp(rint(buffer[i*4]*Kr +
819 buffer[i*4+1]*Kg +
820 buffer[i*4+2]*Kb));
821 break;
825 static int compute_YPbPr(guchar *buffer, float *luma, float *Pb, float *Pr,
826 int width, int height, int planes, int (*check)(void)){
827 int i,j;
828 if(!check && planes>2)
829 gimp_progress_init( "Converting colorspace...");
831 for(j=0;j<height*width;j+=width){
832 switch(planes){
833 case 1:
834 for(i=j;i<j+width;i++)
835 if(luma)luma[i]=buffer[i];
836 break;
837 case 2:
838 for(i=j;i<j+width;i++)
839 if(luma)luma[i]=buffer[i<<1];
840 break;
841 case 3:
842 for(i=j;i<j+width;i++){
843 float Y = buffer[i*3]*Kr + buffer[i*3+1]*Kg + buffer[i*3+2]*Kb;
844 if(luma)luma[i]=Y;
845 if(Pb) Pb[i] = (buffer[i*3+2] - Y) * (.5f / (1.f - Kb));
846 if(Pr) Pr[i] = (buffer[i*3] - Y) * (.5f / (1.f - Kr));
848 if(!check && planes>2)
849 gimp_progress_update((float)i/(width*height));
850 break;
851 case 4:
852 for(i=j;i<j+width;i++){
853 float Y = buffer[i*4]*Kr + buffer[i*4+1]*Kg + buffer[i*4+2]*Kb;
854 if(luma) luma[i]=Y;
855 if(Pb) Pb[i] = (buffer[i*4+2] - Y) * (.5f / (1.f - Kb));
856 if(Pr) Pr[i] = (buffer[i*4] - Y) * (.5f / (1.f - Kr));
858 if(!check && planes>2)
859 gimp_progress_update((float)i/(width*height));
860 break;
863 if(check && check())return 1;
865 if(!check && planes>2)
866 gimp_progress_end();
867 return 0;
870 #define computeR(Y,Pb,Pr) ((Y) - Ir*(Pr))
871 #define computeG(Y,Pb,Pr) ((Y) + (Ir*Kr/Kg)*(Pr) + (Ib*Kb/Kg)*(Pb))
872 #define computeB(Y,Pb,Pr) ((Y) - Ib*(Pb))
874 /* find the variance median for the whole image, not just preview, not just the selection */
875 static void denoise_pre(GimpDrawable *drawable){
877 GimpPixelRgn rgn;
878 gint w = gimp_drawable_width(drawable->drawable_id);
879 gint h = gimp_drawable_height(drawable->drawable_id);
880 gint bpp = gimp_drawable_bpp (drawable->drawable_id);
881 guchar *buffer;
882 guchar *luma;
883 float *v;
884 long d[256];
885 int i,a,a2;
887 pre_run = 1;
888 gimp_pixel_rgn_init (&rgn, drawable,
889 0, 0, w, h, FALSE, FALSE);
890 buffer = g_new (guchar, w * h * bpp);
891 luma = g_new (guchar, w * h);
892 gimp_pixel_rgn_get_rect (&rgn, buffer, 0, 0, w, h);
893 compute_luma(buffer,luma,w,h,bpp);
894 g_free(buffer);
896 /* collect var/mean on the luma plane */
897 v = g_new(float,w*h);
898 collect_var(luma, v, NULL, w, h, 5);
899 g_free(luma);
901 a=0,a2=0;
902 memset(d,0,sizeof(d));
904 for(i=0;i<w*h;i++){
905 int val = clamp(rint(sqrt(v[i])));
906 d[val]++;
908 g_free(v);
910 for(i=0;i<256;i++)
911 a+=d[i];
913 variance_median=256;
914 for(i=0;i<256;i++){
915 a2+=d[i];
916 if(a2>=a*.5){
917 variance_median=i+1;
918 break;
923 static int compute_filter(float *buffer, int w, int h, float f, int (*check)(void), char *m){
924 int i;
925 float T[16];
927 if(!buffer)return 1;
929 if(f>0){
931 for(i=0;i<16;i++)
932 T[i]=f*.2;
934 if(denoise_params.multiscale){
935 T[0]*=(denoise_params.f1+100)*.01;
936 T[1]*=(denoise_params.f2+100)*.01;
937 T[2]*=(denoise_params.f3+100)*.01;
938 for(i=3;i<16;i++)
939 T[i]*=(denoise_params.f4+100)*.01;
941 for(i=0;i<16;i++)
942 if(T[i]!=0.f) break;
943 if(i==16) return 0;
946 if(m)gimp_progress_init(m);
947 i = wavelet_filter(w, h, buffer, (m!=NULL), T, denoise_params.soft, check);
948 if(m)gimp_progress_end();
949 return i;
951 return 0;
954 static int compute_mask(guchar *buffer, float *luma, float *Pb, float *Pr,
955 int w, int h, int p, int(*check)(void)){
956 int i,j;
958 if(!luma) return 1;
959 if(p>2 && (!Pb || !Pr)) return 1;
961 if(denoise_params.lowlight){
962 float l = denoise_params.lowlight_adj*.01;
963 float med = variance_median*8.f;
965 if(!check)gimp_progress_init( "Masking and unconverting...");
967 if(l>0){
968 med += (255.-med)*l;
969 }else{
970 med += med*l;
973 switch(p){
974 case 1:case 2:
975 for(i=0;i<w*h;i+=w){
976 for(j=i;j<i+w;j++){
977 float mask = 1.f - (luma[j]-med)/med;
978 if(mask<0.f)mask=0.f;
979 if(mask>1.f)mask=1.f;
980 buffer[j*p] = clamp(rint(luma[j]*mask + (1.f-mask)*buffer[j*p]));
982 if(!check)gimp_progress_update((gfloat)(i+w)/(w*h));
983 if(check && check()){
984 if(!check)gimp_progress_end();
985 return 1;
988 break;
989 case 3:case 4:
990 for(i=0;i<w*h;i+=w){
991 for(j=i;j<i+w;j++){
992 float mask = 1.f - (luma[j]-med)/med;
993 if(mask<0.f)mask=0.f;
994 if(mask>1.f)mask=1.f;
995 buffer[j*p] = clamp(rint(computeR(luma[j],Pb[j],Pr[j])*mask + (1.f-mask)*buffer[j*p]));
996 buffer[j*p+1] = clamp(rint(computeG(luma[j],Pb[j],Pr[j])*mask + (1.f-mask)*buffer[j*p+1]));
997 buffer[j*p+2] = clamp(rint(computeB(luma[j],Pb[j],Pr[j])*mask + (1.f-mask)*buffer[j*p+2]));
999 if(!check)gimp_progress_update((gfloat)(i+w)/(w*h));
1000 if(check && check()){
1001 if(!check)gimp_progress_end();
1002 return 1;
1005 break;
1007 if(!check)gimp_progress_end();
1008 }else{
1010 if(!check)gimp_progress_init( "Unconverting...");
1012 switch(p){
1013 case 1:case 2:
1014 for(i=0;i<w*h;i+=w){
1015 for(j=i;j<i+w;j++){
1016 buffer[j*p] = clamp(rint(luma[j]));
1018 if(!check)gimp_progress_update((gfloat)(i+w)/(w*h));
1019 if(check && check()){
1020 if(!check)gimp_progress_end();
1021 return 1;
1024 break;
1025 case 3:case 4:
1026 for(i=0;i<w*h;i+=w){
1027 for(j=i;j<i+w;j++){
1028 buffer[j*p] = clamp(rint(computeR(luma[j],Pb[j],Pr[j])));
1029 buffer[j*p+1] = clamp(rint(computeG(luma[j],Pb[j],Pr[j])));
1030 buffer[j*p+2] = clamp(rint(computeB(luma[j],Pb[j],Pr[j])));
1032 if(!check)gimp_progress_update((gfloat)(i+w)/(w*h));
1033 if(check && check()){
1034 if(!check)gimp_progress_end();
1035 return 1;
1038 break;
1040 if(!check)gimp_progress_end();
1042 return 0;
1045 static void preview_update (GtkWidget *preview, GtkWidget *dialog){
1047 GimpDrawable *drawable;
1048 GimpPixelRgn rgn; /* previw region */
1049 gint x, y, w, h;
1050 gint bpp; /* Bytes-per-pixel in image */
1051 guchar *buffer = NULL;
1053 /* Because we do async event processing in this plugin (so that the
1054 UI doesn't freeze during long comutation times), a stray expose
1055 of the GimpPreviewArea already queud after this update call
1056 causes it to reload the original unfiltered data, which is
1057 annoying when doing small parameter tweaks. Make sure the
1058 previous run's data is blitted in if appropriate. */
1059 drawable = gimp_drawable_preview_get_drawable (GIMP_DRAWABLE_PREVIEW (preview));
1060 bpp = gimp_drawable_bpp (drawable->drawable_id);
1061 gimp_preview_get_position (GIMP_PREVIEW(preview), &x, &y);
1062 gimp_preview_get_size (GIMP_PREVIEW(preview), &w, &h);
1063 if(!pre_run) denoise_pre(drawable);
1065 if(x == preview_cache_x &&
1066 y == preview_cache_y &&
1067 w == preview_cache_w &&
1068 h == preview_cache_h){
1070 if(preview_cache_blit)
1071 gimp_preview_draw_buffer (GIMP_PREVIEW(preview), preview_cache_blit, w*bpp);
1073 }else{
1075 /* the preview pane has shifted; dump all caches */
1077 if(preview_cache_blit)
1078 g_free(preview_cache_blit);
1079 preview_cache_blit=NULL;
1081 if(preview_cache_luma)
1082 g_free(preview_cache_luma);
1083 preview_cache_luma=NULL;
1085 if(preview_cache_Pb)
1086 g_free(preview_cache_Pb);
1087 preview_cache_Pb=NULL;
1089 if(preview_cache_Pr)
1090 g_free(preview_cache_Pr);
1091 preview_cache_Pr=NULL;
1095 denoise_active_interrupt=1;
1096 if(denoise_active_interruptable) return;
1097 denoise_active_interruptable = 1;
1099 while(denoise_active_interrupt){
1100 denoise_active_interrupt=0;
1101 set_busy(preview,dialog);
1103 gimp_preview_get_position (GIMP_PREVIEW(preview), &x, &y);
1104 gimp_preview_get_size (GIMP_PREVIEW(preview), &w, &h);
1105 gimp_pixel_rgn_init (&rgn, drawable, x, y, w, h, FALSE, FALSE);
1107 if(buffer) g_free (buffer);
1108 buffer = g_new (guchar, w * h * bpp);
1109 gimp_pixel_rgn_get_rect (&rgn, buffer, x, y, w, h);
1111 /* compute what filtered planes we actually need */
1113 float *tempY = NULL;
1114 float *tempPb = NULL;
1115 float *tempPr = NULL;
1117 if(preview_cache_luma == NULL) tempY = g_new(float,w*h);
1118 if(bpp > 2 && preview_cache_Pb == NULL) tempPb = g_new(float,w*h);
1119 if(bpp > 2 && preview_cache_Pr == NULL) tempPr = g_new(float,w*h);
1121 compute_YPbPr(buffer,tempY,tempPb,tempPr,w,h,bpp, check_recompute);
1123 if(!compute_filter(tempY, w, h, denoise_params.filterY, check_recompute, NULL)){
1124 preview_cache_luma = tempY;
1125 tempY = NULL;
1128 if(!compute_filter(tempPb, w, h, denoise_params.filterC, check_recompute, NULL)){
1129 preview_cache_Pb = tempPb;
1130 tempPb = NULL;
1133 if(!compute_filter(tempPr, w, h, denoise_params.filterC, check_recompute, NULL)){
1134 preview_cache_Pr = tempPr;
1135 tempPr = NULL;
1138 if(tempY) g_free(tempY);
1139 if(tempPb) g_free(tempPb);
1140 if(tempPr) g_free(tempPr);
1142 if(check_recompute())continue;
1145 /* new blit */
1146 if(compute_mask(buffer, preview_cache_luma, preview_cache_Pb, preview_cache_Pr,
1147 w, h, bpp, check_recompute))
1148 continue; /* interrupted */
1150 if(preview_cache_blit)
1151 g_free(preview_cache_blit);
1152 preview_cache_x = x;
1153 preview_cache_y = y;
1154 preview_cache_w = w;
1155 preview_cache_h = h;
1156 preview_cache_blit = buffer;
1157 buffer = NULL;
1159 gimp_preview_draw_buffer (GIMP_PREVIEW(preview), preview_cache_blit, w*bpp);
1160 gimp_drawable_flush (drawable);
1164 if(buffer) g_free(buffer);
1165 denoise_active_interruptable = 0;
1168 static void denoise_work(int w, int h, int p, guchar *buffer){
1169 float *Y = g_new(float,w*h);
1170 float *Pb = (p<3 ? NULL : g_new(float,w*h));
1171 float *Pr = (p<3 ? NULL : g_new(float,w*h));
1173 compute_YPbPr(buffer,Y,Pb,Pr,w,h,p,NULL);
1174 compute_filter(Y, w, h, denoise_params.filterY, NULL, (p>2?"Filtering luma...":"Filtering..."));
1175 compute_filter(Pb, w, h, denoise_params.filterC, NULL, "Filtering blue difference...");
1176 compute_filter(Pr, w, h, denoise_params.filterC, NULL, "Filtering red difference...");
1178 compute_mask(buffer, Y, Pb, Pr, w, h, p, NULL);
1180 if(Y)g_free(Y);
1181 if(Pb)g_free(Pb);
1182 if(Pr)g_free(Pr);