similie 0.2.6 → 0.3.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|