使用OpenCV进行JPEG压缩

3
我正在尝试使用OpenCV执行基本的JPEG压缩(DCT + 量化 + IDCT),而不是使用熵编码/霍夫曼编码。问题是,当我解压缩压缩后的图像时,它的外观与原始图像相差甚远。我正在按照这些教程进行操作:基本JPEG压缩/解压模拟使用OpenCV进行基本JPEG压缩。以下是3个图像(原始、压缩和解压缩图像):Original Image

Compressed Image

Final Image

我正在使用以下矩阵来处理亮度和色度:
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提到了使用imread / imwrite存在的问题,因此我编辑了我的代码,直接从程序中使用压缩图像。

压缩方法是:

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 );
}

我的解压方法:

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);
}

有人知道为什么我得到了这张结果图片吗?

谢谢。


我会从消除问题开始。尝试将你的量化表设置为全1。 - user3344003
@user3344003 我尝试将量化表设置为1;我注意到了一些差异,颜色似乎更加“鲜艳”,我能看到更多的细节,这是结果:新解压缩图像 - Alexandre Lara
2
我没有分析代码,但是 imwrite(".../result.jpg", finalImage); 可能会自己进行一些 JPEG 压缩,并且可能将您现有的图像通道解释为 BGR 图像。请尝试避免使用 imwrite/imread 进行保存和加载,而是直接使用压缩后的图像进行测试解压缩(无需从文件保存/加载)。Mat finalImage = imread(".../result.jpg"); 将生成一个 BGR 图像,无论您尝试保存什么。 - Micka
@Micka 我按照你说的做了,结果比之前好了一些,但还不完美。以下是处理后的图片:图片 - Alexandre Lara
我对压缩算法一无所知,所以无法再提供帮助,抱歉。 - Micka
我正好相反,我知道JPEG但不知道OpenCV。我可以尝试上面的方法,并将量化表设置为1。我怀疑这可能是个问题:// 转换回unsigned int block.convertTo(block, CV_8UC1);如果您取8位值并运行它们通过DCT,您需要16位来表示它们。 - user3344003
1个回答

0

在解压缩时,您需要将块转换回无符号整数之前先加上128,然后再次减去它。

            add(block, 128.0, block);

            // Converting it back to unsigned int
            block.convertTo(block, CV_8UC1);

.

        // Converting the block to float
        block.convertTo(block, CV_64FC1);

        subtract(block, 128.0, block);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接