phashion 1.0.6 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: c79341235f99694415039b6b706bfe48d3d5c425
4
+ data.tar.gz: 651fa7bba67c389b06b60dc24f73283ff1394397
5
+ !binary "U0hBNTEy":
6
+ metadata.gz: c47fdc783f464800a1c3b1bd2b4fe93cdcb938ca2ff0e49887bfc1e74305c7d4acca8fb72afbc4cfccacdd76d3b3c0a698999cb0e47d16780b84d4bd1feef0e1
7
+ data.tar.gz: 73d416a8f6b0393adfaceff857deec9c7d1e33095bc6c0cd0c47782c41acf1bc814cd893ce88bc6b64ae559bfbcae98ffe001795d9652a8327958948ece76f36
data/.gitignore CHANGED
@@ -17,5 +17,15 @@ tmtags
17
17
  coverage
18
18
  rdoc
19
19
  pkg
20
+ *.gem
21
+ .ruby-version
22
+ .ruby-gemset
23
+ Gemfile.lock
24
+ tmp
20
25
 
21
26
  ## PROJECT::SPECIFIC
27
+ ext/phashion_ext
28
+ !ext/phashion_ext/extconf.rb
29
+ !ext/phashion_ext/phashion_ext.c
30
+ !ext/phashion_ext/pHash-0.9.6.tar.gz
31
+ lib/phashion_ext.bundle
data/CHANGES.md CHANGED
@@ -1,6 +1,23 @@
1
1
  History
2
2
  =========
3
3
 
