Reputation: 1355
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 ?
Upvotes: 3
Views: 216
Reputation: 60494
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
.
Upvotes: 2