Opencv卷积图像的最简单方法?

时间:2019-11-16 13:44:42

标签: c++ opencv

我有一个像这样的手写卷积函数:


void convolution(Mat &input, int size, int direction, Mat kernel, Mat &output) {

    int kernelRadiusX = (kernel.size[0] - 1) / 2;
    int kernelRadiusY = (kernel.size[1] - 1) / 2;

    // Create padded version of input
    Mat paddedInput;
    copyMakeBorder(input, paddedInput,
                   kernelRadiusX, kernelRadiusX, kernelRadiusY, kernelRadiusY,
                   BORDER_REPLICATE);


    GaussianBlur(paddedInput, paddedInput, Size(3, 3), 0, 0, BORDER_DEFAULT);



    for (int i = 0; i < input.rows; i++) {
        for (int j = 0; j < input.cols; j++) {

            double sum = 0.0;

            for (int m = -kernelRadiusX; m <= kernelRadiusX; m++) {
                for (int n = -kernelRadiusY; n <= kernelRadiusY; n++) {

                    // find the correct indices we are using
                    int imagex = i + m + kernelRadiusX;
                    int imagey = j + n + kernelRadiusY;
                    int kernelx = m + kernelRadiusX;
                    int kernely = n + kernelRadiusY;

                    // get the values from the padded image and the kernel
                    int imageval = (int) paddedInput.at<uchar>(imagex, imagey);
                    double kernalval = kernel.at<double>(kernelx, kernely);

                    // do the multiplication
                    sum += imageval * kernalval;
                }
            }
            output.at<double>(i, j) = sum;
        }
    }

}

我不确定这是否正确;我可以使用可以实现相同效果的opencv函数吗?我看过filter2D,但它提供的输出与此函数的功能不同。

1 个答案:

答案 0 :(得分:0)

您的代码几乎是准确的。您只是错过了导致不同结果的几件事:

  1. 请注意函数中对natsort($array); 的调用,因此实际上对图像进行了二次卷积
  2. 请注意,cv::GaussianBlur(...)默认使用另一种图像边框类型

this gist的OpenCV实现是正确的并且非常有效,更不用说支持的各种平台和体系结构(CPU,GPU)和并行化了。实际上,这是输入图像与内核的互相关(就信号处理而言,但在图像处理领域中通常称为卷积)。 可以实现比OpenCV更好的性能,但是使用的方法要复杂得多,所以我希望您的程序仅供学习使用。

我对您的功能做了一些改动。

cv::filter2D(...)

并检查我使用的输出是否相等:

    //delete unused arguments
    //add const where possible
    //note another border type used by cv::filter2D
    void convolution(const cv::Mat &input, cv::Mat &output, const cv::Mat & kernel, int borderType = cv::BORDER_REFLECT101)
    {
    // your function works only for single channel images
    // and since you treat input pixels as uchar the only type supported is CV_8UC1
    CV_Assert(input.type() == CV_8UC1);
    CV_Assert(kernel.size[0] % 2 == 1);
    CV_Assert(kernel.size[1] % 2 == 1);

    // setting output image's type and size in processing function leads to clearer caller's code
    // especially when output should match input
    // that's why most of functions in OpenCV does so if possible
    output = cv::Mat(input.size(), input.type());

    int kernelRadiusX = (kernel.size[0] - 1) / 2;
    int kernelRadiusY = (kernel.size[1] - 1) / 2;

    // create padded version of input
    cv::Mat paddedInput;
    cv::copyMakeBorder(input, paddedInput,
        kernelRadiusX, kernelRadiusX, kernelRadiusY, kernelRadiusY, borderType);

    // actually it's not the fastest way to access pixel values
    // but  it's ok for ease of understanding (in study programm)
    for (int i = 0; i < input.rows; i++)
    {
        for (int j = 0; j < input.cols; j++)
        {
            double sum = 0.0;

            // addition of a kernel radius had no sense and decreased readability
            for (int m = 0; m < kernel.size[0]; m++)
            {
                for (int n = 0; n < kernel.size[1]; n++)
                {
                    // find the correct indices we are using
                    int imagex = i + m;
                    int imagey = j + n;

                    // get the values from the padded image and the kernel
                    int imageval = (int)paddedInput.at<uchar>(imagex, imagey);
                    double kernalval = kernel.at<double>(m, n);

                    // do the multiplication
                    sum += imageval * kernalval;
                }
            }
            //trim values out of uchar's range
            output.at<uchar>(i, j) = cv::saturate_cast<uchar>(sum);
        }
    }
}