matlab - 为什么 OpenCV 中的 filter2D 给出的结果与 Matlab 中的 imfilter 不同?
问题描述
我有一个原始图像:
然后我阅读它,创建一个 PSF,并在 Matlab 中对其进行模糊处理:
lenawords1=imread('lenawords.bmp');
%create PSF
sigma=6;
PSFgauss=fspecial('gaussian', 8*sigma+1, sigma);
%blur it
lenablur1=imfilter(lenawords1, PSFgauss, 'conv');
lenablurgray1=mat2gray(lenablur1);
PSFgauss1 = PSFgauss/max(PSFgauss(:));
我保存了模糊的图像:
imwrite(lenablurgray1, 'lenablur.bmp');
imwrite(PSFgauss1, 'PSFgauss.bmp');
它们在 Matlab 和 OpenCV 中的值匹配。
MATLAB:
disp(lenablurgray1(91:93, 71:75)*256)
142.2222 147.9111 153.6000 159.2889 164.9778
153.6000 164.9778 170.6667 176.3556 176.3556
164.9778 176.3556 182.0444 187.7333 187.7333
disp(PSFgauss1(24:26, 24:26)*256)
248.9867 252.4690 248.9867
252.4690 256.0000 252.4690
248.9867 252.4690 248.9867
开放式简历:
Mat img = imread("lenablur.bmp");
cvtColor(img, img, cv::COLOR_BGR2GRAY);
cv::Mat kernel = imread("PSFgauss.bmp");
cvtColor(kernel, kernel, cv::COLOR_BGR2GRAY);
for (int r = 90; r < 93; r++) {
for (int c = 70; c < 75; c++) {
cout << (int)img.at<uchar>(r, c) << " ";
}
cout << endl;
}
142 147 153 159 164
153 164 ...
164 ...
cout << "PSF" << endl;
for (int r = 23; r < 26; r++) {
for (int c = 23; c < 26; c++) {
cout << (int)kernel.at<uchar>(r, c) << " ";
}
cout << endl;
}
248 251 248
251 255 251
248 251 248
但是,filter2D
OpenCV 和imfilter
Matlab 中的值不匹配:
MATLAB:
conv1=imfilter(lenablurgray1, PSFgauss1, 'conv');
disp(conv1(91:93, 71:75))
91.8094 96.1109 99.8904 103.1280 105.8210
97.3049 101.7757 105.6828 109.0073 111.7486
102.0122 106.5953 110.5755 113.9353 116.6769
开放式简历:
Mat conv1;
filter2D(img, conv1, img.depth(), kernel, Point(-1, -1), 0,
BORDER_REFLECT);
for (int r = 90; r < 93; r++) {
for (int c = 70; c < 75; c++) {
cout << (int)conv1.at<uchar>(r, c) << " ";
}
cout << endl;
}
255 255 255 255 255
255 255 255 255 255
255 255 255 255 255
为什么filter2D
价值观错了?
编辑2:
cv::Mat kernel = imread("PSFgauss.bmp");
cvtColor(kernel, kernel, cv::COLOR_BGR2GRAY);
kernel.convertTo(kernel, CV_64F);
cv::Scalar kernelsum= cv::sum(kernel);
divide(kernel, kernelsum, kernel);
filter2D(img, conv1, img.depth(), kernel, Point(-1, -1), 0, BORDER_REFLECT);
for (int r = 90; r < 93; r++) {
for (int c = 70; c < 75; c++) {
cout << (int)conv1.at<uchar>(r, c) << " ";
}
给
103 108 112 116 119
109 ..
115 ..
conv1
与当乘以因子 1.133 时的 Matlab 值相匹配
disp(conv1(91:93, 71:75) * 1.133)
104.0201 108.8937 113.1758 116.8441 119.8952
110.2464 115.3118 119.7386 123.5053 126.6112
115.5798 120.7725 125.2820 129.0887 132.1950
但是,当我除以 时,值会有所img
不同conv1
:
MATLAB:
conv2 = lenablurgray1./conv1
disp(conv2(91:93, 71:75))
0.0061 0.0060 0.0060 0.0060 0.0061
0.0062 0.0063 0.0063 0.0063 0.0062
0.0063 0.0065 0.0064 0.0064 0.0063
开放式简历:
Mat conv2;
divide(img, conv1, conv2);
for (int r = 90; r < 93; r++) {
for (int c = 70; c < 75; c++) {
cout << (int)conv2.at<uchar>(r, c) << " ";
}
cout << endl;
}
1 1 1 1 1
1 1 ...
1 ...
为什么是这样?
解决方案
当你这样做
lenablur1 = imfilter(lenawords1, PSFgauss, 'conv');
在 MATLAB 中,PSFgauss
是标准化的。这意味着它的值总和为 1:
sum(PSFgauss(:)) == 1.0 % or at least it should be very close
接下来,您对其进行缩放,使其最大值为 1,以便您可以将其保存为 BMP 文件。这还导致将值四舍五入为 256 个不同的整数。
然后,在 OpenCV 中,您使用 读入内核imread("PSFgauss.bmp")
,并转换回灰度值图像。这导致内核具有 [0,255] 范围内的整数值。特别是,它没有被标准化。
然后在卷积中发生的事情是将每个内核元素乘以图像像素,并将所有值相加以产生一个输出值。如果内核被归一化,这相当于加权平均。如果内核未归一化,则不会保留平均图像强度。由于这里的内核具有比原来大得多的值,因此输出值将比输入图像的值大得多。因为输入图像是一个 8 位无符号整数,并且 OpenCV 使用饱和加法,所以运算结果每个像素的值为 255。
在数学符号中,在 MATLAB 中你可以
g = f * k
(* 是卷积,f是图像,k是内核)。在 OpenCV 你做
g' = f * Ck
(其中C是一个大约等于 的常数255/max(PSFgauss(:)
,它是在从 MATLAB 到 OpenCV 的转换过程中与内核相乘的因子)。
因此,除以C应该会使内核恢复到您在 MATLAB 中使用它进行卷积时的状态。但请注意,您将无法移除舍入效果。
在 OpenCV中推导Ckernel
的最简单方法是除以它的总和:
kernel.convertTo(kernel, CV_64F);
kernel /= cv::sum(kernel);
推荐阅读
- c# - 在 web.config 中添加 URL 并在控制器中访问它
- visual-studio - 如何在 Visual Studio 2019 (16.2.5) 中使用 .NET Code SDK v3 (RC1)
- ionic4 - Ionic 4 - 关闭相机查看后黑屏
- redmine - Redmine 3.3.0 有时会发送带有更改的电子邮件,有时不会
- django - 警告当前用户退出帐户
- python - 有没有办法将文本文件保存在变量中?
- python - 失败时将错误字符串从 python 脚本发送到 jenkins 作业
- unity3d - Unity VR UNET 让 PC 和 VR 成为同一个播放器
- javascript - 对象正在与对象数组合并
- javascript - 如何在弹出警报中创建下拉按钮?