简体   繁体   中英

Opencv average filter gives different output than Matlab average filter

The following OpenCV code is written to serve as a diagnostic to compare average filter implementation in Matlab and OpenCV. The OpenCV code is

Mat P(Size(5,5),CV_64FC1,Scalar(0));

for(int i = 0; i < 5; i++)
{
    for (int j = 0; j < 5 ; j++)
        P.at<double>(i,j) = i;
}


cout<<"Original Matrix is :"<<endl;
cout<<P<<endl;

Mat averageFilter(2,2,CV_64FC1,Scalar(0)),U;
averageFilter = cv::Scalar::all(1.0/(2*2));


filter2D(P, U, -1 , averageFilter, Point( -1, -1 ), 0, BORDER_REPLICATE );
cout<<"Filtered Matrix is :"<<endl;
cout<<U<<endl;  

The output is

Original Matrix is :
[0, 0, 0, 0, 0;
  1, 1, 1, 1, 1;
  2, 2, 2, 2, 2;
  3, 3, 3, 3, 3;
  4, 4, 4, 4, 4]
Filtered Matrix is :
[0, 0, 0, 0, 0;
  0.5, 0.5, 0.5, 0.5, 0.5;
  1.5, 1.5, 1.5, 1.5, 1.5;
  2.5, 2.5, 2.5, 2.5, 2.5;
  3.5, 3.5, 3.5, 3.5, 3.5]

Matlab code to replicate the same operation is :

ma = [0 0 0 0 0;1 1 1 1 1;2 2 2 2 2;3 3 3 3 3;4 4 4 4 4];
MEANF = fspecial('average',[2 2]);
U = imfilter(ma, MEANF, 'replicate');

The output is

U =

    0.5000    0.5000    0.5000    0.5000    0.5000
    1.5000    1.5000    1.5000    1.5000    1.5000
    2.5000    2.5000    2.5000    2.5000    2.5000
    3.5000    3.5000    3.5000    3.5000    3.5000
    4.0000    4.0000    4.0000    4.0000    4.0000

What is the reason for discrepancy between the two outputs ?

The difference you are seeing is due to the different choice of where the origin within the even-sized filter is. For odd-sized kernels, software tends to be consistent and pick the middle pixel as the origin. But for even-sized kernels there are two choices that make equal sense.

I can replicate the OpenCV output in MATLAB by changing the origin of the filter:

ma = repmat((0:4).',1,5);
filt = zeros(3);
filt(2:3,2:3) = 1/4;
U = imfilter(ma,filt,'replicate')
filt = rot90(filt,2);
V = imfilter(ma,filt,'replicate')

This gives me the same U as you had:

U =
    0.5000    0.5000    0.5000    0.5000    0.5000
    1.5000    1.5000    1.5000    1.5000    1.5000
    2.5000    2.5000    2.5000    2.5000    2.5000
    3.5000    3.5000    3.5000    3.5000    3.5000
    4.0000    4.0000    4.0000    4.0000    4.0000

And the V is what you saw in OpenCV:

V =
         0         0         0         0         0
    0.5000    0.5000    0.5000    0.5000    0.5000
    1.5000    1.5000    1.5000    1.5000    1.5000
    2.5000    2.5000    2.5000    2.5000    2.5000
    3.5000    3.5000    3.5000    3.5000    3.5000

More instructive is creating an input that is all zeros except for one value in the middle:

ma = zeros(5);
ma(3,3) = 1;
filt = zeros(3);
filt(2:3,2:3) = 1/4;
U = imfilter(ma,filt,'replicate')
filt = rot90(filt,2);
V = imfilter(ma,filt,'replicate')

Now I see:

U =
         0         0         0         0         0
         0    0.2500    0.2500         0         0
         0    0.2500    0.2500         0         0
         0         0         0         0         0
         0         0         0         0         0

V =
         0         0         0         0         0
         0         0         0         0         0
         0         0    0.2500    0.2500         0
         0         0    0.2500    0.2500         0
         0         0         0         0         0

Here it is clear that the kernel was shifted by one pixel. Again, with fspecial('average',[2 2]) you'd get the result of U , and if you replicate this in OpenCV you'll see an output like V .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM