similie 0.2.6 → 0.3.1

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.
@@ -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