def __init__(self, image, rows=None, columns=None, convert=False): if convert: self.image = dtype.img_as_float(image) else: self.image = image self.rows = len(image) if rows is None else rows self.columns = len(image[0]) if columns is None else columns
def load(self, path: str): """ImageChain <- load(path)""" img = io.imread(path) # uint8 #self.img = tc.uint8_to_float64(img) self.img = img_as_float(img) self.fname = path.split("/")[-1] return self
def extract_specific_color_from_image(self, figure2transform, color_rep_rgb): rgb_temp = dtype.img_as_float(figure2transform, force_copy=True) + 2 reshaped_rgb = np.reshape(-np.log(rgb_temp), (-1, 3)) single_stain = -np.dot(reshaped_rgb, color_rep_rgb) single_stain = 255 * (single_stain - single_stain.min()) / ( single_stain.max() - single_stain.min()) return np.reshape(single_stain, (rgb_temp.shape[0], rgb_temp.shape[1]))
def getY(self): arr = np.asanyarray(self.image) arr = dtype.img_as_float(arr) mask = arr > 0.04045 arr[mask] = np.power((arr[mask] + 0.055) / 1.055, 2.4) arr[~mask] /= 12.92 Y = arr[:, :, 0] * 0.2126 + arr[:, :, 1] * 0.7152 + arr[:, :, 2] * 0.0722 return Y
def combine_stains(stains, conv_matrix): stains = dtype.img_as_float(stains.astype('float64')).astype( 'float32' ) # stains are out of range [-1, 1] so dtype.img_as_float complains if not float64 logrgb2 = np.dot(-np.reshape(stains, (-1, 3)), conv_matrix) rgb2 = np.exp(logrgb2) return rescale_intensity(np.reshape(rgb2 - 2, stains.shape), in_range=(-1, 1))
def test_4d_input_pixel(): phantom = img_as_float(binary_blobs(length=32, n_dim=4)) reference_image = fft.fftn(phantom) shift = (-2., 1., 5., -3) shifted_image = fourier_shift(reference_image, shift) result, error, diffphase = phase_cross_correlation(reference_image, shifted_image, space="fourier") assert_allclose(result, -np.array(shift), atol=0.05)
def test_unmix_stain_like_me(self): rgb = np.random.randint( 0, 256, self.h*self.w*self.d).astype(np.uint8).reshape(self.h, self.w, self.d) stain = color.hdx_from_rgb.astype(np.float32) dec_np = cdeconv.color_deconvolution(rgb, stain) CD = cdeconvcl.ColorDeconvolution() dec_cl = CD.color_deconvolution(dtype.img_as_float(rgb), stain) np.testing.assert_allclose(dec_cl, dec_np, rtol=1e-5)
def prepare_colorarray(arr): """Check the shape of the array and convert it to floating point representation. """ arr = np.asanyarray(arr) if arr.ndim != 3 or arr.shape[2] != 3: msg = "the input array must be have a shape == (.,.,3))" raise ValueError(msg) return dtype.img_as_float(arr)
def _prepare_colorarray(arr, force_copy=False): """Check the shape of the array and convert it to floating point representation. """ arr = np.asanyarray(arr) if arr.shape[-1] != 3: raise ValueError("Input array must have a shape == (..., 3)), " f"got {arr.shape}") return dtype.img_as_float(arr, force_copy=force_copy)
def preprocess_frame(self, frame): frame = img_as_float(frame) if (frame.ndim == 2): frame_new = np.zeros((frame.shape[0], frame.shape[1], 3)) frame_new[:, :, 0] = frame frame_new[:, :, 1] = frame frame_new[:, :, 2] = frame else: frame_new = frame return frame_new
def color_deconvolution(rgb, conv_matrix): """Unmix stains for histogram analysis. :return: Image values in normal space (not optical density i.e. log space) and in range 0...1. :rtype: float array """ rgb = dtype.img_as_float(rgb, force_copy=True) rgb += 1 # Faster then log1p stains = np.dot(np.reshape(-np.log(rgb), (-1, 3)), conv_matrix) stains = np.exp(-stains) return np.reshape(stains, rgb.shape)
def _prepare_rgba_array(arr): """Check the shape of the array to be RGBA and convert it to floating point representation. """ arr = np.asanyarray(arr) if arr.ndim not in [3, 4] or arr.shape[-1] != 4: msg = ("the input array must have a shape == (.., ..,[ ..,] 4)), " "got {0}".format(arr.shape)) raise ValueError(msg) return dtype.img_as_float(arr)
def test_warp_identity(): img = img_as_float(rgb2gray(astronaut())) assert len(img.shape) == 2 assert np.allclose(img, warp(img, AffineTransform(rotation=0))) assert not np.allclose(img, warp(img, AffineTransform(rotation=0.1))) rgb_img = np.transpose(np.asarray([img, np.zeros_like(img), img]), (1, 2, 0)) warped_rgb_img = warp(rgb_img, AffineTransform(rotation=0.1)) assert np.allclose(rgb_img, warp(rgb_img, AffineTransform(rotation=0))) assert not np.allclose(rgb_img, warped_rgb_img) # assert no cross-talk between bands assert np.all(0 == warped_rgb_img[:, :, 1])
def separate_stains(rgb, color_deconv_vector): rgb = dtype.img_as_float(rgb, force_copy=True) print(type(rgb)) print(rgb[0]) print(np.shape(rgb)) rgb += 2 print('-----') print(rgb[0]) print('-----') print(-np.log(rgb[0])) stains = np.dot(np.reshape(-np.log(rgb), (-1, 3)), color_deconv_vector) print('-----') print() print(np.shape(stains)) print(stains[0]) return np.reshape(stains, rgb.shape)
def test_swirl(): image = img_as_float(checkerboard()) swirl_params = {'radius': 80, 'rotation': 0, 'order': 2, 'mode': 'reflect'} with expected_warnings(['Bi-quadratic.*bug']): swirled = swirl(image, strength=10, **swirl_params) unswirled = swirl(swirled, strength=-10, **swirl_params) assert np.mean(np.abs(image - unswirled)) < 0.01 swirl_params.pop('mode') with expected_warnings(['Bi-quadratic.*bug']): swirled = swirl(image, strength=10, **swirl_params) unswirled = swirl(swirled, strength=-10, **swirl_params) assert np.mean(np.abs(image[1:-1, 1:-1] - unswirled[1:-1, 1:-1])) < 0.01
def test_3d_input(): phantom = img_as_float(binary_blobs(length=32, n_dim=3)) reference_image = fft.fftn(phantom) shift = (-2., 1., 5.) shifted_image = fourier_shift(reference_image, shift) result, error, diffphase = phase_cross_correlation(reference_image, shifted_image, space="fourier") assert_allclose(result, -np.array(shift), atol=0.05) # subpixel precision now available for 3-D data subpixel_shift = (-2.3, 1.7, 5.4) shifted_image = fourier_shift(reference_image, subpixel_shift) result, error, diffphase = phase_cross_correlation(reference_image, shifted_image, upsample_factor=100, space="fourier") assert_allclose(result, -np.array(subpixel_shift), atol=0.05)
def random_noise(image, mode='gaussian', seed=None, clip=True, eq_chns=False, **kwargs): """ Function to add random noise of various types to a floating-point image. Parameters ---------- image : ndarray Input image data. Will be converted to float. mode : str, optional One of the following strings, selecting the type of noise to add: - 'gaussian' Gaussian-distributed additive noise. - 'localvar' Gaussian-distributed additive noise, with specified local variance at each point of `image`. - 'poisson' Poisson-distributed noise generated from the data. - 'salt' Replaces random pixels with 1. - 'pepper' Replaces random pixels with 0 (for unsigned images) or -1 (for signed images). - 's&p' Replaces random pixels with either 1 or `low_val`, where `low_val` is 0 for unsigned images or -1 for signed images. - 'speckle' Multiplicative noise using out = image + n*image, where n is Gaussian noise with specified mean & variance. seed : int, optional If provided, this will set the random seed before generating noise, for valid pseudo-random comparisons. clip : bool, optional If True (default), the output will be clipped after noise applied for modes `'speckle'`, `'poisson'`, and `'gaussian'`. This is needed to maintain the proper image data range. If False, clipping is not applied, and the output may extend beyond the range [-1, 1]. mean : float, optional Mean of random distribution. Used in 'gaussian' and 'speckle'. Default : 0. var : float, optional Variance of random distribution. Used in 'gaussian' and 'speckle'. Note: variance = (standard deviation) ** 2. Default : 0.01 local_vars : ndarray, optional Array of positive floats, same shape as `image`, defining the local variance at every image point. Used in 'localvar'. amount : float, optional Proportion of image pixels to replace with noise on range [0, 1]. Used in 'salt', 'pepper', and 'salt & pepper'. Default : 0.05 salt_vs_pepper : float, optional Proportion of salt vs. pepper noise for 's&p' on range [0, 1]. Higher values represent more salt. Default : 0.5 (equal amounts) Returns ------- out : ndarray Output floating-point image data on range [0, 1] or [-1, 1] if the input `image` was unsigned or signed, respectively. Notes ----- Speckle, Poisson, Localvar, and Gaussian noise may generate noise outside the valid image range. The default is to clip (not alias) these values, but they may be preserved by setting `clip=False`. Note that in this case the output may contain values outside the ranges [0, 1] or [-1, 1]. Use this option with care. Because of the prevalence of exclusively positive floating-point images in intermediate calculations, it is not possible to intuit if an input is signed based on dtype alone. Instead, negative values are explicitly searched for. Only if found does this function assume signed input. Unexpected results only occur in rare, poorly exposes cases (e.g. if all values are above 50 percent gray in a signed `image`). In this event, manually scaling the input to the positive domain will solve the problem. The Poisson distribution is only defined for positive integers. To apply this noise type, the number of unique values in the image is found and the next round power of two is used to scale up the floating-point result, after which it is scaled back down to the floating-point image range. To generate Poisson noise against a signed image, the signed image is temporarily converted to an unsigned image in the floating point domain, Poisson noise is generated, then it is returned to the original range. """ mode = mode.lower() # Detect if a signed image was input if image.min() < 0: low_clip = -1. else: low_clip = 0. image = img_as_float(image) if seed is not None: np.random.seed(seed=seed) allowedtypes = { 'gaussian': 'gaussian_values', 'localvar': 'localvar_values', 'poisson': 'poisson_values', 'salt': 'sp_values', 'pepper': 'sp_values', 's&p': 's&p_values', 'speckle': 'gaussian_values' } kwdefaults = { 'mean': 0., 'var': 0.01, 'amount': 0.05, 'salt_vs_pepper': 0.5, 'local_vars': np.zeros_like(image) + 0.01 } allowedkwargs = { 'gaussian_values': ['mean', 'var'], 'localvar_values': ['local_vars'], 'sp_values': ['amount'], 's&p_values': ['amount', 'salt_vs_pepper'], 'poisson_values': [] } for key in kwargs: if key not in allowedkwargs[allowedtypes[mode]]: raise ValueError('%s keyword not in allowed keywords %s' % (key, allowedkwargs[allowedtypes[mode]])) # Set kwarg defaults for kw in allowedkwargs[allowedtypes[mode]]: kwargs.setdefault(kw, kwdefaults[kw]) if mode == 'gaussian': noise = np.random.normal(kwargs['mean'], kwargs['var']**0.5, image.shape) if eq_chns and len(noise.shape) > 2: for i in range(noise.shape[2]): noise[:, :, i] = noise[:, :, 0] out = image + noise elif mode == 'localvar': # Ensure local variance input is correct if (kwargs['local_vars'] <= 0).any(): raise ValueError('All values of `local_vars` must be > 0.') # Safe shortcut usage broadcasts kwargs['local_vars'] as a ufunc out = image + np.random.normal(0, kwargs['local_vars']**0.5) elif mode == 'poisson': # Determine unique values in image & calculate the next power of two vals = len(np.unique(image)) vals = 2**np.ceil(np.log2(vals)) # Ensure image is exclusively positive if low_clip == -1.: old_max = image.max() image = (image + 1.) / (old_max + 1.) # Generating noise for each unique value in image. out = np.random.poisson(image * vals) / float(vals) # Return image to original range if input was signed if low_clip == -1.: out = out * (old_max + 1.) - 1. elif mode == 'salt': # Re-call function with mode='s&p' and p=1 (all salt noise) out = random_noise(image, mode='s&p', seed=seed, amount=kwargs['amount'], salt_vs_pepper=1., eq_chns=eq_chns) elif mode == 'pepper': # Re-call function with mode='s&p' and p=1 (all pepper noise) out = random_noise(image, mode='s&p', seed=seed, amount=kwargs['amount'], salt_vs_pepper=0., eq_chns=eq_chns) elif mode == 's&p': out = image.copy() p = kwargs['amount'] q = kwargs['salt_vs_pepper'] flipped = np.random.choice([True, False], size=image.shape, p=[p, 1 - p]) salted = np.random.choice([True, False], size=image.shape, p=[q, 1 - q]) if eq_chns and len(flipped.shape) > 2: for i in range(flipped.shape[2]): flipped[:, :, i] = flipped[:, :, 0] salted[:, :, i] = salted[:, :, 0] peppered = ~salted out[flipped & salted] = 1 out[flipped & peppered] = low_clip elif mode == 'speckle': noise = np.random.normal(kwargs['mean'], kwargs['var']**0.5, image.shape) if eq_chns and len(noise.shape) > 2: for i in range(noise.shape[2]): noise[:, :, i] = noise[:, :, 0] out = image + image * noise # Clip back to original range, if necessary if clip: out = np.clip(out, low_clip, 1.0) return out
def imread(image_path): # Takes an image file (.png, .jpg etc) and returns a 2D np.array return img_as_float(skimread(image_path))
def separate_stains(rgb, conv_matrix): rgb = dtype.img_as_float(rgb, force_copy=True).astype('float32') rgb += 2 stains = np.dot(np.reshape(-np.log(rgb), (-1, 3)), conv_matrix) return np.reshape(stains, rgb.shape)
# from invprob import signal ######################################### # This is for production only import importlib importlib.reload(wavelet) ######################################### seed = 78 # Seed for random events np.random.seed(seed=seed) dpi = 100 # Resolution for plotting (230 for small screen, 100 for large one) plt.ion() data_repo = "scripts/../data/images/" # We can blur images # comete = signal.load_image(data_repo + 'comete.png') im = img_as_float(imread(data_repo + 'comete.png')) _ = plt.figure(dpi=dpi) _ = plt.imshow(im, cmap="gray", interpolation="none") def create_kernel(kernel_size, kernel_std): x = np.concatenate( (np.arange(0, kernel_size / 2), np.arange(-kernel_size / 2, 0))) [Y, X] = np.meshgrid(x, x) kernel = np.exp((-X**2 - Y**2) / (2 * kernel_std**2)) kernel = kernel / sum(kernel.flatten()) return kernel def blur(x, h): return np.real(ifft2(fft2(x) * fft2(h)))
def makeDeconv(image): file_data = loadData(image) rgb = dtype.img_as_float(file_data, force_copy=True) rgb += 2 stains = np.dot(np.reshape(-np.log(rgb), (-1, 3)), D) saveNewFile(np.reshape(stains, rgb.shape), os.path.basename(image))
def separate_stains(rgb, color_deconv_vector): rgb = dtype.img_as_float(rgb, force_copy=True) rgb += 2 stains = np.dot(np.reshape(-np.log(rgb), (-1, 3)), color_deconv_vector) return np.reshape(stains, rgb.shape)
P = AffineTransform() P.estimate(out_quad, in_quad) output = warp(data, P, output_shape=(height + 2 * pad, width + 2 * pad)) sub_highlight = warp(highlight, P, output_shape=(height + 2 * pad, width + 2 * pad)) projection_matrix = P.params metadata['use_quad'] = True metadata['projection'] = projection_matrix.tolist() metadata['subimage'] = None else: # import ipdb; ipdb.set_trace() data_array = img_as_float(data_array) ptop = max(0, y0 - pad) pbottom = min(data.height, y1 + pad) pright = min(data.width, x1 + pad) pleft = max(0, x0 - pad) sub_image = data_array[ptop:pbottom, pleft:pright, :].copy() sub_mask = mask[ptop:pbottom, pleft:pright] sub_highlight = highlight[ptop:pbottom, pleft:pright] H = Homography(sub_image, mask=sub_mask) output = H.rectified sub_highlight = warp(sub_highlight, AffineTransform(H.H), preserve_range=True)
def getV(self): preV = np.asanyarray(self.image) preV = dtype.img_as_float(preV) return preV.max(-1)