def generate_blob_mask(flat, size, snr): """ Generate a binary mask of blobs from a flat-field image (Ref. [1]). Parameters ---------- flat : array_like 2D array. Flat-field image. size : float Estimated size of the largest blob. snr : float Ratio used to segment blobs. Returns ------- array_like 2D array. Binary mask. References ---------- .. [1] https://doi.org/10.1364/OE.418448 """ mat = ndi.median_filter(flat, (2, 2)) mask = np.zeros_like(mat) for i in range(mat.shape[0]): line = mat[i] line_fil = ndi.median_filter(line, size) line_fil[line_fil == 0.0] = np.mean(line_fil) line_norm = line / line_fil mask_1d = util.detect_stripe(line_norm, snr) mask_1d[0:2] = 0.0 mask_1d[-2:] = 0.0 mask[i] = np.float32(ndi.binary_dilation(mask_1d, iterations=1)) return mask
def test_detect_stripe(self): np.random.seed(1) lis = np.random.rand(self.size) lis_off = np.linspace(0, 1, len(lis)) lis = lis + lis_off lis[self.size // 2:self.size // 2 + 1] = 6.0 lis_bin = util.detect_stripe(lis, 1.5) pos = np.where(lis_bin == 1.0) self.assertTrue((len(pos) > 0) and (pos[0] == self.size // 2))
def remove_dead_stripe(sinogram, snr=3.0, size=51, residual=True, smooth_strength=10): """ Remove unresponsive or fluctuating stripe artifacts in a sinogram, algorithm 6 in Ref. [1]_. Angular direction is along the axis 0. Parameters ---------- sinogram : array_like 2D array. Sinogram image. snr : float Ratio (>1.0) used to detect stripe locations. Greater is less sensitive. size : int Window size of the median filter. residual : bool, optional Removing residual stripes if True. smooth_strength : int, optional Window size of the uniform filter used to detect stripes. Returns ------- ndarray 2D array. Stripe-removed sinogram. References ---------- .. [1] https://doi.org/10.1364/OE.26.028396 """ sinogram = np.copy(sinogram) (nrow, ncol) = sinogram.shape sino_smooth = np.apply_along_axis(ndi.uniform_filter1d, 0, sinogram, smooth_strength) list_diff = np.sum(np.abs(sinogram - sino_smooth), axis=0) list_diff_bck = ndi.median_filter(list_diff, size) nmean = np.mean(np.abs(list_diff_bck)) list_diff_bck[list_diff_bck == 0.0] = nmean list_fact = list_diff / list_diff_bck list_mask = util.detect_stripe(list_fact, snr) list_mask = np.float32(ndi.binary_dilation(list_mask, iterations=1)) list_mask[0:2] = 0.0 list_mask[-2:] = 0.0 xlist = np.where(list_mask < 1.0)[0] ylist = np.arange(nrow) mat = sinogram[:, xlist] finter = interpolate.interp2d(xlist, ylist, mat, kind='linear') xlist_miss = np.where(list_mask > 0.0)[0] if (ncol // 3) > len(xlist_miss) > 0: sinogram[:, xlist_miss] = finter(xlist_miss, ylist) if residual is True: sinogram = remove_large_stripe(sinogram, snr, size) return sinogram
def remove_stripe_based_interpolation(sinogram, snr=3.0, size=51, drop_ratio=0.1, norm=True, kind="linear", **options): """ Combination of algorithm 4, 5, and 6 in Ref. [1]. Angular direction is along the axis 0. Parameters ---------- sinogram : array_like 2D array. Sinogram image snr : float Ratio (>1.0) used to detect stripe locations. Greater is less sensitive. size : int Window size of the median filter used to detect stripes. drop_ratio : float, optional Ratio of pixels to be dropped, which is used to to reduce the possibility of the false detection of stripes. norm : bool, optional Apply normalization if True. kind : {'linear', 'cubic', 'quintic'}, optional The kind of spline interpolation to use. Default is 'linear'. options : dict, optional Use another smoothing filter rather than the median filter. E.g. options={"method": "gaussian_filter", "para1": (1,21))} Returns ------- array_like 2D array. Stripe-removed sinogram. References ---------- .. [1] https://doi.org/10.1364/OE.26.028396 """ msg = "\n Please use the dictionary format: options={'method':" \ " 'filter_name', 'para1': parameter_1, 'para2': parameter_2}" drop_ratio = np.clip(drop_ratio, 0.0, 0.8) sinogram = np.copy(sinogram) (nrow, ncol) = sinogram.shape ndrop = int(0.5 * drop_ratio * nrow) sino_sort = np.sort(sinogram, axis=0) if len(options) == 0: sino_smooth = ndi.median_filter(sino_sort, (1, size)) else: if not isinstance(options, dict): raise ValueError(msg) sino_smooth = np.copy(sino_sort) for opt_name in options: opt = options[opt_name] method = tuple(opt.values())[0] para = tuple(opt.values())[1:] if method in dir(ndi): try: sino_smooth = getattr(ndi, method)(sino_smooth, *para) except: raise ValueError(msg) else: if method in dir(util): try: sino_smooth = getattr(util, method)(sino_smooth, *para) except: raise ValueError(msg) else: raise ValueError("Can't find the method: '{}' in the" " namespace".format(method)) list1 = np.mean(sino_sort[ndrop:nrow - ndrop], axis=0) list2 = np.mean(sino_smooth[ndrop:nrow - ndrop], axis=0) list_fact = np.divide(list1, list2, out=np.ones_like(list1), where=list2 != 0) list_mask = util.detect_stripe(list_fact, snr) list_mask = np.float32(ndi.binary_dilation(list_mask, iterations=1)) mat_fact = np.tile(list_fact, (nrow, 1)) if norm is True: sinogram = sinogram / mat_fact list_mask[0:2] = 0.0 list_mask[-2:] = 0.0 xlist = np.where(list_mask < 1.0)[0] ylist = np.arange(nrow) zmat = sinogram[:, xlist] finter = interpolate.interp2d(xlist, ylist, zmat, kind=kind) xlist_miss = np.where(list_mask > 0.0)[0] if len(xlist_miss) > 0: sinogram[:, xlist_miss] = finter(xlist_miss, ylist) return sinogram
def remove_large_stripe(sinogram, snr=3.0, size=51, drop_ratio=0.1, norm=True, **options): """ Remove large stripe artifacts in a sinogram, algorithm 5 in Ref. [1]. Angular direction is along the axis 0. Parameters ---------- sinogram : array_like 2D array. Sinogram image snr : float Ratio (>1.0) used to detect stripe locations. Greater is less sensitive. size : int Window size of the median filter. drop_ratio : float, optional Ratio of pixels to be dropped, which is used to to reduce the possibility of the false detection of stripes. norm : bool, optional Apply normalization if True. options : dict, optional Use another smoothing filter rather than the median filter. E.g. options={"method": "gaussian_filter", "para1": (1,21))}. Returns ------- array_like 2D array. Stripe-removed sinogram. References ---------- .. [1] https://doi.org/10.1364/OE.26.028396 """ msg = "\n Please use the dictionary format: options={'method':" \ " 'filter_name', 'para1': parameter_1, 'para2': parameter_2}" sinogram = np.copy(np.float32(sinogram)) drop_ratio = np.clip(drop_ratio, 0.0, 0.8) (nrow, ncol) = sinogram.shape ndrop = int(0.5 * drop_ratio * nrow) sino_sort, sino_index = util.sort_forward(sinogram, axis=0) if len(options) == 0: sino_smooth = ndi.median_filter(sino_sort, (1, size)) else: if not isinstance(options, dict): raise ValueError(msg) sino_smooth = np.copy(sino_sort) for opt_name in options: opt = options[opt_name] method = tuple(opt.values())[0] if method in dir(ndi): para = tuple(opt.values())[1:] try: sino_smooth = getattr(ndi, method)(sino_smooth, *para) except: raise ValueError(msg) else: if method in dir(util): try: sino_smooth = getattr(util, method)(sino_smooth, *para) except: raise ValueError(msg) else: raise ValueError("Can't find the method: '{}' in the" " namespace".format(method)) list1 = np.mean(sino_sort[ndrop:nrow - ndrop], axis=0) list2 = np.mean(sino_smooth[ndrop:nrow - ndrop], axis=0) list_fact = np.divide(list1, list2, out=np.ones_like(list1), where=list2 != 0) list_mask = util.detect_stripe(list_fact, snr) list_mask = np.float32(ndi.binary_dilation(list_mask, iterations=1)) if norm is True: sinogram = sinogram / np.tile(list_fact, (nrow, 1)) sino_corr = util.sort_backward(sino_smooth, sino_index, axis=0) xlist_miss = np.where(list_mask > 0.0)[0] sinogram[:, xlist_miss] = sino_corr[:, xlist_miss] return sinogram