文档结构  
翻译进度:已翻译     翻译赏金:0 元 (?)    ¥ 我要打赏

目标

在本教程中,您将学习如何:

  • 使用OpenCV函数 filter2D 创建自己的线性过滤器。

理论

注意

下面的解释属于由Bradski和Kaehler编写的Learning OpenCV一书。

卷积

一般意义上,卷积是图像的每个部分和运算符(核)之间的运算。

什么是核?

核本质上是一个固定大小的数值数组。该数组带有一个 锚点 ,一般位于数组中央。

卷积与内核是如何工作的呢?

假设您想知道图像中特定位置的结果值。 卷积的值以下列方式计算:

第 1 段(可获 2 积分)
  1. 将核的锚点放在该特定位置的像素上,同时,核内的其他值与该像素邻域的各像素重合;
  2. 将核内各值与相应像素值相乘,并将结果相加;
  3. 将所得结果放到与锚点对应的像素上;
  4. 通过在整个图像上扫描核,对图像所有像素重复上述过程。

用公式表示上述过程如下:

 

H(x,y) = \sum_{i=0}^{M_{i} - 1} \sum_{j=0}^{M_{j}-1} I(x+i - a_{i}, y + j - a_{j})K(i,j)

幸运的是,我们不必自己去实现这些运算,OpenCV为我们提供了函数 filter2D 。

第 2 段(可获 2 积分)

代码

  1. 这个程序的功能?

    • 加载一个图片

    • size = 3, 核心算式:
      K = \dfrac{1}{3 \cdot 3} \begin{bmatrix} 1 & 1 & 1 \\ 1 & 1 & 1 \\ 1 & 1 & 1 \end{bmatrix}

      此程序将使用不同的核心大小执行过滤操作,大小为 3, 5, 7, 9 和 11.

    • 每种核心大小的过滤器输出将在 500 毫秒内显示

  2. 全部代码如下,可从这里 下载

#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"
#include <stdlib.h>
#include <stdio.h>

using namespace cv;

/** @function main */
int main ( int argc, char** argv )
{
  /// Declare variables
  Mat src, dst;

  Mat kernel;
  Point anchor;
  double delta;
  int ddepth;
  int kernel_size;
  char* window_name = "filter2D Demo";

  int c;

  /// Load an image
  src = imread( argv[1] );

  if( !src.data )
  { return -1; }

  /// Create window
  namedWindow( window_name, CV_WINDOW_AUTOSIZE );

  /// 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;
  while( true )
    {
      c = waitKey(500);
      /// Press 'ESC' to exit the program
      if( (char)c == 27 )
        { break; }

      /// 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 );
      ind++;
    }

  return 0;
}
第 3 段(可获 2 积分)

说明

  1. 加载图片

    src = imread( argv[1] );
    
    if( !src.data )
      { return -1; }
    
  2. 创建窗口以显示结果

    namedWindow( window_name, CV_WINDOW_AUTOSIZE );
    
  3. 初始化线性滤波器的参数

    anchor = Point( -1, -1 );
    delta = 0;
    ddepth = -1;
    
  4. 执行无限循环。在循环中,我们更新了核的大小,并将线性滤波器用在输入图像上。下面,我们详细分析一下该循环:

  5. 首先,我们定义滤波器要用到的核。像下面这样:

    kernel_size = 3 + 2*( ind%5 );
    kernel = Mat::ones( kernel_size, kernel_size, CV_32F )/ (float)(kernel_size*kernel_size);
    

    第一行代码将 核的大小 设置为 [3,11] 范围内的奇数。第二行代码把1's填充进矩阵,并执行通过除以矩阵元素数的归一化以构造出所用的核。

  6. 将核设置好之后,使用函数 filter2D 就可以生成滤波器:

    filter2D(src, dst, ddepth , kernel, anchor, delta, BORDER_DEFAULT );
    

    其中各参数含义如下:

    1. src: 源图像
    2. dst: 目标图像
    3. ddepthdst 的深度。若为负值(如 -1 ),则表示其深度与源图像相等。
    4. kernel: 用来遍历图像的核
    5. anchor: 核的锚点的相对位置,其中心点默认为 (-1, -1) 。
    6. delta: 在卷积过程中,该值会加到每个像素上。默认情况下,这个值为 0 。
    7. BORDER_DEFAULT: 这里我们保持其默认值,更多细节将在其他教程中详解
  7. 我们在程序里写了个 while 循环。每隔500毫秒,滤波器的核的大小将在我们指定的范围内更新。

第 4 段(可获 2 积分)

结果

  1. 编译好上述代码之后,输入图像路径的参数,我们就可以执行这个程序。其输出结果是一个窗口,其中显示了由归一化滤波器模糊之后的图像。每过0.5秒,滤波器核的大小会有所变化,如你在下面几张图像中所见:

    kernel example
第 5 段(可获 2 积分)

文章评论