如何将FFTW的2D DCT输出缩放至匹配OpenCV结果?
如何让FFTW的2D DCT输出匹配OpenCV的结果?
我正尝试让FFTW的2D DCT输出与OpenCV的2D DCT输出匹配。观察发现输出缩放规则似乎与行列数的平方根相关,当行列数互为倍数时能找到缩放规则,但非倍数时无法确定通用方案:
示例矩阵
0 1 2 3 4 5 1 2 3 4 5 6 2 3 4 5 6 7 3 4 5 6 7 8 4 5 6 7 8 9 5 6 7 8 9 10 6 7 8 9 10 11 7 8 9 10 11 12
OpenCV输出
[41.56921938165306, -11.77350269189626, -9.906343049429816e-16, -1.154700538379252, 2.654396620040385e-16, -0.2264973081037412; -15.78040416381215, 0, 0, 0, 0, 0; 6.797823324068903e-16, 0, 0, 0, 0, 0; -1.649620627042291, 0, 0, 0, 0, 0; 0, 0, 0, 0, 0, 0; -0.4921096019966774, 0, 0, 0, 0, 0; -1.64113972635833e-15, 0, 0, 0, 0, 0; -0.1241948195350329, 0, 0, 0, 0, 0]
FFTW输出
1152 -230.713 0 -22.6274 0 -4.43842 -309.232 0 0 0 0 0 0 0 0 0 0 0 -32.3258 0 0 0 0 0 0 0 0 0 0 0 -9.64334 0 0 0 0 0 0 0 0 0 0 0 -2.43371 0 0 0 0 0
通用缩放方案
问题核心在于FFTW和OpenCV实现的DCT-II采用了不同的缩放规则:
- FFTW的
FFTW_REDFT10(对应DCT-II)输出是未归一化的原始变换结果,2D变换中行和列各乘2,整体缩放因子为4。 - OpenCV的
cv::dct采用正交归一化的DCT-II,会根据变换位置的索引(是否为0)应用不同的缩放系数。
要将FFTW输出转换为OpenCV格式,对每个元素(i,j)应用以下缩放规则:
double scale_i = (i == 0) ? (1.0 / (2 * sqrt(rows))) : (1.0 / sqrt(2 * rows)); double scale_j = (j == 0) ? (1.0 / (2 * sqrt(cols))) : (1.0 / sqrt(2 * cols)); double opencv_val = fftw_val * scale_i * scale_j;
验证示例
以示例中的元素为例:
- 位置
(0,0):scale_i=1/(2*sqrt(8))≈0.1768,scale_j=1/(2*sqrt(6))≈0.2042,1152*0.1768*0.2042≈41.569,与OpenCV输出一致。 - 位置
(0,1):scale_i=0.1768,scale_j=1/sqrt(12)≈0.2887,-230.713*0.1768*0.2887≈-11.7735,匹配OpenCV结果。 - 位置
(1,0):scale_i=1/sqrt(16)=0.25,scale_j=0.2042,-309.232*0.25*0.2042≈-15.7804,完全吻合。
修改后的代码示例
在FFTW计算完成后,对输出矩阵应用上述缩放即可:
#include <opencv2/core/core.hpp> #include <fftw3.h> #include <iostream> #include <cmath> void compute2Ddct(const double* input, double* output, int rows, int cols) { fftw_plan plan = fftw_plan_r2r_2d(rows, cols, const_cast<double*>(input), output, FFTW_REDFT10, FFTW_REDFT10, FFTW_ESTIMATE); fftw_execute(plan); fftw_destroy_plan(plan); } void scaleFFTWtoOpenCV(double* fftw_output, double* opencv_scaled, int rows, int cols) { for (int i = 0; i < rows; ++i) { double scale_i = (i == 0) ? (1.0 / (2 * sqrt(rows))) : (1.0 / sqrt(2 * rows)); for (int j = 0; j < cols; ++j) { double scale_j = (j == 0) ? (1.0 / (2 * sqrt(cols))) : (1.0 / sqrt(2 * cols)); opencv_scaled[i * cols + j] = fftw_output[i * cols + j] * scale_i * scale_j; } } } int main() { const int rows = 8; const int cols = 6; double image[rows * cols]; for (int i = 0; i < rows; ++i) { for (int j = 0; j < cols; ++j) { image[i * cols + j] = i + j; } } // OpenCV DCT计算 cv::Mat opencv_dct(rows, cols, CV_64F, image); opencv_dct = opencv_dct.clone(); cv::dct(opencv_dct, opencv_dct, 0); std::cout << "OpenCV DCT输出:\n" << opencv_dct << std::endl; // FFTW DCT计算及缩放 double dct_output[rows * cols]; compute2Ddct(image, dct_output, rows, cols); double scaled_output[rows * cols]; scaleFFTWtoOpenCV(dct_output, scaled_output, rows, cols); std::cout << "\nFFTW缩放后输出:\n"; for (int i = 0; i < rows; ++i) { for (int j = 0; j < cols; ++j) { std::cout << scaled_output[i * cols + j] << " "; } std::cout << std::endl; } return 0; }
内容的提问来源于stack exchange,提问作者Iordan Bogdan
相关产品推荐
相关产品推荐

