1
/**************************************************
2
* file: nlfilt/nlfilt.c
4
* Copyright (c) 1997 Eric L. Hernes (erich@rrnet.com)
7
* Redistribution and use in source and binary forms, with or without
8
* modification, are permitted provided that the following conditions
10
* 1. Redistributions of source code must retain the above copyright
11
* notice, this list of conditions and the following disclaimer.
12
* 2. The name of the author may not be used to endorse or promote products
13
* derived from this software withough specific prior written permission
15
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26
* $Id: nlfilt.c,v 1.63 2004/11/14 02:45:11 yosh Exp $
30
* Algorithm fixes, V2.0 compatibility by David Hodson hodsond@ozemail.com.au
40
#include <libgimp/gimp.h>
41
#include <libgimp/gimpui.h>
43
#include "libgimp/stdplugins-intl.h"
60
static NLFilterValues nlfvals =
70
static void query (void);
71
static void run (const gchar *name,
73
const GimpParam *param,
77
static void nlfilter (GimpDrawable *drawable,
78
GimpPreview *preview);
79
static gboolean nlfilter_dialog (GimpDrawable *drawable);
81
static inline gint nlfiltInit (gdouble alpha,
85
static inline void nlfiltRow (guchar *srclast,
93
GimpPlugInInfo PLUG_IN_INFO =
97
query, /* query_proc */
106
static GimpParamDef args[] =
108
{ GIMP_PDB_INT32, "run_mode", "Interactive, non-interactive" },
109
{ GIMP_PDB_IMAGE, "img", "The Image to Filter" },
110
{ GIMP_PDB_DRAWABLE, "drw", "The Drawable" },
111
{ GIMP_PDB_FLOAT, "alpha", "The amount of the filter to apply" },
112
{ GIMP_PDB_FLOAT, "radius", "The filter radius" },
113
{ GIMP_PDB_INT32, "filter", "The Filter to Run, "
114
"0 - alpha trimmed mean; "
115
"1 - optimal estimation (alpha controls noise variance); "
116
"2 - edge enhancement" }
119
gimp_install_procedure ("plug_in_nlfilt",
120
"Nonlinear swiss army knife filter",
121
"This is the pnmnlfilt, in gimp's clothing. "
122
"See the pnmnlfilt manpage for details.",
123
"Graeme W. Gill, gimp 0.99 plugin by Eric L. Hernes",
124
"Graeme W. Gill, Eric L. Hernes",
129
G_N_ELEMENTS (args), 0,
132
gimp_plugin_menu_register ("plug_in_nlfilt", "<Image>/Filters/Enhance");
136
run (const gchar *name,
138
const GimpParam *param,
140
GimpParam **return_vals)
142
static GimpParam values[1];
143
GimpDrawable *drawable;
144
GimpRunMode run_mode;
145
GimpPDBStatusType status = GIMP_PDB_SUCCESS;
147
run_mode = param[0].data.d_int32;
151
drawable = gimp_drawable_get (param[2].data.d_drawable);
154
*return_vals = values;
156
values[0].type = GIMP_PDB_STATUS;
157
values[0].data.d_status = status;
161
case GIMP_RUN_INTERACTIVE:
162
gimp_get_data ("plug_in_nlfilt", &nlfvals);
164
if (! nlfilter_dialog (drawable))
168
case GIMP_RUN_NONINTERACTIVE:
171
status = GIMP_PDB_CALLING_ERROR;
175
nlfvals.alpha = param[3].data.d_float;
176
nlfvals.radius = param[4].data.d_float;
177
nlfvals.filter = param[5].data.d_int32;
182
case GIMP_RUN_WITH_LAST_VALS:
183
gimp_get_data ("plug_in_nlfilt", &nlfvals);
190
if (status == GIMP_PDB_SUCCESS)
192
nlfilter (drawable, NULL);
195
if (run_mode == GIMP_RUN_INTERACTIVE)
196
gimp_set_data ("plug_in_nlfilt", &nlfvals, sizeof (NLFilterValues));
199
values[0].data.d_status = status;
201
gimp_drawable_detach (drawable);
204
/* pnmnlfilt.c - 4 in 1 (2 non-linear) filter
205
** - smooth an anyimage
206
** - do alpha trimmed mean filtering on an anyimage
207
** - do optimal estimation smoothing on an anyimage
208
** - do edge enhancement on an anyimage
212
** The implementation of an alpha-trimmed mean filter
213
** is based on the description in IEEE CG&A May 1990
214
** Page 23 by Mark E. Lee and Richard A. Redner.
216
** The paper recommends using a hexagon sampling region around each
217
** pixel being processed, allowing an effective sub pixel radius to be
218
** specified. The hexagon values are sythesised by area sampling the
219
** rectangular pixels with a hexagon grid. The seven hexagon values
220
** obtained from the 3x3 pixel grid are used to compute the alpha
221
** trimmed mean. Note that an alpha value of 0.0 gives a conventional
222
** mean filter (where the radius controls the contribution of
223
** surrounding pixels), while a value of 0.5 gives a median filter.
224
** Although there are only seven values to trim from before finding
225
** the mean, the algorithm has been extended from that described in
226
** CG&A by using interpolation, to allow a continuous selection of
227
** alpha value between and including 0.0 to 0.5 The useful values
228
** for radius are between 0.3333333 (where the filter will have no
229
** effect because only one pixel is sampled), to 1.0, where all
230
** pixels in the 3x3 grid are sampled.
232
** The optimal estimation filter is taken from an article "Converting Dithered
233
** Images Back to Gray Scale" by Allen Stenger, Dr Dobb's Journal, November
234
** 1992, and this article references "Digital Image Enhancement andNoise Filtering by
235
** Use of Local Statistics", Jong-Sen Lee, IEEE Transactions on Pattern Analysis and
236
** Machine Intelligence, March 1980.
238
** Also borrow the technique used in pgmenhance(1) to allow edge
239
** enhancement if the alpha value is negative.
242
** Graeme W. Gill, 30th Jan 1993
243
** graeme@labtam.oz.au
245
** Permission is hereby granted, to use, copy, modify, distribute,
246
** and sell this software and its associated documentation files
247
** (the "Software") for any purpose without fee, provided
250
** 1) The above copyright notices and this permission notice
251
** accompany all source code copies of the Software and
252
** related documentation.
255
** 2) If executable code based on the Software only is distributed,
256
** then the accompanying documentation must acknowledge that
257
** "this software is based in part on the work of Graeme W. Gill".
260
** 3) It is accepted that Graeme W. Gill (the "Author") accepts
261
** NO LIABILITY for damages of any kind. The Software is
262
** provided without fee by the Author "AS-IS" and without
263
** warranty of any kind, express, implied or otherwise,
264
** including without limitation, any warranty of merchantability
265
** or fitness for a particular purpose.
268
** 4) These conditions apply to any software derived from or based
269
** on the Software, not just to the unmodified library.
273
/* ************************************************** */
274
/* Hexagon intersecting square area functions */
275
/* Compute the area of the intersection of a triangle */
276
/* and a rectangle */
278
static gdouble triang_area(gdouble, gdouble, gdouble, gdouble, gdouble,
279
gdouble, gdouble, gdouble, gint);
280
static gdouble rectang_area(gdouble, gdouble, gdouble, gdouble,
281
gdouble, gdouble, gdouble, gdouble);
282
static gdouble hex_area(gdouble, gdouble, gdouble, gdouble, gdouble);
284
static gint atfilt0 (gint *p);
285
static gint atfilt1 (gint *p);
286
static gint atfilt2 (gint *p);
287
static gint atfilt3 (gint *p);
288
static gint atfilt4 (gint *p);
289
static gint atfilt5 (gint *p);
291
gint (*atfuncs[6])(gint *) =
301
gint noisevariance; /* global so that pixel processing code can get at it quickly */
303
#define MXIVAL 255 /* maximum input value */
304
#define NOIVAL (MXIVAL + 1) /* number of possible input values */
306
#define SCALEB 8 /* scale bits */
307
#define SCALE (1 << SCALEB) /* scale factor */
308
#define MXSVAL (MXIVAL * SCALE) /* maximum scaled values */
310
#define CSCALEB 2 /* coarse scale bits */
311
#define CSCALE (1 << CSCALEB) /* coarse scale factor */
312
#define MXCSVAL (MXIVAL * CSCALE) /* maximum coarse scaled values */
313
#define NOCSVAL (MXCSVAL + 1) /* number of coarse scaled values */
314
#define SCTOCSC(x) ((x) >> (SCALEB - CSCALEB)) /* convert from scaled to coarse scaled */
315
#define CSCTOSC(x) ((x) << (SCALEB - CSCALEB)) /* convert from course scaled to scaled */
317
/* round and scale floating point to scaled integer */
318
#define SROUND(x) ((gint)(((x) * (gdouble)SCALE) + 0.5))
319
/* round and un-scale scaled integer value */
320
#define RUNSCALE(x) (((x) + (1 << (SCALEB-1))) >> SCALEB) /* rounded un-scale */
321
#define UNSCALE(x) ((x) >> SCALEB)
323
/* Note: modified by David Hodson, nlfiltRow now accesses
324
* srclast, srcthis, and srcnext from [-bpp] to [width*bpp-1].
325
* Beware if you use this code anywhere else!
328
nlfiltRow (guchar *srclast, guchar *srcthis, guchar *srcnext, guchar *dst,
329
gint width, gint bpp, gint filtno)
332
guchar *ip0, *ip1, *ip2, *or, *orend;
335
orend = dst + width * bpp;
340
for (or = dst; or < orend; ip0++, ip1++, ip2++, or++)
343
pf[1] = *(ip1 - bpp);
344
pf[2] = *(ip2 - bpp);
346
pf[4] = *(ip2 + bpp);
347
pf[5] = *(ip1 + bpp);
348
pf[6] = *(ip0 + bpp);
350
pf[8] = *(ip0 - bpp);
351
*or=(atfuncs[filtno])(pf);
355
/* We restrict radius to the values: 0.333333 <= radius <= 1.0 */
356
/* so that no fewer and no more than a 3x3 grid of pixels around */
357
/* the pixel in question needs to be read. Given this, we only */
358
/* need 3 or 4 weightings per hexagon, as follows: */
360
/* Virtical hex: |_|_| 1 2 */
364
/* Middle hex: |_| 1 Horizontal hex: |X|_| 0 2 */
369
gint V0[NOIVAL],V1[NOIVAL],V2[NOIVAL],V3[NOIVAL]; /* vertical hex */
370
gint M0[NOIVAL],M1[NOIVAL],M2[NOIVAL]; /* middle hex */
371
gint H0[NOIVAL],H1[NOIVAL],H2[NOIVAL],H3[NOIVAL]; /* horizontal hex */
373
/* alpha trimmed and edge enhancement only */
374
gint ALFRAC[NOIVAL * 8]; /* fractional alpha divider table */
376
/* optimal estimation only */
377
gint AVEDIV[7 * NOCSVAL]; /* divide by 7 to give average value */
378
gint SQUARE[2 * NOCSVAL]; /* scaled square lookup table */
380
/* Table initialisation function - return alpha range */
382
nlfiltInit (gdouble alpha, gdouble radius, FilterType filter)
384
gint alpharange; /* alpha range value 0 - 3 */
385
gdouble meanscale; /* scale for finding mean */
386
gdouble mmeanscale; /* scale for finding mean - midle hex */
387
gdouble alphafraction; /* fraction of next largest/smallest
388
* to subtract from sum
392
case filter_alpha_trim:
395
/* alpha only makes sense in range 0.0 - 0.5 */
397
/* number of elements (out of a possible 7) used in the mean */
398
noinmean = ((0.5 - alpha) * 12.0) + 1.0;
399
mmeanscale = meanscale = 1.0/noinmean;
400
if (alpha == 0.0) { /* mean filter */
402
alphafraction = 0.0; /* not used */
403
} else if (alpha < (1.0/6.0)) { /* mean of 5 to 7 middle values */
405
alphafraction = (7.0 - noinmean)/2.0;
406
} else if (alpha < (1.0/3.0)) { /* mean of 3 to 5 middle values */
408
alphafraction = (5.0 - noinmean)/2.0;
409
} else { /* mean of 1 to 3 middle values */
410
/* alpha==0.5 => median filter */
412
alphafraction = (3.0 - noinmean)/2.0;
416
case filter_opt_est: {
418
gdouble noinmean = 7.0;
420
/* edge enhancement function */
423
/* compute scaled hex values */
424
mmeanscale=meanscale=1.0;
426
/* Set up 1:1 division lookup - not used */
427
alphafraction=1.0/noinmean;
429
/* estimate of noise variance */
430
noisevariance = alpha * (gdouble)255;
431
noisevariance = noisevariance * noisevariance / 8.0;
433
/* set yp optimal estimation specific stuff */
435
for (i=0;i<(7*NOCSVAL);i++) { /* divide scaled value by 7 lookup */
436
AVEDIV[i] = CSCTOSC(i)/7; /* scaled divide by 7 */
438
/* compute square and rescale by
439
* (val >> (2 * SCALEB + 2)) table
441
for (i=0;i<(2*NOCSVAL);i++) {
443
/* NOCSVAL offset to cope with -ve input values */
444
val = CSCTOSC(i - NOCSVAL);
445
SQUARE[i] = (val * val) >> (2 * SCALEB + 2);
449
case filter_edge_enhance: {
450
if (alpha == 1.0) alpha = 0.99;
452
/* mean of 7 and scaled by -alpha/(1-alpha) */
453
meanscale = 1.0 * (-alpha/((1.0 - alpha) * 7.0));
455
/* middle pixel has 1/(1-alpha) as well */
456
mmeanscale = 1.0 * (1.0/(1.0 - alpha) + meanscale);
457
alphafraction = 0.0; /* not used */
461
fprintf(stderr, "unknown filter %d\n", filter);
465
* Setup pixel weighting tables -
466
* note we pre-compute mean division here too.
470
gdouble hexhoff,hexvoff;
471
gdouble tabscale,mtabscale;
472
gdouble v0,v1,v2,v3,m0,m1,m2,h0,h1,h2,h3;
474
/* horizontal offset of virtical hex centers */
477
/* vertical offset of virtical hex centers */
478
hexvoff = 3.0 * radius/sqrt(12.0);
481
* scale tables to normalise by hexagon
482
* area, and number of hexes used in mean
484
tabscale = meanscale / (radius * hexvoff);
485
mtabscale = mmeanscale / (radius * hexvoff);
486
v0 = hex_area(0.0,0.0,hexhoff,hexvoff,radius) * tabscale;
487
v1 = hex_area(0.0,1.0,hexhoff,hexvoff,radius) * tabscale;
488
v2 = hex_area(1.0,1.0,hexhoff,hexvoff,radius) * tabscale;
489
v3 = hex_area(1.0,0.0,hexhoff,hexvoff,radius) * tabscale;
490
m0 = hex_area(0.0,0.0,0.0,0.0,radius) * mtabscale;
491
m1 = hex_area(0.0,1.0,0.0,0.0,radius) * mtabscale;
492
m2 = hex_area(0.0,-1.0,0.0,0.0,radius) * mtabscale;
493
h0 = hex_area(0.0,0.0,radius,0.0,radius) * tabscale;
494
h1 = hex_area(1.0,1.0,radius,0.0,radius) * tabscale;
495
h2 = hex_area(1.0,0.0,radius,0.0,radius) * tabscale;
496
h3 = hex_area(1.0,-1.0,radius,0.0,radius) * tabscale;
498
for (i=0; i <= MXIVAL; i++) {
501
V0[i] = SROUND(fi * v0);
502
V1[i] = SROUND(fi * v1);
503
V2[i] = SROUND(fi * v2);
504
V3[i] = SROUND(fi * v3);
505
M0[i] = SROUND(fi * m0);
506
M1[i] = SROUND(fi * m1);
507
M2[i] = SROUND(fi * m2);
508
H0[i] = SROUND(fi * h0);
509
H1[i] = SROUND(fi * h1);
510
H2[i] = SROUND(fi * h2);
511
H3[i] = SROUND(fi * h3);
513
/* set up alpha fraction lookup table used on big/small */
514
for (i=0; i < (NOIVAL * 8); i++) {
515
ALFRAC[i] = SROUND((gdouble)i * alphafraction);
521
/* Core pixel processing function - hand it 3x3 pixels and return result. */
527
/* map to scaled hexagon values */
528
retv = M0[p[0]] + M1[p[3]] + M2[p[7]];
529
retv += H0[p[0]] + H1[p[2]] + H2[p[1]] + H3[p[8]];
530
retv += V0[p[0]] + V1[p[3]] + V2[p[2]] + V3[p[1]];
531
retv += V0[p[0]] + V1[p[3]] + V2[p[4]] + V3[p[5]];
532
retv += H0[p[0]] + H1[p[4]] + H2[p[5]] + H3[p[6]];
533
retv += V0[p[0]] + V1[p[7]] + V2[p[6]] + V3[p[5]];
534
retv += V0[p[0]] + V1[p[7]] + V2[p[8]] + V3[p[1]];
535
return UNSCALE(retv);
538
/* Mean of 5 - 7 middle values */
542
gint h0,h1,h2,h3,h4,h5,h6; /* hexagon values 2 3 */
546
/* map to scaled hexagon values */
547
h0 = M0[p[0]] + M1[p[3]] + M2[p[7]];
548
h1 = H0[p[0]] + H1[p[2]] + H2[p[1]] + H3[p[8]];
549
h2 = V0[p[0]] + V1[p[3]] + V2[p[2]] + V3[p[1]];
550
h3 = V0[p[0]] + V1[p[3]] + V2[p[4]] + V3[p[5]];
551
h4 = H0[p[0]] + H1[p[4]] + H2[p[5]] + H3[p[6]];
552
h5 = V0[p[0]] + V1[p[7]] + V2[p[6]] + V3[p[5]];
553
h6 = V0[p[0]] + V1[p[7]] + V2[p[8]] + V3[p[1]];
554
/* sum values and also discover the largest and smallest */
560
else if (xx < small) \
569
/* Compute mean of middle 5-7 values */
570
return UNSCALE(h0 -ALFRAC[(big + small)>>SCALEB]);
573
/* Mean of 3 - 5 middle values */
577
gint h0,h1,h2,h3,h4,h5,h6; /* hexagon values 2 3 */
580
gint big0,big1,small0,small1;
581
/* map to scaled hexagon values */
582
h0 = M0[p[0]] + M1[p[3]] + M2[p[7]];
583
h1 = H0[p[0]] + H1[p[2]] + H2[p[1]] + H3[p[8]];
584
h2 = V0[p[0]] + V1[p[3]] + V2[p[2]] + V3[p[1]];
585
h3 = V0[p[0]] + V1[p[3]] + V2[p[4]] + V3[p[5]];
586
h4 = H0[p[0]] + H1[p[4]] + H2[p[5]] + H3[p[6]];
587
h5 = V0[p[0]] + V1[p[7]] + V2[p[6]] + V3[p[5]];
588
h6 = V0[p[0]] + V1[p[7]] + V2[p[8]] + V3[p[1]];
589
/* sum values and also discover the 2 largest and 2 smallest */
622
/* Compute mean of middle 3-5 values */
623
return UNSCALE(h0 -big0 -small0 -ALFRAC[(big1 + small1)>>SCALEB]);
627
* Mean of 1 - 3 middle values.
628
* If only 1 value, then this is a median filter.
633
gint h0,h1,h2,h3,h4,h5,h6; /* hexagon values 2 3 */
636
gint big0,big1,big2,small0,small1,small2;
637
/* map to scaled hexagon values */
638
h0 = M0[p[0]] + M1[p[3]] + M2[p[7]];
639
h1 = H0[p[0]] + H1[p[2]] + H2[p[1]] + H3[p[8]];
640
h2 = V0[p[0]] + V1[p[3]] + V2[p[2]] + V3[p[1]];
641
h3 = V0[p[0]] + V1[p[3]] + V2[p[4]] + V3[p[5]];
642
h4 = H0[p[0]] + H1[p[4]] + H2[p[5]] + H3[p[6]];
643
h5 = V0[p[0]] + V1[p[7]] + V2[p[6]] + V3[p[5]];
644
h6 = V0[p[0]] + V1[p[7]] + V2[p[8]] + V3[p[1]];
645
/* sum values and also discover the 3 largest and 3 smallest */
647
small1 = small2 = G_MAXINT;
696
/* Compute mean of middle 1-3 values */
697
return UNSCALE(h0-big0-big1-small0-small1-ALFRAC[(big2+small2)>>SCALEB]);
700
/* Edge enhancement */
705
/* map to scaled hexagon values and compute enhance value */
706
hav = M0[p[0]] + M1[p[3]] + M2[p[7]];
707
hav += H0[p[0]] + H1[p[2]] + H2[p[1]] + H3[p[8]];
708
hav += V0[p[0]] + V1[p[3]] + V2[p[2]] + V3[p[1]];
709
hav += V0[p[0]] + V1[p[3]] + V2[p[4]] + V3[p[5]];
710
hav += H0[p[0]] + H1[p[4]] + H2[p[5]] + H3[p[6]];
711
hav += V0[p[0]] + V1[p[7]] + V2[p[6]] + V3[p[5]];
712
hav += V0[p[0]] + V1[p[7]] + V2[p[8]] + V3[p[1]];
716
if (hav > (gdouble)255)
721
/* Optimal estimation - do smoothing in inverse proportion */
722
/* to the local variance. */
723
/* notice we use the globals noisevariance */
726
gint mean,variance,temp;
727
gint h0,h1,h2,h3,h4,h5,h6; /* hexagon values 2 3 */
730
/* map to scaled hexagon values */
731
h0 = M0[p[0]] + M1[p[3]] + M2[p[7]];
732
h1 = H0[p[0]] + H1[p[2]] + H2[p[1]] + H3[p[8]];
733
h2 = V0[p[0]] + V1[p[3]] + V2[p[2]] + V3[p[1]];
734
h3 = V0[p[0]] + V1[p[3]] + V2[p[4]] + V3[p[5]];
735
h4 = H0[p[0]] + H1[p[4]] + H2[p[5]] + H3[p[6]];
736
h5 = V0[p[0]] + V1[p[7]] + V2[p[6]] + V3[p[5]];
737
h6 = V0[p[0]] + V1[p[7]] + V2[p[8]] + V3[p[1]];
738
mean = h0 + h1 + h2 + h3 + h4 + h5 + h6;
739
/* compute scaled mean by dividing by 7 */
740
mean = AVEDIV[SCTOCSC(mean)];
742
/* compute scaled variance */
743
temp = (h1 - mean); variance = SQUARE[NOCSVAL + SCTOCSC(temp)];
745
/* and rescale to keep */
746
temp = (h2 - mean); variance += SQUARE[NOCSVAL + SCTOCSC(temp)];
748
/* within 32 bit limits */
749
temp = (h3 - mean); variance += SQUARE[NOCSVAL + SCTOCSC(temp)];
750
temp = (h4 - mean); variance += SQUARE[NOCSVAL + SCTOCSC(temp)];
751
temp = (h5 - mean); variance += SQUARE[NOCSVAL + SCTOCSC(temp)];
752
temp = (h6 - mean); variance += SQUARE[NOCSVAL + SCTOCSC(temp)];
753
/* (temp = h0 - mean) */
754
temp = (h0 - mean); variance += SQUARE[NOCSVAL + SCTOCSC(temp)];
755
if (variance != 0) /* avoid possible divide by 0 */
756
/* optimal estimate */
757
temp = mean + (variance * temp) / (variance + noisevariance);
761
temp = RUNSCALE(temp);
762
if (temp > (gdouble)255) temp = (gdouble)255;
767
/* Triangle orientation is per geometric axes (not graphical axies) */
769
#define NW 0 /* North west triangle /| */
770
#define NE 1 /* North east triangle |\ */
771
#define SW 2 /* South west triangle \| */
772
#define SE 3 /* South east triangle |/ */
776
#define SWAPI(a,b) (t = a, a = -b, b = -t)
778
/* compute the area of overlap of a hexagon diameter d, */
779
/* centered at hx,hy, with a unit square of center sx,sy. */
781
hex_area (gdouble sx, gdouble sy, gdouble hx, gdouble hy, gdouble d)
783
gdouble hx0,hx1,hx2,hy0,hy1,hy2,hy3;
784
gdouble sx0,sx1,sy0,sy1;
786
/* compute square co-ordinates */
791
/* compute hexagon co-ordinates */
795
hy0 = hy - 0.5773502692 * d; /* d / sqrt(3) */
796
hy1 = hy - 0.2886751346 * d; /* d / sqrt(12) */
797
hy2 = hy + 0.2886751346 * d; /* d / sqrt(12) */
798
hy3 = hy + 0.5773502692 * d; /* d / sqrt(3) */
801
triang_area(sx0,sy0,sx1,sy1,hx0,hy2,hx1,hy3,NW) +
802
triang_area(sx0,sy0,sx1,sy1,hx1,hy2,hx2,hy3,NE) +
803
rectang_area(sx0,sy0,sx1,sy1,hx0,hy1,hx2,hy2) +
804
triang_area(sx0,sy0,sx1,sy1,hx0,hy0,hx1,hy1,SW) +
805
triang_area(sx0,sy0,sx1,sy1,hx1,hy0,hx2,hy1,SE);
809
triang_area (gdouble rx0, gdouble ry0, gdouble rx1, gdouble ry1, gdouble tx0,
810
gdouble ty0, gdouble tx1, gdouble ty1, gint tt)
813
gdouble lx0,ly0,lx1,ly1;
814
/* Convert everything to a NW triangle */
824
/* Compute overlapping box */
833
if (rx1 <= rx0 || ry1 <= ry0)
835
/* Need to compute diagonal line intersection with the box */
836
/* First compute co-efficients to formulas x = a + by and y = c + dx */
837
b = (tx1 - tx0)/(ty1 - ty0);
839
d = (ty1 - ty0)/(tx1 - tx0);
842
/* compute top or right intersection */
847
return (rx1 - rx0) * (ry1 - ry0);
848
else if (lx1 > rx1) { /* could be right hand side */
852
return (rx1 - rx0) * (ry1 - ry0);
853
tt = 1; /* right hand side intersection */
855
/* compute left or bottom intersection */
859
return (rx1 - rx0) * (ry1 - ry0);
860
else if (ly0 < ry0) { /* could be right hand side */
864
return (rx1 - rx0) * (ry1 - ry0);
865
tt |= 2; /* bottom intersection */
868
if (tt == 0) { /* top and left intersection */
869
/* rectangle minus triangle */
870
return ((rx1 - rx0) * (ry1 - ry0))
871
- (0.5 * (lx1 - rx0) * (ry1 - ly0));
873
else if (tt == 1) { /* right and left intersection */
874
return ((rx1 - rx0) * (ly0 - ry0))
875
+ (0.5 * (rx1 - rx0) * (ly1 - ly0));
876
} else if (tt == 2) { /* top and bottom intersection */
877
return ((rx1 - lx1) * (ry1 - ry0))
878
+ (0.5 * (lx1 - lx0) * (ry1 - ry0));
879
} else { /* tt == 3 */ /* right and bottom intersection */
881
return (0.5 * (rx1 - lx0) * (ly1 - ry0));
885
/* Compute rectangle area */
887
rectang_area (gdouble rx0, gdouble ry0, gdouble rx1, gdouble ry1, gdouble tx0,
888
gdouble ty0, gdouble tx1, gdouble ty1)
890
/* Compute overlapping box */
899
if (rx1 <= rx0 || ry1 <= ry0)
901
return (rx1 - rx0) * (ry1 - ry0);
905
nlfilter (GimpDrawable *drawable,
906
GimpPreview *preview)
908
GimpPixelRgn srcPr, dstPr;
909
guchar *srcbuf, *dstbuf;
910
guchar *lastrow, *thisrow, *nextrow, *temprow;
912
guint width, height, bpp;
913
gint filtno, y, rowsize, exrowsize, p_update;
917
gimp_preview_get_position (preview, &x1, &y1);
918
gimp_preview_get_size (preview, &width, &height);
924
gimp_drawable_mask_bounds (drawable->drawable_id, &x1, &y1, &x2, &y2);
930
rowsize = width * bpp;
931
exrowsize = (width + 2) * bpp;
932
p_update = width / 20 + 1;
934
gimp_tile_cache_ntiles (2 * (width / gimp_tile_width () + 1));
936
gimp_pixel_rgn_init (&srcPr, drawable,
937
x1, y1, width, height, FALSE, FALSE);
938
gimp_pixel_rgn_init (&dstPr, drawable,
939
x1, y1, width, height,
940
preview == NULL, TRUE);
942
/* source buffer gives one pixel margin all around destination buffer */
943
srcbuf = g_new0 (guchar, exrowsize * 3);
944
dstbuf = g_new0 (guchar, rowsize);
946
/* pointers to second pixel in each source row */
947
lastrow = srcbuf + bpp;
948
thisrow = lastrow + exrowsize;
949
nextrow = thisrow + exrowsize;
951
filtno = nlfiltInit (nlfvals.alpha, nlfvals.radius, nlfvals.filter);
954
gimp_progress_init (_("NL Filter..."));
957
gimp_pixel_rgn_get_row (&srcPr, thisrow, x1, y1, width);
958
/* copy thisrow[0] to thisrow[-1], thisrow[width-1] to thisrow[width] */
959
memcpy (thisrow - bpp, thisrow, bpp);
960
memcpy (thisrow + rowsize, thisrow + rowsize - bpp, bpp);
961
/* copy whole thisrow to lastrow */
962
memcpy (lastrow - bpp, thisrow - bpp, exrowsize);
964
for (y = y1; y < y2 - 1; y++)
966
if (((y % p_update) == 0) && !preview)
967
gimp_progress_update ((gdouble) y / (gdouble) height);
969
gimp_pixel_rgn_get_row (&srcPr, nextrow, x1, y + 1, width);
970
memcpy (nextrow - bpp, nextrow, bpp);
971
memcpy (nextrow + rowsize, nextrow + rowsize - bpp, bpp);
972
nlfiltRow (lastrow, thisrow, nextrow, dstbuf, width, bpp, filtno);
973
gimp_pixel_rgn_set_row (&dstPr, dstbuf, x1, y, width);
974
/* rotate row buffers */
975
temprow = lastrow; lastrow = thisrow;
976
thisrow = nextrow; nextrow = temprow;
980
memcpy (nextrow - bpp, thisrow - bpp, exrowsize);
981
nlfiltRow (lastrow, thisrow, nextrow, dstbuf, width, bpp, filtno);
982
gimp_pixel_rgn_set_row (&dstPr, dstbuf, x1, y2 - 1, width);
989
gimp_drawable_preview_draw_region (GIMP_DRAWABLE_PREVIEW (preview),
994
gimp_drawable_flush (drawable);
995
gimp_drawable_merge_shadow (drawable->drawable_id, TRUE);
996
gimp_drawable_update (drawable->drawable_id, x1, y1, width, height);
997
gimp_displays_flush ();
1002
nlfilter_dialog (GimpDrawable *drawable)
1005
GtkWidget *main_vbox;
1008
GtkWidget *alpha_trim;
1010
GtkWidget *edge_enhance;
1015
gimp_ui_init ("nlfilt", TRUE);
1017
dialog = gimp_dialog_new (_("NL Filter"), "nlfilt",
1019
gimp_standard_help_func, "plug-in-nlfilt",
1021
GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1022
GTK_STOCK_OK, GTK_RESPONSE_OK,
1026
main_vbox = gtk_vbox_new (FALSE, 12);
1027
gtk_container_set_border_width (GTK_CONTAINER (main_vbox), 12);
1028
gtk_container_add (GTK_CONTAINER (GTK_DIALOG (dialog)->vbox), main_vbox);
1029
gtk_widget_show (main_vbox);
1031
preview = gimp_drawable_preview_new (drawable, &nlfvals.preview);
1032
gtk_box_pack_start_defaults (GTK_BOX (main_vbox), preview);
1033
gtk_widget_show (preview);
1034
g_signal_connect_swapped (preview, "invalidated",
1035
G_CALLBACK (nlfilter),
1038
frame = gimp_int_radio_group_new (TRUE, _("Filter"),
1039
G_CALLBACK (gimp_radio_button_update),
1040
&nlfvals.filter, nlfvals.filter,
1042
_("_Alpha trimmed mean"),
1043
filter_alpha_trim, &alpha_trim,
1044
_("Op_timal estimation"),
1045
filter_opt_est, &opt_est,
1046
_("_Edge enhancement"),
1047
filter_edge_enhance, &edge_enhance,
1051
gtk_box_pack_start (GTK_BOX (main_vbox), frame, FALSE, FALSE, 0);
1052
gtk_widget_show (frame);
1054
g_signal_connect_swapped (alpha_trim, "toggled",
1055
G_CALLBACK (gimp_preview_invalidate),
1057
g_signal_connect_swapped (opt_est, "toggled",
1058
G_CALLBACK (gimp_preview_invalidate),
1060
g_signal_connect_swapped (edge_enhance, "toggled",
1061
G_CALLBACK (gimp_preview_invalidate),
1064
table = gtk_table_new (2, 3, FALSE);
1065
gtk_table_set_col_spacings (GTK_TABLE (table), 6);
1066
gtk_table_set_row_spacings (GTK_TABLE (table), 6);
1067
gtk_box_pack_start (GTK_BOX (main_vbox), table, FALSE, FALSE, 0);
1068
gtk_widget_show (table);
1070
adj = gimp_scale_entry_new (GTK_TABLE (table), 0, 0,
1072
nlfvals.alpha, 0.0, 1.0, 0.05, 0.1, 2,
1075
g_signal_connect (adj, "value_changed",
1076
G_CALLBACK (gimp_double_adjustment_update),
1078
g_signal_connect_swapped (adj, "value_changed",
1079
G_CALLBACK (gimp_preview_invalidate),
1082
adj = gimp_scale_entry_new (GTK_TABLE (table), 0, 1,
1083
_("_Radius:"), 0, 0,
1084
nlfvals.radius, 1.0 / 3.0, 1.0, 0.05, 0.1, 2,
1087
g_signal_connect (adj, "value_changed",
1088
G_CALLBACK (gimp_double_adjustment_update),
1090
g_signal_connect_swapped (adj, "value_changed",
1091
G_CALLBACK (gimp_preview_invalidate),
1094
gtk_widget_show (dialog);
1096
run = (gimp_dialog_run (GIMP_DIALOG (dialog)) == GTK_RESPONSE_OK);
1098
gtk_widget_destroy (dialog);