Project

libpuzzle

0.0
No commit activity in last 3 years
No release in over 3 years
This is a C extension for libpuzzle to find similar pictures with easy API.
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
 Dependencies

Development

~> 5.0.0
~> 5.7.0
~> 10.4.2
 Project Readme

Build Status

This Ruby gem is made for image simularity comparison, using the C libary libpuzzle.

Install

gem install libpuzzle

Make sure you have installed libpuzzle (>= 0.11) first.

Install Libpuzzle

Homebrew:

brew install libpuzzle

APT:

apt-get install libpuzzle1 libpuzzle-dev

Compile:

sudo apt-get install -qq libgd2-xpm-dev
wget http://download.pureftpd.org/pub/pure-ftpd/misc/libpuzzle/releases/libpuzzle-0.11.tar.bz2
tar -xvj -f libpuzzle-0.11.tar.bz2
cd libpuzzle-0.11 && ./configure && make && sudo make install

Since the object file is located in /usr/local/lib, you might want to export LD_LIBRARY_PATH before gem installation, for example:

export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib
gem install libpuzzle

Usage

# load image
p1 = Puzzle.new('foo.png')
p2 = Puzzle.new('bar.png')

p1.similar? p2 # default threshold is 0.6
p1.similar? p2, threshold: 0.7

# get exact distance
p1.distance p2

# compress
p1.compress # => compressed string

# uncompress
Puzzle.uncompress 'COMPRESSED_STRING' # => #<Puzzle>

Integrate with ActiveRecord

Follow the naming convention of ActiveRecord, the database schema will look like:

+---------------------------------+
|            signatures           |
+---------------------------------+
|   id   | signature | picture_id |
+--------+-----------+------------+

+-------------------------------------+
|                words                |
+-------------------------------------+
| id | position | word | signature_id |
+----+----------+------+--------------+
class Word < ActiveRecord::Base
  belongs_to :signature
end

class Signature < ActiveRecord::Base
  K, N = 10, 100
  has_many :words

  def cut_vectors_into_words!
    puzzle = Puzzle.uncompress signature
    i = 0
    while i < N && word = puzzle.vector.byteslice(i, K)
      words.find_or_create_by! word: word, position: i
      i += 1
    end
  end

  def similar_signatures
    Signature.distinct.joins(:words)
      .where('EXISTS (SELECT * FROM words AS _words WHERE _words.sig_id = ? AND _words.position = words.position and _words.word = words.word)', id)
      .where.not(id: id)
      .select{ |sig| Puzzle.uncompress(sig.signature) == Puzzle.uncompress(signature) }
  end

end