def denoise(data, num=None, snr=None): """ Denoises nd array with the format n x p x b Parameters: ----------- data : nd array 3-d numpy array with b = band num : int number of bands used snr : int threshold Returns ------- denoised array with same shape as data """ signal = spectral.calc_stats(data) noise = spectral.noise_from_diffs(data) mnfr = spectral.mnf(signal, noise) if num: denoised, trans = mnfr.denoise(data, num=num) print(50*'_') print(trans.shape) elif snr: denoised = mnfr.denoise(data, snr=snr) print("--------------") print(mnfr.num_with_snr(snr=snr)) else: raise ValueError('"snr" or "num" must be given!') return denoised, trans
def test_mnf_all_equals_data(self): '''Test that MNF transform with all components equals original data.''' data = self.data signal = spy.calc_stats(data) noise = spy.noise_from_diffs(data[117:137, 85:122, :]) mnfr = spy.mnf(signal, noise) denoised = mnfr.denoise(data, num=data.shape[-1]) assert_allclose(denoised, data)
def test_mnf_all_equals_data(self): '''Test that MNF transform with all components equals original data.''' data = self.data signal = spy.calc_stats(data) noise = spy.noise_from_diffs(data[117: 137, 85: 122, :]) mnfr = spy.mnf(signal, noise) denoised = mnfr.denoise(data, num=data.shape[-1]) assert(np.allclose(denoised, data))
with rasterio.open(instack) as intif: stack = intif.read() land = stack[7, :, :] > 500 np.count_nonzero(land) out_meta = intif.meta.copy() (stack[:, land]) = 0 t_stack = np.transpose(stack, (1, 2, 0)) help(spectral.calc_stats) ss = stack.shape[0] print(t_stack.shape) # help(spectral.calc_stats) #view =imshow(t_stack,(8,3,2)) signal = spectral.calc_stats(t_stack) noise = spectral.noise_from_diffs(t_stack) mnfr = spectral.mnf(signal, noise) t_denoised = mnfr.denoise(t_stack, num=5) t_reduced = mnfr.reduce(t_stack, num=5) t_reduced.shape t_denoised.shape tt_denoised = np.transpose(t_denoised, (2, 0, 1)) tt_reduced = np.transpose(t_reduced, (2, 0, 1)) tt_stack = np.transpose(t_stack, (2, 0, 1)) tt_reduced.shape out_meta['count'] = 13 tt_denoised.shape tt_denoised.shape np.max(tt_denoised[2:, ]) np.min(tt_denoised[1:, ]) view = spectral.imshow(t_denoised, (1, 3, 2)) for i in range(0, out_meta['count']):
def MNF(hydata, output_bands=20, denoise_bands=40, band_range=None, inplace=False): """ Apply a minimum noise filter to a hyperspectral image. *Arguments*: - hydata = A HyData instance containing the source dataset (e.g. image or point cloud). - output_bands = the number of bands to keep after MNF (dimensionality reduction). Default is 20. - denoise_bands = number of high-noise bands to treat as noise for denoising. - band_range = the spectral range to perform the MNF over. If (int,int) is passed then the values are treated as min/max band IDs, if (float,float) is passed then values are treated as wavelenghts (in nm). If None is passed (default) then the MNF is computed using all bands. Note that wavelengths can only be passed if image is a hyImage object. - inplace = True if the original image should be denoised based on the MNF transform. Default is False. *Returns*: - mnf = a HyData instance containing the MNF bands.Note that only bands 0:*out_bands* will be kept in this dataset. - factors = A 2D numpy array containing the factors applied to the input datset. Useful for plotting/interpreting the regions each MNF band is sensitive too. """ # prepare data for MNF wav = hydata.get_wavelengths() decomp = False if hydata.is_int(): hydata.decompress() #MNF doesn't work very well with ints.... decomp = True #so we can compress again afterwards data = hydata.data # get range of bands to include in calculation if band_range is None: # default to all bands minb = 0 maxb = data.shape[-1] else: minb = hydata.get_band_index(band_range[0]) maxb = hydata.get_band_index(band_range[1]) assert minb < maxb, "Error - invalid range... band_range[0] > band_range[1]??" assert minb < data.shape[-1], "Error - band_range[0] out of range." if maxb == -1 or maxb > data.shape[-1]: maxb = data.shape[-1] # remove invalid bands valid_bands = [] for b in range(minb, maxb): if np.isfinite(data[..., b]).any() \ and not (np.nanmax(data[..., b]) == 0).all(): valid_bands.append(b) #remove invalid bands data = np.array(data[..., valid_bands]) # warn if bands have negative values... if np.nanmin(data) < 0.0: print( "Warning - image contains negative pixels. This can cause unstable behaviour..." ) # calculate signal stats (as in spectral.calc_stats(...) but allowing for nans) X = data.reshape( -1, data.shape[-1]).T # reshape to 1D list of pixels for each band X = X[:, np.isfinite(np.sum(X, axis=0))] # drop columns containing nans X = X[:, np.sum(X, axis=0) > 0] #drop columns containing all zeros mean = np.mean(X, axis=1) cov = np.cov(X) n = X.shape[1] signal = spectral.GaussianStats(mean, cov, n) # calculate noise as per spectral.noise_from_diffs (but allowing for nans) if len(data.shape) == 3: # image data deltas = data[:-1, :-1, :] - data[ 1:, 1:, :] #estimate noise by subtracting adjacent pixels elif len(data.shape) == 2: #point cloud data deltas = data[:-1, :] - data[ 1:, :] # estimate noise by subtracting adjacent points X = deltas.reshape(-1, deltas.shape[-1]).T X = X[:, np.isfinite(np.sum(X, axis=0))] # drop columns containing nans X = X[:, np.sum(X, axis=0) > 0] # drop columns containing all zeros X = X[:, np.sum(X, axis=0) < np.nanpercentile(np.sum( X, axis=0), 50)] #drop high noise data (these relate to edges) mean = np.mean(X, axis=1) cov = np.cov(X) n = X.shape[1] noise = spectral.GaussianStats(mean, cov, n) mnfr = spectral.mnf(signal, noise) # reduce bands reduced = mnfr.reduce(data, num=output_bands) #apply sign correction so there are less positive pixels than negative ones (sign is aribrary, helps maintain #consistency for plotting etc. by having low-valued background with some high-value regions (<50%) sign = np.nanmedian(reduced / np.abs(reduced)) #n.b. this will always be 1.0 or -1.0 assert np.isfinite(sign), "Weird error - no non-nan values in MNF result?" reduced *= sign # denoise and export denoise = mnfr.denoise(data, num=denoise_bands) #update original image bands? if inplace: data[..., valid_bands] = denoise #calculate factors (for determining "important" bands) # noinspection PyProtectedMember factors = sign * mnfr.get_reduction_transform(num=output_bands)._A if not wav is None: wav = wav[valid_bands] #compress input dataset (so we don't change it) if decomp: hydata.compress() #prepare output out = hydata.copy(data=False) out.header.drop_all_bands() # drop band specific attributes out.data = reduced out.push_to_header() return out, factors