similie 0.1.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,30 @@
1
+ = Similie
2
+
3
+ Similie is a simple DCT based image hashing interface that,
4
+
5
+ * computes a fingerprint based on low frequencies of an image.
6
+ * computes hamming distance between 2 fingerprints.
7
+
8
+ == Example
9
+
10
+ require 'similie'
11
+
12
+ img1 = Similie.new("test/lena1.jpg")
13
+ img2 = Similie.new("test/lena2.png") # lena1.jpg cropped and scaled
14
+ img3 = Similie.new("test/lena5.png") # a different image
15
+
16
+ img1.hash #=> unsigned 64bit int
17
+
18
+ img1.distance(img2) #=> 1
19
+ img1.distance(img5) #=> 5
20
+
21
+ Similie.distance("test/lena1.jpg", "test/lena5.jpg") #=> 5
22
+
23
+ == Dependencies
24
+
25
+ * ruby 1.9.1+
26
+ * opencv 2.1+ (libcv-dev and libhighgui-dev on debian systems)
27
+
28
+ = License
29
+
30
+ {CC BY-SA 3.0}[http://creativecommons.org/licenses/by-sa/3.0/]
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.0
@@ -0,0 +1,25 @@
1
+ require 'mkmf'
2
+
3
+ $CFLAGS = "-I/usr/include/opencv -Wall"
4
+ $LDFLAGS = "-lhighgui -lcxcore"
5
+
6
+ dir_config("libcv", ["/usr/local", "/opt/local", "/usr"])
7
+
8
+ headers = [ 'stdio.h', 'stdlib.h', 'string.h', 'opencv/cxcore.h', 'opencv/highgui.h' ]
9
+ lib_1 = [ 'cxcore', 'cvInitFont', headers ]
10
+ lib_2 = [ 'highgui', 'cvEncodeImage', headers ]
11
+
12
+ if have_header('opencv/cxcore.h') && have_library(*lib_1) && have_library(*lib_2)
13
+ create_makefile 'similie'
14
+ else
15
+ puts %q{
16
+ Cannot find opencv headers or libraries.
17
+
18
+ On debian based systems you can install it from apt as,
19
+ sudo apt-get install libcv-dev libhighgui-dev
20
+
21
+ Refer to http://opencv.willowgarage.com/wiki/InstallGuide for other platforms or operating systems.
22
+ }
23
+
24
+ exit 1
25
+ end
@@ -0,0 +1,165 @@
1
+ /*
2
+ (c) Bharanee Rathna 2011
3
+
4
+ CC BY-SA 3.0
5
+ http://creativecommons.org/licenses/by-sa/3.0/
6
+
7
+ Free for every type of use. The author cannot be legally held responsible for
8
+ any damages resulting from the use of this work. All modifications or derivatives
9
+ need to be attributed.
10
+ */
11
+
12
+ #include <ruby.h>
13
+ #include <ruby/encoding.h>
14
+
15
+ #include <stdio.h>
16
+ #include <stdlib.h>
17
+ #include <string.h>
18
+ #include <time.h>
19
+ #include <unistd.h>
20
+ #include <opencv/cv.h>
21
+ #include <opencv/cxcore.h>
22
+ #include <opencv/highgui.h>
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]
29
+
30
+ #undef SIZET2NUM
31
+ #ifdef HAVE_LONG_LONG
32
+ #define SIZET2NUM(x) ULL2NUM(x)
33
+ #else
34
+ #define SIZET2NUM(x) ULONG2NUM(x)
35
+ #endif
36
+
37
+ // http://en.wikipedia.org/wiki/Hamming_weight
38
+
39
+ const uint64 m1 = 0x5555555555555555; //binary: 0101...
40
+ const uint64 m2 = 0x3333333333333333; //binary: 00110011..
41
+ const uint64 m4 = 0x0f0f0f0f0f0f0f0f; //binary: 4 zeros, 4 ones ...
42
+ const uint64 m8 = 0x00ff00ff00ff00ff; //binary: 8 zeros, 8 ones ...
43
+ const uint64 m16 = 0x0000ffff0000ffff; //binary: 16 zeros, 16 ones ...
44
+ const uint64 m32 = 0x00000000ffffffff; //binary: 32 zeros, 32 ones
45
+ const uint64 hff = 0xffffffffffffffff; //binary: all ones
46
+ const uint64 h01 = 0x0101010101010101; //the sum of 256 to the power of 0,1,2,3...
47
+
48
+ int popcount(uint64 x) {
49
+ x -= (x >> 1) & m1; //put count of each 2 bits into those 2 bits
50
+ x = (x & m2) + ((x >> 2) & m2); //put count of each 4 bits into those 4 bits
51
+ x = (x + (x >> 4)) & m4; //put count of each 8 bits into those 8 bits
52
+ return (x * h01)>>56; //returns left 8 bits of x + (x<<8) + (x<<16) + (x<<24) + ...
53
+ }
54
+
55
+ uint64_t image_phash(IplImage *img) {
56
+ uint64_t phash = 0;
57
+ int x, y;
58
+ double avg;
59
+
60
+ IplImage *small = cvCreateImage(cvSize(32, 32), 8, img->nChannels);
61
+ IplImage *mono = cvCreateImage(cvSize(32, 32), 8, 1);
62
+
63
+ cvResize(img, small, CV_INTER_CUBIC);
64
+ cvCvtColor(small, mono, CV_RGB2GRAY);
65
+
66
+ CvMat *dct = cvCreateMat(8, 8, CV_32FC1);
67
+ for (x = 0; x < 8; x++) {
68
+ for (y = 0; y < 8; y++) {
69
+ cvSet2D(dct, x, y, cvScalarAll(cvGetMonoPixel(mono, x, y)));
70
+ }
71
+ }
72
+
73
+ cvDCT(dct, dct, CV_DXT_ROWS);
74
+ cvSet2D(dct, 0, 0, cvScalarAll(0));
75
+
76
+ avg = (double)cvAvg(dct, 0).val[0] * 64.0 / 63.0;
77
+
78
+ uint64_t mask = 1;
79
+ for (x = 0; x < 8; x++) {
80
+ for (y = 0; y < 8; y++) {
81
+ if (cvGet2D(dct, x, y).val[0] > avg) phash |= mask;
82
+ mask = mask << 1;
83
+ }
84
+ }
85
+
86
+ cvReleaseMat(&dct);
87
+ cvReleaseImage(&mono);
88
+ cvReleaseImage(&small);
89
+ return phash;
90
+ }
91
+
92
+ static void rb_image_free(IplImage *handle) {
93
+ if (handle)
94
+ cvReleaseImage(&handle);
95
+ }
96
+
97
+ VALUE rb_image_alloc(VALUE klass) {
98
+ IplImage *handle = 0;
99
+ return Data_Wrap_Struct(klass, 0, rb_image_free, handle);
100
+ }
101
+
102
+ IplImage* rb_image_handle(VALUE self) {
103
+ IplImage *handle = 0;
104
+ Data_Get_Struct(self, IplImage, handle);
105
+ if (!handle)
106
+ rb_raise(rb_eRuntimeError, "Invalid object, did you forget to call super() ?");
107
+
108
+ return handle;
109
+ }
110
+
111
+ VALUE rb_image_hash(VALUE self) {
112
+ VALUE hash = rb_iv_get(self, "@hash");
113
+ if (NIL_P(hash)) {
114
+ hash = SIZET2NUM(image_phash(rb_image_handle(self)));
115
+ rb_iv_set(self, "@hash", hash);
116
+ }
117
+ return hash;
118
+ }
119
+
120
+ VALUE rb_image_initialize(VALUE self, VALUE file) {
121
+ IplImage *img = cvLoadImage(CSTRING(file), -1);
122
+ if (!img)
123
+ rb_raise(rb_eArgError, "Invalid image or unsupported format: %s", CSTRING(file));
124
+
125
+ DATA_PTR(self) = img;
126
+ return self;
127
+ }
128
+
129
+
130
+ VALUE rb_image_distance(VALUE self, VALUE other) {
131
+ VALUE hash1 = rb_image_hash(self);
132
+ VALUE hash2 = rb_image_hash(other);
133
+ int dist = popcount(NUM2ULONG(hash1) ^ NUM2ULONG(hash2));
134
+ return INT2NUM(dist);
135
+ }
136
+
137
+ VALUE rb_image_distance_func(VALUE self, VALUE file1, VALUE file2) {
138
+ IplImage *img1, *img2;
139
+ img1 = cvLoadImage(CSTRING(file1), -1);
140
+ if (!img1)
141
+ rb_raise(rb_eArgError, "Invalid image or unsupported format: %s", CSTRING(file1));
142
+
143
+ img2 = cvLoadImage(CSTRING(file2), -1);
144
+ if (!img2) {
145
+ cvReleaseImage(&img1);
146
+ rb_raise(rb_eArgError, "Invalid image or unsupported format: %s", CSTRING(file2));
147
+ }
148
+
149
+ int dist = popcount(image_phash(img1) ^ image_phash(img2));
150
+
151
+ cvReleaseImage(&img1);
152
+ cvReleaseImage(&img2);
153
+
154
+ return INT2NUM(dist);
155
+ }
156
+
157
+ void Init_similie() {
158
+ VALUE cSimilie = rb_define_class("Similie", rb_cObject);
159
+ rb_define_alloc_func(cSimilie, rb_image_alloc);
160
+ rb_define_method(cSimilie, "initialize", RUBY_METHOD_FUNC(rb_image_initialize), 1);
161
+ rb_define_method(cSimilie, "hash", RUBY_METHOD_FUNC(rb_image_hash), 0);
162
+ rb_define_method(cSimilie, "distance", RUBY_METHOD_FUNC(rb_image_distance), 1);
163
+
164
+ rb_define_singleton_method(cSimilie, "distance", RUBY_METHOD_FUNC(rb_image_distance_func), 2);
165
+ }
@@ -0,0 +1,17 @@
1
+ require_relative 'helper'
2
+
3
+ describe 'Similie image load' do
4
+ it 'should load image' do
5
+ assert Similie.new(File.join($testdir, 'lena1.jpg'))
6
+ end
7
+
8
+ it 'should barf on invalid path' do
9
+ assert_raises(ArgumentError) { Similie.new(File.join($testdir, 'lena2.jpg')) }
10
+ end
11
+
12
+ it 'should hash image' do
13
+ img = Similie.new(File.join($testdir, 'lena1.jpg'))
14
+ assert img
15
+ assert_equal 216455360913932544, img.hash
16
+ end
17
+ end
@@ -0,0 +1,16 @@
1
+ require_relative 'helper'
2
+
3
+ describe 'Similie image distance' do
4
+ it 'should work for similar images' do
5
+ images = %w(lena1.jpg lena2.png lena3.png lena4.png lena5.jpg).map {|file| Similie.new(File.join($testdir, file))}
6
+
7
+ assert_equal 1, images[0].distance(images[1])
8
+ assert_equal 2, images[1].distance(images[2])
9
+ assert_equal 0, images[2].distance(images[3])
10
+ assert_equal 6, images[3].distance(images[4])
11
+ end
12
+
13
+ it 'should work using the singleton method' do
14
+ assert_equal 6, Similie.distance(File.join($testdir, 'lena4.png'), File.join($testdir, 'lena5.jpg'))
15
+ end
16
+ end
metadata ADDED
@@ -0,0 +1,70 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: similie
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 1
8
+ - 0
9
+ version: 0.1.0
10
+ platform: ruby
11
+ authors:
12
+ - Bharanee Rathna
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2011-06-04 00:00:00 +10:00
18
+ default_executable:
19
+ dependencies: []
20
+
21
+ description: "\n Similie does image fingerprinting using discrete cosine transform\n and similarity comparison using Hamming distance on fingerprints.\n "
22
+ email: deepfryed@gmail.com
23
+ executables: []
24
+
25
+ extensions:
26
+ - ext/extconf.rb
27
+ extra_rdoc_files:
28
+ - README.rdoc
29
+ files:
30
+ - VERSION
31
+ - ext/similie.c
32
+ - README.rdoc
33
+ - test/test_basic.rb
34
+ - test/test_distance.rb
35
+ - ext/extconf.rb
36
+ has_rdoc: true
37
+ homepage: http://github.com/deepfryed/similie
38
+ licenses: []
39
+
40
+ post_install_message:
41
+ rdoc_options: []
42
+
43
+ require_paths:
44
+ - lib
45
+ required_ruby_version: !ruby/object:Gem::Requirement
46
+ none: false
47
+ requirements:
48
+ - - ">="
49
+ - !ruby/object:Gem::Version
50
+ segments:
51
+ - 0
52
+ version: "0"
53
+ required_rubygems_version: !ruby/object:Gem::Requirement
54
+ none: false
55
+ requirements:
56
+ - - ">="
57
+ - !ruby/object:Gem::Version
58
+ segments:
59
+ - 0
60
+ version: "0"
61
+ requirements: []
62
+
63
+ rubyforge_project:
64
+ rubygems_version: 1.3.7
65
+ signing_key:
66
+ specification_version: 3
67
+ summary: Compute image fingerprints and similarity
68
+ test_files:
69
+ - test/test_basic.rb
70
+ - test/test_distance.rb