4
+ 1.1.0
5
+ -----
6
+ * Converted to Minitest (issue #35)
7
+ * Added Phashion.distance_from method (issue #30)
8
+ * Add Sqlite3 extension to handle distance calculations (issue #27)
9
+
10
+ 1.0.8
11
+ ------
12
+ * Renamed the pHashion tar.gz archive as "pHash-0.9.6.tar.gz" to solve/enable install from github
13
+
14
+ 1.0.7
15
+ ------
16
+ * Respository transferred to https://github.com/westonplatter/phashion
17
+ * Added custom pHash patch to support alpha channel PNG files (issue #20 and #23)
18
+ * Link `/user/local/lib` (issue #21)
19
+ * Explicitly include -pthreads flag for cpp compiler (issue #15)
20
+
4
21
  1.0.6
5
22
  ------
6
23
 
data/LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2011 Mike Perham
1
+ Copyright (c) 2010-2014 Mike Perham
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
data/README.md CHANGED
@@ -1,33 +1,61 @@
1
1
  Phashion
2
- ===========
2
+ ========
3
3
 
4
- Phashion is a Ruby wrapper around the pHash library, "perceptual hash", which detects duplicate
5
- and near duplicate multimedia files (images, audio, video). The wrapper currently only supports images.
4
+ Phashion is a Ruby wrapper around the [pHash library](http://phash.org/), "perceptual hash", which detects duplicate and near-duplicate multimedia files (e.g. images, audio, video, though Phashion currently only supports images.). "Near-duplicates" are images that come from the same source and show essentially the same thing, but may have differences in such features as dimensions, bytesizes, lossy-compression artifacts, and color levels.
6
5
 
7
- [See an overview of Phashion on my blog](http://www.mikeperham.com/2010/05/21/detecting-duplicate-images-with-phashion/).
6
+ [See an overview of Phashion on Mike's blog]
7
+ (http://www.mikeperham.com/2010/05/21/detecting-duplicate-images-with-phashion/).
8
8
 
9
9
  Installation
10
- -------------
10
+ ------------
11
11
 
12
12
  You install it just like any other Ruby gem:
13
13
 
14
14
  gem install phashion
15
15
 
16
- Phashion is somewhat involved to install as it has a few dependencies. I've wrapped up those
17
- dependencies into a custom tarball that is built locally just for this gem so you don't have to
18
- do anything special. See the code in `ext/phashion_ext` for more details.
16
+ Phashion is somewhat involved to install as it has a few dependencies. Phashion
17
+ wrapps these dependencies into a custom tarball that is built locally just
18
+ for this gem so you don't have to do anything special. Look in the
19
+ `ext/phashion_ext` folder for more details.
20
+
21
+
22
+ ### Compatibility
23
+ Because of this complexity, it is possible the gem install will fail on your
24
+ platform. Phashion has been tested on:
25
+
26
+ * Mac OSX 10.6
27
+ * Mac OSX 10.9
28
+ * Ubuntu 8.04
29
+ * Ubuntu 12.04
30
+
31
+ Please open a [GitHub issue](https://github.com/westonplatter/phashion/issues/) if you have installation problems.
32
+
33
+ ### Prerequisites
34
+
35
+ - `libpng`
36
+ - `libjpeg`
37
+ - [imagemagick](http://www.imagemagick.org/)
38
+
39
+ If you're on a Mac, you can use:
40
+
41
+ - [Homebrew](http://brew.sh/), e.g. `brew install libjpeg`
42
+ - or [Macports](http://www.macports.org/), e.g. `port install jpeg` .
43
+
44
+
45
+ #### Common Errors
19
46
 
20
- Because of this complexity, it is possible the gem install will fail on your platform. I've tested
21
- it on Mac OSX 10.6 and Ubuntu 8.04 but please contact me if you have installation problems.
22
47
 
23
- If you have an error upon install, like:
24
48
 
25
- ld: library not found for -ljpeg
49
+ - `ld: library not found for -ljpeg` – You need to install `libjpeg` if you run into this error upon `gem install`:
50
+
51
+
52
+ - `.....sh: convert: command not found; sh: gm: command not found` – You need to install [imagemagick](http://www.imagemagick.org/).
26
53
 
27
- you need to install libjpeg. "brew install libjpeg" or "port install jpeg" on OSX.
28
54
 
29
55
  Usage
30
- ---------
56
+ -----
57
+
58
+ ### Testing if one image is a duplicate of another
31
59
 
32
60
  require 'phashion'
33
61
  img1 = Phashion::Image.new(filename1)
@@ -35,23 +63,78 @@ Usage
35
63
  img1.duplicate?(img2)
36
64
  --> true
37
65
 
66
+ Optionally, you can set the minimum Hamming distance in the second argument, an options Hash:
67
+
68
+ img1.duplicate?(img2, :threshold => 5)
69
+ --> true
70
+
71
+ img1.duplicate?(img2, :threshold => 0)
72
+ --> false
73
+
74
+
75
+ ### Finding the Hamming distance between two images
76
+
77
+ require 'phashion'
78
+ img1 = Phashion::Image.new(filename1)
79
+ img2 = Phashion::Image.new(filename2)
80
+ img1.distance_from(img2)
81
+ --> 6
82
+
83
+ ### Threshold for dupe-detection
84
+
85
+ Currently, the maximum Hamming distance between two duplicate images is set at 15. As per [mperham's explanation](http://www.mikeperham.com/2010/05/21/detecting-duplicate-images-with-phashion/):
86
+
87
+ > A “perceptual hash” is a 64-bit value based on the discrete cosine transform of the image’s frequency spectrum data. Similar images will have hashes that are close in terms of Hamming distance. That is, a binary hash value of 1000 is closer to 0000 than 0011 because it only has one bit different whereas the latter value has two bits different. The duplicate threshold defines how many bits must be different between two hashes for the two associated images to be considered different images. Our testing showed that 15 bits is a good value to start with, it detected all duplicates with a minimum of false positives.
88
+
89
+ As a reference point, here are the Hamming distances in these test comparisons using [/test/jpg/Broccoli_Super_Food.jpg](https://github.com/westonplatter/phashion/blob/master/test/jpg/Broccoli_Super_Food.jpg) as the source image:
90
+
91
+
92
+ | Variation | Hamming distance
93
+ | ---------------------------------------------------- | ----------------:
94
+ | JPG to PNG | 0
95
+ | Lossy JPG (Photoshop Save for Web quality = 20) | 0
96
+ | Thumbnail (from 500px to 100px) | 2
97
+ | Color correction (saturation +20 w auto-correct) | 2
98
+ | Black and white | 2
99
+ | Extraneous whitespace cropped (500x349 to 466x312) | 12
100
+ | A sloppy rotation of 5 degrees clockwise | 14
101
+ | Horizontally-flipped | 32
102
+
103
+
104
+
105
+
106
+
107
+ Gem uses customized pHash 0.9.6
108
+ -------------------------------
109
+
110
+ In order to detech duplicate alpha PNGs, the gem uses a custom version of pHash
111
+ 0.9.6. The customization is limited to only these changes,
112
+ [westonplatter/phash@ff255d2]
113
+ (https://github.com/westonplatter/phash/commit/ff255d2d3f93c841b98923ecbde997027f21ae36).
114
+ The gem will be moving back to the pHash master branch once it supports
115
+ detection of alpha PNG file types.
116
+
117
+
38
118
  Testing
39
- ------------
119
+ -------
120
+
40
121
 
41
- To run the test suite:
122
+ #### To run the test suite:
123
+
124
+ bundle
125
+ rake compile
126
+ rake
42
127
 
43
- ```bash
44
- bundle
45
- rake compile
46
- rake
47
- ```
48
128
 
49
129
  Author
50
- ==========
130
+ ======
51
131
 
52
- Mike Perham, http://mikeperham.com, http://twitter.com/mperham, mperham AT gmail.com
132
+ Mike Perham,
133
+ http://mikeperham.com,
134
+ http://twitter.com/mperham,
135
+ mperham AT gmail.com
53
136
 
54
137
  Copyright
55
- ----------
138
+ ---------
56
139
 
57
- Copyright (c) 2010 Mike Perham. See LICENSE for details.
140
+ Copyright (c) 2010-2014 Mike Perham. See LICENSE for details.
@@ -5,7 +5,7 @@ BUNDLE = Dir.glob("#{HERE}/pHash-*.tar.gz").first
5
5
  BUNDLE_PATH = BUNDLE.gsub(".tar.gz", "")
6
6
  $CFLAGS = " -x c++ #{ENV["CFLAGS"]}"
7
7
  $includes = " -I#{HERE}/include"
8
- $libraries = " -L#{HERE}/lib"
8
+ $libraries = " -L#{HERE}/lib -L/usr/local/lib"
9
9
  $LIBPATH = ["#{HERE}/lib"]
10
10
  $CFLAGS = "#{$includes} #{$libraries} #{$CFLAGS}"
11
11
  $LDFLAGS = "#{$libraries} #{$LDFLAGS}"
@@ -43,4 +43,6 @@ Dir.chdir(HERE) do
43
43
  $LIBS = " -lpthread -lpHash_gem -lstdc++ -ljpeg -lpng"
44
44
  end
45
45
 
46
+ have_header 'sqlite3ext.h'
47
+
46
48
  create_makefile 'phashion_ext'
@@ -1,13 +1,47 @@
1
1
  #include "ruby.h"
2
2
  #include "pHash.h"
3
+ #ifdef HAVE_RUBY_THREAD_H
4
+ #include <ruby/thread.h>
5
+ #else
6
+ #include <ruby/intern.h>
7
+ void *
8
+ rb_thread_call_without_gvl(void *(*func)(void *data), void *data1,
9
+ rb_unblock_function_t *ubf, void *data2) {
10
+ return (void *)rb_thread_blocking_region(
11
+ (rb_blocking_function_t *)func, data1,
12
+ (rb_unblock_function_t *)ubf, data2);
13
+ }
14
+ #endif
15
+
16
+ struct nogvl_hash_args {
17
+ const char * filename;
18
+ ulong64 hash;
19
+ int retval;
20
+ };
21
+
22
+ static void * nogvl_hash(struct nogvl_hash_args * args) {
23
+ ulong64 hash;
24
+
25
+ args->retval = ph_dct_imagehash(args->filename, hash);
26
+ args->hash = hash;
27
+
28
+ return NULL;
29
+ }
3
30
 
4
31
  static VALUE image_hash_for(VALUE self, VALUE _filename) {
5
- char * filename = StringValuePtr(_filename);
6
32
  ulong64 hash;
7
- if (-1 == ph_dct_imagehash(filename, hash)) {
33
+ struct nogvl_hash_args args;
34
+
35
+ args.filename = StringValuePtr(_filename);
36
+ args.retval = -1;
37
+
38
+ rb_thread_call_without_gvl((void *(*)(void *))nogvl_hash,
39
+ (void *)&args, RUBY_UBF_PROCESS, 0);
40
+
41
+ if (-1 == args.retval) {
8
42
  rb_raise(rb_eRuntimeError, "Unknown pHash error");
9
43
  }
10
- return ULL2NUM(hash);
44
+ return ULL2NUM(args.hash);
11
45
  }
12
46
 
13
47
 
@@ -30,6 +64,54 @@ extern "C" {
30
64
  rb_define_singleton_method(c, "hamming_distance", (VALUE(*)(ANYARGS))hamming_distance, 2);
31
65
  rb_define_singleton_method(c, "image_hash_for", (VALUE(*)(ANYARGS))image_hash_for, 1);
32
66
  }
67
+
68
+ #ifdef HAVE_SQLITE3EXT_H
69
+ #include <sqlite3ext.h>
70
+
71
+ SQLITE_EXTENSION_INIT1
72
+
73
+ static void hamming_distance(sqlite3_context * ctx, int agc, sqlite3_value **argv)
74
+ {
75
+ sqlite3_int64 hashes[4];
76
+ ulong64 left, right;
77
+ int i, result;
78
+
79
+ for(i = 0; i < 4; i++) {
80
+ if (SQLITE_INTEGER == sqlite3_value_type(argv[i])) {
81
+ hashes[i] = sqlite3_value_int64(argv[i]);
82
+ } else {
83
+ hashes[i] = 0;
84
+ }
85
+ }
86
+
87
+ left = (hashes[0] << 32) + hashes[1];
88
+ right = (hashes[2] << 32) + hashes[3];
89
+ result = ph_hamming_distance(left, right);
90
+ sqlite3_result_int(ctx, result);
91
+ }
92
+
93
+ int sqlite3_phashionext_init(
94
+ sqlite3 *db,
95
+ char **pzErrMsg,
96
+ const sqlite3_api_routines *pApi
97
+ ){
98
+ SQLITE_EXTENSION_INIT2(pApi);
99
+
100
+ sqlite3_create_function(
101
+ db,
102
+ "hamming_distance",
103
+ 4,
104
+ SQLITE_UTF8,
105
+ NULL,
106
+ hamming_distance,
107
+ NULL,
108
+ NULL
109
+ );
110
+ return SQLITE_OK;
111
+ }
112
+
113
+ #endif
114
+
33
115
  #ifdef __cplusplus
34
116
  }
35
117
  #endif
@@ -6,21 +6,28 @@
6
6
  # int ph_dct_imagehash(const char *file, ulong64 &hash);
7
7
  # int ph_hamming_distance(ulong64 hasha, ulong64 hashb);
8
8
 
9
+ require 'rbconfig'
10
+
9
11
  module Phashion
10
- VERSION = '1.0.6'
12
+ VERSION = '1.0.8'
11
13
 
12
14
  class Image
13
- SETTINGS = {
14
- :dupe_threshold => 15
15
- }
16
-
15
+ DEFAULT_DUPE_THRESHOLD = 15
16
+
17
17
  attr_reader :filename
18
18
  def initialize(filename)
19
19
  @filename = filename
20
20
  end
21
21
 
22
- def duplicate?(other)
23
- Phashion.hamming_distance(fingerprint, other.fingerprint) < SETTINGS[:dupe_threshold]
22
+ # returns: an Integer representing the hamming distance from :other
23
+ def distance_from(other)
24
+ Phashion.hamming_distance(fingerprint, other.fingerprint)
25
+ end
26
+
27
+ def duplicate?(other, opts={})
28
+ threshold = opts[:threshold] || DEFAULT_DUPE_THRESHOLD
29
+
30
+ distance_from(other) <= threshold
24
31
  end
25
32
 
26
33
  def fingerprint
@@ -28,6 +35,10 @@ module Phashion
28
35
  end
29
36
  end
30
37
 
38
+ def self.so_file
39
+ extname = RbConfig::CONFIG['DLEXT']
40
+ File.join File.dirname(__FILE__), "phashion_ext.#{extname}"
41
+ end
31
42
  end
32
43
 
33
44
  require 'phashion_ext'
@@ -0,0 +1,3 @@
1
+ module Phashion
2
+ VERSION = '1.1.0'
3
+ end
@@ -1,21 +1,25 @@
1
- # Generated by jeweler
2
- # DO NOT EDIT THIS FILE DIRECTLY
3
- # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
4
1
  # -*- encoding: utf-8 -*-
5
-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'phashion/version'
5
+
6
6
  Gem::Specification.new do |s|
7
- s.name = %q{phashion}
8
- s.version = "1.0.6"
9
- s.authors = ["Mike Perham"]
10
- s.description = %q{Simple wrapper around the pHash library}
11
- s.email = %q{mperham@gmail.com}
12
- s.extensions = ["ext/phashion_ext/extconf.rb"]
13
- s.files = `git ls-files`.split("\n")
14
- s.homepage = %q{http://github.com/mperham/phashion}
15
- s.rdoc_options = ["--charset=UTF-8"]
7
+ s.name = 'phashion'
8
+ s.version = Phashion::VERSION
9
+ s.authors = ["Mike Perham"]
10
+ s.email = ["mperham@gmail.com"]
11
+ s.description = 'Simple wrapper around the pHash library'
12
+ s.homepage = 'http://github.com/westonplatter/phashion'
13
+
14
+ s.extensions = ["ext/phashion_ext/extconf.rb"]
15
+ s.files = `git ls-files`.split("\n")
16
+ s.rdoc_options = ["--charset=UTF-8"]
16
17
  s.require_paths = ["lib"]
17
- s.summary = %q{Simple wrapper around the pHash library}
18
- s.test_files = `git ls-files test`.split("\n")
19
- s.add_development_dependency(%q<rake-compiler>, [">= 0.7.0"])
18
+ s.summary = %q{Simple wrapper around the pHash library}
19
+ s.test_files = `git ls-files test`.split("\n")
20
+
21
+ s.add_development_dependency "rake-compiler", ">= 0.7.0"
22
+ s.add_development_dependency "sqlite3"
23
+ s.add_development_dependency "minitest", "~> 5.2.2"
20
24
  end
21
25
 
@@ -1,6 +1,3 @@
1
1
  require 'rubygems'
2
- require 'test/unit'
3
2
  require 'phashion'
4
-
5
- class Test::Unit::TestCase
6
- end
3
+ require 'minitest/autorun'
Binary file
Binary file
@@ -1,6 +1,54 @@
1
1
  require 'helper'
2
+ require 'sqlite3'
2
3
 
3
- class TestPhashion < Test::Unit::TestCase
4
+ class TestPhashion < Minitest::Test
5
+
6
+ def split(hash)
7
+ r = hash & 0xFFFFFFFF
8
+ l = (hash >> 32) & 0xFFFFFFFF
9
+ [l, r]
10
+ end
11
+
12
+ def test_db_bad_arg
13
+ db = SQLite3::Database.new ':memory:'
14
+ return unless db.respond_to? :enable_load_extension
15
+
16
+ db.enable_load_extension true
17
+ db.load_extension Phashion.so_file
18
+
19
+ res = db.execute "SELECT hamming_distance('foo', 'bar', 'baz', 'zot')"
20
+ assert_equal [[0]], res
21
+ end
22
+
23
+ def test_db_extension
24
+ db = SQLite3::Database.new ':memory:'
25
+ return unless db.respond_to? :enable_load_extension
26
+
27
+ db.enable_load_extension true
28
+ db.load_extension Phashion.so_file
29
+
30
+ db.execute <<-eosql
31
+ CREATE TABLE "images" (
32
+ "id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
33
+ "fingerprint_l" integer NOT NULL,
34
+ "fingerprint_r" integer NOT NULL)
35
+ eosql
36
+
37
+ jpg = File.dirname(__FILE__) + '/jpg/Broccoli_Super_Food.jpg'
38
+ png = File.dirname(__FILE__) + '/png/Broccoli_Super_Food.png'
39
+
40
+ hash1 = Phashion.image_hash_for jpg
41
+ hash2 = Phashion.image_hash_for png
42
+
43
+ l, r = split hash1
44
+ db.execute "INSERT INTO images (fingerprint_l, fingerprint_r) VALUES (#{l}, #{r})"
45
+
46
+ expected = Phashion.hamming_distance hash1, hash2
47
+
48
+ l, r = split hash2
49
+ rows = db.execute "SELECT hamming_distance(fingerprint_l, fingerprint_r, #{l}, #{r}) FROM images"
50
+ assert_equal expected, rows.first.first
51
+ end
4
52
 
5
53
  def test_duplicate_detection
6
54
  files = %w(86x86-0a1e.jpeg 86x86-83d6.jpeg 86x86-a855.jpeg)
@@ -10,6 +58,8 @@ class TestPhashion < Test::Unit::TestCase
10
58
  assert_duplicate images[0], images[2]
11
59
  end
12
60
 
61
+
62
+
13
63
  def test_duplicate_detection_2
14
64
  files = %w(b32aade8c590e2d776c24f35868f0c7a588f51e1.jpeg df9cc82f5b32d7463f36620c61854fde9d939f7f.jpeg e7397898a7e395c2524978a5e64de0efabf08290.jpeg)
15
65
  images = files.map {|f| Phashion::Image.new("#{File.dirname(__FILE__) + '/../test/jpg/'}#{f}")}
@@ -34,9 +84,97 @@ class TestPhashion < Test::Unit::TestCase
34
84
  assert_duplicate gif, png
35
85
  assert_duplicate jpg, gif
36
86
  end
87
+
88
+ def test_fingerprint_png_is_different
89
+ png1 = Phashion::Image.new(File.dirname(__FILE__) + '/png/Broccoli_Super_Food.png')
90
+ png2 = Phashion::Image.new(File.dirname(__FILE__) + '/png/linux.png')
91
+ png3 = Phashion::Image.new(File.dirname(__FILE__) + '/png/grass.png')
92
+ png4 = Phashion::Image.new(File.dirname(__FILE__) + '/png/Broccoli_Super_Food.png')
93
+
94
+ fingerprints = []
95
+ fingerprints << png1.fingerprint
96
+ fingerprints << png2.fingerprint
97
+ fingerprints << png3.fingerprint
98
+ fingerprints << png4.fingerprint
99
+
100
+ assert fingerprints.uniq.size == 3, "array should contain 3 unique fingerprints"
101
+ end
102
+
103
+
104
+ def test_duplicate_with_custom_distance_threshold
105
+ # note: this test depends on the smaller_jpg test still asserting a distance of 2
106
+ # note-2: threshold is a :less-than-or-equal-to comparison, which is a change from version 1.0.8
107
+ jpg = Phashion::Image.new(File.dirname(__FILE__) + '/jpg/Broccoli_Super_Food.jpg')
108
+ jpg_x = Phashion::Image.new(File.dirname(__FILE__) + '/jpg/Broccoli_Super_Food.100px.jpg')
109
+
110
+ refute(jpg.duplicate?(jpg_x, threshold: 1))
111
+ assert(jpg.duplicate?(jpg_x, threshold: 2))
112
+ end
113
+
114
+
115
+ ### distance methods
116
+ def test_distance_from_jpg_to_png_dupe
117
+ jpg = Phashion::Image.new(File.dirname(__FILE__) + '/jpg/Broccoli_Super_Food.jpg')
118
+ png = Phashion::Image.new(File.dirname(__FILE__) + '/png/Broccoli_Super_Food.png')
119
+
120
+ assert_equal(jpg.distance_from(png), 0)
121
+ end
122
+
123
+ def test_distance_from_lossy_jpg
124
+ jpg = Phashion::Image.new(File.dirname(__FILE__) + '/jpg/Broccoli_Super_Food.jpg')
125
+ jpg_x = Phashion::Image.new(File.dirname(__FILE__) + '/jpg/Broccoli_Super_Food.lossy.jpg')
126
+
127
+ assert_equal(jpg.distance_from(jpg_x), 0)
128
+ end
129
+
130
+ def test_distance_from_smaller_jpg
131
+ jpg = Phashion::Image.new(File.dirname(__FILE__) + '/jpg/Broccoli_Super_Food.jpg')
132
+ jpg_x = Phashion::Image.new(File.dirname(__FILE__) + '/jpg/Broccoli_Super_Food.100px.jpg')
133
+
134
+ assert_equal(jpg.distance_from(jpg_x), 2)
135
+ end
136
+
137
+ def test_distance_from_color_correction
138
+ jpg = Phashion::Image.new(File.dirname(__FILE__) + '/jpg/Broccoli_Super_Food.jpg')
139
+ jpg_x = Phashion::Image.new(File.dirname(__FILE__) + '/jpg/Broccoli_Super_Food.color-corrected.jpg')
140
+
141
+ assert_equal(jpg.distance_from(jpg_x), 2)
142
+ end
143
+
144
+ def test_distance_from_black_and_white
145
+ jpg = Phashion::Image.new(File.dirname(__FILE__) + '/jpg/Broccoli_Super_Food.jpg')
146
+ jpg_x = Phashion::Image.new(File.dirname(__FILE__) + '/jpg/Broccoli_Super_Food.bw.jpg')
147
+
148
+ assert_equal(jpg.distance_from(jpg_x), 2)
149
+ end
150
+
151
+ def test_distance_from_bounding_box
152
+ # Control-image is cropped to remove empty whitespace around image details
153
+ # from 500x349 to 466x312
154
+ jpg = Phashion::Image.new(File.dirname(__FILE__) + '/jpg/Broccoli_Super_Food.jpg')
155
+ jpg_x = Phashion::Image.new(File.dirname(__FILE__) + '/jpg/Broccoli_Super_Food.bounding-box.jpg')
156
+
157
+ assert_equal(jpg.distance_from(jpg_x), 12)
158
+ end
159
+
160
+ def test_distance_from_rotation_of_5degrees_c2
161
+ jpg = Phashion::Image.new(File.dirname(__FILE__) + '/jpg/Broccoli_Super_Food.jpg')
162
+ jpg_x = Phashion::Image.new(File.dirname(__FILE__) + '/jpg/Broccoli_Super_Food.rotate5cw.jpg')
163
+
164
+ assert_equal(jpg.distance_from(jpg_x), 14)
165
+ end
166
+
167
+
168
+ def test_distance_from_horizontal_flip
169
+ jpg = Phashion::Image.new(File.dirname(__FILE__) + '/jpg/Broccoli_Super_Food.jpg')
170
+ jpg_x = Phashion::Image.new(File.dirname(__FILE__) + '/jpg/Broccoli_Super_Food.horizontal-flip.jpg')
171
+
172
+ assert_operator(jpg.distance_from(jpg_x), :>, Phashion::Image::DEFAULT_DUPE_THRESHOLD)
173
+ end
37
174
 
38
175
  private
39
176
 
177
+
40
178
  def assert_duplicate(a, b)
41
179
  assert a.duplicate?(b), "#{a.filename} not dupe of #{b.filename}"
42
180
  end
metadata CHANGED
@@ -1,20 +1,18 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: phashion
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.6
5
- prerelease:
4
+ version: 1.1.0
6
5
  platform: ruby
7
6
  authors:
8
7
  - Mike Perham
9
8
  autorequire:
10
9
  bindir: bin
11
10
  cert_chain: []
12
- date: 2013-06-01 00:00:00.000000000 Z
11
+ date: 2014-03-19 00:00:00.000000000 Z
13
12
  dependencies:
14
13
  - !ruby/object:Gem::Dependency
15
14
  name: rake-compiler
16
15
  requirement: !ruby/object:Gem::Requirement
17
- none: false
18
16
  requirements:
19
17
  - - ! '>='
20
18
  - !ruby/object:Gem::Version
@@ -22,13 +20,41 @@ dependencies:
22
20
  type: :development
23
21
  prerelease: false
24
22
  version_requirements: !ruby/object:Gem::Requirement
25
- none: false
26
23
  requirements:
27
24
  - - ! '>='
28
25
  - !ruby/object:Gem::Version
29
26
  version: 0.7.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: sqlite3
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ! '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
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: minitest
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ~>
46
+ - !ruby/object:Gem::Version
47
+ version: 5.2.2
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ~>
53
+ - !ruby/object:Gem::Version
54
+ version: 5.2.2
30
55
  description: Simple wrapper around the pHash library
31
- email: mperham@gmail.com
56
+ email:
57
+ - mperham@gmail.com
32
58
  executables: []
33
59
  extensions:
34
60
  - ext/phashion_ext/extconf.rb
@@ -45,43 +71,52 @@ files:
45
71
  - ext/phashion_ext/pHash-0.9.6.tar.gz
46
72
  - ext/phashion_ext/phashion_ext.c
47
73
  - lib/phashion.rb
74
+ - lib/phashion/version.rb
48
75
  - phashion.gemspec
49
76
  - test/gif/Broccoli_Super_Food.gif
50
77
  - test/helper.rb
51
78
  - test/jpg/86x86-0a1e.jpeg
52
79
  - test/jpg/86x86-83d6.jpeg
53
80
  - test/jpg/86x86-a855.jpeg
81
+ - test/jpg/Broccoli_Super_Food.100px.jpg
82
+ - test/jpg/Broccoli_Super_Food.bounding-box.jpg
83
+ - test/jpg/Broccoli_Super_Food.bw.jpg
84
+ - test/jpg/Broccoli_Super_Food.color-corrected.jpg
85
+ - test/jpg/Broccoli_Super_Food.horizontal-flip.jpg
54
86
  - test/jpg/Broccoli_Super_Food.jpg
87
+ - test/jpg/Broccoli_Super_Food.lossy.jpg
88
+ - test/jpg/Broccoli_Super_Food.rotate5cw.jpg
55
89
  - test/jpg/avatar.jpg
56
90
  - test/jpg/b32aade8c590e2d776c24f35868f0c7a588f51e1.jpeg
57
91
  - test/jpg/df9cc82f5b32d7463f36620c61854fde9d939f7f.jpeg
58
92
  - test/jpg/e7397898a7e395c2524978a5e64de0efabf08290.jpeg
59
93
  - test/png/Broccoli_Super_Food.png
94
+ - test/png/grass.png
95
+ - test/png/linux.png
60
96
  - test/test_phashion.rb
61
- homepage: http://github.com/mperham/phashion
97
+ homepage: http://github.com/westonplatter/phashion
62
98
  licenses: []
99
+ metadata: {}
63
100
  post_install_message:
64
101
  rdoc_options:
65
102
  - --charset=UTF-8
66
103
  require_paths:
67
104
  - lib
68
105
  required_ruby_version: !ruby/object:Gem::Requirement
69
- none: false
70
106
  requirements:
71
107
  - - ! '>='
72
108
  - !ruby/object:Gem::Version
73
109
  version: '0'
74
110
  required_rubygems_version: !ruby/object:Gem::Requirement
75
- none: false
76
111
  requirements:
77
112
  - - ! '>='
78
113
  - !ruby/object:Gem::Version
79
114
  version: '0'
80
115
  requirements: []
81
116
  rubyforge_project:
82
- rubygems_version: 1.8.23
117
+ rubygems_version: 2.0.14
83
118
  signing_key:
84
- specification_version: 3
119
+ specification_version: 4
85
120
  summary: Simple wrapper around the pHash library
86
121
  test_files:
87
122
  - test/gif/Broccoli_Super_Food.gif
@@ -89,10 +124,20 @@ test_files:
89
124
  - test/jpg/86x86-0a1e.jpeg
90
125
  - test/jpg/86x86-83d6.jpeg
91
126
  - test/jpg/86x86-a855.jpeg
127
+ - test/jpg/Broccoli_Super_Food.100px.jpg
128
+ - test/jpg/Broccoli_Super_Food.bounding-box.jpg
129
+ - test/jpg/Broccoli_Super_Food.bw.jpg
130
+ - test/jpg/Broccoli_Super_Food.color-corrected.jpg
131
+ - test/jpg/Broccoli_Super_Food.horizontal-flip.jpg
92
132
  - test/jpg/Broccoli_Super_Food.jpg
133
+ - test/jpg/Broccoli_Super_Food.lossy.jpg
134
+ - test/jpg/Broccoli_Super_Food.rotate5cw.jpg
93
135
  - test/jpg/avatar.jpg
94
136
  - test/jpg/b32aade8c590e2d776c24f35868f0c7a588f51e1.jpeg
95
137
  - test/jpg/df9cc82f5b32d7463f36620c61854fde9d939f7f.jpeg
96
138
  - test/jpg/e7397898a7e395c2524978a5e64de0efabf08290.jpeg
97
139
  - test/png/Broccoli_Super_Food.png
140
+ - test/png/grass.png
141
+ - test/png/linux.png
98
142
  - test/test_phashion.rb
143
+ has_rdoc: