def reconstruct(self, coeff): if self.nbands != len(coeff[1]): raise Exception("Unmatched number of orientations") height, width = coeff[0].shape[2], coeff[0].shape[1] log_rad, angle = math_utils.prepare_grid(height, width) Xrcos, Yrcos = math_utils.rcosFn(1, -0.5) Yrcos = np.sqrt(Yrcos) YIrcos = np.sqrt(np.abs(1 - Yrcos**2)) lo0mask = pointOp(log_rad, YIrcos, Xrcos) hi0mask = pointOp(log_rad, Yrcos, Xrcos) # Note that we expand dims to support broadcasting later lo0mask = torch.from_numpy(lo0mask).float()[None,:,:,None].to(self.device) hi0mask = torch.from_numpy(hi0mask).float()[None,:,:,None].to(self.device) # Start recursive reconstruction tempdft = self._reconstruct_levels(coeff[1:], log_rad, Xrcos, Yrcos, angle) hidft = torch.rfft(coeff[0], signal_ndim=2, onesided=False) hidft = math_utils.batch_fftshift2d(hidft) outdft = tempdft * lo0mask + hidft * hi0mask reconstruction = math_utils.batch_ifftshift2d(outdft) reconstruction = torch.ifft(reconstruction, signal_ndim=2) reconstruction = torch.unbind(reconstruction, -1)[0] # real return reconstruction
def reconstruct(self, coeff): if self.nbands != len(coeff[1]): raise Exception("Unmatched number of orientations") height, width = coeff[0].shape log_rad, angle = math_utils.prepare_grid(height, width) Xrcos, Yrcos = math_utils.rcosFn(1, -0.5) Yrcos = np.sqrt(Yrcos) YIrcos = np.sqrt(np.abs(1 - Yrcos**2)) lo0mask = pointOp(log_rad, YIrcos, Xrcos) hi0mask = pointOp(log_rad, Yrcos, Xrcos) tempdft = self._reconstruct_levels(coeff[1:], log_rad, Xrcos, Yrcos, angle) hidft = np.fft.fftshift(np.fft.fft2(coeff[0])) outdft = tempdft * lo0mask + hidft * hi0mask reconstruction = np.fft.ifftshift(outdft) reconstruction = np.fft.ifft2(reconstruction) reconstruction = reconstruction.real return reconstruction
def reconstruct(self, coeff): if self.nbands != len(coeff[1]): raise Exception("Unmatched number of orientations") height, width = coeff[0].shape[2], coeff[0].shape[1] log_rad, angle = math_utils.prepare_grid(height, width) Xrcos, Yrcos = math_utils.rcosFn(1, -0.5) Yrcos = np.sqrt(Yrcos) YIrcos = np.sqrt(np.abs(1 - Yrcos**2)) lo0mask = pointOp(log_rad, YIrcos, Xrcos) hi0mask = pointOp(log_rad, Yrcos, Xrcos) # Note that we expand dims to support broadcasting later lo0mask = tf.convert_to_tensor(lo0mask[None, :, :], self.dtype) hi0mask = tf.convert_to_tensor(hi0mask[None, :, :], self.dtype) tempdft = self._reconstruct_levels(coeff[1:], log_rad, Xrcos, Yrcos, angle) if coeff[0].dtype != self.dtype: hidft = tf.signal.fftshift( tf.signal.fft2d(tf.cast(coeff[0], self.dtype))) else: hidft = tf.signal.fftshift(tf.signal.fft2d(coeff[0])) outdft = tempdft * lo0mask + hidft * hi0mask reconstruction = tf.signal.ifftshift(outdft) reconstruction = tf.signal.ifft2d(reconstruction) reconstruction = tf.math.real(reconstruction) return reconstruction
def build(self, im_batch): ''' Decomposes a batch of images into a complex steerable pyramid. The pyramid typically has ~4 levels and 4-8 orientations. Args: im_batch (torch.Tensor): Batch of images of shape [N,C,H,W] Returns: pyramid: list containing torch.Tensor objects storing the pyramid ''' assert im_batch.device == self.device, 'Devices invalid (pyr = {}, batch = {})'.format(self.device, im_batch.device) assert im_batch.dtype == torch.float32, 'Image batch must be torch.float32' assert im_batch.dim() == 4, 'Image batch must be of shape [N,C,H,W]' assert im_batch.shape[1] == 1, 'Second dimension must be 1 encoding grayscale image' im_batch = im_batch.squeeze(1) # flatten channels dim height, width = im_batch.shape[2], im_batch.shape[1] # Check whether image size is sufficient for number of levels if self.height > int(np.floor(np.log2(min(width, height))) - 2): raise RuntimeError('Cannot build {} levels, image too small.'.format(self.height)) # Prepare a grid log_rad, angle = math_utils.prepare_grid(height, width) # Radial transition function (a raised cosine in log-frequency): Xrcos, Yrcos = math_utils.rcosFn(1, -0.5) Yrcos = np.sqrt(Yrcos) YIrcos = np.sqrt(1 - Yrcos**2) lo0mask = pointOp(log_rad, YIrcos, Xrcos) hi0mask = pointOp(log_rad, Yrcos, Xrcos) # Note that we expand dims to support broadcasting later lo0mask = torch.from_numpy(lo0mask).float()[None,:,:,None].to(self.device) hi0mask = torch.from_numpy(hi0mask).float()[None,:,:,None].to(self.device) # Fourier transform (2D) and shifting batch_dft = torch.rfft(im_batch, signal_ndim=2, onesided=False) batch_dft = math_utils.batch_fftshift2d(batch_dft) # Low-pass lo0dft = batch_dft * lo0mask # Start recursively building the pyramids coeff = self._build_levels(lo0dft, log_rad, angle, Xrcos, Yrcos, self.height-1) # High-pass hi0dft = batch_dft * hi0mask hi0 = math_utils.batch_ifftshift2d(hi0dft) hi0 = torch.ifft(hi0, signal_ndim=2) hi0_real = torch.unbind(hi0, -1)[0] coeff.insert(0, hi0_real) return coeff
def build(self, im_batch): ''' Decomposes a batch of images into a complex steerable pyramid. The pyramid typically has ~4 levels and 4-8 orientations. Args: im_batch (tf.Tensor or np.ndarray): Batch of images of shape [N,C,H,W] Returns: pyramid: list containing tf.Tensor objects storing the pyramid ''' assert len( im_batch.shape) == 4, 'Image batch must be of shape [N,H,W, C]' assert im_batch.shape[ -1] == 1, 'final dimension must be 1 encoding grayscale image' if type(im_batch) == np.ndarray: im_batch = tf.convert_to_tensor(im_batch, self.dtype) assert im_batch.dtype == self.dtype im_batch = tf.squeeze(im_batch, -1) # flatten channels dim height, width = im_batch.shape[2], im_batch.shape[1] # Check whether image size is sufficient for number of levels if self.height > int(np.floor(np.log2(min(width, height))) - 2): raise RuntimeError( 'Cannot build {} levels, image too small.'.format(self.height)) # Prepare a grid log_rad, angle = math_utils.prepare_grid(height, width) # Radial transition function (a raised cosine in log-frequency): Xrcos, Yrcos = math_utils.rcosFn(1, -0.5) Yrcos = np.sqrt(Yrcos) YIrcos = np.sqrt(1 - Yrcos**2) lo0mask = pointOp(log_rad, YIrcos, Xrcos) hi0mask = pointOp(log_rad, Yrcos, Xrcos) # Note that we expand dims to support broadcasting later lo0mask = tf.convert_to_tensor(lo0mask[None, :, :], self.dtype) hi0mask = tf.convert_to_tensor(hi0mask[None, :, :], self.dtype) # Fourier transform (2D) and shifting imdft = tf.signal.fft2d(im_batch) imdft = tf.signal.fftshift(imdft) # Low-pass lo0dft = imdft * lo0mask # Recursive build the steerable pyramid coeff = self._build_levels(lo0dft, log_rad, angle, Xrcos, Yrcos, self.height - 1) # High-pass hi0dft = imdft * hi0mask hi0 = tf.signal.ifft2d(tf.signal.ifftshift(hi0dft)) coeff.insert(0, tf.math.real(hi0)) return coeff
def build_c(self, im): ''' Decomposes an image into it's complex steerable pyramid. Args: im_batch (np.ndarray): single image [H,W] Returns: pyramid: list containing complex np.ndarray objects storing the pyramid ''' assert len(im.shape) == 2, 'Input im must be grayscale' height, width = im.shape # Check whether image size is sufficient for number of levels if self.height > int( np.floor( np.log2(min(width, height)) / np.log2(self.scale_factor)) - 2): raise RuntimeError( 'Cannot build {} levels, image too small.'.format(self.height)) # Prepare a grid log_rad, angle = math_utils.prepare_grid(height, width) # Radial transition function (a raised cosine in log-frequency): Xrcos, Yrcos = math_utils.rcosFn(1, -0.5) Yrcos = np.sqrt(Yrcos) YIrcos = np.sqrt(1 - Yrcos**2) lo0mask = pointOp(log_rad, YIrcos, Xrcos) hi0mask = pointOp(log_rad, Yrcos, Xrcos) # Shift the zero-frequency component to the center of the spectrum. imdft = np.fft.fftshift(np.fft.fft2(im)) # Low-pass lo0dft = imdft * lo0mask # Recursive build the steerable pyramid coeff = self._build_levels_c(lo0dft, log_rad, angle, Xrcos, Yrcos, self.height - 1, np.array(im.shape)) # High-pass hi0dft = imdft * hi0mask # hi0 = np.fft.ifft2(np.fft.ifftshift(hi0dft)) # coeff.insert(0, hi0.real) coeff.insert(0, hi0dft) return coeff