def randint(self, low, high=None, size=None, dtype='l'): """Returns a scalar or an array of integer values over ``[low, high)``. .. seealso:: :func:`cupy.random.randint` for full documentation, :meth:`numpy.random.RandomState.randint` """ if high is None: lo = 0 hi = low else: lo = low hi = high if lo >= hi: raise ValueError('low >= high') if lo < cupy.iinfo(dtype).min: raise ValueError('low is out of bounds for {}'.format( cupy.dtype(dtype).name)) if hi > cupy.iinfo(dtype).max + 1: raise ValueError('high is out of bounds for {}'.format( cupy.dtype(dtype).name)) diff = hi - lo - 1 if diff > cupy.iinfo(cupy.int32).max - cupy.iinfo(cupy.int32).min + 1: raise NotImplementedError( 'Sampling from a range whose extent is larger than int32 ' 'range is currently not supported') x = self._interval(diff, size).astype(dtype, copy=False) cupy.add(x, lo, out=x) return x
def randint(self, low, high=None, size=None, dtype='l'): """Returns a scalar or an array of integer values over ``[low, high)``. .. seealso:: :func:`cupy.random.randint` for full documentation, :meth:`numpy.random.RandomState.randint <numpy.random.mtrand.RandomState.randint>` """ if high is None: lo = 0 hi1 = int(low) - 1 else: lo = int(low) hi1 = int(high) - 1 if lo > hi1: raise ValueError('low >= high') if lo < cupy.iinfo(dtype).min: raise ValueError('low is out of bounds for {}'.format( cupy.dtype(dtype).name)) if hi1 > cupy.iinfo(dtype).max: raise ValueError('high is out of bounds for {}'.format( cupy.dtype(dtype).name)) diff = hi1 - lo x = self._interval(diff, size).astype(dtype, copy=False) cupy.add(x, lo, out=x) return x
def from_4Dcamera_file(filename): with h5py.File(filename, 'r') as f0: frames = f0['electron_events/frames'][:] scan_dimensions = ( f0['electron_events/scan_positions'].attrs['Ny'], f0['electron_events/scan_positions'].attrs['Nx']) frame_dimensions = np.array((576, 576)) def unragged_frames_size(frames): mm = 0 for ev in frames: if ev.shape[0] > mm: mm = ev.shape[0] return mm def make_unragged_frames(frames, scan_dimensions): unragged_frame_size = unragged_frames_size(frames.ravel()) fr_full = cp.zeros((frames.ravel().shape[0], unragged_frame_size), dtype=cp.int32) fr_full[:] = cp.iinfo(fr_full.dtype).max for ii, ev in enumerate(frames.ravel()): fr_full[ii, :ev.shape[0]] = cp.array(ev) fr_full_4d = fr_full.reshape((*scan_dimensions, fr_full.shape[1])) fr_full_4d = fr_full_4d[:, :-1, :] return fr_full_4d d = Sparse4DData() d.indices = cp.ascontiguousarray( make_unragged_frames(frames.ravel(), scan_dimensions)) d.scan_dimensions = np.array(d.indices.shape[:2]) d.frame_dimensions = frame_dimensions d.counts = cp.zeros(d.indices.shape, dtype=cp.bool) d.counts[d.indices != cp.iinfo(d.indices.dtype).max] = 1 return d
def fftshift_and_pad_to(sparse_data, pad_to_frame_dimensions): indices = sparse_data.indices scan_dimensions = sparse_data.scan_dimensions frame_dimensions = sparse_data.frame_dimensions center_frame = frame_dimensions / 2 threadsperblock = (16, 16) blockspergrid = tuple( np.ceil(np.array(indices.shape[:2]) / threadsperblock).astype( np.int)) no_count_indicator_old = np.iinfo(indices.dtype).max inds = np.prod(pad_to_frame_dimensions) if inds > 2**15: dtype = cp.int64 elif inds > 2**15: dtype = cp.int32 elif inds > 2**8: dtype = cp.int16 else: dtype = cp.uint8 no_count_indicator_new = cp.iinfo(dtype).max inds = cp.array(indices, dtype=dtype) fftshift_pad_kernel[blockspergrid, threadsperblock](inds, center_frame, scan_dimensions, cp.array(pad_to_frame_dimensions), no_count_indicator_old, no_count_indicator_new) sparse_data.indices = inds.get() sparse_data.frame_dimensions = np.array(pad_to_frame_dimensions) return sparse_data
def tomaxint(self, size=None): """Draws integers between 0 and max integer inclusive. Return a sample of uniformly distributed random integers in the interval [0, ``np.iinfo(np.int_).max``]. The `np.int_` type translates to the C long integer type and its precision is platform dependent. Args: size (int or tuple of ints): Output shape. Returns: cupy.ndarray: Drawn samples. .. seealso:: :meth:`numpy.random.RandomState.tomaxint` """ if size is None: size = () sample = cupy.empty(size, dtype=cupy.int_) # cupy.random only uses int32 random generator size_in_int = sample.dtype.itemsize // 4 curand.generate(self._generator, sample.data.ptr, sample.size * size_in_int) # Disable sign bit sample &= cupy.iinfo(cupy.int_).max return sample
def tomaxint(self, size=None): """Draws integers between 0 and max integer inclusive. Args: size (int or tuple of ints): Output shape. Returns: cupy.ndarray: Drawn samples. .. seealso:: :meth:`numpy.random.RandomState.tomaxint <numpy.random.mtrand.RandomState.tomaxint>` """ if size is None: size = () sample = cupy.empty(size, dtype=cupy.int_) # cupy.random only uses int32 random generator size_in_int = sample.dtype.itemsize // 4 curand.generate(self._generator, sample.data.ptr, sample.size * size_in_int) # Disable sign bit sample &= cupy.iinfo(cupy.int_).max return sample
def randint(low, high=None, size=None, dtype='l'): """Returns a scalar or an array of integer values over ``[low, high)``. Each element of returned values are independently sampled from uniform distribution over left-close and right-open interval ``[low, high)``. Args: low (int): If ``high`` is not ``None``, it is the lower bound of the interval. Otherwise, it is the **upper** bound of the interval and lower bound of the interval is set to ``0``. high (int): Upper bound of the interval. size (None or int or tuple of ints): The shape of returned value. dtype: Data type specifier. Returns: int or cupy.ndarray of ints: If size is ``None``, it is single integer sampled. If size is integer, it is the 1D-array of length ``size`` element. Otherwise, it is the array whose shape specified by ``size``. """ if high is None: lo = 0 hi = low else: lo = low hi = high if lo >= hi: raise ValueError('low >= high') if lo < cupy.iinfo(dtype).min: raise ValueError('low is out of bounds for {}'.format( cupy.dtype(dtype).name)) if hi > cupy.iinfo(dtype).max + 1: raise ValueError('high is out of bounds for {}'.format( cupy.dtype(dtype).name)) diff = hi - lo - 1 if diff > cupy.iinfo(cupy.int32).max - cupy.iinfo(cupy.int32).min + 1: raise NotImplementedError( 'Sampling from a range whose extent is larger than int32 range is ' 'currently not supported') rs = generator.get_random_state() x = rs.interval(diff, size).astype(dtype, copy=False) cupy.add(x, lo, out=x) return x
def test_relabel_sequential_signed_overflow(): imax = cp.iinfo(cp.int32).max labels = cp.asarray([0, 1, 99, 42, 42], dtype=cp.int32) output, fw, inv = relabel_sequential(labels, offset=imax) reference = np.array([0, imax, imax + 2, imax + 1, imax + 1], dtype=cp.uint32) assert_array_equal(output, reference) assert output.dtype == reference.dtype
def get_index_dtype(arrays=(), maxval=None, check_contents=False): """Based on input (integer) arrays ``a``, determines a suitable index data type that can hold the data in the arrays. Args: arrays (tuple of array_like): Input arrays whose types/contents to check maxval (float, optional): Maximum value needed check_contents (bool, optional): Whether to check the values in the arrays and not just their types. Default: False (check only the types) Returns: dtype: Suitable index data type (int32 or int64) """ int32min = cupy.iinfo(cupy.int32).min int32max = cupy.iinfo(cupy.int32).max dtype = cupy.int32 if maxval is not None: if maxval > int32max: dtype = cupy.int64 if isinstance(arrays, cupy.ndarray): arrays = (arrays,) for arr in arrays: arr = cupy.asarray(arr) if not cupy.can_cast(arr.dtype, cupy.int32): if check_contents: if arr.size == 0: # a bigger type not needed continue elif cupy.issubdtype(arr.dtype, cupy.integer): maxval = arr.max() minval = arr.min() if minval >= int32min and maxval <= int32max: # a bigger type not needed continue dtype = cupy.int64 break return dtype
def iinfo(type: Union[Dtype, Array], /) -> iinfo_object: """ Array API compatible wrapper for :py:func:`np.iinfo <numpy.iinfo>`. See its docstring for more information. """ ii = np.iinfo(type) # type: ignore return iinfo_object(ii.bits, ii.max, ii.min)
def make_unragged_frames(frames, scan_dimensions): unragged_frame_size = unragged_frames_size(frames.ravel()) fr_full = cp.zeros((frames.ravel().shape[0], unragged_frame_size), dtype=cp.int32) fr_full[:] = cp.iinfo(fr_full.dtype).max for ii, ev in enumerate(frames.ravel()): fr_full[ii, :ev.shape[0]] = cp.array(ev) fr_full_4d = fr_full.reshape((*scan_dimensions, fr_full.shape[1])) fr_full_4d = fr_full_4d[:, :-1, :] return fr_full_4d
def kron(A, B, format=None): """Kronecker product of sparse matrices A and B. Args: A (cupyx.scipy.sparse.spmatrix): a sparse matrix. B (cupyx.scipy.sparse.spmatrix): a sparse matrix. format (str): the format of the returned sparse matrix. Returns: cupyx.scipy.sparse.spmatrix: Generated sparse matrix with the specified ``format``. .. seealso:: :func:`scipy.sparse.kron` """ # TODO(leofang): support BSR format when it's added to CuPy # TODO(leofang): investigate if possible to optimize performance by # starting with CSR instead of COO matrices A = _coo.coo_matrix(A) B = _coo.coo_matrix(B) out_shape = (A.shape[0] * B.shape[0], A.shape[1] * B.shape[1]) if A.nnz == 0 or B.nnz == 0: # kronecker product is the zero matrix return _coo.coo_matrix(out_shape).asformat(format) if max(out_shape[0], out_shape[1]) > cupy.iinfo('int32').max: dtype = cupy.int64 else: dtype = cupy.int32 # expand entries of A into blocks row = A.row.astype(dtype, copy=True) * B.shape[0] row = row.repeat(B.nnz) col = A.col.astype(dtype, copy=True) * B.shape[1] col = col.repeat(B.nnz) data = A.data.repeat(B.nnz) # data's dtype follows that of A in SciPy # increment block indices row, col = row.reshape(-1, B.nnz), col.reshape(-1, B.nnz) row += B.row col += B.col row, col = row.ravel(), col.ravel() # compute block entries data = data.reshape(-1, B.nnz) * B.data data = data.ravel() return _coo.coo_matrix( (data, (row, col)), shape=out_shape).asformat(format)
def from_dense(dense, make_float=False): res = Sparse4DData() res.frame_dimensions = np.array(dense.shape[-2:]) res.scan_dimensions = np.array(dense.shape[:2]) inds = np.prod(res.frame_dimensions) if inds > 2**31: dtype = cp.int64 elif inds > 2**15: dtype = cp.int32 elif inds > 2**8: dtype = cp.int16 else: dtype = cp.uint8 nonzeros = cp.sum((dense > 0), (2, 3)) nonzeros = cp.max(nonzeros) bits_counts = np.log2(dense.max()) if make_float: dtype_counts = cp.float32 else: if bits_counts > np.log2(2**31 - 1): dtype_counts = cp.int64 elif bits_counts > np.log2(2**15 - 1): dtype_counts = cp.int32 elif bits_counts > 8: dtype_counts = cp.int16 else: dtype_counts = cp.uint8 threadsperblock = (16, 16) blockspergrid = tuple( np.ceil(res.scan_dimensions / threadsperblock).astype(np.int)) dense = cp.array(dense) indices = cp.zeros((*dense.shape[:2], nonzeros), dtype=dtype) indices[:] = cp.iinfo(dtype).max counts = cp.zeros((*dense.shape[:2], nonzeros), dtype=dtype_counts) dense_to_sparse_kernel[blockspergrid, threadsperblock](dense, indices, counts, cp.array(res.frame_dimensions)) res.indices = indices.get() res.counts = counts.get() print(f'frame_dimensions: {res.frame_dimensions}') print(f'scan_dimensions : {res.scan_dimensions}') print(f'Using dtype: {dtype} for indices') print(f'Using dtype: {dtype_counts} for counts') return res
def crop_symmetric_center_(self, center, max_radius = None): if max_radius is None: y_min_radius = np.min([center[0], self.frame_dimensions[0] - center[0]]) x_min_radius = np.min([center[1], self.frame_dimensions[1] - center[1]]) max_radius = np.min([y_min_radius, x_min_radius]) new_frames, new_frame_dimensions = crop_symmetric_around_center(self.indices, self.frame_dimensions, center, max_radius) print(f'old frames frame_dimensions: {self.frame_dimensions}') print(f'new frames frame_dimensions: {new_frame_dimensions}') self.indices = new_frames self.counts = cp.zeros(self.indices.shape, dtype=cp.bool) self.counts[self.indices != cp.iinfo(self.indices.dtype).max] = 1 self.frame_dimensions = new_frame_dimensions
def pcm2float(sig, output_dtype=cp.float64): # Make sure it's a NumPy array. sig = cp.asnumpy(cp.asarray(sig)) # Check if it is an array of signed integers. assert sig.dtype.kind == 'i', "'sig' must be an array of signed integers!" # Set the array output format. Accepts string as input argument for the # desired output format (e.g. 'f'). out_dtype = cp.dtype(output_dtype) # Note that 'min' has a greater (by 1) absolute value than 'max'! # Therefore, we use 'min' here to avoid clipping. return sig.astype(out_dtype) / out_dtype.type(-cp.iinfo(sig.dtype).min)
def crop_symmetric_center(self, center, max_radius = None): if max_radius is None: y_min_radius = np.min([center[0], self.frame_dimensions[0] - center[0]]) x_min_radius = np.min([center[1], self.frame_dimensions[1] - center[1]]) max_radius = np.min([y_min_radius, x_min_radius]) new_frames, new_frame_dimensions = crop_symmetric_around_center(cp.array(self.indices), cp.array(self.frame_dimensions), center, max_radius) print(f'old frames frame_dimensions: {self.frame_dimensions}') print(f'new frames frame_dimensions: {new_frame_dimensions}') res = Sparse4DData() res.indices = new_frames res.counts = cp.zeros(self.indices.shape, dtype=cp.bool) res.counts[self.indices != cp.iinfo(self.indices.dtype).max] = 1 res.frame_dimensions = new_frame_dimensions res.scan_dimensions = self.scan_dimensions.copy() return res
def _find_boundaries_subpixel(label_img): """See ``find_boundaries(..., mode='subpixel')``. Notes ----- This function puts in an empty row and column between each *actual* row and column of the image, for a corresponding shape of ``2s - 1`` for every image dimension of size ``s``. These "interstitial" rows and columns are filled as ``True`` if they separate two labels in `label_img`, ``False`` otherwise. I used ``view_as_windows`` to get the neighborhood of each pixel. Then I check whether there are two labels or more in that neighborhood. """ ndim = label_img.ndim max_label = cp.iinfo(label_img.dtype).max label_img_expanded = cp.zeros( [(2 * s - 1) for s in label_img.shape], label_img.dtype ) pixels = (slice(None, None, 2),) * ndim label_img_expanded[pixels] = label_img edges = cp.ones(label_img_expanded.shape, dtype=bool) edges[pixels] = False label_img_expanded[edges] = max_label windows = view_as_windows( cp.pad(label_img_expanded, 1, mode="constant", constant_values=0), (3,) * ndim, ) boundaries = cp.zeros_like(edges) for index in np.ndindex(label_img_expanded.shape): if edges[index]: values = cp.unique(windows[index].ravel()) if len(values) > 2: # single value and max_label boundaries[index] = True return boundaries
def _find_boundaries_subpixel(label_img): """See ``find_boundaries(..., mode='subpixel')``. Notes ----- This function puts in an empty row and column between each *actual* row and column of the image, for a corresponding shape of ``2s - 1`` for every image dimension of size ``s``. These "interstitial" rows and columns are filled as ``True`` if they separate two labels in `label_img`, ``False`` otherwise. """ ndim = label_img.ndim max_label = cp.iinfo(label_img.dtype).max label_img_expanded = cp.full([(2 * s - 1) for s in label_img.shape], max_label, label_img.dtype) pixels = (slice(None, None, 2),) * ndim label_img_expanded[pixels] = label_img # CuPy Backend: TODO: Refactor all rank filtering below into a single # ElementwiseKernel that counts # of unique values. # at most 2**ndim non max_label pixels in a 3**ndim shape neighborhood max_possible_unique = 2 ** ndim # Count the number of unique values aside from max_label or # the background. n_unique = cp.zeros(label_img_expanded.shape, dtype=cp.uint8) rank_prev = ndi.minimum_filter(label_img_expanded, size=3) for n in range(1, max_possible_unique + 1): rank = ndi.rank_filter(label_img_expanded, n, size=3) n_unique += (rank != rank_prev) rank_prev = rank # Boundaries occur where there is more than 1 unique value return n_unique > 1
def test_tomaxint_tuple(self): x = self.generate((2, 3)) assert x.shape == (2, 3) assert (0 <= x).all() assert (x <= cupy.iinfo(cupy.int_).max).all()
def test_tomaxint_int(self): x = self.generate(3) assert x.shape == (3, ) assert (0 <= x).all() assert (x <= cupy.iinfo(cupy.int_).max).all()
def test_tomaxint_int(self): x = self.rs.tomaxint(3) self.assertEqual(x.shape, (3,)) self.assertTrue((0 <= x).all()) self.assertTrue((x <= cupy.iinfo(cupy.int_).max).all())
def peak_local_max( image, min_distance=1, threshold_abs=None, threshold_rel=None, exclude_border=True, indices=True, num_peaks=cp.inf, footprint=None, labels=None, num_peaks_per_label=cp.inf, p_norm=cp.inf, ): """Find peaks in an image as coordinate list or boolean mask. Peaks are the local maxima in a region of `2 * min_distance + 1` (i.e. peaks are separated by at least `min_distance`). If both `threshold_abs` and `threshold_rel` are provided, the maximum of the two is chosen as the minimum intensity threshold of peaks. .. versionchanged:: 0.18 Prior to version 0.18, peaks of the same height within a radius of `min_distance` were all returned, but this could cause unexpected behaviour. From 0.18 onwards, an arbitrary peak within the region is returned. See issue gh-2592. Parameters ---------- image : ndarray Input image. min_distance : int, optional The minimal allowed distance separating peaks. To find the maximum number of peaks, use `min_distance=1`. threshold_abs : float, optional Minimum intensity of peaks. By default, the absolute threshold is the minimum intensity of the image. threshold_rel : float, optional Minimum intensity of peaks, calculated as `max(image) * threshold_rel`. exclude_border : int, tuple of ints, or bool, optional If positive integer, `exclude_border` excludes peaks from within `exclude_border`-pixels of the border of the image. If tuple of non-negative ints, the length of the tuple must match the input array's dimensionality. Each element of the tuple will exclude peaks from within `exclude_border`-pixels of the border of the image along that dimension. If True, takes the `min_distance` parameter as value. If zero or False, peaks are identified regardless of their distance from the border. indices : bool, optional If True, the output will be an array representing peak coordinates. The coordinates are sorted according to peaks values (Larger first). If False, the output will be a boolean array shaped as `image.shape` with peaks present at True elements. ``indices`` is deprecated and will be removed in version 0.20. Default behavior will be to always return peak coordinates. You can obtain a mask as shown in the example below. num_peaks : int, optional Maximum number of peaks. When the number of peaks exceeds `num_peaks`, return `num_peaks` peaks based on highest peak intensity. footprint : ndarray of bools, optional If provided, `footprint == 1` represents the local region within which to search for peaks at every point in `image`. labels : ndarray of ints, optional If provided, each unique region `labels == value` represents a unique region to search for peaks. Zero is reserved for background. num_peaks_per_label : int, optional Maximum number of peaks for each label. p_norm : float Which Minkowski p-norm to use. Should be in the range [1, inf]. A finite large p may cause a ValueError if overflow can occur. ``inf`` corresponds to the Chebyshev distance and 2 to the Euclidean distance. Returns ------- output : ndarray or ndarray of bools * If `indices = True` : (row, column, ...) coordinates of peaks. * If `indices = False` : Boolean array shaped like `image`, with peaks represented by True values. Notes ----- The peak local maximum function returns the coordinates of local peaks (maxima) in an image. Internally, a maximum filter is used for finding local maxima. This operation dilates the original image. After comparison of the dilated and original image, this function returns the coordinates or a mask of the peaks where the dilated image equals the original image. See also -------- skimage.feature.corner_peaks Examples -------- >>> import cupy as cp >>> img1 = cp.zeros((7, 7)) >>> img1[3, 4] = 1 >>> img1[3, 2] = 1.5 >>> img1 array([[0. , 0. , 0. , 0. , 0. , 0. , 0. ], [0. , 0. , 0. , 0. , 0. , 0. , 0. ], [0. , 0. , 0. , 0. , 0. , 0. , 0. ], [0. , 0. , 1.5, 0. , 1. , 0. , 0. ], [0. , 0. , 0. , 0. , 0. , 0. , 0. ], [0. , 0. , 0. , 0. , 0. , 0. , 0. ], [0. , 0. , 0. , 0. , 0. , 0. , 0. ]]) >>> peak_local_max(img1, min_distance=1) array([[3, 2], [3, 4]]) >>> peak_local_max(img1, min_distance=2) array([[3, 2]]) >>> img2 = cp.zeros((20, 20, 20)) >>> img2[10, 10, 10] = 1 >>> img2[15, 15, 15] = 1 >>> peak_idx = peak_local_max(img2, exclude_border=0) >>> peak_idx array([[10, 10, 10], [15, 15, 15]]) >>> peak_mask = cp.zeros_like(img2, dtype=bool) >>> peak_mask[tuple(peak_idx.T)] = True >>> np.argwhere(peak_mask) array([[10, 10, 10], [15, 15, 15]]) """ if (footprint is None or footprint.size == 1) and min_distance < 1: warnings.warn( "When min_distance < 1, peak_local_max acts as finding " "image > max(threshold_abs, threshold_rel * max(image)).", RuntimeWarning, stacklevel=2, ) border_width = _get_excluded_border_width(image, min_distance, exclude_border) threshold = _get_threshold(image, threshold_abs, threshold_rel) if footprint is None: size = 2 * min_distance + 1 footprint = cp.ones((size, ) * image.ndim, dtype=bool) else: footprint = cp.asarray(footprint) if labels is None: # Non maximum filter mask = _get_peak_mask(image, footprint, threshold) mask = _exclude_border(mask, border_width) # Select highest intensities (num_peaks) coordinates = _get_high_intensity_peaks(image, mask, num_peaks, min_distance, p_norm) else: _labels = _exclude_border(labels.astype(int), border_width) if np.issubdtype(image.dtype, np.floating): bg_val = cp.finfo(image.dtype).min else: bg_val = cp.iinfo(image.dtype).min # For each label, extract a smaller image enclosing the object of # interest, identify num_peaks_per_label peaks labels_peak_coord = [] # For each label, extract a smaller image enclosing the object of # interest, identify num_peaks_per_label peaks and mark them in # variable out. # TODO: use GPU version of find_objects try: objects = cupyx_ndi.find_objects(_labels) except AttributeError: objects = cpu_find_objects(cp.asnumpy(_labels)) for label_idx, roi in enumerate(objects): if roi is None: continue # Get roi mask label_mask = labels[roi] == label_idx + 1 # Extract image roi img_object = image[roi] # Ensure masked values don't affect roi's local peaks img_object[np.logical_not(label_mask)] = bg_val mask = _get_peak_mask(img_object, footprint, threshold, label_mask) coordinates = _get_high_intensity_peaks(img_object, mask, num_peaks_per_label, min_distance, p_norm) # transform coordinates in global image indices space for idx, s in enumerate(roi): coordinates[:, idx] += s.start labels_peak_coord.append(coordinates) if labels_peak_coord: coordinates = cp.vstack(labels_peak_coord) else: coordinates = cp.empty((0, 2), dtype=int) if len(coordinates) > num_peaks: out = cp.zeros_like(image, dtype=np.bool_) out[tuple(coordinates.T)] = True coordinates = _get_high_intensity_peaks(image, out, num_peaks, min_distance, p_norm) if indices: return coordinates else: out = cp.zeros_like(image, dtype=np.bool_) out[tuple(coordinates.T)] = True return out
def test_tomaxint_tuple(self): x = self.generate((2, 3)) self.assertEqual(x.shape, (2, 3)) self.assertTrue((0 <= x).all()) self.assertTrue((x <= cupy.iinfo(cupy.int_).max).all())
# (some of them are platform specific). For the details, see: # http://www.unix.org/whitepapers/64bit.html _integer_types = ( cp.byte, cp.ubyte, # 8 bits cp.short, cp.ushort, # 16 bits cp.intc, cp.uintc, # 16 or 32 or 64 bits int, cp.int_, cp.uint, # 32 or 64 bits cp.longlong, cp.ulonglong) # 64 bits _integer_ranges = { t: (cp.iinfo(t).min, cp.iinfo(t).max) for t in _integer_types } dtype_range = { bool: (False, True), cp.bool_: (False, True), cp.bool8: (False, True), float: (-1, 1), cp.float_: (-1, 1), cp.float16: (-1, 1), cp.float32: (-1, 1), cp.float64: (-1, 1) } dtype_range.update(_integer_ranges) _supported_types = list(dtype_range.keys())
def relabel_sequential(label_field, offset=1): """Relabel arbitrary labels to {`offset`, ... `offset` + number_of_labels}. This function also returns the forward map (mapping the original labels to the reduced labels) and the inverse map (mapping the reduced labels back to the original ones). Parameters ---------- label_field : numpy array of int, arbitrary shape An array of labels, which must be non-negative integers. offset : int, optional The return labels will start at `offset`, which should be strictly positive. Returns ------- relabeled : numpy array of int, same shape as `label_field` The input label field with labels mapped to {offset, ..., number_of_labels + offset - 1}. The data type will be the same as `label_field`, except when offset + number_of_labels causes overflow of the current data type. forward_map : ArrayMap The map from the original label space to the returned label space. Can be used to re-apply the same mapping. See examples for usage. The output data type will be the same as `relabeled`. inverse_map : ArrayMap The map from the new label space to the original space. This can be used to reconstruct the original label field from the relabeled one. The output data type will be the same as `label_field`. Notes ----- The label 0 is assumed to denote the background and is never remapped. The forward map can be extremely big for some inputs, since its length is given by the maximum of the label field. However, in most situations, ``label_field.max()`` is much smaller than ``label_field.size``, and in these cases the forward map is guaranteed to be smaller than either the input or output images. Examples -------- >>> import cupy as cp >>> from skimage.segmentation import relabel_sequential >>> label_field = cp.asarray([1, 1, 5, 5, 8, 99, 42]) >>> relab, fw, inv = relabel_sequential(label_field) >>> relab array([1, 1, 2, 2, 3, 5, 4]) >>> print(fw) ArrayMap: 1 → 1 5 → 2 8 → 3 42 → 4 99 → 5 >>> cp.asarray(fw) array([0, 1, 0, 0, 0, 2, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5]) >>> cp.asarray(inv) array([ 0, 1, 5, 8, 42, 99]) >>> (fw[label_field] == relab).all() True >>> (inv[relab] == label_field).all() True >>> relab, fw, inv = relabel_sequential(label_field, offset=5) >>> relab array([5, 5, 6, 6, 7, 9, 8]) """ if offset <= 0: raise ValueError("Offset must be strictly positive.") if label_field.min() < 0: raise ValueError("Cannot relabel array that contains negative values.") offset = int(offset) in_vals = cp.unique(label_field) if len(in_vals) > cp.iinfo(cp.int32).max: raise ValueError( "Too many unique values in label_field (current implementation " "uses 32-bit indexing).") out_val_dtype = cp.min_scalar_type(offset + len(in_vals)) if int(in_vals[0]) == 0: # always map 0 to 0 out_vals = cp.concatenate([ cp.asarray([0], dtype=out_val_dtype), cp.arange(offset, offset + len(in_vals) - 1, dtype=out_val_dtype), ]) else: out_vals = cp.arange(offset, offset + len(in_vals), dtype=out_val_dtype) input_type = label_field.dtype if input_type.kind not in "iu": raise TypeError("label_field must have an integer dtype") # Some logic to determine the output type: # - we don't want to return a smaller output type than the input type, # ie if we get uint32 as labels input, don't return a uint8 array. # - but, in some cases, using the input type could result in overflow. The # input type could be a signed integer (e.g. int32) but # `np.min_scalar_type` will always return an unsigned type. We check for # that by casting the largest output value to the input type. If it is # unchanged, we use the input type, else we use the unsigned minimum # required type out_max = int(out_vals[-1]) required_type = cp.min_scalar_type(out_max) if input_type.itemsize < required_type.itemsize: output_type = required_type else: if out_max <= cp.iinfo(input_type).max: output_type = input_type else: output_type = required_type out_array = cp.empty(label_field.shape, dtype=output_type) out_vals = out_vals.astype(output_type, copy=False) map_array(label_field, in_vals, out_vals, out=out_array) fw_map = ArrayMap(in_vals, out_vals) inv_map = ArrayMap(out_vals, in_vals) return out_array, fw_map, inv_map
def find_boundaries(label_img, connectivity=1, mode="thick", background=0): """Return bool array where boundaries between labeled regions are True. Parameters ---------- label_img : array of int or bool An array in which different regions are labeled with either different integers or boolean values. connectivity : int in {1, ..., `label_img.ndim`}, optional A pixel is considered a boundary pixel if any of its neighbors has a different label. `connectivity` controls which pixels are considered neighbors. A connectivity of 1 (default) means pixels sharing an edge (in 2D) or a face (in 3D) will be considered neighbors. A connectivity of `label_img.ndim` means pixels sharing a corner will be considered neighbors. mode : string in {'thick', 'inner', 'outer', 'subpixel'} How to mark the boundaries: - thick: any pixel not completely surrounded by pixels of the same label (defined by `connectivity`) is marked as a boundary. This results in boundaries that are 2 pixels thick. - inner: outline the pixels *just inside* of objects, leaving background pixels untouched. - outer: outline pixels in the background around object boundaries. When two objects touch, their boundary is also marked. - subpixel: return a doubled image, with pixels *between* the original pixels marked as boundary where appropriate. background : int, optional For modes 'inner' and 'outer', a definition of a background label is required. See `mode` for descriptions of these two. Returns ------- boundaries : array of bool, same shape as `label_img` A bool image where ``True`` represents a boundary pixel. For `mode` equal to 'subpixel', ``boundaries.shape[i]`` is equal to ``2 * label_img.shape[i] - 1`` for all ``i`` (a pixel is inserted in between all other pairs of pixels). Examples -------- >>> labels = cp.array([[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], ... [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], ... [0, 0, 0, 0, 0, 5, 5, 5, 0, 0], ... [0, 0, 1, 1, 1, 5, 5, 5, 0, 0], ... [0, 0, 1, 1, 1, 5, 5, 5, 0, 0], ... [0, 0, 1, 1, 1, 5, 5, 5, 0, 0], ... [0, 0, 0, 0, 0, 5, 5, 5, 0, 0], ... [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], ... [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], dtype=cp.uint8) >>> find_boundaries(labels, mode='thick').astype(cp.uint8) array([[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 1, 1, 1, 0, 0], [0, 0, 1, 1, 1, 1, 1, 1, 1, 0], [0, 1, 1, 1, 1, 1, 0, 1, 1, 0], [0, 1, 1, 0, 1, 1, 0, 1, 1, 0], [0, 1, 1, 1, 1, 1, 0, 1, 1, 0], [0, 0, 1, 1, 1, 1, 1, 1, 1, 0], [0, 0, 0, 0, 0, 1, 1, 1, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], dtype=uint8) >>> find_boundaries(labels, mode='inner').astype(cp.uint8) array([[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 1, 1, 1, 0, 0], [0, 0, 1, 1, 1, 1, 0, 1, 0, 0], [0, 0, 1, 0, 1, 1, 0, 1, 0, 0], [0, 0, 1, 1, 1, 1, 0, 1, 0, 0], [0, 0, 0, 0, 0, 1, 1, 1, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], dtype=uint8) >>> find_boundaries(labels, mode='outer').astype(cp.uint8) array([[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 1, 1, 1, 0, 0], [0, 0, 1, 1, 1, 1, 0, 0, 1, 0], [0, 1, 0, 0, 1, 1, 0, 0, 1, 0], [0, 1, 0, 0, 1, 1, 0, 0, 1, 0], [0, 1, 0, 0, 1, 1, 0, 0, 1, 0], [0, 0, 1, 1, 1, 1, 0, 0, 1, 0], [0, 0, 0, 0, 0, 1, 1, 1, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], dtype=uint8) >>> labels_small = labels[::2, ::3] >>> labels_small array([[0, 0, 0, 0], [0, 0, 5, 0], [0, 1, 5, 0], [0, 0, 5, 0], [0, 0, 0, 0]], dtype=uint8) >>> find_boundaries(labels_small, mode='subpixel').astype(cp.uint8) array([[0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 1, 1, 1, 0], [0, 0, 0, 1, 0, 1, 0], [0, 1, 1, 1, 0, 1, 0], [0, 1, 0, 1, 0, 1, 0], [0, 1, 1, 1, 0, 1, 0], [0, 0, 0, 1, 0, 1, 0], [0, 0, 0, 1, 1, 1, 0], [0, 0, 0, 0, 0, 0, 0]], dtype=uint8) >>> bool_image = cp.array([[False, False, False, False, False], ... [False, False, False, False, False], ... [False, False, True, True, True], ... [False, False, True, True, True], ... [False, False, True, True, True]], ... dtype=cp.bool) >>> find_boundaries(bool_image) array([[False, False, False, False, False], [False, False, True, True, True], [False, True, True, True, True], [False, True, True, False, False], [False, True, True, False, False]]) """ if label_img.dtype == 'bool': label_img = label_img.astype(cp.uint8) ndim = label_img.ndim selem = ndi.generate_binary_structure(ndim, connectivity) if mode != 'subpixel': boundaries = dilation(label_img, selem) != erosion(label_img, selem) if mode == 'inner': foreground_image = label_img != background boundaries &= foreground_image elif mode == 'outer': max_label = cp.iinfo(label_img.dtype).max background_image = label_img == background selem = ndi.generate_binary_structure(ndim, ndim) inverted_background = cp.array(label_img, copy=True) inverted_background[background_image] = max_label adjacent_objects = ((dilation(label_img, selem) != erosion(inverted_background, selem)) & ~background_image) boundaries &= (background_image | adjacent_objects) return boundaries else: boundaries = _find_boundaries_subpixel(label_img) return boundaries
def test_very_large_labels(): imax = cp.iinfo(cp.int64).max labels = cp.asarray([0, 1, imax, 42, 42], dtype=cp.int64) output, fw, inv = relabel_sequential(labels, offset=imax) assert int(cp.max(output)) == imax + 2
def ravel_multi_index(multi_index, dims, mode='wrap', order='C'): """ Converts a tuple of index arrays into an array of flat indices, applying boundary modes to the multi-index. Args: multi_index (tuple of cupy.ndarray) : A tuple of integer arrays, one array for each dimension. dims (tuple of ints): The shape of array into which the indices from ``multi_index`` apply. mode ('raise', 'wrap' or 'clip'), optional: Specifies how out-of-bounds indices are handled. Can specify either one mode or a tuple of modes, one mode per index: - *'raise'* -- raise an error - *'wrap'* -- wrap around (default) - *'clip'* -- clip to the range In 'clip' mode, a negative index which would normally wrap will clip to 0 instead. order ('C' or 'F'), optional: Determines whether the multi-index should be viewed as indexing in row-major (C-style) or column-major (Fortran-style) order. Returns: raveled_indices (cupy.ndarray): An array of indices into the flattened version of an array of dimensions ``dims``. .. warning:: This function may synchronize the device when ``mode == 'raise'``. Notes ----- Note that the default `mode` (``'wrap'``) is different than in NumPy. This is done to avoid potential device synchronization. Examples -------- >>> cupy.ravel_multi_index(cupy.asarray([[3,6,6],[4,5,1]]), (7,6)) array([22, 41, 37]) >>> cupy.ravel_multi_index(cupy.asarray([[3,6,6],[4,5,1]]), (7,6), ... order='F') array([31, 41, 13]) >>> cupy.ravel_multi_index(cupy.asarray([[3,6,6],[4,5,1]]), (4,6), ... mode='clip') array([22, 23, 19]) >>> cupy.ravel_multi_index(cupy.asarray([[3,6,6],[4,5,1]]), (4,4), ... mode=('clip', 'wrap')) array([12, 13, 13]) >>> cupy.ravel_multi_index(cupy.asarray((3,1,4,1)), (6,7,8,9)) array(1621) .. seealso:: :func:`numpy.ravel_multi_index`, :func:`unravel_index` """ ndim = len(dims) if len(multi_index) != ndim: raise ValueError( "parameter multi_index must be a sequence of " "length {}".format(ndim)) for d in dims: if not isinstance(d, numbers.Integral): raise TypeError( "{} object cannot be interpreted as an integer".format( type(d))) if isinstance(mode, str): mode = (mode, ) * ndim if functools.reduce(operator.mul, dims) > cupy.iinfo(cupy.int64).max: raise ValueError("invalid dims: array size defined by dims is larger " "than the maximum possible size") s = 1 ravel_strides = [1] * ndim if order is None: order = "C" if order == "C": for i in range(ndim - 2, -1, -1): s = s * dims[i + 1] ravel_strides[i] = s elif order == "F": for i in range(1, ndim): s = s * dims[i - 1] ravel_strides[i] = s else: raise TypeError("order not understood") multi_index = cupy.broadcast_arrays(*multi_index) raveled_indices = cupy.zeros(multi_index[0].shape, dtype=cupy.int64) for d, stride, idx, _mode in zip(dims, ravel_strides, multi_index, mode): if not isinstance(idx, cupy.ndarray): raise TypeError("elements of multi_index must be cupy arrays") if not cupy.can_cast(idx, cupy.int64, 'same_kind'): raise TypeError( 'multi_index entries could not be cast from dtype(\'{}\') to ' 'dtype(\'{}\') according to the rule \'same_kind\''.format( idx.dtype, cupy.int64().dtype)) idx = idx.astype(cupy.int64, copy=False) if _mode == "raise": if cupy.any(cupy.logical_or(idx >= d, idx < 0)): raise ValueError("invalid entry in coordinates array") elif _mode == "clip": idx = cupy.clip(idx, 0, d - 1) elif _mode == 'wrap': idx = idx % d else: raise TypeError("Unrecognized mode: {}".format(_mode)) raveled_indices += stride * idx return raveled_indices
def _convert(image, dtype, force_copy=False, uniform=False): """ Convert an image to the requested data-type. Warnings are issued in case of precision loss, or when negative values are clipped during conversion to unsigned integer types (sign loss). Floating point values are expected to be normalized and will be clipped to the range [0.0, 1.0] or [-1.0, 1.0] when converting to unsigned or signed integers respectively. Numbers are not shifted to the negative side when converting from unsigned to signed integer types. Negative values will be clipped when converting to unsigned integers. Parameters ---------- image : ndarray Input image. dtype : dtype Target data-type. force_copy : bool, optional Force a copy of the data, irrespective of its current dtype. uniform : bool, optional Uniformly quantize the floating point range to the integer range. By default (uniform=False) floating point values are scaled and rounded to the nearest integers, which minimizes back and forth conversion errors. .. versionchanged :: 0.15 ``_convert`` no longer warns about possible precision or sign information loss. See discussions on these warnings at: https://github.com/scikit-image/scikit-image/issues/2602 https://github.com/scikit-image/scikit-image/issues/543#issuecomment-208202228 https://github.com/scikit-image/scikit-image/pull/3575 References ---------- .. [1] DirectX data conversion rules. https://msdn.microsoft.com/en-us/library/windows/desktop/dd607323%28v=vs.85%29.aspx .. [2] Data Conversions. In "OpenGL ES 2.0 Specification v2.0.25", pp 7-8. Khronos Group, 2010. .. [3] Proper treatment of pixels as integers. A.W. Paeth. In "Graphics Gems I", pp 249-256. Morgan Kaufmann, 1990. .. [4] Dirty Pixels. J. Blinn. In "Jim Blinn's corner: Dirty Pixels", pp 47-57. Morgan Kaufmann, 1998. """ dtypeobj_in = image.dtype if dtype is cp.floating: dtypeobj_out = cp.dtype("float64") else: dtypeobj_out = cp.dtype(dtype) dtype_in = dtypeobj_in.type dtype_out = dtypeobj_out.type kind_in = dtypeobj_in.kind kind_out = dtypeobj_out.kind itemsize_in = dtypeobj_in.itemsize itemsize_out = dtypeobj_out.itemsize # Below, we do an `issubdtype` check. Its purpose is to find out # whether we can get away without doing any image conversion. This happens # when: # # - the output and input dtypes are the same or # - when the output is specified as a type, and the input dtype # is a subclass of that type (e.g. `cp.floating` will allow # `float32` and `float64` arrays through) if cp.issubdtype(dtype_in, cp.obj2sctype(dtype)): if force_copy: image = image.copy() return image if not (dtype_in in _supported_types and dtype_out in _supported_types): raise ValueError("Can not convert from {} to {}.".format( dtypeobj_in, dtypeobj_out)) if kind_in in 'ui': imin_in = cp.iinfo(dtype_in).min imax_in = cp.iinfo(dtype_in).max if kind_out in 'ui': imin_out = cp.iinfo(dtype_out).min imax_out = cp.iinfo(dtype_out).max # any -> binary if kind_out == 'b': return image > dtype_in(dtype_range[dtype_in][1] / 2) # binary -> any if kind_in == 'b': result = image.astype(dtype_out) if kind_out != 'f': result *= dtype_out(dtype_range[dtype_out][1]) return result # float -> any if kind_in == 'f': if kind_out == 'f': # float -> float return image.astype(dtype_out) if cp.min(image) < -1.0 or cp.max(image) > 1.0: raise ValueError("Images of type float must be between -1 and 1.") # floating point -> integer # use float type that can represent output integer type computation_type = _dtype_itemsize(itemsize_out, dtype_in, cp.float32, cp.float64) if not uniform: if kind_out == 'u': image_out = cp.multiply(image, imax_out, dtype=computation_type) else: image_out = cp.multiply(image, (imax_out - imin_out) / 2, dtype=computation_type) image_out -= 1.0 / 2.0 cp.rint(image_out, out=image_out) cp.clip(image_out, imin_out, imax_out, out=image_out) elif kind_out == 'u': image_out = cp.multiply(image, imax_out + 1, dtype=computation_type) cp.clip(image_out, 0, imax_out, out=image_out) else: image_out = cp.multiply(image, (imax_out - imin_out + 1.0) / 2.0, dtype=computation_type) cp.floor(image_out, out=image_out) cp.clip(image_out, imin_out, imax_out, out=image_out) return image_out.astype(dtype_out) # signed/unsigned int -> float if kind_out == 'f': # use float type that can exactly represent input integers computation_type = _dtype_itemsize(itemsize_in, dtype_out, cp.float32, cp.float64) if kind_in == 'u': # using cp.divide or cp.multiply doesn't copy the data # until the computation time image = cp.multiply(image, 1. / imax_in, dtype=computation_type) # DirectX uses this conversion also for signed ints # if imin_in: # cp.maximum(image, -1.0, out=image) else: image = cp.add(image, 0.5, dtype=computation_type) image *= 2 / (imax_in - imin_in) return image.astype(dtype_out, copy=False) # unsigned int -> signed/unsigned int if kind_in == 'u': if kind_out == 'i': # unsigned int -> signed int image = _scale(image, 8 * itemsize_in, 8 * itemsize_out - 1) return image.view(dtype_out) else: # unsigned int -> unsigned int return _scale(image, 8 * itemsize_in, 8 * itemsize_out) # signed int -> unsigned int if kind_out == 'u': image = _scale(image, 8 * itemsize_in - 1, 8 * itemsize_out) result = cp.empty(image.shape, dtype_out) cp.maximum(image, 0, out=result, dtype=image.dtype, casting='unsafe') return result # signed int -> signed int if itemsize_in > itemsize_out: return _scale(image, 8 * itemsize_in - 1, 8 * itemsize_out - 1) image = image.astype(_dtype_bits('i', itemsize_out * 8)) image -= imin_in image = _scale(image, 8 * itemsize_in, 8 * itemsize_out, copy=False) image += imin_out return image.astype(dtype_out)