similie 0.2.6 → 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -6,25 +6,34 @@ Similie is a simple DCT based image hashing interface that,
6
6
  * computes hamming distance between 2 fingerprints.
7
7
 
8
8
  == Example
9
-
9
+
10
10
  require 'similie'
11
11
 
12
- img1 = Similie.new("test/lena1.jpg")
13
- img2 = Similie.new("test/lena2.png") # lena1.jpg cropped and scaled
12
+ img1 = Similie.new("test/lena1.png")
13
+ img2 = Similie.new("test/lena2.png") # lena1.png cropped and scaled
14
14
  img3 = Similie.new("test/lena5.png") # a different image
15
15
 
16
- img1.hash #=> unsigned 64bit int
16
+ img1.fingerprint #=> 64bit int
17
+
18
+ img1.distance(img2) #=> 2
19
+ img1.distance(img5) #=> 12
17
20
 
18
- img1.distance(img2) #=> 1
19
- img1.distance(img5) #=> 5
21
+ # class methods, if you want to deallocate image buffers immediately.
22
+ Similie.distance("test/lena1.png", "test/lena5.png") #=> 12
23
+ Similie.fingerprint("test/lena1.png")
20
24
 
21
- Similie.distance("test/lena1.jpg", "test/lena5.jpg") #=> 5
25
+ # utility method that exposes hamming distance http://en.wikipedia.org/wiki/Hamming_weight
26
+ Similie.popcount(0x03 ^ 0x08) #=> 3
22
27
 
23
28
  == Dependencies
24
29
 
25
30
  * ruby 1.9.1+
26
31
  * opencv 2.1+ (libcv-dev and libhighgui-dev on debian systems)
27
32
 
