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.
- data/README.rdoc +16 -7
- data/ext/extconf.rb +17 -4
- data/ext/similie.c +44 -43
- data/test/helper.rb +8 -0
- data/test/test_basic.rb +7 -2
- metadata +10 -11
- data/VERSION +0 -1
data/README.rdoc
CHANGED
@@ -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.
|
13
|
-
img2 = Similie.new("test/lena2.png") # lena1.
|
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.
|
16
|
+
img1.fingerprint #=> 64bit int
|
17
|
+
|
18
|
+
img1.distance(img2) #=> 2
|
19
|
+
img1.distance(img5) #=> 12
|
17
20
|
|
18
|
-
|
19
|
-
|
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
|
-
|
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/]
|
data/ext/extconf.rb
CHANGED
@@ -1,12 +1,25 @@
|
|
1
1
|
require 'mkmf'
|
2
2
|
|
3
|
-
|
4
|
-
|
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
|
-
|
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',
|
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)
|
data/ext/similie.c
CHANGED
@@ -21,11 +21,8 @@
|
|
21
21
|
#include <opencv/cxcore.h>
|
22
22
|
#include <opencv/highgui.h>
|
23
23
|
|
24
|
-
#define TO_S(v)
|
25
|
-
#define CSTRING(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
|
-
|
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 *
|
63
|
-
IplImage *
|
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
|
-
|
66
|
-
|
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,
|
69
|
-
|
70
|
-
|
71
|
-
|
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
|
-
|
79
|
-
|
80
|
-
|
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)
|
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
|
121
|
-
VALUE hash = rb_iv_get(self, "@
|
114
|
+
VALUE rb_image_fingerprint(VALUE self) {
|
115
|
+
VALUE hash = rb_iv_get(self, "@fingerprint");
|
122
116
|
if (NIL_P(hash)) {
|
123
|
-
hash = SIZET2NUM(
|
124
|
-
rb_iv_set(self, "@
|
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 =
|
141
|
-
VALUE hash2 =
|
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(
|
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
|
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 =
|
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",
|
182
|
-
rb_define_method(cSimilie, "
|
183
|
-
rb_define_method(cSimilie, "distance",
|
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",
|
186
|
-
rb_define_singleton_method(cSimilie, "
|
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
|
}
|
data/test/helper.rb
ADDED
data/test/test_basic.rb
CHANGED
@@ -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.
|
15
|
+
assert_equal 36170087496991428, img.fingerprint
|
16
16
|
end
|
17
17
|
|
18
18
|
it 'should hash image using class method' do
|
19
|
-
hash = Similie.
|
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
|
-
-
|
8
|
-
-
|
9
|
-
version: 0.
|
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-
|
17
|
+
date: 2011-11-20 00:00:00 +11:00
|
18
18
|
default_executable:
|
19
19
|
dependencies: []
|
20
20
|
|
21
|
-
description:
|
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:
|
68
|
-
test_files:
|
69
|
-
|
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
|