def _safely_castable_to_int(dt): """Test whether the NumPy data type `dt` can be safely cast to an int.""" int_size = cupy.dtype(int).itemsize safe = ( cupy.issubdtype(dt, cupy.signedinteger) and dt.itemsize <= int_size ) or (cupy.issubdtype(dt, cupy.unsignedinteger) and dt.itemsize < int_size) return safe
def test_column_dtypes_correct(): msg = "mismatch with expected type," region = regionprops(SAMPLE, intensity_image=INTENSITY_SAMPLE)[0] for col in COL_DTYPES: r = region[col] if col in OBJECT_COLUMNS: assert COL_DTYPES[col] == object continue # TODO: grlee77: check desired types for returned. # e.g. currently inertia_tensor_eigvals returns a list of 0-dim # arrays if isinstance(r, (tuple, list)): r0 = r[0] if isinstance(r0, cp.ndarray) and r0.ndim == 0: r0 = r0.item() t = type(r0) elif cp.isscalar(r): t = type(r) else: t = type(r.ravel()[0].item()) if cp.issubdtype(t, cp.floating): assert (COL_DTYPES[col] == float ), f"{col} dtype {t} {msg} {COL_DTYPES[col]}" elif cp.issubdtype(t, cp.integer): assert (COL_DTYPES[col] == int ), f"{col} dtype {t} {msg} {COL_DTYPES[col]}" else: assert False, f"{col} dtype {t} {msg} {COL_DTYPES[col]}"
def setUp(self): if cupy.issubdtype(self.dtype, cupy.unsignedinteger): self.array = cupy.random.randint(0, 10, size=(2, 3)).astype(self.dtype) elif cupy.issubdtype(self.dtype, cupy.integer): self.array = cupy.random.randint(-10, 10, size=(2, 3)).astype(self.dtype) elif cupy.issubdtype(self.dtype, cupy.floating): self.array = cupy.random.rand(2, 3).astype(self.dtype)
def _create_texture_object(data, address_mode: str, filter_mode: str, read_mode: str, border_color=0): if cupy.issubdtype(data.dtype, cupy.unsignedinteger): fmt_kind = runtime.cudaChannelFormatKindUnsigned elif cupy.issubdtype(data.dtype, cupy.integer): fmt_kind = runtime.cudaChannelFormatKindSigned elif cupy.issubdtype(data.dtype, cupy.floating): fmt_kind = runtime.cudaChannelFormatKindFloat else: raise ValueError(f'Unsupported data type {data.dtype}') if address_mode == 'nearest': address_mode = runtime.cudaAddressModeClamp elif address_mode == 'constant': address_mode = runtime.cudaAddressModeBorder else: raise ValueError( f'Unsupported address mode {address_mode} ' '(supported: constant, nearest)') if filter_mode == 'nearest': filter_mode = runtime.cudaFilterModePoint elif filter_mode == 'linear': filter_mode = runtime.cudaFilterModeLinear else: raise ValueError( f'Unsupported filter mode {filter_mode} ' f'(supported: nearest, linear)') if read_mode == 'element_type': read_mode = runtime.cudaReadModeElementType elif read_mode == 'normalized_float': read_mode = runtime.cudaReadModeNormalizedFloat else: raise ValueError( f'Unsupported read mode {read_mode} ' '(supported: element_type, normalized_float)') texture_fmt = texture.ChannelFormatDescriptor( data.itemsize * 8, 0, 0, 0, fmt_kind) # CUDAArray: last dimension is the fastest changing dimension array = texture.CUDAarray(texture_fmt, *data.shape[::-1]) res_desc = texture.ResourceDescriptor( runtime.cudaResourceTypeArray, cuArr=array) # TODO(the-lay): each dimension can have a different addressing mode # TODO(the-lay): border color/value can be defined for up to 4 channels tex_desc = texture.TextureDescriptor( (address_mode, ) * data.ndim, filter_mode, read_mode, borderColors=(border_color, )) tex_obj = texture.TextureObject(res_desc, tex_desc) array.copy_from(data) return tex_obj
def _gen_array(dtype): if cupy.issubdtype(dtype, cupy.unsignedinteger): array = cupy.random.randint(0, 10, size=(2, 3)).astype(dtype) elif cupy.issubdtype(dtype, cupy.integer): array = cupy.random.randint(-10, 10, size=(2, 3)).astype(dtype) elif cupy.issubdtype(dtype, cupy.floating): array = cupy.random.rand(2, 3).astype(dtype) elif cupy.issubdtype(dtype, cupy.complexfloating): array = cupy.random.random((2, 3)).astype(dtype) else: assert False, f'unrecognized dtype: {dtype}' return array
def test_conversion(self, dtype): self.dtype = dtype if cupy.issubdtype(self.dtype, cupy.unsignedinteger): self.array = cupy.random.randint( 0, 10, size=(2, 3)).astype(self.dtype) elif cupy.issubdtype(self.dtype, cupy.integer): self.array = cupy.random.randint( -10, 10, size=(2, 3)).astype(self.dtype) elif cupy.issubdtype(self.dtype, cupy.floating): self.array = cupy.random.rand( 2, 3).astype(self.dtype) tensor = self.array.toDlpack() array = cupy.fromDlpack(tensor) testing.assert_array_equal(self.array, array) testing.assert_array_equal(self.array.data.ptr, array.data.ptr)
def conj(self, copy=True): if cupy.issubdtype(self.dtype, cupy.complexfloating): return self._with_data(self.data.conj(), copy=copy) elif copy: return self.copy() else: return self
def _linspace_scalar(start, stop, num=50, endpoint=True, retstep=False, dtype=None): """Returns an array with evenly-spaced values within a given interval. Instead of specifying the step width like :func:`cupy.arange`, this function requires the total number of elements specified. Args: start: Start of the interval. stop: End of the interval. num: Number of elements. endpoint (bool): If ``True``, the stop value is included as the last element. Otherwise, the stop value is omitted. retstep (bool): If ``True``, this function returns (array, step). Otherwise, it returns only the array. dtype: Data type specifier. It is inferred from the start and stop arguments by default. Returns: cupy.ndarray: The 1-D array of ranged values. """ dt = cupy.result_type(start, stop, float(num)) if dtype is None: # In actual implementation, only float is used dtype = dt ret = cupy.empty((num, ), dtype=dt) div = (num - 1) if endpoint else num if div <= 0: if num > 0: ret.fill(start) step = float('nan') else: step = float(stop - start) / div stop = float(stop) if step == 0.0: # for underflow _linspace_ufunc_underflow(start, stop - start, div, ret) else: _linspace_ufunc(start, step, ret) if endpoint: # Here num == div + 1 > 1 is ensured. ret[-1] = stop if cupy.issubdtype(dtype, cupy.integer): cupy.floor(ret, out=ret) ret = ret.astype(dtype, copy=False) if retstep: return ret, step else: return ret
def _safe_accumulator_op(op, x, *args, **kwargs): """ This function provides numpy accumulator functions with a float64 dtype when used on a floating point input. This prevents accumulator overflow on smaller floating point dtypes. Parameters ---------- op : function A cupy accumulator function such as cp.mean or cp.sum x : cupy array A numpy array to apply the accumulator function *args : positional arguments Positional arguments passed to the accumulator function after the input x **kwargs : keyword arguments Keyword arguments passed to the accumulator function Returns ------- result : The output of the accumulator function passed to this function """ if cp.issubdtype(x.dtype, cp.floating) and x.dtype.itemsize < 8: result = op(x, *args, **kwargs, dtype=cp.float64).astype(cp.float32) else: result = op(x, *args, **kwargs) return result
def black_tophat(image, selem=None, out=None): """Return black top hat of an image. The black top hat of an image is defined as its morphological closing minus the original image. This operation returns the dark spots of the image that are smaller than the structuring element. Note that dark spots in the original image are bright spots after the black top hat. Parameters ---------- image : ndarray Image array. selem : ndarray, optional The neighborhood expressed as a 2-D array of 1's and 0's. If None, use cross-shaped structuring element (connectivity=1). out : ndarray, optional The array to store the result of the morphology. If None is passed, a new array will be allocated. Returns ------- out : array, same shape and type as `image` The result of the morphological black top hat. See also -------- white_tophat References ---------- .. [1] https://en.wikipedia.org/wiki/Top-hat_transform Examples -------- >>> # Change dark peak to bright peak and subtract background >>> import cupy as cp >>> from cucim.skimage.morphology import square >>> dark_on_grey = cp.asarray([[7, 6, 6, 6, 7], ... [6, 5, 4, 5, 6], ... [6, 4, 0, 4, 6], ... [6, 5, 4, 5, 6], ... [7, 6, 6, 6, 7]], dtype=cp.uint8) >>> black_tophat(dark_on_grey, square(3)) array([[0, 0, 0, 0, 0], [0, 0, 1, 0, 0], [0, 1, 5, 1, 0], [0, 0, 1, 0, 0], [0, 0, 0, 0, 0]], dtype=uint8) """ if out is image: original = image.copy() else: original = image out = closing(image, selem, out=out) if cp.issubdtype(out.dtype, bool): cp.logical_xor(out, original, out=out) else: out -= original return out
def test_partition_unsupported_dtype(self, dtype): if self.length != 10 and not cupy.issubdtype(dtype, complex): return a = testing.shaped_random((self.length, ), cupy, dtype) kth = 2 with self.assertRaises(NotImplementedError): return self.partition(a, kth)
def cpasarray(a, dtype=None): if dtype is None: if isinstance(a, cp.ndarray): is_int = cp.issubdtype(a.dtype, cp.integer) elif isinstance(a, np.ndarray): is_int = np.issubdtype(a.dtype, np.integer) elif isinstance(a, Sequence): _a = a[0] while isinstance(_a, Sequence): _a = _a[0] is_int = type(_a) == int \ or cp.issubdtype(_a.dtype, cp.integer) \ or np.issubdtype(_a.dtype, np.integer) else: raise ValueError(f"Cannot deal with type {type(a)}") dtype = cpi if is_int else cpf return cp.asarray(a, dtype)
def _get_bin_edges(a, bins, range): """ Computes the bins used internally by `histogram`. Args: a (ndarray): Ravelled data array bins (int or ndarray): Forwarded argument from `histogram`. range (None or tuple): Forwarded argument from `histogram`. Returns: bin_edges (ndarray): Array of bin edges """ # parse the overloaded bins argument n_equal_bins = None bin_edges = None if isinstance(bins, str): raise NotImplementedError( 'only integer and array bins are implemented') elif isinstance(bins, cupy.ndarray) or numpy.ndim(bins) == 1: # TODO(okuta): After #3060 is merged, `if cupy.ndim(bins) == 1:`. if isinstance(bins, cupy.ndarray): bin_edges = bins else: bin_edges = numpy.asarray(bins) if (bin_edges[:-1] > bin_edges[1:]).any(): # synchronize! when CuPy raise ValueError( '`bins` must increase monotonically, when an array') if isinstance(bin_edges, numpy.ndarray): bin_edges = cupy.asarray(bin_edges) elif numpy.ndim(bins) == 0: try: n_equal_bins = operator.index(bins) except TypeError: raise TypeError('`bins` must be an integer, a string, or an array') if n_equal_bins < 1: raise ValueError('`bins` must be positive, when an integer') first_edge, last_edge = _get_outer_edges(a, range) else: raise ValueError('`bins` must be 1d, when an array') if n_equal_bins is not None: # numpy's gh-10322 means that type resolution rules are dependent on # array shapes. To avoid this causing problems, we pick a type now and # stick with it throughout. bin_type = cupy.result_type(first_edge, last_edge, a) if cupy.issubdtype(bin_type, cupy.integer): bin_type = cupy.result_type(bin_type, float) # bin edges must be computed bin_edges = cupy.linspace(first_edge, last_edge, n_equal_bins + 1, endpoint=True, dtype=bin_type) return bin_edges
def test_uint_image(): img = cp.random.randint(0, 255, (10, 10), dtype=cp.uint8) labels = cp.zeros((10, 10), dtype=cp.int64) labels[1:3, 1:3] = 1 labels[6:9, 6:9] = 2 output = label2rgb(labels, image=img, bg_label=0) # Make sure that the output is made of floats and in the correct range assert cp.issubdtype(output.dtype, cp.floating) assert output.max() <= 1
def ix_(*args): """Construct an open mesh from multiple sequences. This function takes N 1-D sequences and returns N outputs with N dimensions each, such that the shape is 1 in all but one dimension and the dimension with the non-unit shape value cycles through all N dimensions. Using `ix_` one can quickly construct index arrays that will index the cross product. ``a[cupy.ix_([1,3],[2,5])]`` returns the array ``[[a[1,2] a[1,5]], [a[3,2] a[3,5]]]``. Args: *args: 1-D sequences Returns: tuple of ndarrays: N arrays with N dimensions each, with N the number of input sequences. Together these arrays form an open mesh. Examples -------- >>> a = cupy.arange(10).reshape(2, 5) >>> a array([[0, 1, 2, 3, 4], [5, 6, 7, 8, 9]]) >>> ixgrid = cupy.ix_([0,1], [2,4]) >>> ixgrid (array([[0], [1]]), array([[2, 4]])) .. warning:: This function may synchronize the device. .. seealso:: :func:`numpy.ix_` """ # TODO(niboshi): Avoid nonzero which may synchronize the device. out = [] nd = len(args) for k, new in enumerate(args): new = from_data.asarray(new) if new.ndim != 1: raise ValueError('Cross index must be 1 dimensional') if new.size == 0: # Explicitly type empty arrays to avoid float default new = new.astype(numpy.intp) if cupy.issubdtype(new.dtype, cupy.bool_): new, = new.nonzero() # may synchronize new = new.reshape((1,) * k + (new.size,) + (1,) * (nd - k - 1)) out.append(new) return tuple(out)
def map_array(input_arr, input_vals, output_vals, out=None): """Map values from input array from input_vals to output_vals. Parameters ---------- input_arr : array of int, shape (M[, N][, P][, ...]) The input label image. input_vals : array of int, shape (N,) The values to map from. output_vals : array, shape (N,) The values to map to. out: array, same shape as `input_arr` The output array. Will be created if not provided. It should have the same dtype as `output_vals`. Returns ------- out : array, same shape as `input_arr` The array of mapped values. """ if not cp.issubdtype(input_arr.dtype, cp.integer): raise TypeError( "The dtype of an array to be remapped should be integer.") # We ravel the input array for simplicity of iteration in Cython: orig_shape = input_arr.shape # NumPy docs for `np.ravel()` says: # "When a view is desired in as many cases as possible, # arr.reshape(-1) may be preferable." input_arr = input_arr.reshape(-1) if out is None: out = cp.empty(orig_shape, dtype=output_vals.dtype) elif out.shape != orig_shape: raise ValueError( "If out array is provided, it should have the same shape as " f"the input array. Input array has shape {orig_shape}, provided " f"output array has shape {out.shape}.") try: out_view = out.view() out_view.shape = (-1, ) # no-copy reshape/ravel except AttributeError: # if out strides are not compatible with 0-copy raise ValueError( "If out array is provided, it should be either contiguous " f"or 1-dimensional. Got array with shape {out.shape} and " f"strides {out.strides}.") # ensure all arrays have matching types before sending to Cython input_vals = input_vals.astype(input_arr.dtype, copy=False) output_vals = output_vals.astype(out.dtype, copy=False) _map_array(input_arr, input_vals, output_vals, input_vals.size, out_view) return out
def _is_empty_column_selection(column): """ Return True if the column selection is empty (empty list or all-False boolean array). """ if hasattr(column, 'dtype') and np.issubdtype(column.dtype, np.bool_): return not column.any() elif hasattr(column, '__len__'): return (len(column) == 0 or all(isinstance(col, bool) for col in column) and not any(column)) else: return False
def validateaxis(axis): if axis is not None: axis_type = type(axis) if axis_type == tuple: raise TypeError('Tuples are not accepted for the \'axis\' ' 'parameter. Please pass in one of the ' 'following: {-2, -1, 0, 1, None}.') if not cupy.issubdtype(cupy.dtype(axis_type), cupy.integer): raise TypeError('axis must be an integer, not {name}'.format( name=axis_type.__name__)) if not (-2 <= axis <= 1): raise ValueError('axis out of range')
def ix_(*args): """Construct an open mesh from multiple sequences. This function takes N 1-D sequences and returns N outputs with N dimensions each, such that the shape is 1 in all but one dimension and the dimension with the non-unit shape value cycles through all N dimensions. Using `ix_` one can quickly construct index arrays that will index the cross product. ``a[cupy.ix_([1,3],[2,5])]`` returns the array ``[[a[1,2] a[1,5]], [a[3,2] a[3,5]]]``. Args: *args: 1-D sequences Returns: tuple of ndarrays: N arrays with N dimensions each, with N the number of input sequences. Together these arrays form an open mesh. Examples -------- >>> a = cupy.arange(10).reshape(2, 5) >>> a array([[0, 1, 2, 3, 4], [5, 6, 7, 8, 9]]) >>> ixgrid = cupy.ix_([0,1], [2,4]) >>> ixgrid (array([[0], [1]]), array([[2, 4]])) .. seealso:: :func:`numpy.ix_` """ out = [] nd = len(args) for k, new in enumerate(args): new = cupy.asarray(new) if new.ndim != 1: raise ValueError("Cross index must be 1 dimensional") if new.size == 0: # Explicitly type empty arrays to avoid float default new = new.astype(numpy.intp) if cupy.issubdtype(new.dtype, cupy.bool_): new, = new.nonzero() new = new.reshape((1,) * k + (new.size,) + (1,) * (nd - k - 1)) out.append(new) return tuple(out)
def asfarray(a, dtype=cupy.float_): """Converts array elements to float type. Args: a (cupy.ndarray): Source array. dtype: str or dtype object, optional Returns: cupy.ndarray: The input array ``a`` as a float ndarray. .. seealso:: :func`numpy.asfarray` """ if not cupy.issubdtype(dtype, cupy.inexact): dtype = cupy.float_ return cupy.asarray(a, dtype=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 __init__( self, points, values, method="linear", bounds_error=True, fill_value=cp.nan, ): if method not in ["linear", "nearest"]: raise ValueError("Method '%s' is not defined" % method) self.method = method self.bounds_error = bounds_error # allow reasonable duck-typed values values = cp.asarray(values) if len(points) > values.ndim: raise ValueError("There are %d point arrays, but values has %d " "dimensions" % (len(points), values.ndim)) if hasattr(values, "dtype") and hasattr(values, "astype"): if not cp.issubdtype(values.dtype, cp.inexact): values = values.astype(float) self.fill_value = fill_value if fill_value is not None: fill_value_dtype = cp.asarray(fill_value).dtype if hasattr(values, "dtype") and not cp.can_cast( fill_value_dtype, values.dtype, casting="same_kind"): raise ValueError("fill_value must be either 'None' or " "of a type compatible with values") for i, p in enumerate(points): if not cp.all(cp.diff(p) > 0.0): raise ValueError("The points in dimension %d must be strictly " "ascending" % i) if not cp.asarray(p).ndim == 1: raise ValueError("The points in dimension %d must be " "1-dimensional" % i) if not values.shape[i] == len(p): raise ValueError("There are %d points and %d values in " "dimension %d" % (len(p), values.shape[i], i)) self.grid = tuple([cp.asarray(p) for p in points]) self.values = values
def validateaxis(axis): if axis is not None: axis_type = type(axis) # In NumPy, you can pass in tuples for 'axis', but they are # not very useful for sparse matrices given their limited # dimensions, so let's make it explicit that they are not # allowed to be passed in if axis_type == tuple: raise TypeError(("Tuples are not accepted for the 'axis' " "parameter. Please pass in one of the " "following: {-2, -1, 0, 1, None}.")) # If not a tuple, check that the provided axis is actually # an integer and raise a TypeError similar to NumPy's if not cupy.issubdtype(cupy.dtype(axis_type), cupy.integer): raise TypeError("axis must be an integer, not {name}" .format(name=axis_type.__name__)) if not (-2 <= axis <= 1): raise ValueError("axis out of range")
def cuda_median(a, axis=1): a = cupy.asanyarray(a) sz = a.shape[axis] if sz % 2 == 0: szh = sz // 2 kth = [szh - 1, szh] else: kth = [(sz - 1) // 2] if cupy.issubdtype(a.dtype, cupy.inexact): kth.append(-1) part = cupy.partition(a, kth, axis=axis) if part.shape == (): return part.item() if axis is None: axis = 0 indexer = [slice(None)] * part.ndim index = part.shape[axis] // 2 if part.shape[axis] % 2 == 1: indexer[axis] = slice(index, index + 1) else: indexer[axis] = slice(index - 1, index + 1) return cupy.mean(part[indexer], axis=axis)
def regionprops(label_image, intensity_image=None, cache=True, coordinates=None): r"""Measure properties of labeled image regions. Parameters ---------- label_image : (N, M) ndarray Labeled input image. Labels with value 0 are ignored. .. versionchanged:: 0.14.1 Previously, ``label_image`` was processed by ``numpy.squeeze`` and so any number of singleton dimensions was allowed. This resulted in inconsistent handling of images with singleton dimensions. To recover the old behaviour, use ``regionprops(cp.squeeze(label_image), ...)``. intensity_image : (N, M) ndarray, optional Intensity (i.e., input) image with same size as labeled image. Default is None. cache : bool, optional Determine whether to cache calculated properties. The computation is much faster for cached properties, whereas the memory consumption increases. coordinates : DEPRECATED This argument is deprecated and will be removed in a future version of scikit-image. See :ref:`Coordinate conventions <numpy-images-coordinate-conventions>` for more details. .. deprecated:: 0.16.0 Use "rc" coordinates everywhere. It may be sufficient to call ``numpy.transpose`` on your label image to get the same values as 0.15 and earlier. However, for some properties, the transformation will be less trivial. For example, the new orientation is :math:`\frac{\pi}{2}` plus the old orientation. Returns ------- properties : list of RegionProperties Each item describes one labeled region, and can be accessed using the attributes listed below. Notes ----- The following properties can be accessed as attributes or keys: **area** : int Number of pixels of the region. **bbox** : tuple Bounding box ``(min_row, min_col, max_row, max_col)``. Pixels belonging to the bounding box are in the half-open interval ``[min_row; max_row)`` and ``[min_col; max_col)``. **bbox_area** : int Number of pixels of bounding box. **centroid** : array Centroid coordinate tuple ``(row, col)``. **convex_area** : int Number of pixels of convex hull image, which is the smallest convex polygon that encloses the region. **convex_image** : (H, J) ndarray Binary convex hull image which has the same size as bounding box. **coords** : (N, 2) ndarray Coordinate list ``(row, col)`` of the region. **eccentricity** : float Eccentricity of the ellipse that has the same second-moments as the region. The eccentricity is the ratio of the focal distance (distance between focal points) over the major axis length. The value is in the interval [0, 1). When it is 0, the ellipse becomes a circle. **equivalent_diameter** : float The diameter of a circle with the same area as the region. **euler_number** : int Euler characteristic of region. Computed as number of objects (= 1) subtracted by number of holes (8-connectivity). **extent** : float Ratio of pixels in the region to pixels in the total bounding box. Computed as ``area / (rows * cols)`` **filled_area** : int Number of pixels of the region will all the holes filled in. Describes the area of the filled_image. **filled_image** : (H, J) ndarray Binary region image with filled holes which has the same size as bounding box. **image** : (H, J) ndarray Sliced binary region image which has the same size as bounding box. **inertia_tensor** : ndarray Inertia tensor of the region for the rotation around its mass. **inertia_tensor_eigvals** : tuple The eigenvalues of the inertia tensor in decreasing order. **intensity_image** : ndarray Image inside region bounding box. **label** : int The label in the labeled input image. **local_centroid** : array Centroid coordinate tuple ``(row, col)``, relative to region bounding box. **major_axis_length** : float The length of the major axis of the ellipse that has the same normalized second central moments as the region. **max_intensity** : float Value with the greatest intensity in the region. **mean_intensity** : float Value with the mean intensity in the region. **min_intensity** : float Value with the least intensity in the region. **minor_axis_length** : float The length of the minor axis of the ellipse that has the same normalized second central moments as the region. **moments** : (3, 3) ndarray Spatial moments up to 3rd order:: m_ij = sum{ array(row, col) * row^i * col^j } where the sum is over the `row`, `col` coordinates of the region. **moments_central** : (3, 3) ndarray Central moments (translation invariant) up to 3rd order:: mu_ij = sum{ array(row, col) * (row - row_c)^i * (col - col_c)^j } where the sum is over the `row`, `col` coordinates of the region, and `row_c` and `col_c` are the coordinates of the region's centroid. **moments_hu** : tuple Hu moments (translation, scale and rotation invariant). **moments_normalized** : (3, 3) ndarray Normalized moments (translation and scale invariant) up to 3rd order:: nu_ij = mu_ij / m_00^[(i+j)/2 + 1] where `m_00` is the zeroth spatial moment. **orientation** : float Angle between the 0th axis (rows) and the major axis of the ellipse that has the same second moments as the region, ranging from `-pi/2` to `pi/2` counter-clockwise. **perimeter** : float Perimeter of object which approximates the contour as a line through the centers of border pixels using a 4-connectivity. **slice** : tuple of slices A slice to extract the object from the source image. **solidity** : float Ratio of pixels in the region to pixels of the convex hull image. **weighted_centroid** : array Centroid coordinate tuple ``(row, col)`` weighted with intensity image. **weighted_local_centroid** : array Centroid coordinate tuple ``(row, col)``, relative to region bounding box, weighted with intensity image. **weighted_moments** : (3, 3) ndarray Spatial moments of intensity image up to 3rd order:: wm_ij = sum{ array(row, col) * row^i * col^j } where the sum is over the `row`, `col` coordinates of the region. **weighted_moments_central** : (3, 3) ndarray Central moments (translation invariant) of intensity image up to 3rd order:: wmu_ij = sum{ array(row, col) * (row - row_c)^i * (col - col_c)^j } where the sum is over the `row`, `col` coordinates of the region, and `row_c` and `col_c` are the coordinates of the region's weighted centroid. **weighted_moments_hu** : tuple Hu moments (translation, scale and rotation invariant) of intensity image. **weighted_moments_normalized** : (3, 3) ndarray Normalized moments (translation and scale invariant) of intensity image up to 3rd order:: wnu_ij = wmu_ij / wm_00^[(i+j)/2 + 1] where ``wm_00`` is the zeroth spatial moment (intensity-weighted area). Each region also supports iteration, so that you can do:: for prop in region: print(prop, region[prop]) See Also -------- label References ---------- .. [1] Wilhelm Burger, Mark Burge. Principles of Digital Image Processing: Core Algorithms. Springer-Verlag, London, 2009. .. [2] B. Jähne. Digital Image Processing. Springer-Verlag, Berlin-Heidelberg, 6. edition, 2005. .. [3] T. H. Reiss. Recognizing Planar Objects Using Invariant Image Features, from Lecture notes in computer science, p. 676. Springer, Berlin, 1993. .. [4] https://en.wikipedia.org/wiki/Image_moment Examples -------- >>> from skimage import data, util >>> from skimage.measure import label >>> img = util.img_as_ubyte(data.coins()) > 110 >>> label_img = label(img, connectivity=img.ndim) >>> props = regionprops(label_img) >>> # centroid of first labeled object >>> props[0].centroid (22.72987986048314, 81.91228523446583) >>> # centroid of first labeled object >>> props[0]['centroid'] (22.72987986048314, 81.91228523446583) """ if label_image.ndim not in (2, 3): raise TypeError("Only 2-D and 3-D images supported.") if not cp.issubdtype(label_image.dtype, cp.integer): if cp.issubdtype(label_image.dtype, cp.bool): raise TypeError("Non-integer image types are ambiguous: " "use skimage.measure.label to label the connected" "components of label_image," "or label_image.astype(np.uint8) to interpret" "the True values as a single label.") else: raise TypeError("Non-integer label_image types are ambiguous") if coordinates is not None: if coordinates == "rc": msg = ("The coordinates keyword argument to skimage.measure." "regionprops is deprecated. All features are now computed " "in rc (row-column) coordinates. Please remove " '`coordinates="rc"` from all calls to regionprops before ' "updating scikit-image.") warn(msg, stacklevel=2, category=FutureWarning) else: msg = ('Values other than "rc" for the "coordinates" argument ' "to skimage.measure.regionprops are no longer supported. " 'You should update your code to use "rc" coordinates and ' 'stop using the "coordinates" argument, or use skimage ' "version 0.15.x or earlier.") raise ValueError(msg) regions = [] # warn("host/device transfer required: ndimage.find_objects not implemented " # " on the GPU.") objects = cpu_find_objects(cp.asnumpy(label_image)) for i, sl in enumerate(objects): if sl is None: continue label = i + 1 props = RegionProperties(sl, label, label_image, intensity_image, cache) regions.append(props) return regions
def linspace(start, stop, num=50, endpoint=True, retstep=False, dtype=None, axis=0): """Returns an array with evenly-spaced values within a given interval. Instead of specifying the step width like :func:`cupy.arange`, this function requires the total number of elements specified. Args: start (scalar or array_like): Starting value(s) of the sequence. stop (scalar or array_like): Ending value(s) of the sequence, unless ``endpoint`` is set to ``False``. In that case, the sequence consists of all but the last of ``num + 1`` evenly spaced samples, so that ``stop`` is excluded. Note that the step size changes when ``endpoint`` is ``False``. num: Number of elements. endpoint (bool): If ``True``, the stop value is included as the last element. Otherwise, the stop value is omitted. retstep (bool): If ``True``, this function returns (array, step). Otherwise, it returns only the array. dtype: Data type specifier. It is inferred from the start and stop arguments by default. axis (int): The axis in the result to store the samples. Relevant only if start or stop are array-like. By default ``0``, the samples will be along a new axis inserted at the beginning. Use ``-1`` to get an axis at the end. Returns: cupy.ndarray: The 1-D array of ranged values. .. seealso:: :func:`numpy.linspace` """ if num < 0: raise ValueError('linspace with num<0 is not supported') div = (num - 1) if endpoint else num scalar_start = cupy.isscalar(start) scalar_stop = cupy.isscalar(stop) if scalar_start and scalar_stop: return _linspace_scalar(start, stop, num, endpoint, retstep, dtype) if not scalar_start: if not (isinstance(start, cupy.ndarray) and start.dtype.kind == 'f'): start = cupy.asarray(start) * 1.0 if not scalar_stop: if not (isinstance(stop, cupy.ndarray) and stop.dtype.kind == 'f'): stop = cupy.asarray(stop) * 1.0 dt = cupy.result_type(start, stop, float(num)) if dtype is None: # In actual implementation, only float is used dtype = dt delta = stop - start # ret = cupy.arange(0, num, dtype=dt).reshape((-1,) + (1,) * delta.ndim) ret = cupy.empty((num, ), dtype=dt) _arange_ufunc(0.0, 1.0, ret, dtype=dt) ret = ret.reshape((-1, ) + (1, ) * delta.ndim) # In-place multiplication y *= delta/div is faster, but prevents the # multiplicant from overriding what class is produced, and thus prevents, # e.g. use of Quantities, see numpy#7142. Hence, we multiply in place only # for standard scalar types. if num > 1: step = delta / div if cupy.any(step == 0): # Special handling for denormal numbers, numpy#5437 ret /= div ret = ret * delta else: ret = ret * step else: # 0 and 1 item long sequences have an undefined step step = float('nan') # Multiply with delta to allow possible override of output class. ret = ret * delta ret += start if endpoint and num > 1: ret[-1] = stop if axis != 0: ret = cupy.moveaxis(ret, 0, axis) if cupy.issubdtype(dtype, cupy.integer): cupy.floor(ret, out=ret) ret = ret.astype(dtype, copy=False) if retstep: return ret, step else: return ret
def white_tophat(image, selem=None, out=None): """Return white top hat of an image. The white top hat of an image is defined as the image minus its morphological opening. This operation returns the bright spots of the image that are smaller than the structuring element. Parameters ---------- image : ndarray Image array. selem : ndarray, optional The neighborhood expressed as an array of 1's and 0's. If None, use cross-shaped structuring element (connectivity=1). out : ndarray, optional The array to store the result of the morphology. If None is passed, a new array will be allocated. Returns ------- out : array, same shape and type as `image` The result of the morphological white top hat. See also -------- black_tophat References ---------- .. [1] https://en.wikipedia.org/wiki/Top-hat_transform Examples -------- >>> # Subtract grey background from bright peak >>> import cupy as cp >>> from cucim.skimage.morphology import square >>> bright_on_grey = cp.asarray([[2, 3, 3, 3, 2], ... [3, 4, 5, 4, 3], ... [3, 5, 9, 5, 3], ... [3, 4, 5, 4, 3], ... [2, 3, 3, 3, 2]], dtype=cp.uint8) >>> white_tophat(bright_on_grey, square(3)) array([[0, 0, 0, 0, 0], [0, 0, 1, 0, 0], [0, 1, 5, 1, 0], [0, 0, 1, 0, 0], [0, 0, 0, 0, 0]], dtype=uint8) """ if out is image: opened = opening(image, selem) if cp.issubdtype(opened.dtype, cp.bool_): cp.logical_xor(out, opened, out=out) else: out -= opened return out elif out is None: out = cp.empty_like(image) # work-around for NumPy deprecation warning for arithmetic # operations on bool arrays if isinstance(image, cp.ndarray) and image.dtype == bool: image_ = image.view(dtype=cp.uint8) else: image_ = image if isinstance(out, cp.ndarray) and out.dtype == bool: out_ = out.view(dtype=cp.uint8) else: out_ = out out_ = ndi.white_tophat(image_, footprint=selem, output=out_) return out
def _is_integral(dtype): return (cupy.issubdtype(dtype, cupy.integer) or cupy.issubdtype(dtype, cupy.bool_))
def mi_model_1d_gpu_gd(x, y, biascorrect=False, demeaned=False): """Mutual information between a Gaussian and a discrete variable in bits. This method is based on ANOVA style model comparison. I = mi_model_gd(x,y) returns the MI between the (possibly multidimensional) Gaussian variable x and the discrete variable y. Parameters ---------- x, y : array_like Gaussian arrays of shape (n_epochs,) or (n_dimensions, n_epochs). y must be an array of integers biascorrect : bool | True Specifies whether bias correction should be applied to the estimated MI demeaned : bool | False Specifies whether the input data already has zero mean (true if it has been copula-normalized) Returns ------- i : float Information shared by x and y (in bits) """ # Converting to cupy array #x, y = cp.array(x), cp.array(y) x, y = cp.atleast_2d(x), cp.squeeze(y) if x.ndim > 2: raise ValueError("x must be at most 2d") if y.ndim > 1: raise ValueError("only univariate discrete variables supported") if not cp.issubdtype(y.dtype, cp.integer): raise ValueError("y should be an integer array") nvarx, ntrl = x.shape ym = cp.unique(y) if y.size != ntrl: raise ValueError("number of trials do not match") if not demeaned: x = x - x.mean(axis=1)[:, cp.newaxis] # class-conditional entropies ntrl_y = cp.zeros(len(ym)) hcond = cp.zeros(len(ym)) for n_yi, yi in enumerate(ym): idx = y == yi xm = x[:, idx] ntrl_y[n_yi] = xm.shape[1] xm = xm - xm.mean(axis=1)[:, cp.newaxis] cm = cp.dot(xm, xm.T) / float(ntrl_y[n_yi] - 1) chcm = cp.linalg.cholesky(cm) hcond[n_yi] = cp.sum(cp.log(cp.diagonal(chcm))) # class weights w = ntrl_y / float(ntrl) # unconditional entropy from unconditional Gaussian fit cx = cp.dot(x, x.T) / float(ntrl - 1) chc = cp.linalg.cholesky(cx) hunc = cp.sum(cp.log(cp.diagonal(chc))) # + c*nvarx ln2 = cp.log(2) if biascorrect: vars = cp.arange(1, nvarx + 1) psiterms = psi((ntrl - vars).astype(cp.float) / 2.) / 2. dterm = (ln2 - cp.log(float(ntrl - 1))) / 2. hunc = hunc - nvarx * dterm - psiterms.sum() dterm = (ln2 - cp.log((ntrl_y - 1).astype(cp.float))) / 2.0 psiterms = cp.zeros(len(ym)) for vi in vars: idx = ntrl_y - vi psiterms = psiterms + psi(idx.astype(cp.float) / 2.) hcond = hcond - nvarx * dterm - (psiterms / 2.) # MI in bits i = (hunc - cp.sum(w * hcond)) / ln2 return i
def _get_bin_edges(a, bins, range): """ Computes the bins used internally by `histogram`. Args: a (ndarray): Ravelled data array bins (int or ndarray): Forwarded argument from `histogram`. range (None or tuple): Forwarded argument from `histogram`. Returns: bin_edges (ndarray): Array of bin edges uniform_bins (Number, Number, int): The upper bound, lowerbound, and number of bins, used in the implementation of `histogram` that works on uniform bins. """ # parse the overloaded bins argument n_equal_bins = None bin_edges = None # if isinstance(bins, cupy.ndarray) and bins.ndim == 0: # # allow uint8 array, etc # if bins.dtype not in 'bui': # raise TypeError( # "`bins` must be an integer, a string, or an array") # bins = int(bins) # synchronize if isinstance(bins, int): # will not allow 0-dimensional cupy array # if cupy.ndim(bins) == 0: try: n_equal_bins = operator.index(bins) except TypeError: raise TypeError("`bins` must be an integer, a string, or an array") if n_equal_bins < 1: raise ValueError("`bins` must be positive, when an integer") first_edge, last_edge = _get_outer_edges(a, range) elif isinstance(bins, cupy.ndarray): if bins.ndim == 1: # cupy.ndim(bins) == 0: bin_edges = cupy.asarray(bins) if (bin_edges[:-1] > bin_edges[1:]).any(): # synchronize! raise ValueError( "`bins` must increase monotonically, when an array") elif isinstance(bins, str): raise NotImplementedError( "only integer and array bins are implemented") if n_equal_bins is not None: # numpy's gh-10322 means that type resolution rules are dependent on # array shapes. To avoid this causing problems, we pick a type now and # stick with it throughout. bin_type = cupy.result_type(first_edge, last_edge, a) if cupy.issubdtype(bin_type, cupy.integer): bin_type = cupy.result_type(bin_type, float) # bin edges must be computed bin_edges = cupy.linspace( first_edge, last_edge, n_equal_bins + 1, endpoint=True, dtype=bin_type, ) return bin_edges, (first_edge, last_edge, n_equal_bins) else: return bin_edges, None
def _round_if_needed(arr, dtype): """Rounds arr inplace if the destination dtype is an integer. """ if cupy.issubdtype(dtype, cupy.integer): arr.round(out=arr) # bug in round so use rint (cupy/cupy#2330)