33
+ = See Also
34
+
35
+ {pHash - The open source perceptual hash library}[http://www.phash.org/]
36
+
28
37
  = License
29
38
 
30
39
  {CC BY-SA 3.0}[http://creativecommons.org/licenses/by-sa/3.0/]
@@ -1,12 +1,25 @@
1
1
  require 'mkmf'
2
2
 
3
- $CFLAGS = "-I/usr/include/opencv -Wall"
4
- $LDFLAGS = "-lhighgui -lcxcore"
3
+ def inc_paths lib, defaults
4
+ path = %x{pkg-config #{lib} --cflags 2>/dev/null}.strip
5
+ path.size > 0 ? path : defaults.map {|name| "-I#{name}"}.join(' ')
6
+ end
7
+
8
+ def lib_paths lib, defaults
9
+ path = %x{pkg-config #{lib} --libs-only-L 2>/dev/null}.strip
10
+ path.size > 0 ? path : defaults.map {|name| "-L#{name}"}.join(' ')
11
+ end
12
+
13
+ def lib_names lib, defaults
14
+ path = %x{pkg-config #{lib} --libs-only-l 2>/dev/null}.strip
15
+ path.size > 0 ? path : defaults.map {|name| "-l#{name}"}.join(' ')
16
+ end
5
17
 
6
- dir_config("libcv", ["/usr/local", "/opt/local", "/usr"])
18
+ $CFLAGS = inc_paths('opencv', %w(/usr/include/opencv)) + ' -Wall'
19
+ $LDFLAGS = lib_names('opencv', %w(highgui cxcore))
7
20
 
8
21
  headers = [ 'stdio.h', 'stdlib.h', 'string.h', 'opencv/cxcore.h', 'opencv/highgui.h' ]
9
- lib_1 = [ 'cxcore', 'cvInitFont', headers ]
22
+ lib_1 = [ 'cxcore', 'cvInitFont', headers ]
10
23
  lib_2 = [ 'highgui', 'cvEncodeImage', headers ]
11
24
 
12
25
  if have_header('opencv/cxcore.h') && have_library(*lib_1) && have_library(*lib_2)
@@ -21,11 +21,8 @@
21
21
  #include <opencv/cxcore.h>
22
22
  #include <opencv/highgui.h>
23
23
 
24
- #define TO_S(v) rb_funcall(v, rb_intern("to_s"), 0)
25
- #define CSTRING(v) RSTRING_PTR(TO_S(v))
26
- #define ID_CONST_GET rb_intern("const_get")
27
- #define CONST_GET(scope, constant) (rb_funcall(scope, ID_CONST_GET, 1, rb_str_new2(constant)))
28
- #define cvGetMonoPixel(img,y,x) ((uchar *)(img->imageData + x*img->widthStep))[y*img->nChannels]
24
+ #define TO_S(v) rb_funcall(v, rb_intern("to_s"), 0)
25
+ #define CSTRING(v) RSTRING_PTR(TO_S(v))
29
26
 
30
27
  #undef SIZET2NUM
31
28
  #ifdef HAVE_LONG_LONG
@@ -34,6 +31,11 @@
34
31
  #define SIZET2NUM(x) ULONG2NUM(x)
35
32
  #endif
36
33
 
34
+ #define DCT_SIZE 32
35
+
36
+ #if __GNUC__ >= 4 && __GNUC_MINOR__ >= 4
37
+ #define popcount __builtin_popcountll
38
+ #else
37
39
  // http://en.wikipedia.org/wiki/Hamming_weight
38
40
 
39
41
  const uint64_t m1 = 0x5555555555555555; //binary: 0101...
@@ -51,47 +53,39 @@ int popcount(uint64_t x) {
51
53
  x = (x + (x >> 4)) & m4; //put count of each 8 bits into those 8 bits
52
54
  return (x * h01)>>56; //returns left 8 bits of x + (x<<8) + (x<<16) + (x<<24) + ...
53
55
  }
56
+ #endif
54
57
 
55
- #define DCT_SIZE 32
56
-
57
- uint64_t image_phash(IplImage *img) {
58
+ uint64_t image_fingerprint(IplImage *img) {
58
59
  int x, y;
59
60
  double avg = 0;
60
61
  uint64_t phash = 0, phash_mask = 1;
61
62
 
62
- IplImage *small = cvCreateImage(cvSize(DCT_SIZE, DCT_SIZE), img->depth, img->nChannels);
63
- IplImage *mono = cvCreateImage(cvSize(DCT_SIZE, DCT_SIZE), img->depth, 1);
63
+ IplImage *mono = cvCreateImage(cvSize(img->width, img->height), img->depth, 1);
64
+ IplImage *small = cvCreateImage(cvSize(DCT_SIZE, DCT_SIZE), img->depth, 1);
64
65
 
65
- cvResize(img, small, CV_INTER_CUBIC);
66
- img->nChannels == 1 ? cvCopy(small, mono, 0) : cvCvtColor(small, mono, CV_RGB2GRAY);
66
+ img->nChannels == 1 ? cvCopy(img, mono, 0) : cvCvtColor(img, mono, CV_RGB2GRAY);
67
+ cvResize(mono, small, CV_INTER_CUBIC);
67
68
 
68
- CvMat *dct = cvCreateMat(DCT_SIZE, DCT_SIZE, CV_32FC1);
69
- for (x = 0; x < DCT_SIZE; x++) {
70
- for (y = 0; y < DCT_SIZE; y++) {
71
- cvSet2D(dct, x, y, cvScalarAll(cvGetMonoPixel(mono, x, y)));
72
- }
73
- }
69
+ CvMat *dct = cvCreateMat(DCT_SIZE, DCT_SIZE, CV_64FC1);
70
+
71
+ cvConvertScale(small, dct, 1, 0);
72
+ cvTranspose(dct, dct);
74
73
 
75
74
  cvDCT(dct, dct, CV_DXT_ROWS);
76
75
  cvSet2D(dct, 0, 0, cvScalarAll(0));
77
76
 
78
- for (y = 0; y < 8; y++) {
79
- for (x = 0; x < 8; x++) {
80
- avg += cvGet2D(dct, x, y).val[0];
81
- }
82
- }
83
-
84
- avg /= 63.0;
77
+ CvMat roi;
78
+ cvGetSubRect(dct, &roi, cvRect(0, 0, 8, 8));
79
+ avg = cvAvg(&roi, 0).val[0] * 64.0 / 63.0;
85
80
 
86
81
  for (x = 7; x >= 0; x--) {
87
82
  for (y = 7; y >= 0; y--) {
88
- if (cvGet2D(dct, x, y).val[0] > avg) phash |= phash_mask;
83
+ if (cvGet2D(dct, x, y).val[0] > avg)
84
+ phash |= phash_mask;
89
85
  phash_mask = phash_mask << 1;
90
86
  }
91
87
  }
92
88
 
93
- phash = phash & 0x7FFFFFFFFFFFFFFF;
94
-
95
89
  cvReleaseMat(&dct);
96
90
  cvReleaseImage(&mono);
97
91
  cvReleaseImage(&small);
@@ -117,11 +111,11 @@ IplImage* rb_image_handle(VALUE self) {
117
111
  return handle;
118
112
  }
119
113
 
120
- VALUE rb_image_hash(VALUE self) {
121
- VALUE hash = rb_iv_get(self, "@hash");
114
+ VALUE rb_image_fingerprint(VALUE self) {
115
+ VALUE hash = rb_iv_get(self, "@fingerprint");
122
116
  if (NIL_P(hash)) {
123
- hash = SIZET2NUM(image_phash(rb_image_handle(self)));
124
- rb_iv_set(self, "@hash", hash);
117
+ hash = SIZET2NUM(image_fingerprint(rb_image_handle(self)));
118
+ rb_iv_set(self, "@fingerprint", hash);
125
119
  }
126
120
  return hash;
127
121
  }
@@ -135,10 +129,9 @@ VALUE rb_image_initialize(VALUE self, VALUE file) {
135
129
  return self;
136
130
  }
137
131
 
138
-
139
132
  VALUE rb_image_distance(VALUE self, VALUE other) {
140
- VALUE hash1 = rb_image_hash(self);
141
- VALUE hash2 = rb_image_hash(other);
133
+ VALUE hash1 = rb_image_fingerprint(self);
134
+ VALUE hash2 = rb_image_fingerprint(other);
142
135
  int dist = popcount(NUM2ULONG(hash1) ^ NUM2ULONG(hash2));
143
136
  return INT2NUM(dist);
144
137
  }
@@ -155,7 +148,7 @@ VALUE rb_image_distance_func(VALUE self, VALUE file1, VALUE file2) {
155
148
  rb_raise(rb_eArgError, "Invalid image or unsupported format: %s", CSTRING(file2));
156
149
  }
157
150
 
158
- int dist = popcount(image_phash(img1) ^ image_phash(img2));
151
+ int dist = popcount(image_fingerprint(img1) ^ image_fingerprint(img2));
159
152
 
160
153
  cvReleaseImage(&img1);
161
154
  cvReleaseImage(&img2);
@@ -163,13 +156,20 @@ VALUE rb_image_distance_func(VALUE self, VALUE file1, VALUE file2) {
163
156
  return INT2NUM(dist);
164
157
  }
165
158
 
166
- VALUE rb_image_phash_func(VALUE self, VALUE file) {
159
+ VALUE rb_popcount_func(VALUE self, VALUE value) {
160
+ if (TYPE(value) != T_BIGNUM && TYPE(value) != T_FIXNUM)
161
+ rb_raise(rb_eArgError, "value needs to be a number");
162
+ return INT2NUM(popcount(NUM2INT(value)));
163
+ }
164
+
165
+
166
+ VALUE rb_image_fingerprint_func(VALUE self, VALUE file) {
167
167
  IplImage *img;
168
168
  img = cvLoadImage(CSTRING(file), -1);
169
169
  if (!img)
170
170
  rb_raise(rb_eArgError, "Invalid image or unsupported format: %s", CSTRING(file));
171
171
 
172
- uint64_t phash = image_phash(img);
172
+ uint64_t phash = image_fingerprint(img);
173
173
  cvReleaseImage(&img);
174
174
 
175
175
  return SIZET2NUM(phash);
@@ -178,10 +178,11 @@ VALUE rb_image_phash_func(VALUE self, VALUE file) {
178
178
  void Init_similie() {
179
179
  VALUE cSimilie = rb_define_class("Similie", rb_cObject);
180
180
  rb_define_alloc_func(cSimilie, rb_image_alloc);
181
- rb_define_method(cSimilie, "initialize", RUBY_METHOD_FUNC(rb_image_initialize), 1);
182
- rb_define_method(cSimilie, "hash", RUBY_METHOD_FUNC(rb_image_hash), 0);
183
- rb_define_method(cSimilie, "distance", RUBY_METHOD_FUNC(rb_image_distance), 1);
181
+ rb_define_method(cSimilie, "initialize", RUBY_METHOD_FUNC(rb_image_initialize), 1);
182
+ rb_define_method(cSimilie, "fingerprint", RUBY_METHOD_FUNC(rb_image_fingerprint), 0);
183
+ rb_define_method(cSimilie, "distance", RUBY_METHOD_FUNC(rb_image_distance), 1);
184
184
 
185
- rb_define_singleton_method(cSimilie, "distance", RUBY_METHOD_FUNC(rb_image_distance_func), 2);
186
- rb_define_singleton_method(cSimilie, "phash", RUBY_METHOD_FUNC(rb_image_phash_func), 1);
185
+ rb_define_singleton_method(cSimilie, "distance", RUBY_METHOD_FUNC(rb_image_distance_func), 2);
186
+ rb_define_singleton_method(cSimilie, "fingerprint", RUBY_METHOD_FUNC(rb_image_fingerprint_func), 1);
187
+ rb_define_singleton_method(cSimilie, "popcount", RUBY_METHOD_FUNC(rb_popcount_func), 1);
187
188
  }
@@ -0,0 +1,8 @@
1
+ require_relative '../ext/similie'
2
+
3
+ require 'minitest/unit'
4
+ require 'minitest/spec'
5
+
6
+ $testdir = File.absolute_path(File.dirname(__FILE__))
7
+
8
+ MiniTest::Unit.autorun
@@ -12,12 +12,17 @@ describe 'Similie image load' do
12
12
  it 'should hash image' do
13
13
  img = Similie.new(File.join($testdir, 'lena1.png'))
14
14
  assert img
15
- assert_equal 36170087496991428, img.hash
15
+ assert_equal 36170087496991428, img.fingerprint
16
16
  end
17
17
 
18
18
  it 'should hash image using class method' do
19
- hash = Similie.phash(File.join($testdir, 'lena1.png'))
19
+ hash = Similie.fingerprint(File.join($testdir, 'lena1.png'))
20
20
  assert hash
21
21
  assert_equal 36170087496991428, hash
22
22
  end
23
+
24
+ it 'should expose popcount' do
25
+ assert_equal 3, Similie.popcount(0x03 ^ 0x08)
26
+ assert_equal 4, Similie.popcount(0x07 ^ 0x08)
27
+ end
23
28
  end
metadata CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
4
4
  prerelease: false
5
5
  segments:
6
6
  - 0
7
- - 2
8
- - 6
9
- version: 0.2.6
7
+ - 3
8
+ - 1
9
+ version: 0.3.1
10
10
  platform: ruby
11
11
  authors:
12
12
  - Bharanee Rathna
@@ -14,11 +14,11 @@ autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
16
 
17
- date: 2011-06-08 00:00:00 +10:00
17
+ date: 2011-11-20 00:00:00 +11:00
18
18
  default_executable:
19
19
  dependencies: []
20
20
 
21
- description: "\n Similie does image fingerprinting using discrete cosine transform\n and similarity comparison using Hamming distance on fingerprints.\n "
21
+ description: similie is an image fingerprinting & comparison utility
22
22
  email: deepfryed@gmail.com
23
23
  executables: []
24
24
 
@@ -27,12 +27,12 @@ extensions:
27
27
  extra_rdoc_files:
28
28
  - README.rdoc
29
29
  files:
30
- - VERSION
31
30
  - ext/similie.c
32
31
  - README.rdoc
32
+ - ext/extconf.rb
33
+ - test/helper.rb
33
34
  - test/test_basic.rb
34
35
  - test/test_distance.rb
35
- - ext/extconf.rb
36
36
  has_rdoc: true
37
37
  homepage: http://github.com/deepfryed/similie
38
38
  licenses: []
@@ -64,7 +64,6 @@ rubyforge_project:
64
64
  rubygems_version: 1.3.7
65
65
  signing_key:
66
66
  specification_version: 3
67
- summary: Compute image fingerprints and similarity
68
- test_files:
69
- - test/test_basic.rb
70
- - test/test_distance.rb
67
+ summary: compute image fingerprints and similarity
68
+ test_files: []
69
+
data/VERSION DELETED
@@ -1 +0,0 @@
1
- 0.2.6