diff --git a/alibi_detect/utils/perturbation.py b/alibi_detect/utils/perturbation.py index cba1b77ad..cfb6e74da 100644 --- a/alibi_detect/utils/perturbation.py +++ b/alibi_detect/utils/perturbation.py @@ -2,7 +2,6 @@ from io import BytesIO from typing import List, Tuple, Union -import cv2 import numpy as np import skimage as sk from alibi_detect.utils.data import Bunch @@ -14,6 +13,14 @@ from scipy.ndimage.interpolation import map_coordinates from skimage.filters import gaussian +try: + import cv2 +except: + cv2 = None + from scipy.signal import convolve2d + from skimage.transform import AffineTransform, warp + from skimage import img_as_ubyte + def apply_mask(X: np.ndarray, mask_size: tuple = (4, 4), @@ -632,8 +639,17 @@ def disk(radius: float, alias_blur: float = 0.1, dtype=np.float32) -> np.ndarray aliased_disk = np.array((X ** 2 + Y ** 2) <= radius ** 2, dtype=dtype) aliased_disk /= np.sum(aliased_disk) - # supersample disk to antialias - return cv2.GaussianBlur(aliased_disk, ksize=ksize, sigmaX=alias_blur) + if cv2 is not None: + # supersample disk to antialias + return cv2.GaussianBlur(aliased_disk, ksize=ksize, sigmaX=alias_blur) + + else: + # Create Gaussian kernel + kernel = np.exp(-(X**2 + Y**2) / (2.0 * alias_blur**2)) + kernel /= np.sum(kernel) + + # Convolve with the aliased disk + return convolve2d(aliased_disk, kernel, mode='same', boundary='wrap') def defocus_blur(x: np.ndarray, radius: int, alias_blur: float, xrange: tuple = None) -> np.ndarray: @@ -658,9 +674,16 @@ def defocus_blur(x: np.ndarray, radius: int, alias_blur: float, xrange: tuple = x, scale_back = scale_minmax(x, xrange) kernel = disk(radius=radius, alias_blur=alias_blur) channels = [] - for d in range(3): - channels.append(cv2.filter2D(x[:, :, d], -1, kernel)) - x_db = np.array(channels).transpose((1, 2, 0)) + + if cv2 is not None: + for d in range(3): + channels.append(cv2.filter2D(x[:, :, d], -1, kernel)) + x_db = np.array(channels).transpose((1, 2, 0)) + else: + for d in range(3): + channels.append(convolve2d(x[:, :, d], kernel, mode='same', boundary='wrap')) + x_db = np.array(channels).transpose((1, 2, 0)).astype(np.float32) + if scale_back: x_db = x_db * (xrange[1] - xrange[0]) + xrange[0] if isinstance(xrange, tuple): @@ -941,8 +964,19 @@ def elastic_transform(x: np.ndarray, mult_dxdy: float, sigma: float, [center_square[0] + square_size, center_square[1] - square_size], center_square - square_size], dtype=np.float32) pts2 = pts1 + np.random.uniform(-rnd_rng, rnd_rng, size=pts1.shape).astype(np.float32) - M = cv2.getAffineTransform(pts1, pts2) - image = cv2.warpAffine(x, M, shape_size[::-1], borderMode=cv2.BORDER_REFLECT_101) + + if cv2 is not None: + M = cv2.getAffineTransform(pts1, pts2) + image = cv2.warpAffine(x, M, shape_size[::-1], borderMode=cv2.BORDER_REFLECT_101) + + else: + M = AffineTransform() + M.estimate(pts1, pts2) + + image = warp(x, M.inverse, output_shape=shape_size[::-1], mode='reflect') + # image = warp(x, M, output_shape=shape_size[::-1], mode='reflect') + image = img_as_ubyte(image) + dx = (gaussian(np.random.uniform(-1, 1, size=shape_size), sigma, mode='reflect', truncate=3) * mult_dxdy).astype(np.float32) dy = (gaussian(np.random.uniform(-1, 1, size=shape_size), diff --git a/setup.py b/setup.py index a52c19f6a..fc41f2c52 100644 --- a/setup.py +++ b/setup.py @@ -25,12 +25,17 @@ def readme(): "pykeops>=2.0.0, <2.2.0", "torch>=1.7.0, <1.14.0" ], + # https://github.com/SeldonIO/alibi-detect/issues/532 + "opencv-python": [ + "opencv-python>=3.2.0, <5.0.0" + ], "all": [ "prophet>=1.1.0, <2.0.0", "tensorflow_probability>=0.8.0, <0.22.0", "tensorflow>=2.2.0, !=2.6.0, !=2.6.1, <2.14.0", # https://github.com/SeldonIO/alibi-detect/issues/375 and 387 "pykeops>=2.0.0, <2.2.0", "torch>=1.7.0, <1.14.0" + "opencv-python>=3.2.0, <5.0.0" ], } @@ -53,7 +58,7 @@ def readme(): "numpy>=1.16.2, <2.0.0", "pandas>=1.0.0, <3.0.0", "Pillow>=5.4.1, <11.0.0", - "opencv-python>=3.2.0, <5.0.0", + # "opencv-python>=3.2.0, <5.0.0", # Eradicated cv2 (opencv) requirement: https://github.com/SeldonIO/alibi-detect/issues/532 "scipy>=1.3.0, <2.0.0", 'scikit-image>=0.14.2, !=0.17.1, <0.22', # https://github.com/SeldonIO/alibi/issues/215 "scikit-learn>=0.20.2, <2.0.0",