Skip to content

Commit

Permalink
Visual image comparison (#4089)
Browse files Browse the repository at this point in the history
* NEW: compare images

* Fix

* add comparison

* Implement n_tiles

* add unittests for checkerboard

* Fancier plots

* finalize example

* Add versionadded tag

* Add note section

* unittest for blend

* test ValueErrors

* float instead of float64

* Fix tests

* Update skimage/util/compare.py

Co-Authored-By: Mark Harfouche <mark.harfouche@gmail.com>

* Update skimage/util/compare.py

Co-Authored-By: Mark Harfouche <mark.harfouche@gmail.com>

* Update skimage/util/tests/test_compare.py

Co-Authored-By: Mark Harfouche <mark.harfouche@gmail.com>

* Update skimage/util/tests/test_compare.py

Co-Authored-By: Mark Harfouche <mark.harfouche@gmail.com>
  • Loading branch information
sciunto and hmaarrfk committed Aug 18, 2019
1 parent ada405f commit 6de08ed
Show file tree
Hide file tree
Showing 4 changed files with 226 additions and 0 deletions.
100 changes: 100 additions & 0 deletions doc/examples/applications/plot_image_comparison.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
"""
=======================
Visual image comparison
=======================
Image comparison is particularly useful when performing image processing tasks
such as exposure manipulations, filtering, and restauration.
This example shows how to easily compare two images with various approaches.
"""
import matplotlib.pyplot as plt
from matplotlib.gridspec import GridSpec

from skimage import data, transform, exposure
from skimage.util import compare_images


img1 = data.coins()
img1_equalized = exposure.equalize_hist(img1)
img2 = transform.rotate(img1, 5)


comp_equalized = compare_images(img1, img1_equalized, method='checkerboard')
diff_rotated = compare_images(img1, img2, method='diff')
blend_rotated = compare_images(img1, img2, method='blend')


######################################################################
# Checkerboard
# ============
#
# The `checkerboard` method alternates tiles from the first and the second
# images.

fig = plt.figure(figsize=(8, 9))

gs = GridSpec(3, 2)
ax0 = fig.add_subplot(gs[0, 0])
ax1 = fig.add_subplot(gs[0, 1])
ax2 = fig.add_subplot(gs[1:, :])

ax0.imshow(img1, cmap='gray')
ax0.set_title('Original')
ax1.imshow(img1_equalized, cmap='gray')
ax1.set_title('Equalized')
ax2.imshow(comp_equalized, cmap='gray')
ax2.set_title('Checkerboard comparison')
for a in (ax0, ax1, ax2):
a.axis('off')
plt.tight_layout()
plt.plot()

######################################################################
# Diff
# ====
#
# The `diff` method computes the absolute difference between the two images.

fig = plt.figure(figsize=(8, 9))

gs = GridSpec(3, 2)
ax0 = fig.add_subplot(gs[0, 0])
ax1 = fig.add_subplot(gs[0, 1])
ax2 = fig.add_subplot(gs[1:, :])

ax0.imshow(img1, cmap='gray')
ax0.set_title('Original')
ax1.imshow(img1_equalized, cmap='gray')
ax1.set_title('Rotated')
ax2.imshow(diff_rotated, cmap='gray')
ax2.set_title('Diff comparison')
for a in (ax0, ax1, ax2):
a.axis('off')
plt.tight_layout()
plt.plot()

######################################################################
# Blend
# =====
#
# `blend` is the result of the average of the two images.

fig = plt.figure(figsize=(8, 9))

gs = GridSpec(3, 2)
ax0 = fig.add_subplot(gs[0, 0])
ax1 = fig.add_subplot(gs[0, 1])
ax2 = fig.add_subplot(gs[1:, :])

ax0.imshow(img1, cmap='gray')
ax0.set_title('Original')
ax1.imshow(img1_equalized, cmap='gray')
ax1.set_title('Rotated')
ax2.imshow(blend_rotated, cmap='gray')
ax2.set_title('Blend comparison')
for a in (ax0, ax1, ax2):
a.axis('off')
plt.tight_layout()
plt.plot()
2 changes: 2 additions & 0 deletions skimage/util/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from .apply_parallel import apply_parallel

from .arraycrop import crop
from .compare import compare_images
from ._regular_grid import regular_grid, regular_seeds
from .unique import unique_rows
from ._invert import invert
Expand All @@ -29,6 +30,7 @@
'view_as_windows',
'pad',
'crop',
'compare_images',
'montage',
'random_noise',
'regular_grid',
Expand Down
60 changes: 60 additions & 0 deletions skimage/util/compare.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import numpy as np
from ..util import img_as_float
from itertools import product


def compare_images(image1, image2, method='diff', *, n_tiles=(8, 8)):
"""
Return an image showing the differences between two images.
.. versionadded:: 0.16
Parameters
----------
image1, image2 : 2-D array
Images to process, must be of the same shape.
method : string, optional
Method used for the comparison.
Valid values are {'diff', 'blend', 'checkerboard'}.
Details are provided in the note section.
n_tiles : tuple, optional
Used only for the `checkerboard` method. Specifies the number
of tiles (row, column) to divide the image.
Returns
-------
comparison : 2-D array
Image showing the differences.
Notes
-----
`diff` computes the absolute difference between the two images.
`bend` computes the mean value.
`checkerboard` makes tiles of dimension `n_tiles` that display
alternatively the first and the second image.
"""
if image1.shape != image2.shape:
raise ValueError('Images must have the same shape.')

img1 = img_as_float(image1)
img2 = img_as_float(image2)

if method == 'diff':
comparison = np.abs(img2 - img1)
elif method == 'blend':
comparison = 0.5 * (img2 + img1)
elif method == 'checkerboard':
shapex, shapey = img1.shape
mask = np.full((shapex, shapey), False)
stepx = int(shapex / n_tiles[0])
stepy = int(shapey / n_tiles[1])
for i, j in product(range(n_tiles[0]), range(n_tiles[1])):
if (i + j) % 2 == 0:
mask[i * stepx:(i + 1)*stepx, j * stepy:(j + 1) * stepy] = True
comparison = np.zeros_like(img1)
comparison[mask] = img1[mask]
comparison[~mask] = img2[~mask]
else:
raise ValueError('Wrong value for `method`. '
'Must be either "diff", "blend" or "checkerboard".')
return comparison
64 changes: 64 additions & 0 deletions skimage/util/tests/test_compare.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import numpy as np

from skimage._shared.testing import assert_array_equal
from skimage._shared import testing

from skimage.util.compare import compare_images


def test_compate_images_ValueError_shape():
img1 = np.zeros((10, 10), dtype=np.uint8)
img2 = np.zeros((10, 1), dtype=np.uint8)
with testing.raises(ValueError):
compare_images(img1, img2)


def test_compare_images_diff():
img1 = np.zeros((10, 10), dtype=np.uint8)
img1[3:8, 3:8] = 255
img2 = np.zeros_like(img1)
img2[3:8, 0:8] = 255
expected_result = np.zeros_like(img1, dtype=np.float64)
expected_result[3:8, 0:3] = 1
result = compare_images(img1, img2, method='diff')
assert_array_equal(result, expected_result)


def test_compare_images_blend():
img1 = np.zeros((10, 10), dtype=np.uint8)
img1[3:8, 3:8] = 255
img2 = np.zeros_like(img1)
img2[3:8, 0:8] = 255
expected_result = np.zeros_like(img1, dtype=np.float64)
expected_result[3:8, 3:8] = 1
expected_result[3:8, 0:3] = 0.5
result = compare_images(img1, img2, method='blend')
assert_array_equal(result, expected_result)


def test_compare_images_checkerboard_default():
img1 = np.zeros((2**4, 2**4), dtype=np.uint8)
img2 = np.full(img1.shape, fill_value=255, dtype=np.uint8)
res = compare_images(img1, img2, method='checkerboard')
exp_row1 = np.array([0., 0., 1., 1., 0., 0., 1., 1., 0., 0., 1., 1., 0., 0., 1., 1.])
exp_row2 = np.array([1., 1., 0., 0., 1., 1., 0., 0., 1., 1., 0., 0., 1., 1., 0., 0.])
for i in (0, 1, 4, 5, 8, 9, 12, 13):
assert_array_equal(res[i, :], exp_row1)
for i in (2, 3, 6, 7, 10, 11, 14, 15):
assert_array_equal(res[i, :], exp_row2)


def test_compare_images_checkerboard_tuple():
img1 = np.zeros((2**4, 2**4), dtype=np.uint8)
img2 = np.full(img1.shape, fill_value=255, dtype=np.uint8)
res = compare_images(img1, img2, method='checkerboard', n_tiles=(4, 8))
exp_row1 = np.array(
[0., 0., 1., 1., 0., 0., 1., 1., 0., 0., 1., 1., 0., 0., 1., 1.]
)
exp_row2 = np.array(
[1., 1., 0., 0., 1., 1., 0., 0., 1., 1., 0., 0., 1., 1., 0., 0.]
)
for i in (0, 1, 2, 3, 8, 9, 10, 11):
assert_array_equal(res[i, :], exp_row1)
for i in (4, 5, 6, 7, 12, 13, 14, 15):
assert_array_equal(res[i, :], exp_row2)

0 comments on commit 6de08ed

Please sign in to comment.