Module: DHashVips::IDHash

Extended by:
IDHash
Included in:
IDHash
Defined in:
lib/dhash-vips.rb

Constant Summary collapse

@@median =
lambda do |array|
  h = array.size / 2
  return array[h] if array[h] != array[h - 1]
  right = array.dup
  left = right.shift h
  right.shift if right.size > left.size
  return right.first if left.last != right.first
  return right.uniq[1] if left.count(left.last) > right.count(right.first)
  left.last
end

Instance Method Summary collapse

Instance Method Details

#distance(a, b) ⇒ Object


62
63
64
65
66
67
68
69
70
71
72
# File 'lib/dhash-vips.rb', line 62

def distance a, b
  size_a, size_b = [a, b].map do |x|
    # TODO write a test about possible hash sizes
    #      they were 32 and 128, 124, 120 for MRI 2.0
    #      but also 31, 30 happens for MRI 2.3
    x.size <= 32 ? 8 : 16
  end
  return distance3 a, b if [8, 8] == [size_a, size_b]
  fail "fingerprints were taken with different `power` param: #{size_a} and #{size_b}" if size_a != size_b
  ((a ^ b) & (a | b) >> 2 * size_a * size_a).to_s(2).count "1"
end

#distance3_ruby(a, b) ⇒ Object


32
33
34
# File 'lib/dhash-vips.rb', line 32

def distance3_ruby a, b
  ((a ^ b) & (a | b) >> 128).to_s(2).count "1"
end

#fingerprint(filename, power = 3) ⇒ Object


94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
# File 'lib/dhash-vips.rb', line 94

def fingerprint filename, power = 3
  size = 2 ** power
  image = Vips::Image.new_from_file filename, access: :sequential
  image = image.resize(size.fdiv(image.width), vscale: size.fdiv(image.height)).colourspace("b-w").flatten

  array = image.to_a.map &:flatten
  d1, i1, d2, i2 = [array, array.transpose].flat_map do |a|
    d = a.zip(a.rotate(1)).flat_map{ |r1, r2| r1.zip(r2).map{ |i,j| i - j } }
    m = @@median.call d.map(&:abs).sort
    [
      d.map{ |c| c     <  0 ? 1 : 0 }.join.to_i(2),
      d.map{ |c| c.abs >= m ? 1 : 0 }.join.to_i(2),
    ]
  end
  (((((i1 << size * size) + i2) << size * size) + d1) << size * size) + d2
end