dhasher 1.0.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.
- checksums.yaml +7 -0
- data/.gitignore +1 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +35 -0
- data/dhasher.gemspec +18 -0
- data/lib/dhasher.rb +47 -0
- data/spec/dhash_spec.rb +83 -0
- data/spec/images/in_flames1.jpg +0 -0
- data/spec/images/in_flames2.jpg +0 -0
- data/spec/images/in_flames3.jpg +0 -0
- data/spec/images/wrath.jpg +0 -0
- data/spec/spec_helper.rb +8 -0
- metadata +96 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA1:
|
|
3
|
+
metadata.gz: 0fb5e093f9d9e84cee470e97cbdf341d267f0ca7
|
|
4
|
+
data.tar.gz: 7b10d8ec7fd36cfedfd0c6848e8af13be28d8ade
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 046ec7098213e9f6adb8af74cfd1d3f90abca6daabab6880ca1dac1a37ada11ba26d7baecd01555afa063aade5748b31c3698d8dbf2d0f6e41114ee8e970c400
|
|
7
|
+
data.tar.gz: 6d214be1557f676523bb561008ded73673fb0c7f0695f75672dd75bf640162cb938a90cd1fc783e8f48acb185d81e17fc5b8576f9b4cb4d2768b0cbd29e19851
|
data/.gitignore
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
.idea/*
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
PATH
|
|
2
|
+
remote: .
|
|
3
|
+
specs:
|
|
4
|
+
dhasher (1.0.0)
|
|
5
|
+
rmagick
|
|
6
|
+
|
|
7
|
+
GEM
|
|
8
|
+
remote: https://rubygems.org/
|
|
9
|
+
specs:
|
|
10
|
+
diff-lcs (1.2.5)
|
|
11
|
+
rmagick (2.15.4)
|
|
12
|
+
rspec (3.4.0)
|
|
13
|
+
rspec-core (~> 3.4.0)
|
|
14
|
+
rspec-expectations (~> 3.4.0)
|
|
15
|
+
rspec-mocks (~> 3.4.0)
|
|
16
|
+
rspec-core (3.4.1)
|
|
17
|
+
rspec-support (~> 3.4.0)
|
|
18
|
+
rspec-expectations (3.4.0)
|
|
19
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
|
20
|
+
rspec-support (~> 3.4.0)
|
|
21
|
+
rspec-mocks (3.4.0)
|
|
22
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
|
23
|
+
rspec-support (~> 3.4.0)
|
|
24
|
+
rspec-support (3.4.1)
|
|
25
|
+
|
|
26
|
+
PLATFORMS
|
|
27
|
+
ruby
|
|
28
|
+
|
|
29
|
+
DEPENDENCIES
|
|
30
|
+
bundler
|
|
31
|
+
dhasher!
|
|
32
|
+
rspec
|
|
33
|
+
|
|
34
|
+
BUNDLED WITH
|
|
35
|
+
1.11.2
|
data/dhasher.gemspec
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
Gem::Specification.new do |s|
|
|
2
|
+
s.name = 'dhasher'
|
|
3
|
+
s.version = '1.0.0'
|
|
4
|
+
s.date = '2015-12-21'
|
|
5
|
+
s.description = "Calculate the DHash of an image file"
|
|
6
|
+
s.summary = ''
|
|
7
|
+
s.authors = ["Rohan Patel"]
|
|
8
|
+
s.email = 'rohan@rohan.io'
|
|
9
|
+
s.files = `git ls-files`.split("\n")
|
|
10
|
+
s.test_files = `git ls-files -- {spec}/*`.split("\n")
|
|
11
|
+
s.require_paths = ["lib"]
|
|
12
|
+
s.homepage =
|
|
13
|
+
'https://github.com/rohanpatel2602/ruby-dhash'
|
|
14
|
+
s.license = 'MIT'
|
|
15
|
+
s.add_development_dependency 'bundler'
|
|
16
|
+
s.add_dependency 'rmagick'
|
|
17
|
+
s.add_development_dependency "rspec"
|
|
18
|
+
end
|
data/lib/dhasher.rb
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
require 'RMagick'
|
|
2
|
+
|
|
3
|
+
class DHasher
|
|
4
|
+
|
|
5
|
+
def self.hash_from_blob(blob, hash_size = 8)
|
|
6
|
+
image = Magick::Image.from_blob(blob).first
|
|
7
|
+
hash_from_image(image, hash_size)
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def self.hash_from_path(path, hash_size = 8)
|
|
11
|
+
image = Magick::Image.read(path).first
|
|
12
|
+
hash_from_image(image, hash_size)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def self.hash_from_image(image, hash_size = 8)
|
|
16
|
+
image = image.quantize(256, Magick::RGBColorspace, Magick::NoDitherMethod, 8).resize!(hash_size + 1, hash_size + 1)
|
|
17
|
+
|
|
18
|
+
count = 0
|
|
19
|
+
result = 0
|
|
20
|
+
hash_size.times do |row|
|
|
21
|
+
hash_size.times do |col|
|
|
22
|
+
pixels_left = image.pixel_color(col, row).marshal_dump
|
|
23
|
+
grey_left = (pixels_left[:red] + pixels_left[:green] + pixels_left[:blue]) / 3
|
|
24
|
+
|
|
25
|
+
pixels_right = image.pixel_color(col + 1, row).marshal_dump
|
|
26
|
+
grey_right = (pixels_right[:red] + pixels_right[:green] + pixels_right[:blue]) / 3
|
|
27
|
+
|
|
28
|
+
if grey_left > grey_right
|
|
29
|
+
result |= (1<<count)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
count+=1
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
result
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def self.distance(hash1, hash2)
|
|
40
|
+
(hash1 ^ hash2).to_s(2).count('1')
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def self.similar?(hash1, hash2)
|
|
44
|
+
distance(hash1, hash2) <= 10
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
end
|
data/spec/dhash_spec.rb
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe DHasher do
|
|
4
|
+
|
|
5
|
+
describe 'hash_from_path' do
|
|
6
|
+
subject { DHasher.hash_from_path(File.expand_path('../images/in_flames2.jpg', __FILE__)) }
|
|
7
|
+
|
|
8
|
+
it 'should load the image given a path' do
|
|
9
|
+
DHasher.should_receive(:hash_from_image)
|
|
10
|
+
subject
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
describe 'hash_from_blob' do
|
|
15
|
+
subject { DHasher.hash_from_blob(File.read(File.expand_path('../images/in_flames2.jpg', __FILE__))) }
|
|
16
|
+
|
|
17
|
+
it 'should load the image given a blob' do
|
|
18
|
+
DHasher.should_receive(:hash_from_image)
|
|
19
|
+
subject
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
describe 'hash_from_image' do
|
|
24
|
+
let(:path) { File.expand_path('../images/in_flames2.jpg', __FILE__) }
|
|
25
|
+
let(:image) { Magick::Image.read(path).first }
|
|
26
|
+
|
|
27
|
+
it 'should calculate the dhash of an image' do
|
|
28
|
+
hash = DHasher.hash_from_image(image)
|
|
29
|
+
hash.should == 14605438543144808556
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
describe 'distance' do
|
|
34
|
+
let(:hash1) { 14605438543144808556 }
|
|
35
|
+
let(:hash2) { 14605438543144808555 }
|
|
36
|
+
|
|
37
|
+
it 'should compare two hashes and return the hamming distance' do
|
|
38
|
+
distance = DHasher.distance(hash1, hash2)
|
|
39
|
+
distance.should == 3
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
describe 'when the hashes are identical' do
|
|
43
|
+
|
|
44
|
+
it 'should return 0' do
|
|
45
|
+
distance = DHasher.distance(hash1, hash1)
|
|
46
|
+
distance.should == 0
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
describe 'similar?' do
|
|
53
|
+
let(:flames1) { File.expand_path('../images/in_flames1.jpg', __FILE__) }
|
|
54
|
+
let(:flames2) { File.expand_path('../images/in_flames2.jpg', __FILE__) }
|
|
55
|
+
let(:flames3) { File.expand_path('../images/in_flames3.jpg', __FILE__) }
|
|
56
|
+
let(:wrath) { File.expand_path('../images/wrath.jpg', __FILE__) }
|
|
57
|
+
|
|
58
|
+
describe 'when given two identical images' do
|
|
59
|
+
it 'should return true' do
|
|
60
|
+
DHasher.similar?(DHasher.hash_from_path(flames2), DHasher.hash_from_path(flames2)).should == true
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
describe 'when given one image that is a resized version of another' do
|
|
65
|
+
it 'should return true' do
|
|
66
|
+
DHasher.similar?(DHasher.hash_from_path(flames1), DHasher.hash_from_path(flames2)).should == true
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
describe 'when given two similar images' do
|
|
71
|
+
it 'should return true' do
|
|
72
|
+
DHasher.similar?(DHasher.hash_from_path(flames1), DHasher.hash_from_path(flames3)).should == true
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
describe 'when given two different images' do
|
|
77
|
+
it 'should return false' do
|
|
78
|
+
DHasher.similar?(DHasher.hash_from_path(flames1), DHasher.hash_from_path(wrath)).should == false
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
end
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: dhasher
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 1.0.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Rohan Patel
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2015-12-21 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: bundler
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - ">="
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '0'
|
|
20
|
+
type: :development
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - ">="
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: '0'
|
|
27
|
+
- !ruby/object:Gem::Dependency
|
|
28
|
+
name: rmagick
|
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
|
30
|
+
requirements:
|
|
31
|
+
- - ">="
|
|
32
|
+
- !ruby/object:Gem::Version
|
|
33
|
+
version: '0'
|
|
34
|
+
type: :runtime
|
|
35
|
+
prerelease: false
|
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
+
requirements:
|
|
38
|
+
- - ">="
|
|
39
|
+
- !ruby/object:Gem::Version
|
|
40
|
+
version: '0'
|
|
41
|
+
- !ruby/object:Gem::Dependency
|
|
42
|
+
name: rspec
|
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
|
44
|
+
requirements:
|
|
45
|
+
- - ">="
|
|
46
|
+
- !ruby/object:Gem::Version
|
|
47
|
+
version: '0'
|
|
48
|
+
type: :development
|
|
49
|
+
prerelease: false
|
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
51
|
+
requirements:
|
|
52
|
+
- - ">="
|
|
53
|
+
- !ruby/object:Gem::Version
|
|
54
|
+
version: '0'
|
|
55
|
+
description: Calculate the DHash of an image file
|
|
56
|
+
email: rohan@rohan.io
|
|
57
|
+
executables: []
|
|
58
|
+
extensions: []
|
|
59
|
+
extra_rdoc_files: []
|
|
60
|
+
files:
|
|
61
|
+
- ".gitignore"
|
|
62
|
+
- Gemfile
|
|
63
|
+
- Gemfile.lock
|
|
64
|
+
- dhasher.gemspec
|
|
65
|
+
- lib/dhasher.rb
|
|
66
|
+
- spec/dhash_spec.rb
|
|
67
|
+
- spec/images/in_flames1.jpg
|
|
68
|
+
- spec/images/in_flames2.jpg
|
|
69
|
+
- spec/images/in_flames3.jpg
|
|
70
|
+
- spec/images/wrath.jpg
|
|
71
|
+
- spec/spec_helper.rb
|
|
72
|
+
homepage: https://github.com/rohanpatel2602/ruby-dhash
|
|
73
|
+
licenses:
|
|
74
|
+
- MIT
|
|
75
|
+
metadata: {}
|
|
76
|
+
post_install_message:
|
|
77
|
+
rdoc_options: []
|
|
78
|
+
require_paths:
|
|
79
|
+
- lib
|
|
80
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
81
|
+
requirements:
|
|
82
|
+
- - ">="
|
|
83
|
+
- !ruby/object:Gem::Version
|
|
84
|
+
version: '0'
|
|
85
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
86
|
+
requirements:
|
|
87
|
+
- - ">="
|
|
88
|
+
- !ruby/object:Gem::Version
|
|
89
|
+
version: '0'
|
|
90
|
+
requirements: []
|
|
91
|
+
rubyforge_project:
|
|
92
|
+
rubygems_version: 2.4.6
|
|
93
|
+
signing_key:
|
|
94
|
+
specification_version: 4
|
|
95
|
+
summary: ''
|
|
96
|
+
test_files: []
|