|  |  | 
					
						
						|  |  | 
					
						
						|  |  | 
					
						
						|  |  | 
					
						
						|  |  | 
					
						
						|  | import numpy as np | 
					
						
						|  | import cv2 | 
					
						
						|  | from scipy import ndimage as ndi | 
					
						
						|  | from skimage import filters | 
					
						
						|  |  | 
					
						
						|  |  | 
					
						
						|  | class XDoG: | 
					
						
						|  |  | 
					
						
						|  | def __init__(self, | 
					
						
						|  | gamma=0.98, | 
					
						
						|  | phi=200, | 
					
						
						|  | eps=-0.1, | 
					
						
						|  | sigma=0.8, | 
					
						
						|  | k=10, | 
					
						
						|  | binarize: bool = True): | 
					
						
						|  | """ | 
					
						
						|  | XDoG algorithm. | 
					
						
						|  |  | 
					
						
						|  | Args: | 
					
						
						|  | gamma: Control the size of the Gaussian filter | 
					
						
						|  | phi: Control changes in edge strength | 
					
						
						|  | eps: Threshold for controlling edge strength | 
					
						
						|  | sigma: The standard deviation of the Gaussian filter controls the degree of smoothness | 
					
						
						|  | k: Control the size ratio of Gaussian filter, (k=10 or k=1.6) | 
					
						
						|  | binarize(bool): Whether to binarize the output | 
					
						
						|  | """ | 
					
						
						|  |  | 
					
						
						|  | super(XDoG, self).__init__() | 
					
						
						|  |  | 
					
						
						|  | self.gamma = gamma | 
					
						
						|  | assert 0 <= self.gamma <= 1 | 
					
						
						|  |  | 
					
						
						|  | self.phi = phi | 
					
						
						|  | assert 0 <= self.phi <= 1500 | 
					
						
						|  |  | 
					
						
						|  | self.eps = eps | 
					
						
						|  | assert -1 <= self.eps <= 1 | 
					
						
						|  |  | 
					
						
						|  | self.sigma = sigma | 
					
						
						|  | assert 0.1 <= self.sigma <= 10 | 
					
						
						|  |  | 
					
						
						|  | self.k = k | 
					
						
						|  | assert 1 <= self.k <= 100 | 
					
						
						|  |  | 
					
						
						|  | self.binarize = binarize | 
					
						
						|  |  | 
					
						
						|  | def __call__(self, img): | 
					
						
						|  |  | 
					
						
						|  | if len(img.shape) == 3 and img.shape[2] == 3: | 
					
						
						|  | img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) | 
					
						
						|  | elif len(img.shape) == 3 and img.shape[2] == 4: | 
					
						
						|  | img = cv2.cvtColor(img, cv2.COLOR_BGRA2GRAY) | 
					
						
						|  |  | 
					
						
						|  | if np.isnan(img).any(): | 
					
						
						|  | img[np.isnan(img)] = np.mean(img[~np.isnan(img)]) | 
					
						
						|  |  | 
					
						
						|  |  | 
					
						
						|  | imf1 = ndi.gaussian_filter(img, self.sigma) | 
					
						
						|  | imf2 = ndi.gaussian_filter(img, self.sigma * self.k) | 
					
						
						|  | imdiff = imf1 - self.gamma * imf2 | 
					
						
						|  |  | 
					
						
						|  |  | 
					
						
						|  | imdiff = (imdiff < self.eps) * 1.0 + (imdiff >= self.eps) * (1.0 + np.tanh(self.phi * imdiff)) | 
					
						
						|  |  | 
					
						
						|  |  | 
					
						
						|  | imdiff -= imdiff.min() | 
					
						
						|  | imdiff /= imdiff.max() | 
					
						
						|  |  | 
					
						
						|  | if self.binarize: | 
					
						
						|  | th = filters.threshold_otsu(imdiff) | 
					
						
						|  | imdiff = (imdiff >= th).astype('float32') | 
					
						
						|  |  | 
					
						
						|  | return imdiff | 
					
						
						|  |  |