为什么OpenCV中的filter2D与Matlab中的imfilter给出不同的结果?

时间:2018-09-20 18:33:08

标签: matlab opencv image-processing convolution

我有原始图片:

enter image description here

然后我阅读它,创建一个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

OpenCV:

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

但是,OpenCV中filter2D和Matlab中imfilter的值不匹配:

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

OpenCV:

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值错误?

EDIT2:

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的Matlab值乘以系数1.133匹配

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

OpenCV:

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 ...

这是为什么?

1 个答案:

答案 0 :(得分:0)

这样做的时候

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中派生 C 的最简单方法是将kernel除以它的和:

kernel.convertTo(kernel, CV_64F);
kernel /= cv::sum(kernel);