158
static GimpParamDef args[] =
135
static const GimpParamDef args[] =
160
{ GIMP_PDB_INT32, "run_mode", "Interactive, non-interactive" },
137
{ GIMP_PDB_INT32, "run-mode", "Interactive, non-interactive" },
161
138
{ GIMP_PDB_IMAGE, "image", "Input image" },
162
139
{ GIMP_PDB_DRAWABLE, "drawable", "Input drawable" },
163
140
{ GIMP_PDB_INT32, "radius", "Filter box radius (default = 3)" },
164
141
{ GIMP_PDB_INT32, "type", "Filter type (0 = median, 1 = adaptive, 2 = recursive-median, 3 = recursive-adaptive)" },
165
{ GIMP_PDB_INT32, "black", "Black level (-1 to 255)" },
166
{ GIMP_PDB_INT32, "white", "White level (0 to 256)" }
142
{ GIMP_PDB_INT32, "black", "Black level (0 to 255)" },
143
{ GIMP_PDB_INT32, "white", "White level (0 to 255)" }
169
gimp_install_procedure (PLUG_IN_NAME,
170
"Despeckle filter, typically used to \'despeckle\' "
171
"a photographic image.",
146
gimp_install_procedure (PLUG_IN_PROC,
147
N_("Remove speckle noise from the image"),
172
148
"This plug-in selectively performs a median or "
173
149
"adaptive box filter on an image.",
174
150
"Michael Sweet <mike@easysw.com>",
406
382
GtkWidget *dialog;
407
383
GtkWidget *main_vbox;
410
385
GtkWidget *table;
411
386
GtkWidget *frame;
412
387
GtkWidget *button;
416
gimp_ui_init ("despeckle", TRUE);
391
gimp_ui_init (PLUG_IN_BINARY, TRUE);
418
dialog = gimp_dialog_new (_("Despeckle"), "despeckle",
393
dialog = gimp_dialog_new (_("Despeckle"), PLUG_IN_BINARY,
420
gimp_standard_help_func, HELP_ID,
395
gimp_standard_help_func, PLUG_IN_PROC,
422
397
GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
423
398
GTK_STOCK_OK, GTK_RESPONSE_OK,
402
gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
407
gimp_window_set_transient (GTK_WINDOW (dialog));
427
409
main_vbox = gtk_vbox_new (FALSE, 12);
428
410
gtk_container_set_border_width (GTK_CONTAINER (main_vbox), 12);
429
411
gtk_container_add (GTK_CONTAINER (GTK_DIALOG (dialog)->vbox), main_vbox);
437
419
G_CALLBACK (preview_update),
441
* Filter type controls...
444
frame = gimp_frame_new (_("Type"));
422
frame = gimp_frame_new (_("Median"));
445
423
gtk_box_pack_start (GTK_BOX (main_vbox), frame, FALSE, FALSE, 0);
446
424
gtk_widget_show (frame);
449
hbox = gtk_hbox_new (FALSE, 12);
450
gtk_box_pack_start (GTK_BOX (main_vbox), hbox, FALSE, FALSE, 0);
451
gtk_widget_show (hbox);
452
fvals.method = MEDIAN;
453
/* parameter settings */
454
frame = gimp_frame_new (_("Median"));
456
426
vbox = gtk_vbox_new (FALSE, 6);
457
427
gtk_container_add (GTK_CONTAINER (frame), vbox);
458
428
gtk_widget_show (vbox);
460
430
button = gtk_check_button_new_with_mnemonic (_("_Adaptive"));
461
431
gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);
462
432
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button),
463
(filter_type & FILTER_ADAPTIVE) ? TRUE : FALSE);
433
filter_type & FILTER_ADAPTIVE);
464
434
gtk_widget_show (button);
466
436
g_signal_connect (button, "toggled",
470
440
button = gtk_check_button_new_with_mnemonic (_("R_ecursive"));
471
441
gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);
472
442
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button),
473
(filter_type & FILTER_RECURSIVE) ? TRUE : FALSE);
443
filter_type & FILTER_RECURSIVE);
474
444
gtk_widget_show (button);
476
446
g_signal_connect (button, "toggled",
477
447
G_CALLBACK (dialog_recursive_callback),
480
gtk_box_pack_start (GTK_BOX (hbox), frame, FALSE, FALSE, 0);
481
gtk_widget_show (frame);
483
450
table = gtk_table_new (4, 3, FALSE);
484
451
gtk_table_set_col_spacings (GTK_TABLE (table), 6);
485
452
gtk_table_set_row_spacings (GTK_TABLE (table), 6);
495
462
despeckle_radius, 1, MAX_RADIUS, 1, 5, 0,
498
g_signal_connect (adj, "value_changed",
465
g_signal_connect (adj, "value-changed",
499
466
G_CALLBACK (gimp_int_adjustment_update),
500
467
&despeckle_radius);
501
g_signal_connect_swapped (adj, "value_changed",
468
g_signal_connect_swapped (adj, "value-changed",
502
469
G_CALLBACK (gimp_preview_invalidate),
509
476
adj = gimp_scale_entry_new (GTK_TABLE (table), 0, 1,
510
477
_("_Black level:"), SCALE_WIDTH, ENTRY_WIDTH,
511
black_level, -1, 255, 1, 8, 0,
478
black_level, 0, 255, 1, 8, 0,
514
g_signal_connect (adj, "value_changed",
481
g_signal_connect (adj, "value-changed",
515
482
G_CALLBACK (gimp_int_adjustment_update),
517
g_signal_connect_swapped (adj, "value_changed",
484
g_signal_connect_swapped (adj, "value-changed",
518
485
G_CALLBACK (gimp_preview_invalidate),
525
492
adj = gimp_scale_entry_new (GTK_TABLE (table), 0, 2,
526
493
_("_White level:"), SCALE_WIDTH, ENTRY_WIDTH,
527
white_level, 0, 256, 1, 8, 0,
494
white_level, 0, 255, 1, 8, 0,
530
g_signal_connect (adj, "value_changed",
497
g_signal_connect (adj, "value-changed",
531
498
G_CALLBACK (gimp_int_adjustment_update),
533
g_signal_connect_swapped (adj, "value_changed",
500
g_signal_connect_swapped (adj, "value-changed",
534
501
G_CALLBACK (gimp_preview_invalidate),
567
534
gint width, height;
569
img_bpp = gimp_drawable_bpp (drawable->drawable_id);
570
536
preview = GIMP_PREVIEW (widget);
538
img_bpp = gimp_drawable_bpp (drawable->drawable_id);
572
540
width = preview->width;
573
541
height = preview->height;
575
* Setup for filter...
577
543
gimp_preview_get_position (preview, &x1, &y1);
579
545
gimp_pixel_rgn_init (&src_rgn, drawable, x1, y1, width, height, FALSE, FALSE);
582
* Pre-load the preview rectangle...
584
547
dst = g_new (guchar, width * height * img_bpp);
585
548
src = g_new (guchar, width * height * img_bpp);
586
550
gimp_pixel_rgn_get_rect (&src_rgn, src, x1, y1, width, height);
588
552
despeckle_median (src, dst, width, height, img_bpp, despeckle_radius, TRUE);
591
* Update the screen...
593
554
gimp_preview_draw_buffer (preview, dst, width * img_bpp);
630
592
gboolean preview)
632
gint pos1, pos2, med, x, y, jh,jv, box, hist0, hist255, diameter;
636
gdouble prog, maxprog;
640
gimp_progress_init(_("Despeckle"));
641
gimp_progress_update (0.0);
644
maxprog = width * height;
605
max_progress = width * height;
647
607
diameter = (2 * radius) + 1;
648
608
box = SQR (diameter);
649
609
buf = g_new (guchar *, box);
650
610
ibuf = g_new (guchar, box);
652
for (x = 0; x < width; x++)
613
gimp_progress_init(_("Despeckle"));
615
for (y = 0; y < height; y++)
654
for (y = 0; y < height; y++)
617
gint off = y * width * bpp;
618
gint ymin = MAX (0, y - radius);
619
gint ymax = MIN (height, y + radius);
621
for (x = 0; x < width; x++, off += bpp)
658
if (x >= radius && y >= radius &&
659
x + radius < width && y + radius < height)
661
/* Make sure Svm is ininialized to a sufficient large value */
664
for (jh = x-radius; jh <= x+radius; jh++)
666
for (jv = y-radius, pos1 = 0; jv <= y+radius; jv++)
668
pos2 = (jh + (jv * width)) * bpp;
669
if (src[pos2] > black_level && src[pos2] < white_level)
672
buf[med] = src + pos2;
673
ibuf[med] = pixel_intensity (src + pos2, bpp);
677
if (src[pos2] > black_level)
680
if (src[pos2] >= white_level)
688
pos1 = (x + ( y * width)) * bpp;
689
pixel_copy (dst + pos1, src + pos1, bpp);
693
pos1 = (x + (y * width)) * bpp;
694
med = quick_median_select (buf, ibuf, med + 1);
697
if (filter_type & FILTER_RECURSIVE)
698
pixel_copy (src + pos1, pixel, bpp);
700
pixel_copy (dst + pos1, pixel, bpp);
623
gint xmin = MAX (0, x - radius);
624
gint xmax = MIN (width, x + radius);
629
for (v = ymin; v < ymax; v++)
631
gint off2 = v * width * bpp;
633
for (u = xmin, off2 += xmin * bpp; u < xmax; u++, off2 += bpp)
635
guchar value = pixel_luminance (src + off2, bpp);
637
if (value < black_level)
641
else if (value > white_level)
647
buf[count] = src + off2;
656
pixel_copy (dst + off, src + off, bpp);
705
pos1 = (x + (y * width)) * bpp;
706
pixel_copy (dst + pos1, src + pos1, bpp);
660
pixel = buf[quick_median_select (buf, ibuf, count)];
662
if (filter_type & FILTER_RECURSIVE)
663
pixel_copy (src + off, pixel, bpp);
665
pixel_copy (dst + off, pixel, bpp);
728
if (!preview && x % 5 == 0)
729
gimp_progress_update (prog / maxprog);
688
if (!preview && y % 20 == 0)
689
gimp_progress_update ((gdouble) progress / (gdouble) max_progress);
733
693
gimp_progress_update (1.0);
739
/* * This Quickselect routine is based on the algorithm described in
740
* "Numerical recipes in C", Second Edition,
741
* Cambridge University Press, 1992, Section 8.5, ISBN 0-521-43108-5
742
* This code by Nicolas Devillard - 1998. Public domain.
744
* modified to swap pointers: swap is done by comparing intensity value
745
* for the pointer to RGB
700
* This Quickselect routine is based on the algorithm described in
701
* "Numerical recipes in C", Second Edition,
702
* Cambridge University Press, 1992, Section 8.5, ISBN 0-521-43108-5
703
* This code by Nicolas Devillard - 1998. Public domain.
705
* Modified to swap pointers: swap is done by comparing luminance
706
* value for the pointer to RGB.
748
709
quick_median_select (guchar **p,
758
median = (low + high) / 2;
715
gint median = (low + high) / 2;
762
721
if (high <= low) /* One element only */
780
739
if (i[middle] > i[high])
782
VALUE_SWAP (i[middle], i[high]) ;
783
POINTER_SWAP (p[middle], p[high]) ;
741
VALUE_SWAP (i[middle], i[high]);
742
POINTER_SWAP (p[middle], p[high]);
786
745
if (i[low] > i[high])
788
VALUE_SWAP (i[low], i[high]) ;
789
POINTER_SWAP (p[low], p[high]) ;
747
VALUE_SWAP (i[low], i[high]);
748
POINTER_SWAP (p[low], p[high]);
792
751
if (i[middle] > i[low])
794
VALUE_SWAP (i[middle], i[low]) ;
795
POINTER_SWAP (p[middle], p[low]) ;
753
VALUE_SWAP (i[middle], i[low]);
754
POINTER_SWAP (p[middle], p[low]);
798
757
/* Swap low item (now in position middle) into position (low+1) */
799
VALUE_SWAP (i[middle], i[low+1]) ;
800
POINTER_SWAP (p[middle], p[low+1])
758
VALUE_SWAP (i[middle], i[low+1]);
759
POINTER_SWAP (p[middle], p[low+1]);
802
761
/* Nibble from each end towards middle, swapping items when stuck */
809
768
while (i[low] > i[ll]);
834
pixel_intensity (const guchar *p,
793
pixel_luminance (const guchar *p,
840
return GIMP_RGB_INTENSITY (p[0], p[1], p[2]);
804
return GIMP_RGB_LUMINANCE (p[0], p[1], p[2]);
807
return 0; /* should not be reached */
844
812
pixel_copy (guchar *dest,
845
813
const guchar *src,
848
for (; n > 0; n--, dest++, src++)
816
for (; bpp > 0; bpp--, dest++, src++)