redeye 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/README.md +5 -0
- data/Rakefile +48 -0
- data/ext/redeye/extconf.rb +102 -0
- data/ext/redeye/redeye.c +885 -0
- data/ext/redeye/redeye.cr +650 -0
- data/ext/redeye/redeye.rd +23 -0
- data/lib/redeye.rb +2 -0
- metadata +93 -0
@@ -0,0 +1,650 @@
|
|
1
|
+
%pkg-config gtk+-2.0
|
2
|
+
%include gdk-pixbuf/gdk-pixbuf.h
|
3
|
+
|
4
|
+
|
5
|
+
%map VALUE > GdkPixbuf* : GDK_PIXBUF(RVAL2GOBJ(%%))
|
6
|
+
%map GdkPixbuf* > VALUE : GOBJ2RVAL(GDK_PIXBUF(%%))
|
7
|
+
%map unref_pixbuf > VALUE : unref_pixbuf((%%))
|
8
|
+
|
9
|
+
%{
|
10
|
+
|
11
|
+
#define assert(x) if (!(x)) { rb_raise(rb_eRuntimeError, "Assertion: '%s' failed.", #x); }
|
12
|
+
|
13
|
+
typedef struct {
|
14
|
+
char red,green,blue;
|
15
|
+
} rgb_t;
|
16
|
+
|
17
|
+
typedef struct {
|
18
|
+
int minX, maxX, minY, maxY;
|
19
|
+
int width, height;
|
20
|
+
int noPixels, mergeWith;
|
21
|
+
} region_info;
|
22
|
+
|
23
|
+
typedef struct {
|
24
|
+
struct {
|
25
|
+
int minX, maxX, minY, maxY;
|
26
|
+
int width, height;
|
27
|
+
} area;
|
28
|
+
struct {
|
29
|
+
int *data;
|
30
|
+
region_info *region;
|
31
|
+
int len, size;
|
32
|
+
} regions;
|
33
|
+
int *mask;
|
34
|
+
GdkPixbuf *pixbuf, *preview;
|
35
|
+
} redeyeop_t;
|
36
|
+
|
37
|
+
#define MIN_RED_VAL 20
|
38
|
+
|
39
|
+
static inline VALUE
|
40
|
+
unref_pixbuf(GdkPixbuf *pixbuf)
|
41
|
+
{
|
42
|
+
volatile VALUE pb = Qnil;
|
43
|
+
|
44
|
+
pb = GOBJ2RVAL(pixbuf);
|
45
|
+
|
46
|
+
g_object_unref(pixbuf);
|
47
|
+
|
48
|
+
return pb;
|
49
|
+
}
|
50
|
+
|
51
|
+
static void identify_possible_redeye_pixels(redeyeop_t *op,
|
52
|
+
double green_sensitivity, double blue_sensitivity,
|
53
|
+
int min_red_val)
|
54
|
+
{
|
55
|
+
guchar *data = gdk_pixbuf_get_pixels(op->pixbuf);
|
56
|
+
int rowstride = gdk_pixbuf_get_rowstride(op->pixbuf);
|
57
|
+
int pixWidth = gdk_pixbuf_get_has_alpha(op->pixbuf) ? 4 : 3;
|
58
|
+
|
59
|
+
int y, ry = 0, x, rx = 0;
|
60
|
+
for ( y = op->area.minY; y < op->area.maxY; y++ )
|
61
|
+
{
|
62
|
+
guchar *thisLine = data + (rowstride * y);
|
63
|
+
guchar *pixel;
|
64
|
+
|
65
|
+
pixel = thisLine + (op->area.minX * pixWidth);
|
66
|
+
rx = 0;
|
67
|
+
|
68
|
+
for ( x = op->area.minX; x < op->area.maxX; x++ )
|
69
|
+
{
|
70
|
+
|
71
|
+
int r,g,b;
|
72
|
+
|
73
|
+
r = pixel[0];
|
74
|
+
g = pixel[1];
|
75
|
+
b = pixel[2];
|
76
|
+
|
77
|
+
gboolean threshMet;
|
78
|
+
|
79
|
+
threshMet = (((double)r) > (green_sensitivity * (double)g)) &&
|
80
|
+
(((double)r) > (blue_sensitivity * (double)b)) &&
|
81
|
+
(r > min_red_val);
|
82
|
+
|
83
|
+
if(threshMet)
|
84
|
+
op->mask[ rx + ry ] = r;
|
85
|
+
else
|
86
|
+
op->mask[ rx + ry ] = 0; /* MEMZERO should have done its job ? */
|
87
|
+
|
88
|
+
pixel += pixWidth;
|
89
|
+
rx ++;
|
90
|
+
}
|
91
|
+
|
92
|
+
ry += op->area.width;
|
93
|
+
}
|
94
|
+
}
|
95
|
+
|
96
|
+
|
97
|
+
inline int group_at(redeyeop_t *op, int px, int py)
|
98
|
+
{
|
99
|
+
int index, region;
|
100
|
+
|
101
|
+
if (px < 0 || py < 0)
|
102
|
+
return 0;
|
103
|
+
|
104
|
+
index = px + ( py * op->area.width );
|
105
|
+
|
106
|
+
if (index < 0)
|
107
|
+
return 0;
|
108
|
+
if (index > (op->area.width * op->area.height))
|
109
|
+
return 0;
|
110
|
+
|
111
|
+
region = op->regions.data[ index ];
|
112
|
+
if (region > 0) {
|
113
|
+
if (op->regions.region[ region ].mergeWith) {
|
114
|
+
return op->regions.region[ region ].mergeWith;
|
115
|
+
} else {
|
116
|
+
return region;
|
117
|
+
}
|
118
|
+
} else {
|
119
|
+
return 0;
|
120
|
+
}
|
121
|
+
}
|
122
|
+
|
123
|
+
#define group_for(x,y) group_at(op, x, y)
|
124
|
+
|
125
|
+
static void identify_blob_groupings(redeyeop_t *op)
|
126
|
+
{
|
127
|
+
volatile int next_blob_id = 1, blob_id, y, x;
|
128
|
+
|
129
|
+
|
130
|
+
for( y = 0; y < op->area.height; y++ )
|
131
|
+
{
|
132
|
+
for ( x = 0; x < op->area.width; x++ )
|
133
|
+
{
|
134
|
+
if (op->mask[ x + (y * op->area.width) ] > 0) {
|
135
|
+
gboolean existing = FALSE;
|
136
|
+
int sx, sy, group = 0;
|
137
|
+
// Target pixel is true
|
138
|
+
blob_id = 0;
|
139
|
+
|
140
|
+
for (sy = y; sy >= y - 1; sy --) {
|
141
|
+
sx = (sy == y) ? x : x + 1;
|
142
|
+
for (; sx >= (x - 1); sx --) {
|
143
|
+
/*if ((sx >= x) && (sy >= y))
|
144
|
+
goto blob_scan_done;*/
|
145
|
+
|
146
|
+
if (sx >= 0 && sy >= 0)
|
147
|
+
group = group_for(sx, sy);
|
148
|
+
|
149
|
+
if (group) {
|
150
|
+
existing = TRUE;
|
151
|
+
if (blob_id) {
|
152
|
+
int target = MIN(blob_id, group);
|
153
|
+
int from = MAX(blob_id, group);
|
154
|
+
|
155
|
+
if (op->regions.region[target].mergeWith > 0) {
|
156
|
+
// Already merged
|
157
|
+
target = op->regions.region[target].mergeWith;
|
158
|
+
}
|
159
|
+
op->regions.region[from].mergeWith = target;
|
160
|
+
|
161
|
+
// Merge blob_id & group
|
162
|
+
}
|
163
|
+
blob_id = group;
|
164
|
+
}
|
165
|
+
}
|
166
|
+
}
|
167
|
+
|
168
|
+
if (blob_id == 0)
|
169
|
+
{ // Allocate new group
|
170
|
+
blob_id = next_blob_id;
|
171
|
+
op->regions.region[blob_id].minX = x;
|
172
|
+
op->regions.region[blob_id].maxX = x;
|
173
|
+
op->regions.region[blob_id].minY = y;
|
174
|
+
op->regions.region[blob_id].maxY = y;
|
175
|
+
op->regions.region[blob_id].width = 1;
|
176
|
+
op->regions.region[blob_id].height = 1;
|
177
|
+
op->regions.region[blob_id].noPixels = 1;
|
178
|
+
op->regions.region[blob_id].mergeWith = 0;
|
179
|
+
|
180
|
+
next_blob_id ++;
|
181
|
+
op->regions.len = next_blob_id;
|
182
|
+
|
183
|
+
if (next_blob_id >= op->regions.size) {
|
184
|
+
int extra, new_size;
|
185
|
+
|
186
|
+
/*
|
187
|
+
* Realloc in increasingly large chunks to reduce memory fragmentation
|
188
|
+
*/
|
189
|
+
extra = op->regions.size;
|
190
|
+
new_size = op->regions.size + extra;
|
191
|
+
|
192
|
+
REALLOC_N(op->regions.region, region_info, new_size);
|
193
|
+
|
194
|
+
op->regions.size = new_size;
|
195
|
+
}
|
196
|
+
}
|
197
|
+
|
198
|
+
if (existing)
|
199
|
+
{
|
200
|
+
op->regions.region[blob_id].minX = MIN(x, op->regions.region[blob_id].minX);
|
201
|
+
op->regions.region[blob_id].maxX = MAX(x, op->regions.region[blob_id].maxX);
|
202
|
+
op->regions.region[blob_id].minY = MIN(y, op->regions.region[blob_id].minY);
|
203
|
+
op->regions.region[blob_id].maxY = MAX(y, op->regions.region[blob_id].maxY);
|
204
|
+
op->regions.region[blob_id].width = op->regions.region[blob_id].maxX -
|
205
|
+
op->regions.region[blob_id].minX + 1;
|
206
|
+
op->regions.region[blob_id].height = op->regions.region[blob_id].maxY -
|
207
|
+
op->regions.region[blob_id].minY + 1;
|
208
|
+
op->regions.region[blob_id].noPixels ++;
|
209
|
+
}
|
210
|
+
|
211
|
+
op->regions.data[ x + (y * op->area.width) ] = blob_id;
|
212
|
+
}
|
213
|
+
}
|
214
|
+
}
|
215
|
+
/*FILE *fp = fopen("regions.txt","w");*/
|
216
|
+
for (y = 0; y < op->area.height; y++) {
|
217
|
+
for (x = 0; x < op->area.width; x++) {
|
218
|
+
int g = group_at(op, x, y); // Returns the merged group...
|
219
|
+
op->regions.data[ x + (y * op->area.width) ] = g;
|
220
|
+
/*
|
221
|
+
if (op->regions.len <= 0xf || 1)
|
222
|
+
{
|
223
|
+
if (g == 0)
|
224
|
+
fprintf(fp, " ");
|
225
|
+
else
|
226
|
+
fprintf(fp, "%x", g);
|
227
|
+
}
|
228
|
+
else
|
229
|
+
{
|
230
|
+
if (g == 0)
|
231
|
+
fprintf(fp, " ");
|
232
|
+
else
|
233
|
+
fprintf(fp, "%x ", g);
|
234
|
+
}*/
|
235
|
+
}
|
236
|
+
/*fprintf(fp, "\n");*/
|
237
|
+
}
|
238
|
+
/*fclose(fp);*/
|
239
|
+
}
|
240
|
+
#define NO_REGIONS_DEFAULT 20
|
241
|
+
#define MIN_ID 1
|
242
|
+
|
243
|
+
|
244
|
+
static redeyeop_t *new_redeye(void)
|
245
|
+
{
|
246
|
+
redeyeop_t *ptr = ALLOC(redeyeop_t);
|
247
|
+
MEMZERO(ptr, redeyeop_t, 1);
|
248
|
+
return ptr;
|
249
|
+
}
|
250
|
+
|
251
|
+
static void free_redeye(redeyeop_t *ptr)
|
252
|
+
{
|
253
|
+
if (ptr->mask)
|
254
|
+
free(ptr->mask);
|
255
|
+
if (ptr->regions.data);
|
256
|
+
free(ptr->regions.data);
|
257
|
+
if (ptr->regions.region);
|
258
|
+
free(ptr->regions.region);
|
259
|
+
if (ptr->pixbuf)
|
260
|
+
g_object_unref(ptr->pixbuf);
|
261
|
+
if (ptr->preview)
|
262
|
+
g_object_unref(ptr->preview);
|
263
|
+
free(ptr);
|
264
|
+
}
|
265
|
+
|
266
|
+
|
267
|
+
inline gboolean in_region(redeyeop_t *op, int x, int y, int blob_id)
|
268
|
+
{
|
269
|
+
int index;
|
270
|
+
|
271
|
+
if ( x < op->area.minX || x > op->area.maxX ||
|
272
|
+
y < op->area.minY || y > op->area.maxY )
|
273
|
+
return FALSE;
|
274
|
+
|
275
|
+
index = (x - op->area.minX) + ((y - op->area.minY) * op->area.width);
|
276
|
+
|
277
|
+
return op->regions.data[index] == blob_id;
|
278
|
+
}
|
279
|
+
|
280
|
+
inline double alpha_level_for_pixel(redeyeop_t *op, int x, int y, int blob_id)
|
281
|
+
{
|
282
|
+
int j = 0, c = 0, xm, ym;
|
283
|
+
|
284
|
+
if (in_region(op, x, y, blob_id))
|
285
|
+
return 1.0;
|
286
|
+
|
287
|
+
for ( xm = -2; xm <= 2; xm++ )
|
288
|
+
{
|
289
|
+
for ( ym = -2; ym <= 2; ym ++ )
|
290
|
+
{
|
291
|
+
c ++;
|
292
|
+
if (xm == 0 && ym == 0)
|
293
|
+
continue;
|
294
|
+
if (in_region(op, x+xm, y+ym, blob_id))
|
295
|
+
j ++;
|
296
|
+
}
|
297
|
+
}
|
298
|
+
|
299
|
+
return ((double)j)/((double)c);
|
300
|
+
}
|
301
|
+
|
302
|
+
inline char col(double val)
|
303
|
+
{
|
304
|
+
if (val < 0) return 0;
|
305
|
+
if (val > 255) return 255;
|
306
|
+
return val;
|
307
|
+
|
308
|
+
}
|
309
|
+
|
310
|
+
static GdkPixbuf *redeye_preview(redeyeop_t *op, gboolean reset)
|
311
|
+
{
|
312
|
+
int width, height;
|
313
|
+
width = op->area.width;
|
314
|
+
height = op->area.height;
|
315
|
+
|
316
|
+
if (width + op->area.minX > gdk_pixbuf_get_width(op->pixbuf)) {
|
317
|
+
width = gdk_pixbuf_get_width(op->pixbuf) - op->area.minX;
|
318
|
+
}
|
319
|
+
if (height + op->area.minY > gdk_pixbuf_get_height(op->pixbuf)) {
|
320
|
+
height = gdk_pixbuf_get_height(op->pixbuf) - op->area.minY;
|
321
|
+
}
|
322
|
+
|
323
|
+
if ( op->preview == NULL )
|
324
|
+
{
|
325
|
+
GdkPixbuf *sub = NULL;
|
326
|
+
sub = gdk_pixbuf_new_subpixbuf(op->pixbuf, op->area.minX, op->area.minY,
|
327
|
+
width, height);
|
328
|
+
|
329
|
+
op->preview = gdk_pixbuf_copy(sub);
|
330
|
+
g_object_unref(sub);
|
331
|
+
} else if (reset) {
|
332
|
+
gdk_pixbuf_copy_area(op->pixbuf, op->area.minX, op->area.minY,
|
333
|
+
width, height, op->preview, 0, 0);
|
334
|
+
}
|
335
|
+
|
336
|
+
return op->preview;
|
337
|
+
}
|
338
|
+
|
339
|
+
static void desaturate_blob(redeyeop_t *op, int blob_id)
|
340
|
+
{
|
341
|
+
int y, x;
|
342
|
+
int minX, minY, maxX, maxY;
|
343
|
+
|
344
|
+
minY = MAX(0, op->area.minY + op->regions.region[blob_id].minY - 1);
|
345
|
+
maxY = MIN(op->area.maxY + op->regions.region[blob_id].maxY + 1,
|
346
|
+
gdk_pixbuf_get_height(op->pixbuf)-1);
|
347
|
+
minX = MAX(0, op->area.minX + op->regions.region[blob_id].minX - 1);
|
348
|
+
maxX = MIN(op->area.maxX + op->regions.region[blob_id].maxX + 1,
|
349
|
+
gdk_pixbuf_get_width(op->pixbuf)-1);
|
350
|
+
|
351
|
+
guchar *data = gdk_pixbuf_get_pixels(op->pixbuf);
|
352
|
+
int rowstride = gdk_pixbuf_get_rowstride(op->pixbuf);
|
353
|
+
int pixWidth = gdk_pixbuf_get_has_alpha(op->pixbuf) ? 4 : 3;
|
354
|
+
|
355
|
+
for ( y = minY; y <= maxY; y++ )
|
356
|
+
{
|
357
|
+
guchar *thisLine = data + (rowstride * y);
|
358
|
+
guchar *pixel;
|
359
|
+
|
360
|
+
pixel = thisLine + (minX * pixWidth);
|
361
|
+
|
362
|
+
for ( x = minX; x <= maxX; x++ )
|
363
|
+
{
|
364
|
+
|
365
|
+
double alpha = alpha_level_for_pixel(op, x, y, blob_id);
|
366
|
+
int r,g,b,grey;
|
367
|
+
|
368
|
+
r = pixel[0];
|
369
|
+
g = pixel[1];
|
370
|
+
b = pixel[2];
|
371
|
+
|
372
|
+
if (alpha > 0)
|
373
|
+
{
|
374
|
+
grey = alpha * ((double)( 5 * (double)r + 60 * (double)g + 30 * (double)b)) / 100.0 +
|
375
|
+
(1 - alpha) * r;
|
376
|
+
|
377
|
+
pixel[0] = col((grey * alpha) + (1-alpha) * r);
|
378
|
+
pixel[1] = col((grey * alpha) + (1-alpha) * g);
|
379
|
+
pixel[2] = col((grey * alpha) + (1-alpha) * b);
|
380
|
+
}
|
381
|
+
|
382
|
+
pixel += pixWidth;
|
383
|
+
}
|
384
|
+
}
|
385
|
+
|
386
|
+
}
|
387
|
+
|
388
|
+
static void highlight_blob(redeyeop_t *op, int blob_id, int colour)
|
389
|
+
{
|
390
|
+
int y, x;
|
391
|
+
int minX, minY, maxX, maxY;
|
392
|
+
int hr, hg, hb;
|
393
|
+
|
394
|
+
hr = (colour >> 16) & 0xff;
|
395
|
+
hg = (colour >> 8) & 0xff;
|
396
|
+
hb = (colour) & 0xff;
|
397
|
+
|
398
|
+
minY = MAX(0, op->area.minY - 1);
|
399
|
+
maxY = MIN(op->area.maxY + 1, gdk_pixbuf_get_height(op->pixbuf)-1);
|
400
|
+
minX = MAX(0, op->area.minX - 1);
|
401
|
+
maxX = MIN(op->area.maxX + 1, gdk_pixbuf_get_width(op->pixbuf)-1);
|
402
|
+
|
403
|
+
guchar *data = gdk_pixbuf_get_pixels(op->pixbuf);
|
404
|
+
int rowstride = gdk_pixbuf_get_rowstride(op->pixbuf);
|
405
|
+
int pixWidth = gdk_pixbuf_get_has_alpha(op->pixbuf) ? 4 : 3;
|
406
|
+
|
407
|
+
for ( y = minY; y <= maxY; y++ )
|
408
|
+
{
|
409
|
+
guchar *thisLine = data + (rowstride * y);
|
410
|
+
guchar *pixel;
|
411
|
+
|
412
|
+
pixel = thisLine + (minX * pixWidth);
|
413
|
+
|
414
|
+
for ( x = minX; x <= maxX; x++ )
|
415
|
+
{
|
416
|
+
|
417
|
+
double alpha = alpha_level_for_pixel(op, x, y, blob_id);
|
418
|
+
int r,g,b;
|
419
|
+
|
420
|
+
r = (pixel[0]);
|
421
|
+
g = (pixel[1]);
|
422
|
+
b = (pixel[2]);
|
423
|
+
|
424
|
+
|
425
|
+
if (alpha > 0)
|
426
|
+
{
|
427
|
+
|
428
|
+
pixel[0] = col((1-alpha) * r + (alpha * hr));
|
429
|
+
pixel[1] = col((1-alpha) * g + (alpha * hg));
|
430
|
+
pixel[2] = col((1-alpha) * b + (alpha * hb));
|
431
|
+
}
|
432
|
+
|
433
|
+
pixel += pixWidth;
|
434
|
+
}
|
435
|
+
}
|
436
|
+
|
437
|
+
}
|
438
|
+
|
439
|
+
|
440
|
+
static void preview_blob(redeyeop_t *op, int blob_id, int colour, gboolean reset_preview)
|
441
|
+
{
|
442
|
+
int y, x;
|
443
|
+
int minX, minY, maxX, maxY;
|
444
|
+
int hr, hg, hb;
|
445
|
+
|
446
|
+
redeye_preview(op, reset_preview);
|
447
|
+
|
448
|
+
hr = (colour >> 16) & 0xff;
|
449
|
+
hg = (colour >> 8) & 0xff;
|
450
|
+
hb = (colour) & 0xff;
|
451
|
+
|
452
|
+
minY = 0;
|
453
|
+
maxY = gdk_pixbuf_get_height(op->preview)-1;
|
454
|
+
minX = 0;
|
455
|
+
maxX = gdk_pixbuf_get_width(op->preview)-1;
|
456
|
+
|
457
|
+
guchar *data = gdk_pixbuf_get_pixels(op->preview);
|
458
|
+
int rowstride = gdk_pixbuf_get_rowstride(op->preview);
|
459
|
+
int pixWidth = gdk_pixbuf_get_has_alpha(op->preview) ? 4 : 3;
|
460
|
+
|
461
|
+
for ( y = minY; y <= maxY; y++ )
|
462
|
+
{
|
463
|
+
guchar *thisLine = data + (rowstride * y);
|
464
|
+
guchar *pixel;
|
465
|
+
|
466
|
+
pixel = thisLine + (minX * pixWidth);
|
467
|
+
|
468
|
+
for ( x = minX; x <= maxX; x++ )
|
469
|
+
{
|
470
|
+
|
471
|
+
double alpha = alpha_level_for_pixel(op, x + op->area.minX, y + op->area.minY, blob_id);
|
472
|
+
int r,g,b;
|
473
|
+
|
474
|
+
r = (pixel[0]);
|
475
|
+
g = (pixel[1]);
|
476
|
+
b = (pixel[2]);
|
477
|
+
|
478
|
+
|
479
|
+
if (alpha > 0)
|
480
|
+
{
|
481
|
+
|
482
|
+
pixel[0] = col((1-alpha) * r + (alpha * hr));
|
483
|
+
pixel[1] = col((1-alpha) * g + (alpha * hg));
|
484
|
+
pixel[2] = col((1-alpha) * b + (alpha * hb));
|
485
|
+
}
|
486
|
+
|
487
|
+
pixel += pixWidth;
|
488
|
+
}
|
489
|
+
}
|
490
|
+
|
491
|
+
}
|
492
|
+
|
493
|
+
%}
|
494
|
+
|
495
|
+
class RedEye
|
496
|
+
struct Region(op, id, minX, minY, maxX, maxY, width, height, noPixels)
|
497
|
+
def double:ratio
|
498
|
+
int width,height;
|
499
|
+
double min,max,ratio;
|
500
|
+
width = <{VALUE>int:rb_struct_getmember(self, rb_intern("width"))}>;
|
501
|
+
height = <{VALUE>int:rb_struct_getmember(self, rb_intern("height"))}>;
|
502
|
+
min = (double)MIN(width,height);
|
503
|
+
max = (double)MAX(width,height);
|
504
|
+
ratio = (min / max);
|
505
|
+
return ratio;
|
506
|
+
end
|
507
|
+
def double:density
|
508
|
+
int noPixels, width, height;
|
509
|
+
double density;
|
510
|
+
|
511
|
+
noPixels = <{VALUE>int:rb_struct_getmember(self, rb_intern("noPixels"))}>;
|
512
|
+
width = <{VALUE>int:rb_struct_getmember(self, rb_intern("width"))}>;
|
513
|
+
height = <{VALUE>int:rb_struct_getmember(self, rb_intern("height"))}>;
|
514
|
+
density = ((double)noPixels / (double)(width * height));
|
515
|
+
|
516
|
+
return density;
|
517
|
+
end
|
518
|
+
def gboolean:squareish?(double min_ratio = 0.5, double min_density = 0.5)
|
519
|
+
int noPixels, width, height;
|
520
|
+
double min, max, ratio, density;
|
521
|
+
|
522
|
+
noPixels = <{VALUE>int:rb_struct_getmember(self, rb_intern("noPixels"))}>;
|
523
|
+
width = <{VALUE>int:rb_struct_getmember(self, rb_intern("width"))}>;
|
524
|
+
height = <{VALUE>int:rb_struct_getmember(self, rb_intern("height"))}>;
|
525
|
+
|
526
|
+
min = (double)MIN(width,height);
|
527
|
+
max = (double)MAX(width,height);
|
528
|
+
ratio = (min / max);
|
529
|
+
|
530
|
+
density = ((double)noPixels / (double)(width * height));
|
531
|
+
|
532
|
+
return ((ratio >= min_ratio) && (density > min_density));
|
533
|
+
end
|
534
|
+
end
|
535
|
+
|
536
|
+
def __alloc__
|
537
|
+
return Data_Wrap_Struct(self, NULL, free_redeye, new_redeye());
|
538
|
+
end
|
539
|
+
|
540
|
+
def initialize(GdkPixbuf *pixbuf, int minX, int minY, int maxX, int maxY)
|
541
|
+
redeyeop_t *op;
|
542
|
+
|
543
|
+
Data_Get_Struct(self, redeyeop_t, op);
|
544
|
+
|
545
|
+
op->pixbuf = pixbuf;
|
546
|
+
op->preview = NULL;
|
547
|
+
g_object_ref(op->pixbuf);
|
548
|
+
|
549
|
+
op->area.minX = minX;
|
550
|
+
op->area.maxX = maxX;
|
551
|
+
op->area.minY = minY;
|
552
|
+
op->area.maxY = maxY;
|
553
|
+
op->area.width = maxX - minX + 1;
|
554
|
+
op->area.height = maxY - minY + 1;
|
555
|
+
|
556
|
+
assert(op->pixbuf != NULL);
|
557
|
+
assert(op->area.maxX <= gdk_pixbuf_get_width(op->pixbuf));
|
558
|
+
assert(op->area.minX >= 0);
|
559
|
+
assert(op->area.minX < op->area.maxX);
|
560
|
+
assert(op->area.maxY <= gdk_pixbuf_get_height(op->pixbuf));
|
561
|
+
assert(op->area.minY >= 0);
|
562
|
+
assert(op->area.minY < op->area.maxY);
|
563
|
+
|
564
|
+
|
565
|
+
op->mask = ALLOC_N(int, op->area.width * op->area.height);
|
566
|
+
|
567
|
+
op->regions.data = ALLOC_N(int, op->area.width * op->area.height);
|
568
|
+
|
569
|
+
op->regions.region = ALLOC_N(region_info, NO_REGIONS_DEFAULT);
|
570
|
+
|
571
|
+
op->regions.len = 0;
|
572
|
+
op->regions.size = NO_REGIONS_DEFAULT;
|
573
|
+
end
|
574
|
+
|
575
|
+
def identify_blobs(double green_sensitivity=2.0, double blue_sensitivity=0.0, int min_red_val = MIN_RED_VAL)
|
576
|
+
redeyeop_t *op;
|
577
|
+
|
578
|
+
Data_Get_Struct(self, redeyeop_t, op);
|
579
|
+
|
580
|
+
MEMZERO(op->mask, int, op->area.width * op->area.height);
|
581
|
+
MEMZERO(op->regions.data, int, op->area.width * op->area.height);
|
582
|
+
|
583
|
+
identify_possible_redeye_pixels(op, green_sensitivity, blue_sensitivity, min_red_val);
|
584
|
+
identify_blob_groupings(op);
|
585
|
+
|
586
|
+
volatile VALUE ary = rb_ary_new2(op->regions.len);
|
587
|
+
int i;
|
588
|
+
for (i = MIN_ID; i < op->regions.len; i++) {
|
589
|
+
region_info *r = &op->regions.region[i];
|
590
|
+
/* Ignore CCD noise */
|
591
|
+
if (r->noPixels < 2)
|
592
|
+
continue;
|
593
|
+
rb_ary_push(ary, rb_struct_new(structRegion, self, INT2NUM(i),
|
594
|
+
INT2NUM(r->minX), INT2NUM(r->minY), INT2NUM(r->maxX), INT2NUM(r->maxY),
|
595
|
+
INT2NUM(r->width), INT2NUM(r->height), INT2NUM(r->noPixels)));
|
596
|
+
}
|
597
|
+
return ary;
|
598
|
+
end
|
599
|
+
|
600
|
+
def correct_blob(int blob_id)
|
601
|
+
redeyeop_t *op;
|
602
|
+
|
603
|
+
Data_Get_Struct(self, redeyeop_t, op);
|
604
|
+
|
605
|
+
if (op->regions.len <= blob_id)
|
606
|
+
rb_raise(rb_eIndexError, "Only %i blobs in region - %i is invalid", op->regions.len, blob_id);
|
607
|
+
|
608
|
+
|
609
|
+
desaturate_blob(op, blob_id);
|
610
|
+
end
|
611
|
+
|
612
|
+
def highlight_blob(int blob_id, int col = 0x00ff00)
|
613
|
+
redeyeop_t *op;
|
614
|
+
|
615
|
+
Data_Get_Struct(self, redeyeop_t, op);
|
616
|
+
|
617
|
+
if (op->regions.len <= blob_id)
|
618
|
+
rb_raise(rb_eIndexError, "Only %i blobs in region - %i is invalid", op->regions.len, blob_id);
|
619
|
+
|
620
|
+
highlight_blob(op, blob_id, col);
|
621
|
+
end
|
622
|
+
|
623
|
+
def GdkPixbuf*:preview_blob(int blob_id, int col = 0x00ff00, gboolean reset_preview = TRUE)
|
624
|
+
redeyeop_t *op;
|
625
|
+
|
626
|
+
Data_Get_Struct(self, redeyeop_t, op);
|
627
|
+
|
628
|
+
if (op->regions.len <= blob_id)
|
629
|
+
rb_raise(rb_eIndexError, "Only %i blobs in region - %i is invalid", op->regions.len, blob_id);
|
630
|
+
|
631
|
+
preview_blob(op, blob_id, col, reset_preview);
|
632
|
+
|
633
|
+
return op->preview;
|
634
|
+
end
|
635
|
+
def GdkPixbuf*:preview
|
636
|
+
redeyeop_t *op;
|
637
|
+
|
638
|
+
Data_Get_Struct(self, redeyeop_t, op);
|
639
|
+
|
640
|
+
return redeye_preview(op, FALSE);
|
641
|
+
end
|
642
|
+
def GdkPixbuf*:pixbuf
|
643
|
+
redeyeop_t *op;
|
644
|
+
|
645
|
+
Data_Get_Struct(self, redeyeop_t, op);
|
646
|
+
|
647
|
+
return op->pixbuf;
|
648
|
+
end
|
649
|
+
end
|
650
|
+
|