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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 6f0acad32d2e9ee0122e452c3cff1f4823cd4193
4
+ data.tar.gz: fbc4592fd714bf7bbddd1a4e0631484e8f56b590
5
+ SHA512:
6
+ metadata.gz: d481371139a391fa4b863e11b65b8b6599da2cf7d021a25f92250a2f835d018cda834c490baa5b72a6d479ca58a64bc07092ffc7c4d8fae60c425680a264f2df
7
+ data.tar.gz: 6211f766b9827c43c6001e7f0b30ac9ce47b1389059fa590a93e70a10cd34f7117e1d091f8c2b03037ea7fc1b33d1f4d888fb618e5606f6c0c9204f30e8cd2ec
@@ -0,0 +1,5 @@
1
+ redye
2
+ =====
3
+
4
+ Simple Ruby / GdkPixbuf redeye correction library
5
+
@@ -0,0 +1,48 @@
1
+ require 'rubygems'
2
+ gem 'rake-compiler'
3
+ require 'rake/extensiontask'
4
+ BASE_DIR = Dir.pwd
5
+ require 'rubygems/package_task'
6
+ require 'rake/testtask'
7
+
8
+ exts = []
9
+
10
+ namespace :prepare do
11
+ FileList["ext/*/*.cr"].each do |cr|
12
+ dir = File.dirname(cr)
13
+ name = File.basename(dir)
14
+ desc "Generate source for #{name}"
15
+ task(name.intern) do
16
+ sh 'rubber-generate', '--build-dir', dir, cr
17
+ end
18
+ end
19
+ end
20
+
21
+ spec = Gem::Specification.new do |s|
22
+ s.name = "redeye"
23
+ s.author = "Geoff Youngs"
24
+ s.email = "git@intersect-uk.co.uk"
25
+ s.version = "1.0.0"
26
+ s.homepage = "http://github.com/geoffyoungs/redeye"
27
+ s.summary = "Redeye correction for Gdk::Pixbuf"
28
+ s.add_dependency("rubber-generate", ">= 0.0.17")
29
+ s.add_dependency("glib2", ">= 1.1.9")
30
+ s.add_dependency("gdk_pixbuf2", ">= 1.1.9")
31
+ s.platform = Gem::Platform::RUBY
32
+ s.extensions = FileList["ext/*/extconf.rb"]
33
+ s.files = FileList['ext/*/*.{c,h,cr,rd}'] + ['Rakefile', 'README.md'] + FileList['lib/**/*.rb']
34
+ s.description = <<-EOF
35
+ Redeye correction methods for redeye
36
+ EOF
37
+ end
38
+ Gem::PackageTask.new(spec) do |pkg|
39
+ pkg.need_tar = true
40
+ end
41
+ Rake::ExtensionTask.new("redeye", spec)
42
+
43
+ Rake::TestTask.new do |t|
44
+ t.test_files = FileList['test/*_test.rb']
45
+ end
46
+
47
+ task :default, :compile
48
+
@@ -0,0 +1,102 @@
1
+ require 'mkmf'
2
+ use_gems = false
3
+ begin
4
+ require 'mkmf-gnome2'
5
+ rescue LoadError
6
+ use_gems = true
7
+ end
8
+
9
+ if use_gems or Object.const_defined?('Gem')
10
+ require 'rubygems'
11
+ gem 'glib2'
12
+ require 'mkmf-gnome2'
13
+ %w[rbglib.h rbgtk.h rbpango.h rbatk.h].each do |header|
14
+ Gem.find_files(header).each do |f|
15
+ $CFLAGS += " '-I#{File.dirname(f)}'"
16
+ end
17
+ end
18
+ end
19
+ # Look for headers in {gem_root}/ext/{package}
20
+ if use_gems
21
+ %w[
22
+ glib2 gdk_pixbuf2 atk gtk2 ].each do |package|
23
+ require package
24
+ $CFLAGS += " -I"+Gem.loaded_specs[package].full_gem_path+"/ext/"+package
25
+ end
26
+ end
27
+ if RbConfig::CONFIG.has_key?('rubyhdrdir')
28
+ $CFLAGS += " -I" + RbConfig::CONFIG['rubyhdrdir']+'/ruby'
29
+ end
30
+
31
+ $CFLAGS += " -I."
32
+ have_func("rb_errinfo")
33
+ PKGConfig.have_package("gtk+-2.0") or exit(-1)
34
+ have_header("gdk-pixbuf/gdk-pixbuf.h") or exit(-1)
35
+
36
+ STDOUT.print("checking for new allocation framework... ") # for ruby-1.7
37
+ if Object.respond_to? :allocate
38
+ STDOUT.print "yes
39
+ "
40
+ $defs << "-DHAVE_OBJECT_ALLOCATE"
41
+ else
42
+ STDOUT.print "no
43
+ "
44
+ end
45
+
46
+ top = File.expand_path(File.dirname(__FILE__) + '/..') # XXX
47
+ $CFLAGS += " " + ['glib/src'].map{|d|
48
+ "-I" + File.join(top, d)
49
+ }.join(" ")
50
+
51
+ have_func("rb_define_alloc_func") # for ruby-1.8
52
+
53
+ #set_output_lib('libruby-redeye.a')
54
+ if /cygwin|mingw/ =~ RUBY_PLATFORM
55
+ top = "../.."
56
+ [
57
+ ["glib/src", "ruby-glib2"],
58
+ ].each{|d,l|
59
+ $LDFLAGS << sprintf(" -L%s/%s", top, d)
60
+ $libs << sprintf(" -l%s", l)
61
+ }
62
+ end
63
+ begin
64
+ srcdir = File.expand_path(File.dirname($0))
65
+
66
+ begin
67
+
68
+ obj_ext = "."+$OBJEXT
69
+
70
+ $libs = $libs.split(/ /).uniq.join(' ')
71
+ $source_files = Dir.glob(sprintf("%s/*.c", srcdir)).map{|fname|
72
+ fname[0, srcdir.length+1] = ''
73
+ fname
74
+ }
75
+ $objs = $source_files.collect do |item|
76
+ item.gsub(/.c$/, obj_ext)
77
+ end
78
+
79
+ #
80
+ # create Makefile
81
+ #
82
+ $defs << "-DRUBY_REDEYE_COMPILATION"
83
+ # $CFLAGS << $defs.join(' ')
84
+ create_makefile("redeye", srcdir)
85
+ raise Interrupt if not FileTest.exist? "Makefile"
86
+
87
+ File.open("Makefile", "a") do |mfile|
88
+ $source_files.each do |e|
89
+ mfile.print sprintf("%s: %s
90
+ ", e.gsub(/.c$/, obj_ext), e)
91
+ end
92
+ end
93
+ ensure
94
+ #Dir.chdir ".."
95
+ end
96
+
97
+ #create_top_makefile()
98
+ rescue Interrupt
99
+ print " [error] " + $!.to_s + "
100
+ "
101
+ end
102
+
@@ -0,0 +1,885 @@
1
+ #pragma GCC diagnostic ignored "-Wunused-but-set-variable"
2
+ /* Includes */
3
+ #include <ruby.h>
4
+ #include <stdlib.h>
5
+ #include <stdio.h>
6
+ #include <string.h>
7
+ #include "gdk-pixbuf/gdk-pixbuf.h"
8
+
9
+ /* Setup types */
10
+ /* Try not to clash with other definitions of bool... */
11
+ typedef int rubber_bool;
12
+ #define bool rubber_bool
13
+
14
+ /* Prototypes */
15
+ #include "rbglib.h"
16
+
17
+ #include "rbgtk.h"
18
+
19
+ #if defined(G_PLATFORM_WIN32) && !defined(RUBY_GTK2_STATIC_COMPILATION)
20
+ # ifdef RUBY_GTK2_COMPILATION
21
+ # define RUBY_GTK2_VAR __declspec(dllexport)
22
+ # else
23
+ # define RUBY_GTK2_VAR extern __declspec(dllimport)
24
+ # endif
25
+ #else
26
+ # define RUBY_GTK2_VAR extern
27
+ #endif
28
+
29
+ RUBY_GTK2_VAR VALUE mGtk;
30
+ RUBY_GTK2_VAR VALUE mGdk;
31
+
32
+ #define RBGTK_INITIALIZE(obj,gtkobj) (rbgtk_initialize_gtkobject(obj, GTK_OBJECT(gtkobj)))
33
+ static VALUE cRedEye;
34
+ static VALUE
35
+ RedEye___alloc__(VALUE self);
36
+ static VALUE
37
+ RedEye_initialize(VALUE self, VALUE __v_pixbuf, VALUE __v_minX, VALUE __v_minY, VALUE __v_maxX, VALUE __v_maxY);
38
+ static VALUE
39
+ RedEye_identify_blobs(int __p_argc, VALUE *__p_argv, VALUE self);
40
+ static VALUE
41
+ RedEye_correct_blob(VALUE self, VALUE __v_blob_id);
42
+ static VALUE
43
+ RedEye_highlight_blob(int __p_argc, VALUE *__p_argv, VALUE self);
44
+ static VALUE
45
+ RedEye_preview_blob(int __p_argc, VALUE *__p_argv, VALUE self);
46
+ static VALUE
47
+ RedEye_preview(VALUE self);
48
+ static VALUE
49
+ RedEye_pixbuf(VALUE self);
50
+ static VALUE structRegion;
51
+
52
+ /* Inline C code */
53
+
54
+
55
+ #define assert(x) if (!(x)) { rb_raise(rb_eRuntimeError, "Assertion: '%s' failed.", #x); }
56
+
57
+ typedef struct {
58
+ char red,green,blue;
59
+ } rgb_t;
60
+
61
+ typedef struct {
62
+ int minX, maxX, minY, maxY;
63
+ int width, height;
64
+ int noPixels, mergeWith;
65
+ } region_info;
66
+
67
+ typedef struct {
68
+ struct {
69
+ int minX, maxX, minY, maxY;
70
+ int width, height;
71
+ } area;
72
+ struct {
73
+ int *data;
74
+ region_info *region;
75
+ int len, size;
76
+ } regions;
77
+ int *mask;
78
+ GdkPixbuf *pixbuf, *preview;
79
+ } redeyeop_t;
80
+
81
+ #define MIN_RED_VAL 20
82
+
83
+ static inline VALUE
84
+ unref_pixbuf(GdkPixbuf *pixbuf)
85
+ {
86
+ volatile VALUE pb = Qnil;
87
+
88
+ pb = GOBJ2RVAL(pixbuf);
89
+
90
+ g_object_unref(pixbuf);
91
+
92
+ return pb;
93
+ }
94
+
95
+ static void identify_possible_redeye_pixels(redeyeop_t *op,
96
+ double green_sensitivity, double blue_sensitivity,
97
+ int min_red_val)
98
+ {
99
+ guchar *data = gdk_pixbuf_get_pixels(op->pixbuf);
100
+ int rowstride = gdk_pixbuf_get_rowstride(op->pixbuf);
101
+ int pixWidth = gdk_pixbuf_get_has_alpha(op->pixbuf) ? 4 : 3;
102
+
103
+ int y, ry = 0, x, rx = 0;
104
+ for ( y = op->area.minY; y < op->area.maxY; y++ )
105
+ {
106
+ guchar *thisLine = data + (rowstride * y);
107
+ guchar *pixel;
108
+
109
+ pixel = thisLine + (op->area.minX * pixWidth);
110
+ rx = 0;
111
+
112
+ for ( x = op->area.minX; x < op->area.maxX; x++ )
113
+ {
114
+
115
+ int r,g,b;
116
+
117
+ r = pixel[0];
118
+ g = pixel[1];
119
+ b = pixel[2];
120
+
121
+ gboolean threshMet;
122
+
123
+ threshMet = (((double)r) > (green_sensitivity * (double)g)) &&
124
+ (((double)r) > (blue_sensitivity * (double)b)) &&
125
+ (r > min_red_val);
126
+
127
+ if(threshMet)
128
+ op->mask[ rx + ry ] = r;
129
+ else
130
+ op->mask[ rx + ry ] = 0; /* MEMZERO should have done its job ? */
131
+
132
+ pixel += pixWidth;
133
+ rx ++;
134
+ }
135
+
136
+ ry += op->area.width;
137
+ }
138
+ }
139
+
140
+
141
+ inline int group_at(redeyeop_t *op, int px, int py)
142
+ {
143
+ int index, region;
144
+
145
+ if (px < 0 || py < 0)
146
+ return 0;
147
+
148
+ index = px + ( py * op->area.width );
149
+
150
+ if (index < 0)
151
+ return 0;
152
+ if (index > (op->area.width * op->area.height))
153
+ return 0;
154
+
155
+ region = op->regions.data[ index ];
156
+ if (region > 0) {
157
+ if (op->regions.region[ region ].mergeWith) {
158
+ return op->regions.region[ region ].mergeWith;
159
+ } else {
160
+ return region;
161
+ }
162
+ } else {
163
+ return 0;
164
+ }
165
+ }
166
+
167
+ #define group_for(x,y) group_at(op, x, y)
168
+
169
+ static void identify_blob_groupings(redeyeop_t *op)
170
+ {
171
+ volatile int next_blob_id = 1, blob_id, y, x;
172
+
173
+
174
+ for( y = 0; y < op->area.height; y++ )
175
+ {
176
+ for ( x = 0; x < op->area.width; x++ )
177
+ {
178
+ if (op->mask[ x + (y * op->area.width) ] > 0) {
179
+ gboolean existing = FALSE;
180
+ int sx, sy, group = 0;
181
+ // Target pixel is true
182
+ blob_id = 0;
183
+
184
+ for (sy = y; sy >= y - 1; sy --) {
185
+ sx = (sy == y) ? x : x + 1;
186
+ for (; sx >= (x - 1); sx --) {
187
+ /*if ((sx >= x) && (sy >= y))
188
+ goto blob_scan_done;*/
189
+
190
+ if (sx >= 0 && sy >= 0)
191
+ group = group_for(sx, sy);
192
+
193
+ if (group) {
194
+ existing = TRUE;
195
+ if (blob_id) {
196
+ int target = MIN(blob_id, group);
197
+ int from = MAX(blob_id, group);
198
+
199
+ if (op->regions.region[target].mergeWith > 0) {
200
+ // Already merged
201
+ target = op->regions.region[target].mergeWith;
202
+ }
203
+ op->regions.region[from].mergeWith = target;
204
+
205
+ // Merge blob_id & group
206
+ }
207
+ blob_id = group;
208
+ }
209
+ }
210
+ }
211
+
212
+ if (blob_id == 0)
213
+ { // Allocate new group
214
+ blob_id = next_blob_id;
215
+ op->regions.region[blob_id].minX = x;
216
+ op->regions.region[blob_id].maxX = x;
217
+ op->regions.region[blob_id].minY = y;
218
+ op->regions.region[blob_id].maxY = y;
219
+ op->regions.region[blob_id].width = 1;
220
+ op->regions.region[blob_id].height = 1;
221
+ op->regions.region[blob_id].noPixels = 1;
222
+ op->regions.region[blob_id].mergeWith = 0;
223
+
224
+ next_blob_id ++;
225
+ op->regions.len = next_blob_id;
226
+
227
+ if (next_blob_id >= op->regions.size) {
228
+ int extra, new_size;
229
+
230
+ /*
231
+ * Realloc in increasingly large chunks to reduce memory fragmentation
232
+ */
233
+ extra = op->regions.size;
234
+ new_size = op->regions.size + extra;
235
+
236
+ REALLOC_N(op->regions.region, region_info, new_size);
237
+
238
+ op->regions.size = new_size;
239
+ }
240
+ }
241
+
242
+ if (existing)
243
+ {
244
+ op->regions.region[blob_id].minX = MIN(x, op->regions.region[blob_id].minX);
245
+ op->regions.region[blob_id].maxX = MAX(x, op->regions.region[blob_id].maxX);
246
+ op->regions.region[blob_id].minY = MIN(y, op->regions.region[blob_id].minY);
247
+ op->regions.region[blob_id].maxY = MAX(y, op->regions.region[blob_id].maxY);
248
+ op->regions.region[blob_id].width = op->regions.region[blob_id].maxX -
249
+ op->regions.region[blob_id].minX + 1;
250
+ op->regions.region[blob_id].height = op->regions.region[blob_id].maxY -
251
+ op->regions.region[blob_id].minY + 1;
252
+ op->regions.region[blob_id].noPixels ++;
253
+ }
254
+
255
+ op->regions.data[ x + (y * op->area.width) ] = blob_id;
256
+ }
257
+ }
258
+ }
259
+ /*FILE *fp = fopen("regions.txt","w");*/
260
+ for (y = 0; y < op->area.height; y++) {
261
+ for (x = 0; x < op->area.width; x++) {
262
+ int g = group_at(op, x, y); // Returns the merged group...
263
+ op->regions.data[ x + (y * op->area.width) ] = g;
264
+ /*
265
+ if (op->regions.len <= 0xf || 1)
266
+ {
267
+ if (g == 0)
268
+ fprintf(fp, " ");
269
+ else
270
+ fprintf(fp, "%x", g);
271
+ }
272
+ else
273
+ {
274
+ if (g == 0)
275
+ fprintf(fp, " ");
276
+ else
277
+ fprintf(fp, "%x ", g);
278
+ }*/
279
+ }
280
+ /*fprintf(fp, "\n");*/
281
+ }
282
+ /*fclose(fp);*/
283
+ }
284
+ #define NO_REGIONS_DEFAULT 20
285
+ #define MIN_ID 1
286
+
287
+
288
+ static redeyeop_t *new_redeye(void)
289
+ {
290
+ redeyeop_t *ptr = ALLOC(redeyeop_t);
291
+ MEMZERO(ptr, redeyeop_t, 1);
292
+ return ptr;
293
+ }
294
+
295
+ static void free_redeye(redeyeop_t *ptr)
296
+ {
297
+ if (ptr->mask)
298
+ free(ptr->mask);
299
+ if (ptr->regions.data);
300
+ free(ptr->regions.data);
301
+ if (ptr->regions.region);
302
+ free(ptr->regions.region);
303
+ if (ptr->pixbuf)
304
+ g_object_unref(ptr->pixbuf);
305
+ if (ptr->preview)
306
+ g_object_unref(ptr->preview);
307
+ free(ptr);
308
+ }
309
+
310
+
311
+ inline gboolean in_region(redeyeop_t *op, int x, int y, int blob_id)
312
+ {
313
+ int index;
314
+
315
+ if ( x < op->area.minX || x > op->area.maxX ||
316
+ y < op->area.minY || y > op->area.maxY )
317
+ return FALSE;
318
+
319
+ index = (x - op->area.minX) + ((y - op->area.minY) * op->area.width);
320
+
321
+ return op->regions.data[index] == blob_id;
322
+ }
323
+
324
+ inline double alpha_level_for_pixel(redeyeop_t *op, int x, int y, int blob_id)
325
+ {
326
+ int j = 0, c = 0, xm, ym;
327
+
328
+ if (in_region(op, x, y, blob_id))
329
+ return 1.0;
330
+
331
+ for ( xm = -2; xm <= 2; xm++ )
332
+ {
333
+ for ( ym = -2; ym <= 2; ym ++ )
334
+ {
335
+ c ++;
336
+ if (xm == 0 && ym == 0)
337
+ continue;
338
+ if (in_region(op, x+xm, y+ym, blob_id))
339
+ j ++;
340
+ }
341
+ }
342
+
343
+ return ((double)j)/((double)c);
344
+ }
345
+
346
+ inline char col(double val)
347
+ {
348
+ if (val < 0) return 0;
349
+ if (val > 255) return 255;
350
+ return val;
351
+
352
+ }
353
+
354
+ static GdkPixbuf *redeye_preview(redeyeop_t *op, gboolean reset)
355
+ {
356
+ int width, height;
357
+ width = op->area.width;
358
+ height = op->area.height;
359
+
360
+ if (width + op->area.minX > gdk_pixbuf_get_width(op->pixbuf)) {
361
+ width = gdk_pixbuf_get_width(op->pixbuf) - op->area.minX;
362
+ }
363
+ if (height + op->area.minY > gdk_pixbuf_get_height(op->pixbuf)) {
364
+ height = gdk_pixbuf_get_height(op->pixbuf) - op->area.minY;
365
+ }
366
+
367
+ if ( op->preview == NULL )
368
+ {
369
+ GdkPixbuf *sub = NULL;
370
+ sub = gdk_pixbuf_new_subpixbuf(op->pixbuf, op->area.minX, op->area.minY,
371
+ width, height);
372
+
373
+ op->preview = gdk_pixbuf_copy(sub);
374
+ g_object_unref(sub);
375
+ } else if (reset) {
376
+ gdk_pixbuf_copy_area(op->pixbuf, op->area.minX, op->area.minY,
377
+ width, height, op->preview, 0, 0);
378
+ }
379
+
380
+ return op->preview;
381
+ }
382
+
383
+ static void desaturate_blob(redeyeop_t *op, int blob_id)
384
+ {
385
+ int y, x;
386
+ int minX, minY, maxX, maxY;
387
+
388
+ minY = MAX(0, op->area.minY + op->regions.region[blob_id].minY - 1);
389
+ maxY = MIN(op->area.maxY + op->regions.region[blob_id].maxY + 1,
390
+ gdk_pixbuf_get_height(op->pixbuf)-1);
391
+ minX = MAX(0, op->area.minX + op->regions.region[blob_id].minX - 1);
392
+ maxX = MIN(op->area.maxX + op->regions.region[blob_id].maxX + 1,
393
+ gdk_pixbuf_get_width(op->pixbuf)-1);
394
+
395
+ guchar *data = gdk_pixbuf_get_pixels(op->pixbuf);
396
+ int rowstride = gdk_pixbuf_get_rowstride(op->pixbuf);
397
+ int pixWidth = gdk_pixbuf_get_has_alpha(op->pixbuf) ? 4 : 3;
398
+
399
+ for ( y = minY; y <= maxY; y++ )
400
+ {
401
+ guchar *thisLine = data + (rowstride * y);
402
+ guchar *pixel;
403
+
404
+ pixel = thisLine + (minX * pixWidth);
405
+
406
+ for ( x = minX; x <= maxX; x++ )
407
+ {
408
+
409
+ double alpha = alpha_level_for_pixel(op, x, y, blob_id);
410
+ int r,g,b,grey;
411
+
412
+ r = pixel[0];
413
+ g = pixel[1];
414
+ b = pixel[2];
415
+
416
+ if (alpha > 0)
417
+ {
418
+ grey = alpha * ((double)( 5 * (double)r + 60 * (double)g + 30 * (double)b)) / 100.0 +
419
+ (1 - alpha) * r;
420
+
421
+ pixel[0] = col((grey * alpha) + (1-alpha) * r);
422
+ pixel[1] = col((grey * alpha) + (1-alpha) * g);
423
+ pixel[2] = col((grey * alpha) + (1-alpha) * b);
424
+ }
425
+
426
+ pixel += pixWidth;
427
+ }
428
+ }
429
+
430
+ }
431
+
432
+ static void highlight_blob(redeyeop_t *op, int blob_id, int colour)
433
+ {
434
+ int y, x;
435
+ int minX, minY, maxX, maxY;
436
+ int hr, hg, hb;
437
+
438
+ hr = (colour >> 16) & 0xff;
439
+ hg = (colour >> 8) & 0xff;
440
+ hb = (colour) & 0xff;
441
+
442
+ minY = MAX(0, op->area.minY - 1);
443
+ maxY = MIN(op->area.maxY + 1, gdk_pixbuf_get_height(op->pixbuf)-1);
444
+ minX = MAX(0, op->area.minX - 1);
445
+ maxX = MIN(op->area.maxX + 1, gdk_pixbuf_get_width(op->pixbuf)-1);
446
+
447
+ guchar *data = gdk_pixbuf_get_pixels(op->pixbuf);
448
+ int rowstride = gdk_pixbuf_get_rowstride(op->pixbuf);
449
+ int pixWidth = gdk_pixbuf_get_has_alpha(op->pixbuf) ? 4 : 3;
450
+
451
+ for ( y = minY; y <= maxY; y++ )
452
+ {
453
+ guchar *thisLine = data + (rowstride * y);
454
+ guchar *pixel;
455
+
456
+ pixel = thisLine + (minX * pixWidth);
457
+
458
+ for ( x = minX; x <= maxX; x++ )
459
+ {
460
+
461
+ double alpha = alpha_level_for_pixel(op, x, y, blob_id);
462
+ int r,g,b;
463
+
464
+ r = (pixel[0]);
465
+ g = (pixel[1]);
466
+ b = (pixel[2]);
467
+
468
+
469
+ if (alpha > 0)
470
+ {
471
+
472
+ pixel[0] = col((1-alpha) * r + (alpha * hr));
473
+ pixel[1] = col((1-alpha) * g + (alpha * hg));
474
+ pixel[2] = col((1-alpha) * b + (alpha * hb));
475
+ }
476
+
477
+ pixel += pixWidth;
478
+ }
479
+ }
480
+
481
+ }
482
+
483
+
484
+ static void preview_blob(redeyeop_t *op, int blob_id, int colour, gboolean reset_preview)
485
+ {
486
+ int y, x;
487
+ int minX, minY, maxX, maxY;
488
+ int hr, hg, hb;
489
+
490
+ redeye_preview(op, reset_preview);
491
+
492
+ hr = (colour >> 16) & 0xff;
493
+ hg = (colour >> 8) & 0xff;
494
+ hb = (colour) & 0xff;
495
+
496
+ minY = 0;
497
+ maxY = gdk_pixbuf_get_height(op->preview)-1;
498
+ minX = 0;
499
+ maxX = gdk_pixbuf_get_width(op->preview)-1;
500
+
501
+ guchar *data = gdk_pixbuf_get_pixels(op->preview);
502
+ int rowstride = gdk_pixbuf_get_rowstride(op->preview);
503
+ int pixWidth = gdk_pixbuf_get_has_alpha(op->preview) ? 4 : 3;
504
+
505
+ for ( y = minY; y <= maxY; y++ )
506
+ {
507
+ guchar *thisLine = data + (rowstride * y);
508
+ guchar *pixel;
509
+
510
+ pixel = thisLine + (minX * pixWidth);
511
+
512
+ for ( x = minX; x <= maxX; x++ )
513
+ {
514
+
515
+ double alpha = alpha_level_for_pixel(op, x + op->area.minX, y + op->area.minY, blob_id);
516
+ int r,g,b;
517
+
518
+ r = (pixel[0]);
519
+ g = (pixel[1]);
520
+ b = (pixel[2]);
521
+
522
+
523
+ if (alpha > 0)
524
+ {
525
+
526
+ pixel[0] = col((1-alpha) * r + (alpha * hr));
527
+ pixel[1] = col((1-alpha) * g + (alpha * hg));
528
+ pixel[2] = col((1-alpha) * b + (alpha * hb));
529
+ }
530
+
531
+ pixel += pixWidth;
532
+ }
533
+ }
534
+
535
+ }
536
+
537
+
538
+ /* Code */
539
+ static VALUE
540
+ RedEye___alloc__(VALUE self)
541
+ {
542
+ VALUE __p_retval = Qnil;
543
+
544
+ #line 536 "/home/geoff/Projects/redeye/ext/redeye/redeye.cr"
545
+ do { __p_retval = Data_Wrap_Struct(self, NULL, free_redeye, new_redeye()); goto out; } while(0);
546
+ out:
547
+ return __p_retval;
548
+ }
549
+
550
+ static VALUE
551
+ RedEye_initialize(VALUE self, VALUE __v_pixbuf, VALUE __v_minX, VALUE __v_minY, VALUE __v_maxX, VALUE __v_maxY)
552
+ {
553
+ GdkPixbuf * pixbuf; GdkPixbuf * __orig_pixbuf;
554
+ int minX; int __orig_minX;
555
+ int minY; int __orig_minY;
556
+ int maxX; int __orig_maxX;
557
+ int maxY; int __orig_maxY;
558
+ __orig_pixbuf = pixbuf = GDK_PIXBUF(RVAL2GOBJ(__v_pixbuf));
559
+ __orig_minX = minX = NUM2INT(__v_minX);
560
+ __orig_minY = minY = NUM2INT(__v_minY);
561
+ __orig_maxX = maxX = NUM2INT(__v_maxX);
562
+ __orig_maxY = maxY = NUM2INT(__v_maxY);
563
+
564
+ #line 540 "/home/geoff/Projects/redeye/ext/redeye/redeye.cr"
565
+
566
+ do {
567
+ redeyeop_t * op ;
568
+ Data_Get_Struct(self, redeyeop_t, op);
569
+ op->pixbuf = pixbuf;
570
+ op->preview = NULL;
571
+ g_object_ref(op->pixbuf);
572
+ op->area.minX = minX;
573
+ op->area.maxX = maxX;
574
+ op->area.minY = minY;
575
+ op->area.maxY = maxY;
576
+ op->area.width = maxX - minX + 1;
577
+ op->area.height = maxY - minY + 1;
578
+ assert(op->pixbuf != NULL);
579
+ assert(op->area.maxX <= gdk_pixbuf_get_width(op->pixbuf));
580
+ assert(op->area.minX >= 0);
581
+ assert(op->area.minX < op->area.maxX);
582
+ assert(op->area.maxY <= gdk_pixbuf_get_height(op->pixbuf));
583
+ assert(op->area.minY >= 0);
584
+ assert(op->area.minY < op->area.maxY);
585
+ op->mask = ALLOC_N(int, op->area.width * op->area.height);
586
+ op->regions.data = ALLOC_N(int, op->area.width * op->area.height);
587
+ op->regions.region = ALLOC_N(region_info, NO_REGIONS_DEFAULT);
588
+ op->regions.len = 0;
589
+ op->regions.size = NO_REGIONS_DEFAULT;
590
+
591
+
592
+ } while(0);
593
+
594
+ ;
595
+ return Qnil;
596
+ }
597
+
598
+ static VALUE
599
+ RedEye_identify_blobs(int __p_argc, VALUE *__p_argv, VALUE self)
600
+ {
601
+ VALUE __p_retval = Qnil;
602
+ VALUE __v_green_sensitivity = Qnil;
603
+ double green_sensitivity; double __orig_green_sensitivity;
604
+ VALUE __v_blue_sensitivity = Qnil;
605
+ double blue_sensitivity; double __orig_blue_sensitivity;
606
+ VALUE __v_min_red_val = Qnil;
607
+ int min_red_val; int __orig_min_red_val;
608
+
609
+ /* Scan arguments */
610
+ rb_scan_args(__p_argc, __p_argv, "03",&__v_green_sensitivity, &__v_blue_sensitivity, &__v_min_red_val);
611
+
612
+ /* Set defaults */
613
+ if (__p_argc > 0)
614
+ __orig_green_sensitivity = green_sensitivity = NUM2DBL(__v_green_sensitivity);
615
+ else
616
+ green_sensitivity = 2.0;
617
+
618
+ if (__p_argc > 1)
619
+ __orig_blue_sensitivity = blue_sensitivity = NUM2DBL(__v_blue_sensitivity);
620
+ else
621
+ blue_sensitivity = 0.0;
622
+
623
+ if (__p_argc > 2)
624
+ __orig_min_red_val = min_red_val = NUM2INT(__v_min_red_val);
625
+ else
626
+ min_red_val = MIN_RED_VAL;
627
+
628
+
629
+ #line 575 "/home/geoff/Projects/redeye/ext/redeye/redeye.cr"
630
+
631
+ do {
632
+ redeyeop_t * op ;
633
+ Data_Get_Struct(self, redeyeop_t, op);
634
+ MEMZERO(op->mask, int, op->area.width * op->area.height);
635
+ MEMZERO(op->regions.data, int, op->area.width * op->area.height);
636
+ identify_possible_redeye_pixels(op, green_sensitivity, blue_sensitivity, min_red_val);
637
+ identify_blob_groupings(op);
638
+ volatile VALUE ary =
639
+ rb_ary_new2(op->regions.len);
640
+ int i ;
641
+ for (i = MIN_ID;
642
+ i < op->regions.len;
643
+ i++) { region_info * r =
644
+ &op->regions.region[i];
645
+ /* Ignore CCD noise */ if (r->noPixels < 2) continue;
646
+ rb_ary_push(ary, rb_struct_new(structRegion, self, INT2NUM(i), INT2NUM(r->minX), INT2NUM(r->minY), INT2NUM(r->maxX), INT2NUM(r->maxY), INT2NUM(r->width), INT2NUM(r->height), INT2NUM(r->noPixels)));
647
+ } do { __p_retval = ary; goto out; } while(0);
648
+
649
+ } while(0);
650
+
651
+ out:
652
+ return __p_retval;
653
+ }
654
+
655
+ static VALUE
656
+ RedEye_correct_blob(VALUE self, VALUE __v_blob_id)
657
+ {
658
+ int blob_id; int __orig_blob_id;
659
+ __orig_blob_id = blob_id = NUM2INT(__v_blob_id);
660
+
661
+ #line 600 "/home/geoff/Projects/redeye/ext/redeye/redeye.cr"
662
+
663
+ do {
664
+ redeyeop_t * op ;
665
+ Data_Get_Struct(self, redeyeop_t, op);
666
+ if (op->regions.len <= blob_id) rb_raise(rb_eIndexError, "Only %i blobs in region - %i is invalid", op->regions.len, blob_id);
667
+ desaturate_blob(op, blob_id);
668
+
669
+
670
+ } while(0);
671
+
672
+ return Qnil;
673
+ }
674
+
675
+ static VALUE
676
+ RedEye_highlight_blob(int __p_argc, VALUE *__p_argv, VALUE self)
677
+ {
678
+ VALUE __v_blob_id = Qnil;
679
+ int blob_id; int __orig_blob_id;
680
+ VALUE __v_col = Qnil;
681
+ int col; int __orig_col;
682
+
683
+ /* Scan arguments */
684
+ rb_scan_args(__p_argc, __p_argv, "11",&__v_blob_id, &__v_col);
685
+
686
+ /* Set defaults */
687
+ __orig_blob_id = blob_id = NUM2INT(__v_blob_id);
688
+
689
+ if (__p_argc > 1)
690
+ __orig_col = col = NUM2INT(__v_col);
691
+ else
692
+ col = 0x00ff00;
693
+
694
+
695
+ #line 612 "/home/geoff/Projects/redeye/ext/redeye/redeye.cr"
696
+
697
+ do {
698
+ redeyeop_t * op ;
699
+ Data_Get_Struct(self, redeyeop_t, op);
700
+ if (op->regions.len <= blob_id) rb_raise(rb_eIndexError, "Only %i blobs in region - %i is invalid", op->regions.len, blob_id);
701
+ highlight_blob(op, blob_id, col);
702
+
703
+
704
+ } while(0);
705
+
706
+ return Qnil;
707
+ }
708
+
709
+ static VALUE
710
+ RedEye_preview_blob(int __p_argc, VALUE *__p_argv, VALUE self)
711
+ {
712
+ VALUE __p_retval = Qnil;
713
+ VALUE __v_blob_id = Qnil;
714
+ int blob_id; int __orig_blob_id;
715
+ VALUE __v_col = Qnil;
716
+ int col; int __orig_col;
717
+ VALUE __v_reset_preview = Qnil;
718
+ gboolean reset_preview; gboolean __orig_reset_preview;
719
+
720
+ /* Scan arguments */
721
+ rb_scan_args(__p_argc, __p_argv, "12",&__v_blob_id, &__v_col, &__v_reset_preview);
722
+
723
+ /* Set defaults */
724
+ __orig_blob_id = blob_id = NUM2INT(__v_blob_id);
725
+
726
+ if (__p_argc > 1)
727
+ __orig_col = col = NUM2INT(__v_col);
728
+ else
729
+ col = 0x00ff00;
730
+
731
+ if (__p_argc > 2)
732
+ __orig_reset_preview = reset_preview = RTEST(__v_reset_preview);
733
+ else
734
+ reset_preview = TRUE;
735
+
736
+
737
+ #line 623 "/home/geoff/Projects/redeye/ext/redeye/redeye.cr"
738
+
739
+ do {
740
+ redeyeop_t * op ;
741
+ Data_Get_Struct(self, redeyeop_t, op);
742
+ if (op->regions.len <= blob_id) rb_raise(rb_eIndexError, "Only %i blobs in region - %i is invalid", op->regions.len, blob_id);
743
+ preview_blob(op, blob_id, col, reset_preview);
744
+ do { __p_retval = GOBJ2RVAL(GDK_PIXBUF(op->preview)); goto out; } while(0);
745
+
746
+ } while(0);
747
+
748
+ out:
749
+ return __p_retval;
750
+ }
751
+
752
+ static VALUE
753
+ RedEye_preview(VALUE self)
754
+ {
755
+ VALUE __p_retval = Qnil;
756
+
757
+ #line 635 "/home/geoff/Projects/redeye/ext/redeye/redeye.cr"
758
+
759
+ do {
760
+ redeyeop_t * op ;
761
+ Data_Get_Struct(self, redeyeop_t, op);
762
+ do { __p_retval = GOBJ2RVAL(GDK_PIXBUF(redeye_preview(op, FALSE))); goto out; } while(0);
763
+
764
+ } while(0);
765
+
766
+ out:
767
+ return __p_retval;
768
+ }
769
+
770
+ static VALUE
771
+ RedEye_pixbuf(VALUE self)
772
+ {
773
+ VALUE __p_retval = Qnil;
774
+
775
+ #line 642 "/home/geoff/Projects/redeye/ext/redeye/redeye.cr"
776
+
777
+ do {
778
+ redeyeop_t * op ;
779
+ Data_Get_Struct(self, redeyeop_t, op);
780
+ do { __p_retval = GOBJ2RVAL(GDK_PIXBUF(op->pixbuf)); goto out; } while(0);
781
+
782
+ } while(0);
783
+
784
+ out:
785
+ return __p_retval;
786
+ }
787
+
788
+ static VALUE
789
+ Region_ratio(VALUE self)
790
+ {
791
+ VALUE __p_retval = Qnil;
792
+
793
+ #line 497 "/home/geoff/Projects/redeye/ext/redeye/redeye.cr"
794
+ int width,height;
795
+ double min,max,ratio;
796
+ width = NUM2INT(rb_struct_getmember(self, rb_intern("width")));
797
+ height = NUM2INT(rb_struct_getmember(self, rb_intern("height")));
798
+ min = (double)MIN(width,height);
799
+ max = (double)MAX(width,height);
800
+ ratio = (min / max);
801
+ do { __p_retval = rb_float_new(ratio); goto out; } while(0);
802
+ out:
803
+ return __p_retval;
804
+ }
805
+
806
+ static VALUE
807
+ Region_density(VALUE self)
808
+ {
809
+ VALUE __p_retval = Qnil;
810
+
811
+ #line 507 "/home/geoff/Projects/redeye/ext/redeye/redeye.cr"
812
+
813
+ do {
814
+ int noPixels, width, height;
815
+ double density ;
816
+ noPixels = NUM2INT(rb_struct_getmember(self, rb_intern("noPixels")));
817
+ width = NUM2INT(rb_struct_getmember(self, rb_intern("width")));
818
+ height = NUM2INT(rb_struct_getmember(self, rb_intern("height")));
819
+ density = ((double)noPixels / (double)(width * height));
820
+ do { __p_retval = rb_float_new(density); goto out; } while(0);
821
+
822
+ } while(0);
823
+
824
+ out:
825
+ return __p_retval;
826
+ }
827
+
828
+ static VALUE
829
+ Region_squareish_query(int __p_argc, VALUE *__p_argv, VALUE self)
830
+ {
831
+ VALUE __p_retval = Qnil;
832
+ VALUE __v_min_ratio = Qnil;
833
+ double min_ratio; double __orig_min_ratio;
834
+ VALUE __v_min_density = Qnil;
835
+ double min_density; double __orig_min_density;
836
+
837
+ /* Scan arguments */
838
+ rb_scan_args(__p_argc, __p_argv, "02",&__v_min_ratio, &__v_min_density);
839
+
840
+ /* Set defaults */
841
+ if (__p_argc > 0)
842
+ __orig_min_ratio = min_ratio = NUM2DBL(__v_min_ratio);
843
+ else
844
+ min_ratio = 0.5;
845
+
846
+ if (__p_argc > 1)
847
+ __orig_min_density = min_density = NUM2DBL(__v_min_density);
848
+ else
849
+ min_density = 0.5;
850
+
851
+
852
+ #line 518 "/home/geoff/Projects/redeye/ext/redeye/redeye.cr"
853
+ int noPixels, width, height;
854
+ double min, max, ratio, density;
855
+ noPixels = NUM2INT(rb_struct_getmember(self, rb_intern("noPixels")));
856
+ width = NUM2INT(rb_struct_getmember(self, rb_intern("width")));
857
+ height = NUM2INT(rb_struct_getmember(self, rb_intern("height")));
858
+ min = (double)MIN(width,height);
859
+ max = (double)MAX(width,height);
860
+ ratio = (min / max);
861
+ density = ((double)noPixels / (double)(width * height));
862
+ do { __p_retval = ((((ratio >= min_ratio) && (density > min_density))) ? Qtrue : Qfalse); goto out; } while(0);
863
+ out:
864
+ return __p_retval;
865
+ }
866
+
867
+ /* Init */
868
+ void
869
+ Init_redeye(void)
870
+ {
871
+ cRedEye = rb_define_class("RedEye", rb_cObject);
872
+ rb_define_alloc_func(cRedEye, RedEye___alloc__);
873
+ rb_define_method(cRedEye, "initialize", RedEye_initialize, 5);
874
+ rb_define_method(cRedEye, "identify_blobs", RedEye_identify_blobs, -1);
875
+ rb_define_method(cRedEye, "correct_blob", RedEye_correct_blob, 1);
876
+ rb_define_method(cRedEye, "highlight_blob", RedEye_highlight_blob, -1);
877
+ rb_define_method(cRedEye, "preview_blob", RedEye_preview_blob, -1);
878
+ rb_define_method(cRedEye, "preview", RedEye_preview, 0);
879
+ rb_define_method(cRedEye, "pixbuf", RedEye_pixbuf, 0);
880
+ structRegion = rb_struct_define("Region", "op", "id", "minX", "minY", "maxX", "maxY", "width", "height", "noPixels", NULL);
881
+ rb_define_const(cRedEye, "Region", structRegion);
882
+ rb_define_method(structRegion, "ratio", Region_ratio, 0);
883
+ rb_define_method(structRegion, "density", Region_density, 0);
884
+ rb_define_method(structRegion, "squareish?", Region_squareish_query, -1);
885
+ }