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
Exemple #2
0
    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
Exemple #3
0
    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
Exemple #5
0
    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
Exemple #6
0
    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