import numpy as np from scipy import ndimage from ._geometric import warp, SimilarityTransform, AffineTransform from skimage.util.shape import view_as_blocks, _pad_asymmetric_zeros def resize(image, output_shape, order=1, mode='constant', cval=0.): """Resize image to match a certain size. Resize performs interpolation to upsample or downsample 2D arrays. For downsampling any n-dimensional array by performing arithmetic sum or arithmetic mean, see measure._sum_blocks.sum_blocks and transform._warps.downscale_local_means respectively. Parameters ---------- image : ndarray Input image. output_shape : tuple or ndarray Size of the generated output image `(rows, cols[, dim])`. If `dim` is not provided, the number of channels is preserved. In case the number of input channels does not equal the number of output channels a 3-dimensional interpolation is applied. Returns ------- resized : ndarray Resized version of the input. Other parameters ---------------- order : int, optional The order of the spline interpolation, default is 3. The order has to be in the range 0-5. mode : string, optional Points outside the boundaries of the input are filled according to the given mode ('constant', 'nearest', 'reflect' or 'wrap'). cval : float, optional Used in conjunction with mode 'constant', the value outside the image boundaries. Examples -------- >>> from skimage import data >>> from skimage.transform import resize >>> image = data.camera() >>> resize(image, (100, 100)).shape (100, 100) """ rows, cols = output_shape[0], output_shape[1] orig_rows, orig_cols = image.shape[0], image.shape[1] row_scale = float(orig_rows) / rows col_scale = float(orig_cols) / cols # 3-dimensional interpolation if len(output_shape) == 3 and (image.ndim == 2 or output_shape[2] != image.shape[2]): dim = output_shape[2] orig_dim = 1 if image.ndim == 2 else image.shape[2] dim_scale = float(orig_dim) / dim map_rows, map_cols, map_dims = np.mgrid[:rows, :cols, :dim] map_rows = row_scale * (map_rows + 0.5) - 0.5 map_cols = col_scale * (map_cols + 0.5) - 0.5 map_dims = dim_scale * (map_dims + 0.5) - 0.5 coord_map = np.array([map_rows, map_cols, map_dims]) out = ndimage.map_coordinates(image, coord_map, order=order, mode=mode, cval=cval) else: # 2-dimensional interpolation # 3 control points necessary to estimate exact AffineTransform src_corners = np.array([[1, 1], [1, rows], [cols, rows]]) - 1 dst_corners = np.zeros(src_corners.shape, dtype=np.double) # take into account that 0th pixel is at position (0.5, 0.5) dst_corners[:, 0] = col_scale * (src_corners[:, 0] + 0.5) - 0.5 dst_corners[:, 1] = row_scale * (src_corners[:, 1] + 0.5) - 0.5 tform = AffineTransform() tform.estimate(src_corners, dst_corners) out = warp(image, tform, output_shape=output_shape, order=order, mode=mode, cval=cval) return out def rescale(image, scale, order=1, mode='constant', cval=0.): """Scale image by a certain factor. Rescale performs interpolation to upsample or downsample 2D arrays. For downsampling any n-dimensional array by performing arithmetic sum or arithmetic mean, see measure._sum_blocks.sum_blocks and transform._warps.downscale_local_means respectively. Parameters ---------- image : ndarray Input image. scale : {float, tuple of floats} Scale factors. Separate scale factors can be defined as `(row_scale, col_scale)`. Returns ------- scaled : ndarray Scaled version of the input. Other parameters ---------------- order : int, optional The order of the spline interpolation, default is 3. The order has to be in the range 0-5. mode : string, optional Points outside the boundaries of the input are filled according to the given mode ('constant', 'nearest', 'reflect' or 'wrap'). cval : float, optional Used in conjunction with mode 'constant', the value outside the image boundaries. Examples -------- >>> from skimage import data >>> from skimage.transform import rescale >>> image = data.camera() >>> rescale(image, 0.1).shape (51, 51) >>> rescale(image, 0.5).shape (256, 256) """ try: row_scale, col_scale = scale except TypeError: row_scale = col_scale = scale orig_rows, orig_cols = image.shape[0], image.shape[1] rows = np.round(row_scale * orig_rows) cols = np.round(col_scale * orig_cols) output_shape = (rows, cols) return resize(image, output_shape, order=order, mode=mode, cval=cval) def rotate(image, angle, resize=False, order=1, mode='constant', cval=0.): """Rotate image by a certain angle around its center. Parameters ---------- image : ndarray Input image. angle : float Rotation angle in degrees in counter-clockwise direction. resize : bool, optional Determine whether the shape of the output image will be automatically calculated, so the complete rotated image exactly fits. Default is False. Returns ------- rotated : ndarray Rotated version of the input. Other parameters ---------------- order : int, optional The order of the spline interpolation, default is 3. The order has to be in the range 0-5. mode : string, optional Points outside the boundaries of the input are filled according to the given mode ('constant', 'nearest', 'reflect' or 'wrap'). cval : float, optional Used in conjunction with mode 'constant', the value outside the image boundaries. Examples -------- >>> from skimage import data >>> from skimage.transform import rotate >>> image = data.camera() >>> rotate(image, 2).shape (512, 512) >>> rotate(image, 2, resize=True).shape (530, 530) >>> rotate(image, 90, resize=True).shape (512, 512) """ rows, cols = image.shape[0], image.shape[1] # rotation around center translation = np.array((cols, rows)) / 2. - 0.5 tform1 = SimilarityTransform(translation=-translation) tform2 = SimilarityTransform(rotation=np.deg2rad(angle)) tform3 = SimilarityTransform(translation=translation) tform = tform1 + tform2 + tform3 output_shape = None if resize: # determine shape of output image corners = np.array([[1, 1], [1, rows], [cols, rows], [cols, 1]]) corners = tform(corners - 1) minc = corners[:, 0].min() minr = corners[:, 1].min() maxc = corners[:, 0].max() maxr = corners[:, 1].max() out_rows = maxr - minr + 1 out_cols = maxc - minc + 1 output_shape = np.ceil((out_rows, out_cols)) # fit output image in new shape translation = ((cols - out_cols) / 2., (rows - out_rows) / 2.) tform4 = SimilarityTransform(translation=translation) tform = tform4 + tform return warp(image, tform, output_shape=output_shape, order=order, mode=mode, cval=cval) def _swirl_mapping(xy, center, rotation, strength, radius): x, y = xy.T x0, y0 = center rho = np.sqrt((x - x0) ** 2 + (y - y0) ** 2) # Ensure that the transformation decays to approximately 1/1000-th # within the specified radius. radius = radius / 5 * np.log(2) theta = rotation + strength * \ np.exp(-rho / radius) + \ np.arctan2(y - y0, x - x0) xy[..., 0] = x0 + rho * np.cos(theta) xy[..., 1] = y0 + rho * np.sin(theta) return xy def swirl(image, center=None, strength=1, radius=100, rotation=0, output_shape=None, order=1, mode='constant', cval=0): """Perform a swirl transformation. Parameters ---------- image : ndarray Input image. center : (x,y) tuple or (2,) ndarray, optional Center coordinate of transformation. strength : float, optional The amount of swirling applied. radius : float, optional The extent of the swirl in pixels. The effect dies out rapidly beyond `radius`. rotation : float, optional Additional rotation applied to the image. Returns ------- swirled : ndarray Swirled version of the input. Other parameters ---------------- output_shape : tuple (rows, cols), optional Shape of the output image generated. By default the shape of the input image is preserved. order : int, optional The order of the spline interpolation, default is 3. The order has to be in the range 0-5. mode : string, optional Points outside the boundaries of the input are filled according to the given mode ('constant', 'nearest', 'reflect' or 'wrap'). cval : float, optional Used in conjunction with mode 'constant', the value outside the image boundaries. """ if center is None: center = np.array(image.shape)[:2] / 2 warp_args = {'center': center, 'rotation': rotation, 'strength': strength, 'radius': radius} return warp(image, _swirl_mapping, map_args=warp_args, output_shape=output_shape, order=order, mode=mode, cval=cval) def _downsample(array, factors, sum=True): """Performs downsampling with integer factors. Parameters ---------- array : ndarray Input n-dimensional array. factors: tuple Tuple containing downsampling factor along each axis. sum : bool If True, downsampled element is the sum of its corresponding constituent elements in the input array. Default is True. Returns ------- array : ndarray Downsampled array with same number of dimensions as that of input array. """ pad_size = [] if len(factors) != array.ndim: raise ValueError("'factors' must have the same length " "as 'array.shape'") else: for i in range(len(factors)): if array.shape[i] % factors[i] != 0: pad_size.append(factors[i] - (array.shape[i] % factors[i])) else: pad_size.append(0) for i in range(len(pad_size)): array = _pad_asymmetric_zeros(array, pad_size[i], i) out = view_as_blocks(array, factors) block_shape = out.shape if sum: for i in range(len(block_shape) // 2): out = out.sum(-1) else: for i in range(len(block_shape) // 2): out = out.mean(-1) return out def downscale_local_means(array, factors): """Downsamples the array in blocks of input integer factors after padding the original array with zeroes if the dimensions are not perfectly divisible by factors and replaces it with mean i.e. average value. This function is different from resize and rescale in the sense that they use interpolation to upsample or downsample on a 2D array, while this function performs only dawnsampling but on any n-dimensional array and returns the arithmetic mean of elements in a block of size factors in the original array. Parameters ---------- array : ndarray Input n-dimensional array. factors: tuple Tuple containing integer values representing block length along each axis. Returns ------- array : ndarray Downsampled array with same number of dimensions as that of input array. Example ------- >>> a = np.arange(15).reshape(3, 5) >>> a array([[ 0, 1, 2, 3, 4], [ 5, 6, 7, 8, 9], [10, 11, 12, 13, 14]]) >>> downscale_local_means(a, (2,3)) array([[3.5, 4.], [5.5, 4.5]]) """ return _downsample(array, factors, False)