Search code examples
pythonimagenumpyscipydct

Python scipy DCT on smaller block on image not working


I am using the scipy.fftpack.dct and scipy.fftpack.idct to an image array in python. I don't however want to apply it to the entire image, but to individual 8x8 blocks in the image. This is the simple class I wrote to for testing this

from PIL import Image
import numpy as np
from scipy.fftpack import dct, idct

class ImageController():
    def __init__(self, image_name):
        im = np.asarray(Image.open(image_name))
        self.origional_size = im.shape
        im_x_dim, im_y_dim = self.origional_size
        self.image = im
        if not self.image.flags.writeable:
            self.image.flags.writeable = True

    def get(self):
        return self.image

    def display(self):
        Image.fromarray(self.image).show()
        return self

    def apply_dct(self):
        # self.image = dct(self.image, norm='ortho')
        self.loop_through_8_x_8(dct)
        return self

    def apply_idct(self):
        # self.image = idct(self.image, norm='ortho')
        self.loop_through_8_x_8(idct)
        return self

    def loop_through_8_x_8(self, appyFunc):
        print appyFunc
        row = 0
        while row < len(self.image):
            col = 0
            while col < len(self.image[row]):
                self.image[row:row+8, self.get_list(col)] = appyFunc(self.image[row:row+8, self.get_list(col)] , norm='ortho')
                col += 8
            row += 8
        print row, col
        return self;

    def get_list(self, index):
        x = []
        for i in range(index, index + 8):
            x.append(i)
        return x

The issue I am having is that when I apply DCT to the 8x8 blocks, then IDCT right afterwards all the information is lost and the image looks like a mess. All I am calling is

ImageController('lena.jpg').apply_dct().apply_idct().display()

When I run this, the image is all just noise. However if you see in apply_dct() and apply_idct() I have something commented out which is where I tried DCT and IDCT on the entire image instead of on the 8x8 blocks. When I do this it works perfectly, It just doesn't work when I try on the 8x8 blocks, and I need to apply it to the 8x8 blocks and not the entire image.

Extra information if needed, the images are in grey scale so there is only 1 channel.


Solution

  • Check the data type of your image array (self.image.dtype). It is probably 8 bit unsigned integers. The DCT will be floating point values, but when you assign the result of the DCT to the 8x8 block in place, the floating point values are cast to 8 bit integers. Then the same thing happens when you apply the IDCT.

    One option to avoid the problem is to convert the image to 64 bit floating point in __init__(), e.g, im = np.asarray(Image.open(image_name), dtype=np.float64). Whether that makes sense depends on what else you are going to do with the array.