4
* $Id: hot.c,v 1.38 2004/10/12 21:48:38 mitch Exp $
8
* hot.c - Scan an image for pixels with RGB values that will give
9
* "unsafe" values of chrominance signal or composite signal
10
* amplitude when encoded into an NTSC or PAL colour signal.
11
* (This happens for certain high-intensity high-saturation colours
12
* that are rare in real scenes, but can easily be present
13
* in synthetic images.)
15
* Such pixels can be flagged so the user may then choose other
16
* colours. Or, the offending pixels can be made "safe"
17
* in a manner that preserves hue.
19
* There are two reasonable ways to make a pixel "safe":
20
* We can reduce its intensity (luminance) while leaving
21
* hue and saturation the same. Or, we can reduce saturation
22
* while leaving hue and luminance the same. A #define selects
23
* which strategy to use.
25
* Note to the user: You must add your own read_pixel() and write_pixel()
26
* routines. You may have to modify pix_decode() and pix_encode().
27
* MAXPIX, WID, and HGT are likely to need modification.
31
* Originally written as "ikNTSC.c" by Alan Wm Paeth,
32
* University of Waterloo, August, 1985
33
* Updated by Dave Martindale, Imax Systems Corp., December 1990
37
* Compile time options:
40
* CHROMA_LIM is the limit (in IRE units) of the overall
41
* chrominance amplitude; it should be 50 or perhaps
42
* very slightly higher.
44
* COMPOS_LIM is the maximum amplitude (in IRE units) allowed for
45
* the composite signal. A value of 100 is the maximum
46
* monochrome white, and is always safe. 120 is the absolute
47
* limit for NTSC broadcasting, since the transmitter's carrier
48
* goes to zero with 120 IRE input signal. Generally, 110
49
* is a good compromise - it allows somewhat brighter colours
50
* than 100, while staying safely away from the hard limit.
56
* Define either NTSC or PAL as 1 to select the colour system.
57
* Define the other one as zero, or leave it undefined.
59
* Define FLAG_HOT as 1 if you want "hot" pixels set to black
60
* to identify them. Otherwise they will be made safe.
62
* Define REDUCE_SAT as 1 if you want hot pixels to be repaired by
63
* reducing their saturation. By default, luminance is reduced.
75
#include <libgimp/gimp.h>
76
#include <libgimp/gimpui.h>
78
#include "libgimp/stdplugins-intl.h"
102
#define CHROMA_LIM 50.0 /* chroma amplitude limit */
103
#define COMPOS_LIM 110.0 /* max IRE amplitude */
106
* RGB to YIQ encoding matrix.
119
{ 0.2989, 0.5866, 0.1144 },
120
{ 0.5959, -0.2741, -0.3218 },
121
{ 0.2113, -0.5227, 0.3113 }
128
{ 0.2989, 0.5866, 0.1144 },
129
{ -0.1473, -0.2891, 0.4364 },
130
{ 0.6149, -0.5145, -0.1004 }
136
#define SCALE 8192 /* scale factor: do floats with int math */
137
#define MAXPIX 255 /* white value */
139
static gint tab[3][3][MAXPIX+1]; /* multiply lookup table */
140
static gdouble chroma_lim; /* chroma limit */
141
static gdouble compos_lim; /* composite amplitude limit */
142
static glong ichroma_lim2; /* chroma limit squared (scaled integer) */
143
static gint icompos_lim; /* composite amplitude limit (scaled integer) */
145
static void query (void);
146
static void run (const gchar *name,
148
const GimpParam *param,
150
GimpParam **retvals);
152
static gint pluginCore (piArgs *argp);
153
static gint pluginCoreIA (piArgs *argp);
154
static gboolean hotp (guint8 r,
157
static void build_tab (gint m);
160
* gc: apply the gamma correction specified for this video standard.
161
* inv_gc: inverse function of gc.
163
* These are generally just a call to pow(), but be careful!
164
* Future standards may use more complex functions.
165
* (e.g. SMPTE 240M's "electro-optic transfer characteristic").
167
#define gc(x,m) pow(x, 1.0 / mode[m].gamma)
168
#define inv_gc(x,m) pow(x, mode[m].gamma)
171
* pix_decode: decode an integer pixel value into a floating-point
172
* intensity in the range [0, 1].
174
* pix_encode: encode a floating-point intensity into an integer
177
* The code given here assumes simple linear encoding; you must change
178
* these routines if you use a different pixel encoding technique.
180
#define pix_decode(v) ((double)v / (double)MAXPIX)
181
#define pix_encode(v) ((int)(v * (double)MAXPIX + 0.5))
183
GimpPlugInInfo PLUG_IN_INFO =
185
NULL, /* init_proc */
186
NULL, /* quit_proc */
187
query, /* query_proc */
196
static GimpParamDef args[] =
198
{ GIMP_PDB_INT32, "run_mode", "Interactive, non-interactive" },
199
{ GIMP_PDB_IMAGE, "image", "The Image" },
200
{ GIMP_PDB_DRAWABLE, "drawable", "The Drawable" },
201
{ GIMP_PDB_INT32, "mode", "Mode -- NTSC/PAL" },
202
{ GIMP_PDB_INT32, "action", "The action to perform" },
203
{ GIMP_PDB_INT32, "new_layerp", "Create a new layer iff True" }
206
gimp_install_procedure ("plug_in_hot",
207
"Look for hot NTSC or PAL pixels ",
208
"hot scans an image for pixels that will give unsave "
209
"values of chrominance or composite signale "
210
"amplitude when encoded into an NTSC or PAL signal. "
211
"Three actions can be performed on these ``hot'' "
212
"pixels. (0) reduce luminance, (1) reduce "
213
"saturation, or (2) Blacken.",
214
"Eric L. Hernes, Alan Wm Paeth",
220
G_N_ELEMENTS (args), 0,
223
gimp_plugin_menu_register ("plug_in_hot", "<Image>/Filters/Colors");
227
run (const gchar *name,
229
const GimpParam *param,
233
static GimpParam rvals[1];
241
memset (&args, (int) 0, sizeof (args));
244
gimp_get_data ("plug_in_hot", &args);
246
args.image = param[1].data.d_image;
247
args.drawable = param[2].data.d_drawable;
249
rvals[0].type = GIMP_PDB_STATUS;
250
rvals[0].data.d_status = GIMP_PDB_SUCCESS;
252
switch (param[0].data.d_int32)
254
case GIMP_RUN_INTERACTIVE:
255
/* XXX: add code here for interactive running */
258
args.mode = MODE_NTSC;
259
args.action = ACT_LREDUX;
263
if (pluginCoreIA(&args) == -1)
265
rvals[0].data.d_status = GIMP_PDB_EXECUTION_ERROR;
267
gimp_set_data ("plug_in_hot", &args, sizeof (args));
271
case GIMP_RUN_NONINTERACTIVE:
272
/* XXX: add code here for non-interactive running */
275
rvals[0].data.d_status = GIMP_PDB_CALLING_ERROR;
278
args.mode = param[3].data.d_int32;
279
args.action = param[4].data.d_int32;
280
args.new_layerp = param[5].data.d_int32;
282
if (pluginCore(&args) == -1)
284
rvals[0].data.d_status = GIMP_PDB_EXECUTION_ERROR;
289
case GIMP_RUN_WITH_LAST_VALS:
290
/* XXX: add code here for last-values running */
291
if (pluginCore (&args) == -1)
293
rvals[0].data.d_status = GIMP_PDB_EXECUTION_ERROR;
300
pluginCore (piArgs *argp)
302
GimpDrawable *drw, *ndrw=NULL;
303
GimpPixelRgn srcPr, dstPr;
308
guint width, height, bpp;
309
gint sel_x1, sel_x2, sel_y1, sel_y2;
311
guchar *src, *s, *dst, *d;
312
guchar r, prev_r=0, new_r=0;
313
guchar g, prev_g=0, new_g=0;
314
guchar b, prev_b=0, new_b=0;
315
gdouble fy, fc, t, scale;
319
drw = gimp_drawable_get (argp->drawable);
322
height = drw->height;
324
if (argp->new_layerp)
327
gchar *mode_names[] =
332
gchar *action_names[] =
339
g_snprintf (name, sizeof (name), "hot mask (%s, %s)",
340
mode_names[argp->mode],
341
action_names[argp->action]);
343
nl = gimp_layer_new (argp->image, name, width, height,
344
GIMP_RGBA_IMAGE, (gdouble)100, GIMP_NORMAL_MODE);
345
ndrw = gimp_drawable_get (nl);
346
gimp_drawable_fill (nl, GIMP_TRANSPARENT_FILL);
347
gimp_image_add_layer (argp->image, nl, 0);
350
gimp_drawable_mask_bounds (drw->drawable_id,
351
&sel_x1, &sel_y1, &sel_x2, &sel_y2);
353
width = sel_x2 - sel_x1;
354
height = sel_y2 - sel_y1;
356
src = g_new (guchar, width * height * bpp);
357
dst = g_new (guchar, width * height * 4);
358
gimp_pixel_rgn_init (&srcPr, drw, sel_x1, sel_y1, width, height,
361
if (argp->new_layerp)
363
gimp_pixel_rgn_init (&dstPr, ndrw, sel_x1, sel_y1, width, height,
368
gimp_pixel_rgn_init (&dstPr, drw, sel_x1, sel_y1, width, height,
372
gimp_pixel_rgn_get_rect (&srcPr, src, sel_x1, sel_y1, width, height);
377
build_tab (argp->mode);
379
gimp_progress_init (_("Hot..."));
380
prog_interval = height / 10;
382
for (y = sel_y1; y < sel_y2; y++)
384
if (y % prog_interval == 0)
385
gimp_progress_update ((double) y / (double) (sel_y2 - sel_y1));
387
for (x = sel_x1; x < sel_x2; x++)
389
if (hotp (r = *(s + 0), g = *(s + 1), b = *(s + 2)))
391
if (argp->action == ACT_FLAG)
393
for (i = 0; i < 3; i++)
398
else if (argp->new_layerp)
404
* Optimization: cache the last-computed hot pixel.
406
if (r == prev_r && g == prev_g && b == prev_b)
414
else if (argp->new_layerp)
419
Y = tab[0][0][r] + tab[0][1][g] + tab[0][2][b];
420
I = tab[1][0][r] + tab[1][1][g] + tab[1][2][b];
421
Q = tab[2][0][r] + tab[2][1][g] + tab[2][2][b];
427
* Get Y and chroma amplitudes in floating point.
429
* If your C library doesn't have hypot(), just use
430
* hypot(a,b) = sqrt(a*a, b*b);
432
* Then extract linear (un-gamma-corrected)
433
* floating-point pixel RGB values.
435
fy = (double)Y / (double)SCALE;
436
fc = hypot ((double) I / (double) SCALE,
437
(double) Q / (double) SCALE);
439
pr = (double) pix_decode (r);
440
pg = (double) pix_decode (g);
441
pb = (double) pix_decode (b);
444
* Reducing overall pixel intensity by scaling R,
445
* G, and B reduces Y, I, and Q by the same factor.
446
* This changes luminance but not saturation, since
447
* saturation is determined by the chroma/luminance
450
* On the other hand, by linearly interpolating
451
* between the original pixel value and a grey
452
* pixel with the same luminance (R=G=B=Y), we
453
* change saturation without affecting luminance.
455
if (argp->action == ACT_LREDUX)
458
* Calculate a scale factor that will bring the pixel
459
* within both chroma and composite limits, if we scale
460
* luminance and chroma simultaneously.
462
* The calculated chrominance reduction applies
463
* to the gamma-corrected RGB values that are
464
* the input to the RGB-to-YIQ operation.
465
* Multiplying the original un-gamma-corrected
466
* pixel values by the scaling factor raised to
467
* the "gamma" power is equivalent, and avoids
468
* calling gc() and inv_gc() three times each. */
469
scale = chroma_lim / fc;
470
t = compos_lim / (fy + fc);
473
scale = pow (scale, mode[argp->mode].gamma);
475
r = (guint8) pix_encode (scale * pr);
476
g = (guint8) pix_encode (scale * pg);
477
b = (guint8) pix_encode (scale * pb);
480
{ /* ACT_SREDUX hopefully */
482
* Calculate a scale factor that will bring the
483
* pixel within both chroma and composite
484
* limits, if we scale chroma while leaving
485
* luminance unchanged.
487
* We have to interpolate gamma-corrected RGB
488
* values, so we must convert from linear to
489
* gamma-corrected before interpolation and then
490
* back to linear afterwards.
492
scale = chroma_lim / fc;
493
t = (compos_lim - fy) / fc;
497
pr = gc (pr, argp->mode);
498
pg = gc (pg, argp->mode);
499
pb = gc (pb, argp->mode);
500
py = pr * mode[argp->mode].code[0][0] + pg *
501
mode[argp->mode].code[0][1] + pb *
502
mode[argp->mode].code[0][2];
503
r = pix_encode (inv_gc (py + scale * (pr - py),
505
g = pix_encode (inv_gc (py + scale * (pg - py),
507
b = pix_encode (inv_gc (py + scale * (pb - py),
516
else if (argp->new_layerp)
523
if (!argp->new_layerp)
525
for (i = 0; i < bpp; i++)
536
gimp_pixel_rgn_set_rect (&dstPr, dst, sel_x1, sel_y1, width, height);
541
if (argp->new_layerp)
543
gimp_drawable_flush (ndrw);
544
gimp_drawable_update (nl, sel_x1, sel_y1, width, height);
548
gimp_drawable_flush (drw);
549
gimp_drawable_merge_shadow (drw->drawable_id, TRUE);
550
gimp_drawable_update (drw->drawable_id, sel_x1, sel_y1, width, height);
553
gimp_displays_flush ();
559
pluginCoreIA (piArgs *argp)
568
gimp_ui_init ("hot", FALSE);
570
dlg = gimp_dialog_new (_("Hot"), "hot",
572
gimp_standard_help_func, "plug-in-hot",
574
GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
575
GTK_STOCK_OK, GTK_RESPONSE_OK,
579
hbox = gtk_hbox_new (FALSE, 12);
580
gtk_container_set_border_width (GTK_CONTAINER (hbox), 12);
581
gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dlg)->vbox), hbox, TRUE, TRUE, 0);
582
gtk_widget_show (hbox);
584
vbox = gtk_vbox_new (FALSE, 12);
585
gtk_box_pack_start (GTK_BOX (hbox), vbox, TRUE, TRUE, 0);
586
gtk_widget_show (vbox);
588
frame = gimp_int_radio_group_new (TRUE, _("Mode"),
589
G_CALLBACK (gimp_radio_button_update),
590
&argp->mode, argp->mode,
592
"N_TSC", MODE_NTSC, NULL,
593
"_PAL", MODE_PAL, NULL,
597
gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
598
gtk_widget_show (frame);
600
toggle = gtk_check_button_new_with_mnemonic (_("Create _New layer"));
601
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), argp->new_layerp);
602
gtk_box_pack_start (GTK_BOX (vbox), toggle, FALSE, FALSE, 0);
603
gtk_widget_show (toggle);
605
g_signal_connect (toggle, "toggled",
606
G_CALLBACK (gimp_toggle_button_update),
609
frame = gimp_int_radio_group_new (TRUE, _("Action"),
610
G_CALLBACK (gimp_radio_button_update),
611
&argp->action, argp->action,
613
_("Reduce _Luminance"), ACT_LREDUX, NULL,
614
_("Reduce _Saturation"), ACT_SREDUX, NULL,
615
_("_Blacken"), ACT_FLAG, NULL,
619
gtk_box_pack_start (GTK_BOX (hbox), frame, FALSE, FALSE, 0);
620
gtk_widget_show (frame);
622
gtk_widget_show (dlg);
624
run = (gimp_dialog_run (GIMP_DIALOG (dlg)) == GTK_RESPONSE_OK);
626
gtk_widget_destroy (dlg);
629
return pluginCore (argp);
635
* build_tab: Build multiply lookup table.
637
* For each possible pixel value, decode value into floating-point
638
* intensity. Then do gamma correction required by the video
639
* standard. Scale the result by our fixed-point scale factor.
640
* Then calculate 9 lookup table entries for this pixel value.
642
* We also calculate floating-point and scaled integer versions
643
* of our limits here. This prevents evaluating expressions every pixel
644
* when the compiler is too stupid to evaluate constant-valued
645
* floating-point expressions at compile time.
647
* For convenience, the limits are #defined using IRE units.
648
* We must convert them here into the units in which YIQ
649
* are measured. The conversion from IRE to internal units
650
* depends on the pedestal level in use, since as Y goes from
651
* 0 to 1, the signal goes from the pedestal level to 100 IRE.
652
* Chroma is always scaled to remain consistent with Y.
660
for (pv = 0; pv <= MAXPIX; pv++)
662
f = (double)SCALE * (double)gc((double)pix_decode(pv),m);
663
tab[0][0][pv] = (int)(f * mode[m].code[0][0] + 0.5);
664
tab[0][1][pv] = (int)(f * mode[m].code[0][1] + 0.5);
665
tab[0][2][pv] = (int)(f * mode[m].code[0][2] + 0.5);
666
tab[1][0][pv] = (int)(f * mode[m].code[1][0] + 0.5);
667
tab[1][1][pv] = (int)(f * mode[m].code[1][1] + 0.5);
668
tab[1][2][pv] = (int)(f * mode[m].code[1][2] + 0.5);
669
tab[2][0][pv] = (int)(f * mode[m].code[2][0] + 0.5);
670
tab[2][1][pv] = (int)(f * mode[m].code[2][1] + 0.5);
671
tab[2][2][pv] = (int)(f * mode[m].code[2][2] + 0.5);
674
chroma_lim = (double)CHROMA_LIM / (100.0 - mode[m].pedestal);
675
compos_lim = ((double)COMPOS_LIM - mode[m].pedestal) /
676
(100.0 - mode[m].pedestal);
678
ichroma_lim2 = (int)(chroma_lim * SCALE + 0.5);
679
ichroma_lim2 *= ichroma_lim2;
680
icompos_lim = (int)(compos_lim * SCALE + 0.5);
692
* Pixel decoding, gamma correction, and matrix multiplication
693
* all done by lookup table.
695
* "i" and "q" are the two chrominance components;
696
* they are I and Q for NTSC.
697
* For PAL, "i" is U (scaled B-Y) and "q" is V (scaled R-Y).
698
* Since we only care about the length of the chroma vector,
699
* not its angle, we don't care which is which.
701
y = tab[0][0][r] + tab[0][1][g] + tab[0][2][b];
702
i = tab[1][0][r] + tab[1][1][g] + tab[1][2][b];
703
q = tab[2][0][r] + tab[2][1][g] + tab[2][2][b];
706
* Check to see if the chrominance vector is too long or the
707
* composite waveform amplitude is too large.
709
* Chrominance is too large if
711
* sqrt(i^2, q^2) > chroma_lim.
713
* The composite signal amplitude is too large if
715
* y + sqrt(i^2, q^2) > compos_lim.
717
* We avoid doing the sqrt by checking
719
* i^2 + q^2 > chroma_lim^2
721
* y + sqrt(i^2 + q^2) > compos_lim
722
* sqrt(i^2 + q^2) > compos_lim - y
723
* i^2 + q^2 > (compos_lim - y)^2
727
c2 = (long)i * i + (long)q * q;
728
y2 = (long)icompos_lim - y;
731
if (c2 <= ichroma_lim2 && c2 <= y2)