diff --git a/tests/compare_hashes.py b/tests/compare_hashes.py index 0146bf70..af41f9f8 100644 --- a/tests/compare_hashes.py +++ b/tests/compare_hashes.py @@ -80,58 +80,6 @@ def dhash(image, hash_size=8): return diff -def colorhash(image, binbits=3): - """ - Color Hash computation. - Computes fractions of image in intensity, hue and saturation bins: - - * the first binbits encode the black fraction of the image - * the next binbits encode the gray fraction of the remaining image (low saturation) - * the next 6*binbits encode the fraction in 6 bins of saturation, for highly saturated parts of the remaining image - * 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 - """ - - # 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()] - # black bin - mask_black = intensity < 256 // 8 - frac_black = mask_black.mean() - # gray bin (low saturation, but not black) - mask_gray = s < 256 // 3 - frac_gray = numpy.logical_and(~mask_black, mask_gray).mean() - # two color bins (medium and high saturation, not in the two above) - mask_colors = numpy.logical_and(~mask_black, ~mask_gray) - mask_faint_colors = numpy.logical_and(mask_colors, s < 256 * 2 // 3) - mask_bright_colors = numpy.logical_and(mask_colors, s > 256 * 2 // 3) - - c = max(1, mask_colors.sum()) - # in the color bins, make sub-bins by hue - hue_bins = numpy.linspace(0, 255, 6 + 1) - if mask_faint_colors.any(): - h_faint_counts, _ = numpy.histogram(h[mask_faint_colors], bins=hue_bins) - else: - h_faint_counts = numpy.zeros(len(hue_bins) - 1) - if mask_bright_colors.any(): - h_bright_counts, _ = numpy.histogram(h[mask_bright_colors], bins=hue_bins) - else: - h_bright_counts = numpy.zeros(len(hue_bins) - 1) - - # now we have fractions in each category (6*2 + 2 = 14 bins) - # convert to hash and discretize: - maxvalue = 2**binbits - values = [min(maxvalue - 1, int(frac_black * maxvalue)), min(maxvalue - 1, int(frac_gray * maxvalue))] - for counts in list(h_faint_counts) + list(h_bright_counts): - values.append(min(maxvalue - 1, int(counts * maxvalue * 1.0 / c))) - # print(values) - bitarray = [] - for v in values: - bitarray += [v // (2 ** (binbits - i - 1)) % 2 ** (binbits - i) > 0 for i in range(binbits)] - return numpy.asarray(bitarray).reshape((-1, binbits)) - - def compare_hashes(pillow_images: list, hash_type="average", hash_size=16, max_difference=0): image_hashes = [] for pillow_image in pillow_images: @@ -140,8 +88,6 @@ def compare_hashes(pillow_images: list, hash_type="average", hash_size=16, max_d pillow_image = ImageOps.exif_transpose(pillow_image) if hash_type == "dhash": image_hash = dhash(pillow_image, hash_size) - elif hash_type == "colorhash": - image_hash = colorhash(pillow_image) else: image_hash = average_hash(pillow_image, hash_size) image_hash = image_hash.flatten() diff --git a/tests/convert_mode_test.py b/tests/convert_mode_test.py index 979599e7..2a4a7e9d 100644 --- a/tests/convert_mode_test.py +++ b/tests/convert_mode_test.py @@ -17,7 +17,7 @@ @pytest.mark.parametrize("mode", ("RGB;16", "BGR;16")) -def test_rgb8_to_16bit_color_mode(mode): +def test_rgb8_to_16_10_bit_color_mode(mode): png_pillow = Image.open(Path("images/jpeg_gif_png/RGB_8.png")) heif_file = from_pillow(png_pillow) assert heif_file.bit_depth == 8 @@ -29,11 +29,31 @@ def test_rgb8_to_16bit_color_mode(mode): heif_file = open_heif(out_heic, convert_hdr_to_8bit=False) assert heif_file.bit_depth == 10 assert not heif_file.has_alpha - imagehash.compare_hashes([png_pillow, out_heic], hash_size=16, max_difference=0) + imagehash.compare_hashes([png_pillow, out_heic], hash_size=16) + + +@pytest.mark.parametrize("mode", ("RGB;16", "BGR;16")) +def test_rgb8_to_16_12_bit_color_mode(mode): + try: + options().save_to_12bit = True + png_pillow = Image.open(Path("images/jpeg_gif_png/RGB_8.png")) + heif_file = from_pillow(png_pillow) + assert heif_file.bit_depth == 8 + heif_file[0].convert_to(mode) + out_heic = BytesIO() + heif_file.save(out_heic, quality=-1) + assert heif_file.bit_depth == 16 + assert not heif_file.has_alpha + heif_file = open_heif(out_heic, convert_hdr_to_8bit=False) + assert heif_file.bit_depth == 12 + assert not heif_file.has_alpha + imagehash.compare_hashes([png_pillow, out_heic], hash_size=8) + finally: + options().reset() @pytest.mark.parametrize("mode", ("RGBA;16", "BGRA;16")) -def test_rgba8_to_16bit_color_mode(mode): +def test_rgba8_to_16_10_bit_color_mode(mode): png_pillow = Image.open(Path("images/jpeg_gif_png/RGBA_8.png")) heif_file = from_pillow(png_pillow) assert heif_file.bit_depth == 8 @@ -45,7 +65,27 @@ def test_rgba8_to_16bit_color_mode(mode): heif_file = open_heif(out_heic, convert_hdr_to_8bit=False) assert heif_file.bit_depth == 10 assert heif_file.has_alpha - imagehash.compare_hashes([png_pillow, out_heic], hash_size=8, max_difference=0) + imagehash.compare_hashes([png_pillow, out_heic], hash_size=8) + + +@pytest.mark.parametrize("mode", ("RGBA;16", "BGRA;16")) +def test_rgba8_to_16_12_bit_color_mode(mode): + try: + options().save_to_12bit = True + png_pillow = Image.open(Path("images/jpeg_gif_png/RGBA_8.png")) + heif_file = from_pillow(png_pillow) + assert heif_file.bit_depth == 8 + heif_file[0].convert_to(mode) + out_heic = BytesIO() + heif_file.save(out_heic, quality=-1) + assert heif_file.bit_depth == 16 + assert heif_file.has_alpha + heif_file = open_heif(out_heic, convert_hdr_to_8bit=False) + assert heif_file.bit_depth == 12 + assert heif_file.has_alpha + imagehash.compare_hashes([png_pillow, out_heic], hash_size=8) + finally: + options().reset() @pytest.mark.parametrize("mode", ("RGB;16", "BGR;16")) @@ -61,7 +101,7 @@ def test_rgb10_to_16bit_color_mode(mode): heif_file = open_heif(out_heic, convert_hdr_to_8bit=False) assert heif_file.bit_depth == 10 assert not heif_file.has_alpha - imagehash.compare_hashes([img_path, out_heic], hash_size=8, max_difference=0) + imagehash.compare_hashes([img_path, out_heic], hash_size=8) @pytest.mark.parametrize("mode", ("RGBA;16", "BGRA;16")) @@ -77,7 +117,7 @@ def test_rgba10_to_16bit_color_mode(mode): heif_file = open_heif(out_heic, convert_hdr_to_8bit=False) assert heif_file.bit_depth == 10 assert heif_file.has_alpha - imagehash.compare_hashes([img_path, out_heic], hash_size=8, max_difference=0) + imagehash.compare_hashes([img_path, out_heic], hash_size=8) @pytest.mark.parametrize("mode", ("RGB;16", "BGR;16")) @@ -93,7 +133,7 @@ def test_rgb12_to_16bit_color_mode(mode): heif_file = open_heif(out_heic, convert_hdr_to_8bit=False) assert heif_file.bit_depth == 10 assert not heif_file.has_alpha - imagehash.compare_hashes([img_path, out_heic], hash_size=8, max_difference=0) + imagehash.compare_hashes([img_path, out_heic], hash_size=8) @pytest.mark.parametrize("mode", ("RGBA;16", "BGRA;16")) @@ -109,4 +149,4 @@ def test_rgba12_to_16bit_color_mode(mode): heif_file = open_heif(out_heic, convert_hdr_to_8bit=False) assert heif_file.bit_depth == 10 assert heif_file.has_alpha - imagehash.compare_hashes([img_path, out_heic], hash_size=8, max_difference=0) + imagehash.compare_hashes([img_path, out_heic], hash_size=8) diff --git a/tests/opencv_test.py b/tests/opencv_test.py index e9df8ba8..339d4d4b 100644 --- a/tests/opencv_test.py +++ b/tests/opencv_test.py @@ -20,32 +20,70 @@ register_heif_opener() -def test_save_bgr_16bit_color_mode(): +def test_save_bgr_16bit_to_10_bit_color_mode(): image_path = "images/jpeg_gif_png/RGB_16.png" cv_img = cv2.imread(image_path, cv2.IMREAD_UNCHANGED) assert cv_img.shape[2] == 3 # 3 channels(BGR) heif_file = from_bytes(mode="BGR;16", size=(cv_img.shape[1], cv_img.shape[0]), data=bytes(cv_img)) out_heic = BytesIO() heif_file.save(out_heic, quality=-1) - assert open_heif(out_heic, convert_hdr_to_8bit=False).bit_depth == 10 + heif_file = open_heif(out_heic, convert_hdr_to_8bit=False) + assert heif_file.bit_depth == 10 png_pillow = Image.open(Path(image_path)) heif_pillow = Image.open(out_heic) imagehash.compare_hashes([png_pillow, heif_pillow], hash_type="dhash", hash_size=8, max_difference=0) -def test_save_bgra_16bit_color_mode(): +def test_save_bgr_16bit_to_12_bit_color_mode(): + try: + options().save_to_12bit = True + image_path = "images/jpeg_gif_png/RGB_16.png" + cv_img = cv2.imread(image_path, cv2.IMREAD_UNCHANGED) + assert cv_img.shape[2] == 3 # 3 channels(BGR) + heif_file = from_bytes(mode="BGR;16", size=(cv_img.shape[1], cv_img.shape[0]), data=bytes(cv_img)) + out_heic = BytesIO() + heif_file.save(out_heic, quality=-1) + heif_file = open_heif(out_heic, convert_hdr_to_8bit=False) + assert heif_file.bit_depth == 12 + png_pillow = Image.open(Path(image_path)) + heif_pillow = Image.open(out_heic) + imagehash.compare_hashes([png_pillow, heif_pillow], hash_type="dhash", hash_size=8, max_difference=0) + finally: + options().reset() + + +def test_save_bgra_16bit_to_10_bit_color_mode(): image_path = "images/jpeg_gif_png/RGBA_16.png" cv_img = cv2.imread(image_path, cv2.IMREAD_UNCHANGED) assert cv_img.shape[2] == 4 # 4 channels(BGRA) heif_file = from_bytes(mode="BGRA;16", size=(cv_img.shape[1], cv_img.shape[0]), data=bytes(cv_img)) out_heic = BytesIO() heif_file.save(out_heic, quality=-1) - assert open_heif(out_heic, convert_hdr_to_8bit=False).bit_depth == 10 + heif_file = open_heif(out_heic, convert_hdr_to_8bit=False) + assert heif_file.bit_depth == 10 png_pillow = Image.open(Path(image_path)) heif_pillow = Image.open(out_heic) imagehash.compare_hashes([png_pillow, heif_pillow], hash_type="dhash", hash_size=8, max_difference=1) +def test_save_bgra_16bit_to_12_bit_color_mode(): + try: + options().save_to_12bit = True + image_path = "images/jpeg_gif_png/RGBA_16.png" + cv_img = cv2.imread(image_path, cv2.IMREAD_UNCHANGED) + assert cv_img.shape[2] == 4 # 4 channels(BGRA) + heif_file = from_bytes(mode="BGRA;16", size=(cv_img.shape[1], cv_img.shape[0]), data=bytes(cv_img)) + out_heic = BytesIO() + heif_file.save(out_heic, quality=-1) + heif_file = open_heif(out_heic, convert_hdr_to_8bit=False) + assert heif_file.bit_depth == 12 + png_pillow = Image.open(Path(image_path)) + heif_pillow = Image.open(out_heic) + imagehash.compare_hashes([png_pillow, heif_pillow], hash_type="dhash", hash_size=8, max_difference=1) + finally: + options().reset() + + def test_save_bgr_8bit_color_mode(): image_path = "images/jpeg_gif_png/RGB_8.png" cv_img = cv2.imread(image_path, cv2.IMREAD_UNCHANGED) diff --git a/tests/opener_encoder_test.py b/tests/opener_encoder_test.py index 08b5a255..14834045 100644 --- a/tests/opener_encoder_test.py +++ b/tests/opener_encoder_test.py @@ -155,7 +155,7 @@ def test_1_color_mode(): @pytest.mark.parametrize("img_path", ("images/jpeg_gif_png/I_color_mode_image.pgm",)) # "images/jpeg_gif_png/I_color_mode_image.png" # when Pillow will be able to properly convert PNG from "I" mode to "L"(for imagehash) - add to test. -def test_I_color_modes(img_path): +def test_I_color_modes_to_10bit(img_path): src_pillow = Image.open(Path(img_path)) assert src_pillow.mode == "I" for mode in ("I", "I;16", "I;16L"): @@ -164,7 +164,26 @@ def test_I_color_modes(img_path): i_mode_img.save(out_heic, format="HEIF", quality=-1) assert open_heif(out_heic, convert_hdr_to_8bit=False).bit_depth == 10 heic_pillow = Image.open(out_heic) - imagehash.compare_hashes([src_pillow, heic_pillow], hash_type="dhash", hash_size=8, max_difference=0) + imagehash.compare_hashes([src_pillow, heic_pillow], hash_type="dhash", hash_size=8) + + +@pytest.mark.parametrize("img_path", ("images/jpeg_gif_png/I_color_mode_image.pgm",)) +# "images/jpeg_gif_png/I_color_mode_image.png" +# when Pillow will be able to properly convert PNG from "I" mode to "L"(for imagehash) - add to test. +def test_I_color_modes_to_12bit(img_path): + try: + options().save_to_12bit = True + src_pillow = Image.open(Path(img_path)) + assert src_pillow.mode == "I" + for mode in ("I", "I;16", "I;16L"): + i_mode_img = src_pillow.convert(mode=mode) + out_heic = BytesIO() + i_mode_img.save(out_heic, format="HEIF", quality=-1) + assert open_heif(out_heic, convert_hdr_to_8bit=False).bit_depth == 12 + heic_pillow = Image.open(out_heic) + imagehash.compare_hashes([src_pillow, heic_pillow], hash_type="dhash", hash_size=8) + finally: + options().reset() def test_append_images():