如何使用OpenCV C++离散余弦变换(DCT)及逆变换恢复原始图像
错误原因
- 最核心的问题是对DCT系数的格式转换操作完全错误:OpenCV的
dct接口输出的是CV_32FC1类型的矩阵,系数包含正负浮点值,且数值范围远超出0~255区间,直接调用DCT.convertTo(DCTconv, CV_8UC1)会强制截断所有小于0的值为0、大于255的值为255,几乎丢失了全部有效系数信息,逆变换自然无法还原原始图像。 - 次要逻辑问题:
Image2ByteList函数内存在冗余的k++操作,每次内循环都会重新计算k的取值,多余的自增操作无实际作用,属于不严谨的代码写法。
修正方案
如果你的需求是无损存储DCT系数用于后续逆变换,直接存储原始浮点类型的DCT系数即可,不要转成8位无符号格式,修正后的参考代码如下:
#include <iostream> #include <fstream> #include <vector> #include <opencv2/core/core.hpp> #include <opencv2/highgui/highgui.hpp> #include <opencv2/imgproc.hpp> #include <opencv2/imgcodecs.hpp> // 存储浮点型DCT系数到vector void DCT2FloatList(const cv::Mat& dctMat, std::vector<float>& coeffList){ coeffList.resize(dctMat.rows * dctMat.cols); for (int i = 0; i < dctMat.rows; i++) { for (int j = 0; j < dctMat.cols; j++) { int k = i * dctMat.cols + j; coeffList[k] = dctMat.at<float>(i, j); } } // 可选:存储系数到文本文件 std::ofstream fout("DCTList.txt"); for (size_t i = 0; i < coeffList.size(); i++) { fout << coeffList[i] << " "; } } // 从vector恢复DCT系数矩阵 void floatList2DCT(const std::vector<float>& coeffList, cv::Mat& dctMat, int rows, int cols) { cv::Mat newMat(rows, cols, CV_32FC1); for (int k = 0; k < coeffList.size(); k++) { newMat.at<float>(k / cols, k % cols) = coeffList[k]; } dctMat = newMat.clone(); } void function() { cv::Mat img, imgGray, imgConv, DCT, recoverDCT, recoverImg; img = cv::imread("jpg_44.jpg"); cv::cvtColor(img, imgGray, cv::COLOR_BGR2GRAY); imgGray.convertTo(imgConv, CV_32FC1); // 正向DCT变换 cv::dct(imgConv, DCT); // 存储DCT系数 std::vector<float> coeffsList; DCT2FloatList(DCT, coeffsList); // 恢复DCT系数矩阵 floatList2DCT(coeffsList, recoverDCT, DCT.rows, DCT.cols); // 逆DCT变换 cv::idct(recoverDCT, recoverImg); // 逆变换输出是32F格式,转成8U再保存 recoverImg.convertTo(recoverImg, CV_8UC1); cv::imwrite("Test.jpg", recoverImg); } int main() { function(); return 0; }
如果你确实有需求要把DCT系数压缩成字节存储,需要先对DCT系数做归一化处理,把所有值映射到0~255区间,逆变换前再映射回原始数值范围,不能直接做格式截断。
内容的提问来源于stack exchange,提问作者Popescu Daniel
相关产品推荐
相关产品推荐

