_blur_effect.py 3.2 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798
  1. import numpy as np
  2. import scipy.ndimage as ndi
  3. from ..color import rgb2gray
  4. from ..util import img_as_float
  5. # TODO: when minimum numpy dependency is 1.25 use:
  6. # np..exceptions.AxisError instead of AxisError
  7. # and remove this try-except
  8. try:
  9. from numpy import AxisError
  10. except ImportError:
  11. from numpy.exceptions import AxisError
  12. __all__ = ['blur_effect']
  13. _EPSILON = np.spacing(np.float64(1))
  14. def blur_effect(image, h_size=11, channel_axis=None, reduce_func=np.max):
  15. """Compute a metric that indicates the strength of blur in an image
  16. (0 for no blur, 1 for maximal blur).
  17. Parameters
  18. ----------
  19. image : ndarray
  20. RGB or grayscale nD image. The input image is converted to grayscale
  21. before computing the blur metric.
  22. h_size : int, optional
  23. Size of the re-blurring filter.
  24. channel_axis : int or None, optional
  25. If None, the image is assumed to be grayscale (single-channel).
  26. Otherwise, this parameter indicates which axis of the array
  27. corresponds to color channels.
  28. reduce_func : callable, optional
  29. Function used to calculate the aggregation of blur metrics along all
  30. axes. If set to None, the entire list is returned, where the i-th
  31. element is the blur metric along the i-th axis.
  32. Returns
  33. -------
  34. blur : float (0 to 1) or list of floats
  35. Blur metric: by default, the maximum of blur metrics along all axes.
  36. Notes
  37. -----
  38. `h_size` must keep the same value in order to compare results between
  39. images. Most of the time, the default size (11) is enough. This means that
  40. the metric can clearly discriminate blur up to an average 11x11 filter; if
  41. blur is higher, the metric still gives good results but its values tend
  42. towards an asymptote.
  43. References
  44. ----------
  45. .. [1] Frederique Crete, Thierry Dolmiere, Patricia Ladret, and Marina
  46. Nicolas "The blur effect: perception and estimation with a new
  47. no-reference perceptual blur metric" Proc. SPIE 6492, Human Vision and
  48. Electronic Imaging XII, 64920I (2007)
  49. https://hal.archives-ouvertes.fr/hal-00232709
  50. :DOI:`10.1117/12.702790`
  51. """
  52. if channel_axis is not None:
  53. try:
  54. # ensure color channels are in the final dimension
  55. image = np.moveaxis(image, channel_axis, -1)
  56. except AxisError:
  57. print('channel_axis must be one of the image array dimensions')
  58. raise
  59. except TypeError:
  60. print('channel_axis must be an integer')
  61. raise
  62. image = rgb2gray(image)
  63. n_axes = image.ndim
  64. image = img_as_float(image)
  65. shape = image.shape
  66. B = []
  67. from ..filters import sobel
  68. slices = tuple([slice(2, s - 1) for s in shape])
  69. for ax in range(n_axes):
  70. filt_im = ndi.uniform_filter1d(image, h_size, axis=ax)
  71. im_sharp = np.abs(sobel(image, axis=ax))
  72. im_blur = np.abs(sobel(filt_im, axis=ax))
  73. # avoid numerical instabilities
  74. im_sharp = np.maximum(_EPSILON, im_sharp)
  75. im_blur = np.maximum(_EPSILON, im_blur)
  76. T = np.maximum(0, im_sharp - im_blur)
  77. M1 = np.sum(im_sharp[slices])
  78. M2 = np.sum(T[slices])
  79. B.append(np.abs(M1 - M2) / M1)
  80. return B if reduce_func is None else reduce_func(B)