dhash-vips 0.0.0.2 → 0.0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Rakefile +40 -1
- data/{dhash.gemspec → dhash-vips.gemspec} +1 -1
- data/lib/dhash-vips.rb +60 -16
- data/lib/dhash-vips/version.rb +2 -2
- data/spec/_spec.rb +46 -21
- metadata +3 -6
- data/spec/images/face-high.jpg +0 -0
- data/spec/images/face-low.jpg +0 -0
- data/spec/images/face-with-nose.jpg +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 07665dfb99e7720dd2406286235a1a6623576830
|
4
|
+
data.tar.gz: 6ecf29f185c958959d556b3ceb12b1da15db69e4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b48e8ad679501b2ea2b6c7d7e579f9ff7a1c1bd04c7a2f2d12e6b2f249d0ce96cabe959767b28146a96c9cb1cb3e71f187199a9250485dd1ec645495860a0206
|
7
|
+
data.tar.gz: 6e3b6b9cca29187047f34a12543862494cfb557a009efa83fd8fac0592a76c446e425d03163a2ab1e9eb6f9a00bd500961181964b541b77f197b3d1c20c59eb2
|
data/Rakefile
CHANGED
@@ -1,8 +1,47 @@
|
|
1
|
+
STDOUT.sync = true
|
2
|
+
|
1
3
|
require "bundler/gem_tasks"
|
2
4
|
|
3
5
|
task :default => %w{ spec }
|
4
6
|
|
5
7
|
require "rspec/core/rake_task"
|
6
|
-
RSpec::Core::RakeTask.new
|
8
|
+
RSpec::Core::RakeTask.new :spec do |t|
|
7
9
|
t.verbose = false
|
8
10
|
end
|
11
|
+
|
12
|
+
visualize_hash = lambda do |hash|
|
13
|
+
puts hash.to_s(2).rjust(64, ?0).gsub(/(?<=.)/, '\0 ').scan(/.{16}/)
|
14
|
+
end
|
15
|
+
|
16
|
+
task :compare_pixelation do |_, args|
|
17
|
+
require_relative "lib/dhash-vips"
|
18
|
+
require "dhash"
|
19
|
+
|
20
|
+
ARGV.drop(1).each do |arg|
|
21
|
+
FileUtils.mkdir_p "compare_pixelation/#{File.dirname arg}"
|
22
|
+
|
23
|
+
puts filename = "compare_pixelation/#{arg}.dhash-vips.png"
|
24
|
+
DHashVips::DHash.pixelate(arg, 8).
|
25
|
+
colourspace(:srgb). # otherwise we may get `Vips::Error` `RGB color space not permitted on grayscale PNG` when the image was already bw
|
26
|
+
write_to_file filename
|
27
|
+
visualize_hash.call DHashVips::DHash.calculate arg
|
28
|
+
|
29
|
+
puts filename = "compare_pixelation/#{arg}.dhash.png"
|
30
|
+
Magick::Image.read(arg).first.quantize(256, Magick::Rec601LumaColorspace, Magick::NoDitherMethod, 8).resize!(9, 8).
|
31
|
+
write filename
|
32
|
+
visualize_hash.call Dhash.calculate arg
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
task :compare_kernels do |_, args|
|
37
|
+
require_relative "lib/dhash-vips"
|
38
|
+
require "dhash"
|
39
|
+
|
40
|
+
%i{ nearest linear cubic lanczos2 lanczos3 }.each do |kernel|
|
41
|
+
hashes = ARGV.drop(1).map do |arg|
|
42
|
+
puts arg
|
43
|
+
DHashVips::DHash.calculate(arg, 8, kernel).tap &visualize_hash
|
44
|
+
end
|
45
|
+
puts "kernel: #{kernel}, distance: #{DHashVips::DHash.hamming *hashes}"
|
46
|
+
end
|
47
|
+
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Gem::Specification.new do |spec|
|
2
2
|
spec.name = "dhash-vips"
|
3
|
-
spec.version = (require_relative "lib/dhash-vips/version";
|
3
|
+
spec.version = (require_relative "lib/dhash-vips/version"; DHashVips::VERSION)
|
4
4
|
spec.author = "Victor Maslov"
|
5
5
|
spec.email = "nakilon@gmail.com"
|
6
6
|
spec.summary = "dHash powered by Vips"
|
data/lib/dhash-vips.rb
CHANGED
@@ -1,29 +1,73 @@
|
|
1
1
|
require_relative "dhash-vips/version"
|
2
2
|
require "vips"
|
3
3
|
|
4
|
-
module
|
5
|
-
|
4
|
+
module DHashVips
|
5
|
+
module DHash
|
6
|
+
|
7
|
+
extend self
|
8
|
+
|
9
|
+
def hamming a, b
|
10
|
+
(a ^ b).to_s(2).count "1"
|
11
|
+
end
|
12
|
+
|
13
|
+
def pixelate file, hash_size, kernel = nil
|
14
|
+
image = Vips::Image.new_from_file file
|
15
|
+
if kernel
|
16
|
+
image.resize((hash_size + 1).fdiv(image.width), vscale: hash_size.fdiv(image.height), kernel: kernel).colourspace("b-w")
|
17
|
+
else
|
18
|
+
image.resize((hash_size + 1).fdiv(image.width), vscale: hash_size.fdiv(image.height) ).colourspace("b-w")
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def calculate file, hash_size = 8, kernel = nil
|
23
|
+
image = pixelate file, hash_size, kernel
|
24
|
+
|
25
|
+
image.cast("int").conv([1, -1]).crop(1, 0, 8, 8).>(0)./(255).cast("uchar").to_a.join.to_i(2)
|
26
|
+
end
|
6
27
|
|
7
|
-
def hamming a, b
|
8
|
-
(a ^ b).to_s(2).count "1"
|
9
28
|
end
|
10
29
|
|
11
|
-
|
12
|
-
|
13
|
-
image = image.resize((hash_size + 1).fdiv(image.width), vscale: hash_size.fdiv(image.height)).colourspace "b-w"
|
30
|
+
module IDHash
|
31
|
+
extend self
|
14
32
|
|
15
|
-
|
33
|
+
def hamming a, b
|
34
|
+
ad = a >> 64
|
35
|
+
ai = a - (ad << 64)
|
36
|
+
bd = b >> 64
|
37
|
+
bi = b - (bd << 64)
|
38
|
+
((ai | bi) & (ad ^ bd)).to_s(2).count "1"
|
39
|
+
end
|
16
40
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
41
|
+
def median array
|
42
|
+
h = array.size / 2
|
43
|
+
return array[h] if array[h] != array[h - 1]
|
44
|
+
right = array.dup
|
45
|
+
left = right.shift h
|
46
|
+
right.shift if right.size > left.size
|
47
|
+
return right.first if left.last != right.first
|
48
|
+
return right.uniq[1] if left.count(left.last) > right.count(right.first)
|
49
|
+
left.last
|
50
|
+
end
|
51
|
+
fail unless 2 == median([1, 2, 2, 2, 2, 2, 3])
|
52
|
+
fail unless 3 == median([1, 2, 2, 2, 2, 3, 3])
|
53
|
+
fail unless 3 == median([1, 1, 2, 2, 3, 3, 3])
|
54
|
+
fail unless 2 == median([1, 1, 1, 2, 3, 3, 3])
|
55
|
+
fail unless 2 == median([1, 1, 2, 2, 2, 2, 3])
|
56
|
+
fail unless 2 == median([1, 2, 2, 2, 2, 3])
|
57
|
+
fail unless 3 == median([1, 2, 2, 3, 3, 3])
|
58
|
+
fail unless 1 == median([1, 1, 1])
|
59
|
+
fail unless 1 == median([1, 1])
|
60
|
+
|
61
|
+
def calculate file, hash_size = 8
|
62
|
+
image = Vips::Image.new_from_file file
|
63
|
+
image = image.resize((hash_size + 1).fdiv(image.width), vscale: hash_size.fdiv(image.height)).colourspace("b-w")
|
64
|
+
|
65
|
+
conv = image.cast("int").conv([1, -1]).crop(1, 0, 8, 8)
|
66
|
+
d = conv.>(0)./(255).cast("uchar").to_a.join.to_i(2)
|
67
|
+
i = conv.abs.>=(median conv.abs.to_a.flatten.sort)./(255).cast("uchar").to_a.join.to_i(2)
|
68
|
+
(d << 64) + i
|
24
69
|
end
|
25
70
|
|
26
|
-
difference
|
27
71
|
end
|
28
72
|
|
29
73
|
end
|
data/lib/dhash-vips/version.rb
CHANGED
@@ -1,3 +1,3 @@
|
|
1
|
-
module
|
2
|
-
VERSION = "0.0.0
|
1
|
+
module DHashVips
|
2
|
+
VERSION = "0.0.1.0"
|
3
3
|
end
|
data/spec/_spec.rb
CHANGED
@@ -1,8 +1,25 @@
|
|
1
1
|
require "dhash-vips"
|
2
2
|
|
3
|
-
|
3
|
+
[
|
4
|
+
[DHashVips::DHash, 18, 22],
|
5
|
+
# [[0, 17, 29, 27, 22, 29],
|
6
|
+
# [17, 0, 30, 26, 33, 36],
|
7
|
+
# [29, 30, 0, 18, 39, 30],
|
8
|
+
# [27, 26, 18, 0, 35, 30],
|
9
|
+
# [22, 33, 39, 35, 0, 17],
|
10
|
+
# [29, 36, 30, 30, 17, 0]]
|
11
|
+
[DHashVips::IDHash, 10, 15],
|
12
|
+
# [[0, 5, 21, 23, 18, 23],
|
13
|
+
# [5, 0, 15, 16, 17, 28],
|
14
|
+
# [21, 15, 0, 10, 31, 25],
|
15
|
+
# [23, 16, 10, 0, 26, 28],
|
16
|
+
# [18, 17, 31, 26, 0, 8],
|
17
|
+
# [23, 28, 25, 28, 8, 0]]
|
18
|
+
].each do |lib, max_similar, min_not_similar|
|
19
|
+
|
20
|
+
describe lib do
|
4
21
|
|
5
|
-
require "tmpdir"
|
22
|
+
# require "tmpdir"
|
6
23
|
require "fileutils"
|
7
24
|
require "open-uri"
|
8
25
|
require "digest"
|
@@ -17,10 +34,14 @@ describe DhashVips do
|
|
17
34
|
df0a3b93e9412536ee8a11255f974141.jpg
|
18
35
|
679634ff89a31279a39f03e278bc9a01.jpg
|
19
36
|
} # these images a consecutive pairs of slightly (but enough for nice asserts) silimar images
|
37
|
+
bw1, bw2 = %w{
|
38
|
+
71662d4d4029a3b41d47d5baf681ab9a.jpg
|
39
|
+
ad8a37f872956666c3077a3e9e737984.jpg
|
40
|
+
} # these is the same photo but of different size and bw
|
20
41
|
|
21
42
|
example.metadata[:extra_failure_lines] = []
|
22
|
-
FileUtils.mkdir_p dir =
|
23
|
-
images.
|
43
|
+
FileUtils.mkdir_p dir = "images"
|
44
|
+
*images, bw1, bw2 = [*images, bw1, bw2].map do |image|
|
24
45
|
"#{dir}/#{image}".tap do |filename|
|
25
46
|
unless File.exist?(filename) && Digest::MD5.file(filename) == File.basename(filename, ".jpg")
|
26
47
|
example.metadata[:extra_failure_lines] << "copying image from web to #{filename}"
|
@@ -33,28 +54,32 @@ describe DhashVips do
|
|
33
54
|
end
|
34
55
|
end
|
35
56
|
|
36
|
-
hashes = images.map &
|
37
|
-
table = MLL::table[
|
57
|
+
hashes = images.map &described_class.method(:calculate)
|
58
|
+
table = MLL::table[described_class.method(:hamming), [hashes], [hashes]]
|
59
|
+
|
38
60
|
# require "pp"
|
39
61
|
# pp table
|
40
|
-
#
|
41
|
-
# [17, 0, 30, 26, 33, 36],
|
42
|
-
# [29, 30, 0, 18, 39, 30],
|
43
|
-
# [27, 26, 18, 0, 35, 30],
|
44
|
-
# [22, 33, 39, 35, 0, 17],
|
45
|
-
# [29, 36, 30, 30, 17, 0]]
|
62
|
+
# abort
|
46
63
|
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
64
|
+
aggregate_failures do
|
65
|
+
hashes.size.times.to_a.repeated_combination(2) do |i, j|
|
66
|
+
case
|
67
|
+
when i == j
|
68
|
+
expect(table[i][j]).to eq 0
|
69
|
+
when (j - i).abs == 1 && (i + j - 1) % 4 == 0
|
70
|
+
expect(table[i][j]).to be > 0
|
71
|
+
expect(table[i][j]).to be <= max_similar
|
72
|
+
else
|
73
|
+
expect(table[i][j]).to be >= min_not_similar
|
74
|
+
end
|
56
75
|
end
|
76
|
+
|
77
|
+
hashes = [bw1, bw2].map &described_class.method(:calculate)
|
78
|
+
expect(described_class.hamming(*hashes)).to eq 0
|
57
79
|
end
|
80
|
+
|
58
81
|
end
|
59
82
|
|
60
83
|
end
|
84
|
+
|
85
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: dhash-vips
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.0
|
4
|
+
version: 0.0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Victor Maslov
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-09-
|
11
|
+
date: 2017-09-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: vips
|
@@ -91,13 +91,10 @@ files:
|
|
91
91
|
- LICENSE.txt
|
92
92
|
- README.md
|
93
93
|
- Rakefile
|
94
|
-
- dhash.gemspec
|
94
|
+
- dhash-vips.gemspec
|
95
95
|
- lib/dhash-vips.rb
|
96
96
|
- lib/dhash-vips/version.rb
|
97
97
|
- spec/_spec.rb
|
98
|
-
- spec/images/face-high.jpg
|
99
|
-
- spec/images/face-low.jpg
|
100
|
-
- spec/images/face-with-nose.jpg
|
101
98
|
homepage: https://github.com/nakilon/dhash-vips
|
102
99
|
licenses:
|
103
100
|
- MIT
|
data/spec/images/face-high.jpg
DELETED
Binary file
|
data/spec/images/face-low.jpg
DELETED
Binary file
|
Binary file
|