Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add rudimentary ICC color profile support to colorhash #110

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
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
5 changes: 5 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,17 @@ install:
- conda install --quiet --file conda-requirements.txt
- pip install coveralls

# Work around conda's Pillow package lacking ImageCms
Copy link
Owner

Choose a reason for hiding this comment

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

in line 9 add:

env:
    - REINSTALL_PILLOW=no
    - REINSTALL_PILLOW=yes

- conda uninstall pillow
Copy link
Owner

Choose a reason for hiding this comment

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

and then here prefix the two commands here with [[ "$REINSTALL_PILLOW" == "yes" ]] &&

- pip install pillow

# Summerise environment
# ---------------------
- conda list
- conda info -a

# Install and test imagehash
# --------------------------
- python setup.py install

script:
Expand Down
23 changes: 18 additions & 5 deletions imagehash.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,11 @@

from __future__ import (absolute_import, division, print_function)

from PIL import Image
import numpy
#import scipy.fftpack
#import pywt
#import scipy.fftpack
from PIL import Image, ImageCms

__version__ = "4.1.0"

"""
Expand Down Expand Up @@ -270,7 +271,7 @@ def dhash_vertical(image, hash_size=8):
return ImageHash(diff)


def whash(image, hash_size = 8, image_scale = None, mode = 'haar', remove_max_haar_ll = True):
def whash(image, hash_size=8, image_scale=None, mode='haar', remove_max_haar_ll=True):
"""
Wavelet Hash computation.

Expand Down Expand Up @@ -320,7 +321,7 @@ def whash(image, hash_size = 8, image_scale = None, mode = 'haar', remove_max_ha



def colorhash(image, binbits=3):
def colorhash(image, binbits=3, ignore_icc=False):
Copy link
Owner

Choose a reason for hiding this comment

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

use a positive variable name here to avoid the double negative. For example: apply_icc_profile=True

"""
Color Hash computation.

Expand All @@ -332,11 +333,23 @@ def colorhash(image, binbits=3):
* the next 6*binbits encode the fraction in 6 bins of saturation, for mildly saturated parts of the remaining image

@binbits number of bits to use to encode each pixel fractions
@ignore_icc use raw color values, ignoring embedded ICC profiles
"""

if not ignore_icc:
image_profile = image.info.get("icc_profile")
if image_profile:
from io import BytesIO
# standardize color space to sRGB and preserve relative
# color values by using perceptual rendering intent
srgb_profile = ImageCms.createProfile("sRGB")
image_profile = ImageCms.ImageCmsProfile(BytesIO(image_profile))
ImageCms.profileToProfile(image, image_profile, srgb_profile,
renderingIntent=ImageCms.INTENT_PERCEPTUAL, inPlace=True)

# bin in hsv space:
intensity = numpy.asarray(image.convert("L")).flatten()
h, s, v = [numpy.asarray(v).flatten() for v in image.convert("HSV").split()]
h, s, _ = [numpy.asarray(v).flatten() for v in image.convert("HSV").split()]
# black bin
mask_black = intensity < 256 // 8
frac_black = mask_black.mean()
Expand Down
Binary file added tests/data/bigbuckbunny-with-diff-icc.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added tests/data/bigbuckbunny.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
30 changes: 30 additions & 0 deletions tests/test_colorhash.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ def setUp(self):
self.image = self.get_data_image()
self.func = imagehash.colorhash

def tearDown(self):
self.image.close()

def test_colorhash(self):
self.check_hash_algorithm(self.func, self.image)

Expand Down Expand Up @@ -63,6 +66,33 @@ def check_hash_size(self, func, image, binbits=range(-1,1)):
with self.assertRaises(ValueError):
func(image, bit)

def test_colorhash_icc_support(self):
image = self.get_data_image('bigbuckbunny.png')
image_with_icc = self.get_data_image('bigbuckbunny-with-diff-icc.png')
with image, image_with_icc:
self.check_icc_support(self.func, image, image_with_icc)

def check_icc_support(self, func, orig_image, image_with_diff_icc):
orig_data_hash = func(orig_image)
diff_icc_hash = func(image_with_diff_icc)
emsg = ('same image data but with a different ICC profile should '
'result in different hashes: {} == {}'.format(orig_data_hash,
diff_icc_hash))
self.assertNotEqual(orig_data_hash, diff_icc_hash, emsg)

def test_colorhash_icc_ignored(self):
image = self.get_data_image('bigbuckbunny.png')
image_with_icc = self.get_data_image('bigbuckbunny-with-diff-icc.png')
with image, image_with_icc:
self.check_icc_ignored(self.func, image, image_with_icc)

def check_icc_ignored(self, func, orig_image, image_with_diff_icc):
orig_data_hash = func(orig_image)
diff_icc_hash = func(image_with_diff_icc, ignore_icc=True)
emsg = ('ignoring the ICC profile in an image with the same image '
'data as one without a profile should result in the same hash: '
'{} != {}'.format(orig_data_hash, diff_icc_hash))
self.assertEqual(orig_data_hash, diff_icc_hash, emsg)

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