def test_four_quadrants(self): image = cp.asarray(np.random.uniform(size=(20, 30))) i, j = cp.mgrid[0:20, 0:30] labels = 1 + (i >= 10) + (j >= 15) * 2 i, j = cp.mgrid[-3:4, -3:4] footprint = i * i + j * j <= 9 expected = cp.zeros(image.shape, float) for imin, imax in ((0, 10), (10, 20)): for jmin, jmax in ((0, 15), (15, 30)): expected[imin:imax, jmin:jmax] = ndi.maximum_filter(image[imin:imax, jmin:jmax], footprint=footprint) expected = expected == image with expected_warnings(["indices argument is deprecated"]): result = peak.peak_local_max(image, labels=labels, footprint=footprint, min_distance=1, threshold_rel=0, indices=False, exclude_border=False) assert cp.all(result == expected)
def test_indices_with_labels(self): image = cp.asarray(np.random.uniform(size=(40, 60))) i, j = cp.mgrid[0:40, 0:60] labels = 1 + (i >= 20) + (j >= 30) * 2 i, j = cp.mgrid[-3:4, -3:4] footprint = i * i + j * j <= 9 expected = cp.zeros(image.shape, float) for imin, imax in ((0, 20), (20, 40)): for jmin, jmax in ((0, 30), (30, 60)): expected[imin:imax, jmin:jmax] = ndi.maximum_filter(image[imin:imax, jmin:jmax], footprint=footprint) expected = cp.stack(cp.nonzero(expected == image), axis=-1) expected = expected[cp.argsort(image[tuple(expected.T)])[::-1]] result = peak.peak_local_max(image, labels=labels, min_distance=1, threshold_rel=0, footprint=footprint, exclude_border=False) result = result[cp.argsort(image[tuple(result.T)])[::-1]] assert (result == expected).all()
def _get_peak_mask(image, footprint, threshold, mask=None): """ Return the mask containing all peak candidates above thresholds. """ if footprint.size == 1 or image.size == 1: return image > threshold image_max = ndi.maximum_filter(image, footprint=footprint, mode='constant') out = image == image_max # no peak for a trivial image image_is_trivial = np.all(out) if mask is None else np.all(out[mask]) if image_is_trivial: # synchronize out[:] = False if mask is not None: # isolated pixels in masked area are returned as peaks isolated_px = cp.logical_xor(mask, ndi.binary_opening(mask)) out[isolated_px] = True out &= image > threshold return out
def test_reorder_labels(self): image = cp.asarray(np.random.uniform(size=(40, 60))) i, j = cp.mgrid[0:40, 0:60] labels = 1 + (i >= 20) + (j >= 30) * 2 labels[labels == 4] = 5 i, j = cp.mgrid[-3:4, -3:4] footprint = i * i + j * j <= 9 expected = cp.zeros(image.shape, float) for imin, imax in ((0, 20), (20, 40)): for jmin, jmax in ((0, 30), (30, 60)): expected[imin:imax, jmin:jmax] = ndi.maximum_filter(image[imin:imax, jmin:jmax], footprint=footprint) expected = expected == image with expected_warnings(["indices argument is deprecated"]): result = peak.peak_local_max(image, labels=labels, min_distance=1, threshold_rel=0, footprint=footprint, indices=False, exclude_border=False) assert (result == expected).all()
def prominent_peaks(img, min_xdistance=1, min_ydistance=1, threshold=None, num_peaks=cp.inf): """Return peaks with non-maximum suppression. Identifies most prominent features separated by certain distances. Non-maximum suppression with different sizes is applied separately in the first and second dimension of the image to identify peaks. Parameters ---------- image : (M, N) ndarray Input image. min_xdistance : int Minimum distance separating features in the x dimension. min_ydistance : int Minimum distance separating features in the y dimension. threshold : float Minimum intensity of peaks. Default is `0.5 * max(image)`. num_peaks : int Maximum number of peaks. When the number of peaks exceeds `num_peaks`, return `num_peaks` coordinates based on peak intensity. Returns ------- intensity, xcoords, ycoords : tuple of array Peak intensity values, x and y indices. Notes ----- Modified from https://github.com/mritools/cupyimg _prominent_peaks method """ t00 = time.time() #img = image.copy() rows, cols = img.shape if threshold is None: threshold = 0.5 * cp.max(img) ycoords_size = 2 * min_ydistance + 1 xcoords_size = 2 * min_xdistance + 1 t0 = time.time() img_max = ndi.maximum_filter(img, size=(ycoords_size, xcoords_size), mode="constant", cval=0) te = (time.time() - t0) * 1e3 logger.debug(f"Maxfilter: {te:2.2f} ms") t0 = time.time() mask = img == img_max img *= mask mask = img > threshold te = (time.time() - t0) * 1e3 logger.debug(f"bitbash: {te:2.2f} ms") t0 = time.time() # Find array (x,y) indexes corresponding to max pixels peak_idxs = cp.argwhere(mask) # Find corresponding maximum values peak_vals = img[peak_idxs[:, 0], peak_idxs[:, 1]] # Sort peak values low to high ## Sort the list of peaks by intensity, not left-right, so larger peaks ## in Hough space cannot be arbitrarily suppressed by smaller neighbors val_sort_idx = cp.argsort(peak_vals)[::-1] # Return (x,y) coordinates corresponding to sorted max pixels coords = peak_idxs[val_sort_idx] te = (time.time() - t0) * 1e3 logger.debug(f"coord search: {te:2.2f} ms") t0 = time.time() img_peaks = [] ycoords_peaks = [] xcoords_peaks = [] # relative coordinate grid for local neighbourhood suppression ycoords_ext, xcoords_ext = cp.mgrid[-min_ydistance:min_ydistance + 1, -min_xdistance:min_xdistance + 1] for ycoords_idx, xcoords_idx in coords: accum = img_max[ycoords_idx, xcoords_idx] if accum > threshold: # absolute coordinate grid for local neighbourhood suppression ycoords_nh = ycoords_idx + ycoords_ext xcoords_nh = xcoords_idx + xcoords_ext # no reflection for distance neighbourhood ycoords_in = cp.logical_and(ycoords_nh > 0, ycoords_nh < rows) ycoords_nh = ycoords_nh[ycoords_in] xcoords_nh = xcoords_nh[ycoords_in] # reflect xcoords and assume xcoords are continuous, # e.g. for angles: # (..., 88, 89, -90, -89, ..., 89, -90, -89, ...) xcoords_low = xcoords_nh < 0 ycoords_nh[xcoords_low] = rows - ycoords_nh[xcoords_low] xcoords_nh[xcoords_low] += cols xcoords_high = xcoords_nh >= cols ycoords_nh[xcoords_high] = rows - ycoords_nh[xcoords_high] xcoords_nh[xcoords_high] -= cols # suppress neighbourhood img_max[ycoords_nh, xcoords_nh] = 0 # add current feature to peaks img_peaks.append(accum) ycoords_peaks.append(ycoords_idx) xcoords_peaks.append(xcoords_idx) img_peaks = cp.array(img_peaks) ycoords_peaks = cp.array(ycoords_peaks) xcoords_peaks = cp.array(xcoords_peaks) te = (time.time() - t0) * 1e3 logger.debug(f"crazyloop: {te:2.2f} ms") if num_peaks < len(img_peaks): idx_maxsort = cp.argsort(img_peaks)[::-1][:num_peaks] img_peaks = img_peaks[idx_maxsort] ycoords_peaks = ycoords_peaks[idx_maxsort] xcoords_peaks = xcoords_peaks[idx_maxsort] te = (time.time() - t0) * 1e3 logger.debug(f"prominent_peaks total: {te:2.2f} ms") return img_peaks, xcoords_peaks, ycoords_peaks