compare.py 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899
  1. import functools
  2. from itertools import product
  3. import numpy as np
  4. from .dtype import img_as_float
  5. def _rename_image_params(func):
  6. @functools.wraps(func)
  7. def wrapper(*args, **kwargs):
  8. # Turn all args into kwargs
  9. for i, (value, param) in enumerate(
  10. zip(args, ["image0", "image1", "method", "n_tiles"])
  11. ):
  12. if param in kwargs:
  13. raise ValueError(
  14. f"{param} passed both as positional and keyword argument."
  15. )
  16. else:
  17. kwargs[param] = value
  18. args = tuple()
  19. return func(*args, **kwargs)
  20. return wrapper
  21. @_rename_image_params
  22. def compare_images(image0, image1, *, method='diff', n_tiles=(8, 8)):
  23. """
  24. Return an image showing the differences between two images.
  25. .. versionadded:: 0.16
  26. Parameters
  27. ----------
  28. image0, image1 : ndarray, shape (M, N)
  29. Images to process, must be of the same shape.
  30. .. versionchanged:: 0.24
  31. `image1` and `image2` were renamed into `image0` and `image1`
  32. respectively.
  33. method : {'diff', 'blend', 'checkerboard'}, optional
  34. Method used for the comparison.
  35. Details are provided in the note section.
  36. .. versionchanged:: 0.24
  37. This parameter and following ones are keyword-only.
  38. n_tiles : tuple, optional
  39. Used only for the `checkerboard` method. Specifies the number
  40. of tiles (row, column) to divide the image.
  41. Returns
  42. -------
  43. comparison : ndarray, shape (M, N)
  44. Image showing the differences.
  45. Notes
  46. -----
  47. ``'diff'`` computes the absolute difference between the two images.
  48. ``'blend'`` computes the mean value.
  49. ``'checkerboard'`` makes tiles of dimension `n_tiles` that display
  50. alternatively the first and the second image. Note that images must be
  51. 2-dimensional to be compared with the checkerboard method.
  52. """
  53. if image1.shape != image0.shape:
  54. raise ValueError('Images must have the same shape.')
  55. img1 = img_as_float(image0)
  56. img2 = img_as_float(image1)
  57. if method == 'diff':
  58. comparison = np.abs(img2 - img1)
  59. elif method == 'blend':
  60. comparison = 0.5 * (img2 + img1)
  61. elif method == 'checkerboard':
  62. if img1.ndim != 2:
  63. raise ValueError(
  64. 'Images must be 2-dimensional to be compared with the '
  65. 'checkerboard method.'
  66. )
  67. shapex, shapey = img1.shape
  68. mask = np.full((shapex, shapey), False)
  69. stepx = int(shapex / n_tiles[0])
  70. stepy = int(shapey / n_tiles[1])
  71. for i, j in product(range(n_tiles[0]), range(n_tiles[1])):
  72. if (i + j) % 2 == 0:
  73. mask[i * stepx : (i + 1) * stepx, j * stepy : (j + 1) * stepy] = True
  74. comparison = np.zeros_like(img1)
  75. comparison[mask] = img1[mask]
  76. comparison[~mask] = img2[~mask]
  77. else:
  78. raise ValueError(
  79. 'Wrong value for `method`. '
  80. 'Must be either "diff", "blend" or "checkerboard".'
  81. )
  82. return comparison