I'm trying to perform a basic JPEG Compression (DCT + quantization + IDCT) using OpenCV not using entropy-encoding/Huffman-coding. The problem is that after I decompress the compressed image, it is not even close in appearance to the original one.
I'm following these tutorials:
Basic JPEG Compressing/Decompressing Simulation
Basic JPEG Compression using OpenCV
Following are the 3 images (original, compressed and decompressed images):
I'm using the following matrix to luminance and chrominance:
double dataLuminance[8][8] = {
{16, 11, 10, 16, 24, 40, 51, 61},
{12, 12, 14, 19, 26, 58, 60, 55},
{14, 13, 16, 24, 40, 57, 69, 56},
{14, 17, 22, 29, 51, 87, 80, 62},
{18, 22, 37, 56, 68, 109, 103, 77},
{24, 35, 55, 64, 81, 104, 113, 92},
{49, 64, 78, 87, 103, 121, 120, 101},
{72, 92, 95, 98, 112, 100, 103, 99}
};
double dataChrominance[8][8] = {
{17, 18, 24, 27, 99, 99, 99, 99},
{18, 21, 26, 66, 99, 99, 99, 99},
{24, 26, 56, 99, 99, 99, 99, 99},
{47, 66, 99, 99, 99, 99, 99, 99},
{99, 99, 99, 99, 99, 99, 99, 99},
{99, 99, 99, 99, 99, 99, 99, 99},
{99, 99, 99, 99, 99, 99, 99, 99},
{99, 99, 99, 99, 99, 99, 99, 99}
};
// EDIT 1: @Micka told about the problem of using imread/imwrite, so I edited my code to use the compressed image directly from my program.
The compression method is:
void ImageCompression::compression(){
// Getting original image size
int height = imgOriginal.size().height;
int width = imgOriginal.size().width;
// Converting image color
Mat imgColorConverted;
cvtColor(imgOriginal, imgColorConverted, CV_BGR2YCrCb);
// Transforming 2D Array in Image Matrix
Mat luminance = Mat(8,8, CV_64FC1, &dataLuminance);
Mat chrominance = Mat(8,8, CV_64FC1, &dataChrominance);
cout << "Luminance: " << luminance << endl << endl;
cout << "Chrominance" << chrominance << endl << endl;
// Splitting the image into 3 planes
vector<Mat> planes;
split(imgColorConverted, planes);
// Downsampling chrominance
// Resizing to 1/4 of original image
resize(planes[1], planes[1], Size(width/2, height/2));
resize(planes[2], planes[2], Size(width/2, height/2));
// Resizing to original image size
resize(planes[1], planes[1], Size(width, height));
resize(planes[2], planes[2], Size(width, height));
// Dividing image in blocks 8x8
for ( int i = 0; i < height; i+=8 ){
for( int j = 0; j < width; j+=8 ){
// For each plane
for( int plane = 0; plane < imgColorConverted.channels(); plane++ ){
// Creating a block
Mat block = planes[plane](Rect(j, i, 8, 8));
// Converting the block to float
block.convertTo( block, CV_64FC1 );
// Subtracting the block by 128
subtract( block, 128.0, block );
// DCT
dct( block, block );
// Applying quantization
if( plane == 0 ){
divide( block, luminance, block );
}
else {
divide( block, chrominance, block );
}
// Converting it back to unsigned int
block.convertTo( block, CV_8UC1 );
// Copying the block to the original image
block.copyTo( planes[plane](Rect(j, i, 8, 8)) );
}
}
}
merge( planes, finalImage );
}
And my decompression method:
ImageCompression::decompression{
// Getting the size of the image
int height = finalImage.size().height;
int width = finalImage.size().width;
// Transforming 2D Array in Image Matrix
Mat luminance = Mat(8,8, CV_64FC1, &dataLuminance);
Mat chrominance = Mat(8,8, CV_64FC1, &dataChrominance);
// Splitting the image into 3 planes
vector<Mat> planes;
split(finalImage, planes);
// Dividing the image in blocks 8x8
for ( int i = 0; i < height; i+=8 ){
for( int j = 0; j < width; j+=8 ){
// For each plane
for( int plane = 0; plane < finalImage.channels(); plane++ ){
// Creating a block
Mat block = planes[plane](Rect(j, i, 8, 8));
// Converting the block to float
block.convertTo( block, CV_64FC1 );
// Applying dequantization
if( plane == 0 ){
multiply( block, luminance, block );
}
else {
multiply( block, chrominance, block );
}
// IDCT
idct( block, block );
// Adding 128 to the block
add( block, 128.0, block );
// Converting it back to unsigned int
block.convertTo( block, CV_8UC1 );
// Copying the block to the original image
block.copyTo( planes[plane](Rect(j, i, 8, 8)) );
}
}
}
merge(planes, finalImage);
cvtColor( finalImage, finalImage, CV_YCrCb2BGR );
imshow("Decompressed image", finalImage);
waitKey(0);
imwrite(".../finalResult.jpg", finalImage);
}
Does someone have any idea of why I'm getting that resulting image?
Thank you.
Best Answer
You need to add 128 back to the block before converting it back to unsigned int and then subtract it again in decompression.
.