opencv变换:自定义线性滤波器

158 阅读3分钟

1. 目标

在本文中,将学习如何:

  • 使用 OpenCV 函数filter2D()创建自定义线性滤波器。

2. 理论

  • 笔记

    下面的解释来自Bradski 和 Kaehler的《Learning OpenCV 》一书。

2.1 相关性 Correlation

在一般的意义上,相关性(Correlation)是图像的每个部分与运算符(滤波核)之间的操作。

2.2 什么是滤波核?

滤波核本质上是一个固定大小的数值数组以及该数组中的一个锚点,该锚点通常位于中心。

filter_2d_tutorial_kernel_theory.png

2.3 如何计算图像与滤波核的相关性(Correlation)?

假设想知道图像中特定位置的相关值。相关值(Correlation)的计算方式如下:

  1. 将滤波核锚点放置在确定的像素之上,滤波核的其余部分覆盖图像中相应的局部像素。
  2. 将滤波核系数乘以相应的图像像素值并对结果求和。
  3. 将结果放在输入图像中锚点的位置。
  4. 通过在整个图像上滑动扫描滤波核来对所有像素重复该过程。

以方程的形式表达上述过程,如下:

H(x,y)=i=0Mi1j=0Mj1I(x+iai,y+iaj)K(i,j)H(x,y)=\sum_{i=0}^{M_i-1} \sum_{j=0}^{M_j-1} I(x+i-a_i,y+i-a_j)K(i,j)

幸运的是,OpenCV 提供了filter2D() 函数,因此不必编写所有这些操作。

2.4 这个程序有什么作用?

  • 加载图像

  • 归一化滤波核。例如,对于大小为 3的内核是:

K=133111111111K=\frac{1}{3\cdot 3} \begin{vmatrix} 1& 1& 1 \\ 1& 1& 1 \\ 1& 1& 1 \\ \end{vmatrix}

该程序将使用大小为 3、5、7、9 和 11 的内核执行过滤操作。

  • 过滤器输出(每个滤波核)将在 500 毫秒内显示

3. 代码

教程代码显示在下面的行中。

也可以从这里下载


#include "opencv2/imgproc.hpp"
#include "opencv2/imgcodecs.hpp"
#include "opencv2/highgui.hpp"
using namespace cv;
int main ( int argc, char** argv )
{
    // Declare variables
    Mat src, dst;
    Mat kernel;
    Point anchor;
    double delta;
    int ddepth;
    int kernel_size;
    const char* window_name = "filter2D Demo";
    const char* imageName = argc >=2 ? argv[1] : "lena.jpg";
    // Loads an image
    src = imread( samples::findFile( imageName ), IMREAD_COLOR ); // Load an image
    if( src.empty() )
    {
        printf(" Error opening image\n");
        printf(" Program Arguments: [image_name -- default lena.jpg] \n");
        return EXIT_FAILURE;
    }
    // Initialize arguments for the filter
    anchor = Point( -1, -1 );
    delta = 0;
    ddepth = -1;
    // Loop - Will filter the image with different kernel sizes each 0.5 seconds
    int ind = 0;
    for(;;)
    {
        // Update kernel size for a normalized box filter
        kernel_size = 3 + 2*( ind%5 );
        kernel = Mat::ones( kernel_size, kernel_size, CV_32F )/ (float)(kernel_size*kernel_size);
        // Apply filter
        filter2D(src, dst, ddepth , kernel, anchor, delta, BORDER_DEFAULT );
        imshow( window_name, dst );
        char c = (char)waitKey(500);
        // Press 'ESC' to exit the program
        if( c == 27 )
        { break; }
        ind++;
    }
    return EXIT_SUCCESS;
}

4. 代码解释

4.1 加载图像

const char* imageName = argc >=2 ? argv[1] : "lena.jpg";
    // Loads an image
    src = imread( samples::findFile( imageName ), IMREAD_COLOR ); // Load an image
    if( src.empty() )
    {
        printf(" Error opening image\n");
        printf(" Program Arguments: [image_name -- default lena.jpg] \n");
        return EXIT_FAILURE;
    }

4.2 初始化参数

// Initialize arguments for the filter
anchor = Point( -1, -1 );
delta = 0;
ddepth = -1;

4.3 循环

执行无限循环更新滤波核大小并将线性滤波器应用于输入图像。更详细地分析一下:

  • 首先自定义滤波核。如下:
 // Update kernel size for a normalized box filter
  kernel_size = 3 + 2*( ind%5 );
  kernel = Mat::ones( kernel_size, kernel_size, CV_32F )/ (float)(kernel_size*kernel_size);

第一行是将kernel_size更新为[3,11]范围内的奇数值. 第二行实际上是通过将其值初始化为1来构建内核并通过将其除以元素数量来对其进行标准化。

  • 设置内核后,使用函数filter2D()应用过滤器:
  // Apply filter
  filter2D(src, dst, ddepth , kernel, anchor, delta, BORDER_DEFAULT );
  • 参数表示:

    • src:源图像
    • dst:目标图像
    • ddepthdst的深度。负值(例如− 1) 表示深度与源相同。
    • kernel : 滤波核
    • anchor:锚点相对于其滤波核的位置。位置Point(-1, -1) 默认表示中心。
    • delta:在相关过程中要添加到每个像素的值。默认情况下是0
    • BORDER_DEFAULT:我们默认设置这个值(更多细节在下面的教程中)
  • 程序将执行一个while循环,过滤器的内核大小每 500 毫秒将在指定的范围内更新。

5. 结果

  1. 编译上面的代码后,并执行,将图像的路径作为参数。结果应该是一个窗口,显示被归一化过滤器模糊的图像。每 0.5 秒内核大小应该改变一次,如下面的一系列快照所示:

filter_2d_tutorial_result.jpg

原文地址