我试图实现convolute2D
(在OpenCV中为filter2D
),并提出了以下代码。
Mat convolute2D(Mat image, double** kernel, int W){
Mat filtered_image = image.clone();
// find center position of kernel (half of kernel size)
int kCenterX = W / 2;
int kCenterY = W / 2;
int xx = 0;
int yy = 0;
cout << endl << "Performing convolution .." << endl;
cout << "Image Size : " << image.rows << ", " << image.cols <<endl;
for (int i = 0; i < image.rows; ++i){
for (int j = 0; j < image.cols; ++j){
for(int x = 0; x < W; ++x){
xx = W - 1 - x;
for(int y = 0; y < W; ++y){
yy = W - 1 - y;
int ii = i + (x - kCenterX);
int jj = j + (y - kCenterY);
if( ii >= 0 && ii < image.rows && jj >= 0 && jj < image.cols) {
filtered_image.at<uchar>(Point(j, i)) += image.at<uchar>(Point(jj, ii)) * kernel[xx][yy];
}
}
}
}
}
return filtered_image;
}
假设我们总是有一个正方形核。但是我的结果与
filter2D
有很大不同。是因为可能的溢出,还是我的实现存在问题?谢谢
最佳答案
您的代码有两个问题:
kernel
的值非常小,则“input pixel * kernel value”可能会产生一个很小的数字,当写入uchar
时会四舍五入。将所有这些值加到内核中,最终结果将太低。 我建议您这样做:
double res = 0;
for(int x = 0; x < W; ++x){
int xx = W - 1 - x;
for(int y = 0; y < W; ++y){
int yy = W - 1 - y;
int ii = i + (x - kCenterX);
int jj = j + (y - kCenterY);
if( ii >= 0 && ii < image.rows && jj >= 0 && jj < image.cols) {
res += image.at<uchar>(Point(jj, ii)) * kernel[xx][yy];
}
}
}
filtered_image.at<uchar>(Point(j, i)) = res;
这可以立即解决两个问题。同样,这应该更快一些,因为访问输出镜像会有一些开销。
要获得更快的速度,请考虑检查越界读取(内部循环中的
if
)会显着降低代码速度,并且对于大多数像素(由于很少的像素靠近图像边缘)完全不需要。相反,您可以将循环拆分为[0,kCenterX]
,[kCenterX,image.rows-kCenterX]
和[image.rows-kCenterX,image.rows]
。中间循环(通常是迄今为止最大的循环)将不需要检查越界读取。关于c++ - filter2D实现中的差异,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/54283585/