diff --git a/lib/phashion.rb b/lib/phashion.rb index ed1ba13..c17822a 100644 --- a/lib/phashion.rb +++ b/lib/phashion.rb @@ -12,9 +12,9 @@ module Phashion TextHashPoint = Struct.new :hash, :index TextMatch = Struct.new :first_index, :second_index, :length + DEFAULT_DUPE_THRESHOLD = 15 + class Image - DEFAULT_DUPE_THRESHOLD = 15 - attr_reader :filename def initialize(filename) @filename = filename @@ -52,6 +52,12 @@ def self.so_file extname = RbConfig::CONFIG['DLEXT'] File.join File.dirname(__FILE__), "phashion_ext.#{extname}" end + + def self.duplicate?(fingerprint1, fingerprint2, opts={}) + threshold = opts[:threshold] || DEFAULT_DUPE_THRESHOLD + + Phashion.hamming_distance(fingerprint1, fingerprint2) <= threshold + end end require 'phashion_ext' diff --git a/test/test_phashion.rb b/test/test_phashion.rb index 3286ce1..7047803 100644 --- a/test/test_phashion.rb +++ b/test/test_phashion.rb @@ -122,19 +122,19 @@ def test_multiple_types assert_duplicate gif, png assert_duplicate jpg, gif end - + def test_fingerprint_png_is_different png1 = Phashion::Image.new(File.dirname(__FILE__) + '/png/Broccoli_Super_Food.png') png2 = Phashion::Image.new(File.dirname(__FILE__) + '/png/linux.png') png3 = Phashion::Image.new(File.dirname(__FILE__) + '/png/grass.png') png4 = Phashion::Image.new(File.dirname(__FILE__) + '/png/Broccoli_Super_Food.png') - + fingerprints = [] fingerprints << png1.fingerprint fingerprints << png2.fingerprint fingerprints << png3.fingerprint fingerprints << png4.fingerprint - + assert fingerprints.uniq.size == 3, "array should contain 3 unique fingerprints" end @@ -145,8 +145,8 @@ def test_duplicate_with_custom_distance_threshold jpg = Phashion::Image.new(File.dirname(__FILE__) + '/jpg/Broccoli_Super_Food.jpg') jpg_x = Phashion::Image.new(File.dirname(__FILE__) + '/jpg/Broccoli_Super_Food.100px.jpg') - refute(jpg.duplicate?(jpg_x, threshold: 1)) - assert(jpg.duplicate?(jpg_x, threshold: 2)) + refute(jpg.duplicate?(jpg_x, threshold: 1)) + assert(jpg.duplicate?(jpg_x, threshold: 2)) end @@ -161,28 +161,28 @@ def test_distance_from_jpg_to_png_dupe def test_distance_from_lossy_jpg jpg = Phashion::Image.new(File.dirname(__FILE__) + '/jpg/Broccoli_Super_Food.jpg') jpg_x = Phashion::Image.new(File.dirname(__FILE__) + '/jpg/Broccoli_Super_Food.lossy.jpg') - + assert_equal(jpg.distance_from(jpg_x), 0) end def test_distance_from_smaller_jpg jpg = Phashion::Image.new(File.dirname(__FILE__) + '/jpg/Broccoli_Super_Food.jpg') jpg_x = Phashion::Image.new(File.dirname(__FILE__) + '/jpg/Broccoli_Super_Food.100px.jpg') - + assert_equal(jpg.distance_from(jpg_x), 2) end def test_distance_from_color_correction jpg = Phashion::Image.new(File.dirname(__FILE__) + '/jpg/Broccoli_Super_Food.jpg') jpg_x = Phashion::Image.new(File.dirname(__FILE__) + '/jpg/Broccoli_Super_Food.color-corrected.jpg') - + assert_equal(jpg.distance_from(jpg_x), 2) end def test_distance_from_black_and_white jpg = Phashion::Image.new(File.dirname(__FILE__) + '/jpg/Broccoli_Super_Food.jpg') jpg_x = Phashion::Image.new(File.dirname(__FILE__) + '/jpg/Broccoli_Super_Food.bw.jpg') - + assert_equal(jpg.distance_from(jpg_x), 2) end @@ -191,14 +191,14 @@ def test_distance_from_bounding_box # from 500x349 to 466x312 jpg = Phashion::Image.new(File.dirname(__FILE__) + '/jpg/Broccoli_Super_Food.jpg') jpg_x = Phashion::Image.new(File.dirname(__FILE__) + '/jpg/Broccoli_Super_Food.bounding-box.jpg') - + assert_equal(jpg.distance_from(jpg_x), 12) end - + def test_distance_from_rotation_of_5degrees_c2 jpg = Phashion::Image.new(File.dirname(__FILE__) + '/jpg/Broccoli_Super_Food.jpg') jpg_x = Phashion::Image.new(File.dirname(__FILE__) + '/jpg/Broccoli_Super_Food.rotate5cw.jpg') - + assert_equal(jpg.distance_from(jpg_x), 14) end @@ -206,12 +206,18 @@ def test_distance_from_rotation_of_5degrees_c2 def test_distance_from_horizontal_flip jpg = Phashion::Image.new(File.dirname(__FILE__) + '/jpg/Broccoli_Super_Food.jpg') jpg_x = Phashion::Image.new(File.dirname(__FILE__) + '/jpg/Broccoli_Super_Food.horizontal-flip.jpg') - - assert_operator(jpg.distance_from(jpg_x), :>, Phashion::Image::DEFAULT_DUPE_THRESHOLD) + + assert_operator(jpg.distance_from(jpg_x), :>, Phashion::DEFAULT_DUPE_THRESHOLD) end - private + def test_duplicate_with_module_method + jpg = Phashion::Image.new(File.dirname(__FILE__) + '/jpg/Broccoli_Super_Food.jpg') + jpg_x = Phashion::Image.new(File.dirname(__FILE__) + '/jpg/Broccoli_Super_Food.bw.jpg') + + assert_duplicate_with_module_method(jpg, jpg_x) + end + private def assert_duplicate(a, b) assert a.duplicate?(b), "#{a.filename} not dupe of #{b.filename}" @@ -219,5 +225,9 @@ def assert_duplicate(a, b) def assert_not_duplicate(a, b) assert !a.duplicate?(b), "#{a.filename} dupe of #{b.filename}" - end + end + + def assert_duplicate_with_module_method(a, b) + assert Phashion.duplicate?(a.fingerprint, b.fingerprint), "#{a.filename} not dupe of #{b.filename}" + end end