Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions blockhash/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from blockhash import blockhash, blockhash_even
62 changes: 5 additions & 57 deletions blockhash.py → blockhash/blockhash.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,7 @@
# Distributed under an MIT license, please see LICENSE in the top dir.

import math
import argparse
import PIL.Image as Image


def median(data):
data = sorted(data)
Expand All @@ -17,17 +16,20 @@ def median(data):
return (data[length // 2 - 1] + data[length // 2]) / 2.0
return data[length // 2]


def total_value_rgba(im, data, x, y):
r, g, b, a = data[y * im.size[0] + x]
if a == 0:
return 765
else:
return r + g + b


def total_value_rgb(im, data, x, y):
r, g, b = data[y * im.size[0] + x]
return r + g + b


def translate_blocks_to_bits(blocks, pixels_per_block):
half_block_value = pixels_per_block * 256 * 3 / 2

Expand Down Expand Up @@ -81,6 +83,7 @@ def blockhash_even(im, bits):
translate_blocks_to_bits(result, blocksize_x * blocksize_y)
return bits_to_hexhash(result)


def blockhash(im, bits):
if im.mode == 'RGBA':
total_value = total_value_rgba
Expand Down Expand Up @@ -152,58 +155,3 @@ def blockhash(im, bits):
translate_blocks_to_bits(result, block_width * block_height)
return bits_to_hexhash(result)

if __name__ == '__main__':
parser = argparse.ArgumentParser()

parser.add_argument('--quick', type=bool, default=False,
help='Use quick hashing method. Default: False')
parser.add_argument('--bits', type=int, default=16,
help='Create hash of size N^2 bits. Default: 16')
parser.add_argument('--size',
help='Resize image to specified size before hashing, e.g. 256x256')
parser.add_argument('--interpolation', type=int, default=1, choices=[1, 2, 3, 4],
help='Interpolation method: 1 - nearest neightbor, 2 - bilinear, 3 - bicubic, 4 - antialias. Default: 1')
parser.add_argument('--debug', action='store_true',
help='Print hashes as 2D maps (for debugging)')
parser.add_argument('filenames', nargs='+')

args = parser.parse_args()

if args.interpolation == 1:
interpolation = Image.NEAREST
elif args.interpolation == 2:
interpolation = Image.BILINEAR
elif args.interpolation == 3:
interpolation = Image.BICUBIC
elif args.interpolation == 4:
interpolation = Image.ANTIALIAS

if args.quick:
method = blockhash_even
else:
method = blockhash

for fn in args.filenames:
im = Image.open(fn)

# convert indexed/grayscale images to RGB
if im.mode == '1' or im.mode == 'L' or im.mode == 'P':
im = im.convert('RGB')
elif im.mode == 'LA':
im = im.convert('RGBA')

if args.size:
size = args.size.split('x')
size = (int(size[0]), int(size[1]))
im = im.resize(size, interpolation)

hash = method(im, args.bits)

print('{hash} {fn}'.format(fn=fn, hash=hash))

if args.debug:
bin_hash = '{:0{width}b}'.format(int(hash, 16), width=args.bits ** 2)
map = [bin_hash[i:i+args.bits] for i in range(0, len(bin_hash), args.bits)]
print("")
print("\n".join(map))
print("")
73 changes: 73 additions & 0 deletions blockhash/command_line.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
#! /usr/bin/env python
#
# Perceptual image hash calculation tool based on algorithm descibed in
# Block Mean Value Based Image Perceptual Hashing by Bian Yang, Fan Gu and Xiamu Niu
#
# Copyright 2014 Commons Machinery http://commonsmachinery.se/
# Distributed under an MIT license, please see LICENSE in the top dir.

import argparse
import PIL.Image as Image

from blockhash import blockhash


def main():
parser = argparse.ArgumentParser()

parser.add_argument('--quick', type=bool, default=False,
help='Use quick hashing method. Default: False')
parser.add_argument('--bits', type=int, default=16,
help='Create hash of size N^2 bits. Default: 16')
parser.add_argument('--size',
help='Resize image to specified size before hashing, e.g. 256x256')
parser.add_argument('--interpolation', type=int, default=1, choices=[1, 2, 3, 4],
help='Interpolation method: 1 - nearest neightbor, 2 - bilinear, 3 - bicubic, 4 - antialias. Default: 1')
parser.add_argument('--debug', action='store_true',
help='Print hashes as 2D maps (for debugging)')
parser.add_argument('filenames', nargs='+')

args = parser.parse_args()

if args.interpolation == 1:
interpolation = Image.NEAREST
elif args.interpolation == 2:
interpolation = Image.BILINEAR
elif args.interpolation == 3:
interpolation = Image.BICUBIC
elif args.interpolation == 4:
interpolation = Image.ANTIALIAS

if args.quick:
method = blockhash_even
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

blockhash_even is not imported above, is this gonna work?

else:
method = blockhash

for fn in args.filenames:
im = Image.open(fn)

# convert indexed/grayscale images to RGB
if im.mode == '1' or im.mode == 'L' or im.mode == 'P':
im = im.convert('RGB')
elif im.mode == 'LA':
im = im.convert('RGBA')

if args.size:
size = args.size.split('x')
size = (int(size[0]), int(size[1]))
im = im.resize(size, interpolation)

hash = method(im, args.bits)

print('{hash} {fn}'.format(fn=fn, hash=hash))

if args.debug:
bin_hash = '{:0{width}b}'.format(int(hash, 16), width=args.bits ** 2)
map = [bin_hash[i:i+args.bits] for i in range(0, len(bin_hash), args.bits)]
print("")
print("\n".join(map))
print("")


if __name__ == '__main__':
main()
13 changes: 9 additions & 4 deletions setup.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,19 @@
#!/usr/bin/env python

from distutils.core import setup
from setuptools import setup, find_packages

setup(
name='blockhash',
version='0.1',
version='0.1.1',
description='Perceptual image hash calculation tool',
author='Commons Machinery',
author_email='dev@commonsmachinery.se',
license='MIT',
scripts=['blockhash.py'],
requires=['pillow'],
packages=find_packages(),
entry_points={
'console_scripts': [
'blockhash=blockhash.command_line:main',
],
},
install_requires=['pillow'],
)
14 changes: 12 additions & 2 deletions test/test_blockhash.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,18 @@
# Copyright 2014 Commons Machinery http://commonsmachinery.se/
# Distributed under an MIT license, please see LICENSE in the top dir.

import glob
import os
import unittest
import blockhash

import PIL.Image as Image
import os, glob

import blockhash


datadir = os.path.join(os.path.dirname(__file__), 'data')


class BlockhashTestCase(unittest.TestCase):
def __init__(self, img_filename=None, hash_filename=None, method=None, bits=None):
unittest.TestCase.__init__(self)
Expand Down Expand Up @@ -40,6 +45,7 @@ def runTest(self):
hash = "".join([str(x) for x in hash])
self.assertEqual(expected_hash, hash)


def load_tests(loader, tests, pattern):
test_cases = unittest.TestSuite()
for img_fn in (glob.glob(os.path.join(datadir, '*.jpg')) +
Expand All @@ -52,3 +58,7 @@ def load_tests(loader, tests, pattern):
test_cases.addTest(BlockhashTestCase(img_fn, hash_fn, method, bits))
pass
return test_cases


if __name__ == '__main__':
unittest.main()