filter2D 实现的差异

Differences in filter2D implementation

我试图实现 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 有很大不同。是因为可能溢出还是我的实现有问题?

谢谢

您的代码有两个问题:

  1. 在向输出图像添加值之前不要将输出图像设置为零。因此,您正在计算 "input + filtered input",而不仅仅是 "filtered input".

  2. 假设 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]。中间的循环通常是迄今为止最大的循环,不需要检查越界读取。

并使用cv::saturate_cast来正确赋值给uchar,例如:

filtered_image.at<uchar>(Point(j, i)) = cv::saturate_cast<uchar>(res);