filter_2d.markdown 5.7 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
Making your own linear filters! {#tutorial_filter_2d}
===============================

Goal
----

In this tutorial you will learn how to:

-   Use the OpenCV function @ref cv::filter2D to create your own linear filters.

Theory
------

@note The explanation below belongs to the book **Learning OpenCV** by Bradski and Kaehler.

### Convolution

In a very general sense, convolution is an operation between every part of an image and an operator
(kernel).

### What is a kernel?

A kernel is essentially a fixed size array of numerical coefficeints along with an *anchor point* in
that array, which is tipically located at the center.

M
Maksim Shabunin 已提交
26
![](images/filter_2d_tutorial_kernel_theory.png)
27 28 29 30 31 32

### How does convolution with a kernel work?

Assume you want to know the resulting value of a particular location in the image. The value of the
convolution is calculated in the following way:

M
Maksim Shabunin 已提交
33
-#  Place the kernel anchor on top of a determined pixel, with the rest of the kernel overlaying the
34
    corresponding local pixels in the image.
M
Maksim Shabunin 已提交
35 36 37
-#  Multiply the kernel coefficients by the corresponding image pixel values and sum the result.
-#  Place the result to the location of the *anchor* in the input image.
-#  Repeat the process for all pixels by scanning the kernel over the entire image.
38 39 40 41 42 43 44 45 46 47 48

Expressing the procedure above in the form of an equation we would have:

\f[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)\f]

Fortunately, OpenCV provides you with the function @ref cv::filter2D so you do not have to code all
these operations.

Code
----

M
Maksim Shabunin 已提交
49
-#  **What does this program do?**
50 51 52 53 54 55 56 57 58 59 60 61 62 63
    -   Loads an image
    -   Performs a *normalized box filter*. For instance, for a kernel of size \f$size = 3\f$, the
        kernel would be:

        \f[K = \dfrac{1}{3 \cdot 3} \begin{bmatrix}
        1 & 1 & 1  \\
        1 & 1 & 1  \\
        1 & 1 & 1
        \end{bmatrix}\f]

        The program will perform the filter operation with kernels of sizes 3, 5, 7, 9 and 11.

    -   The filter output (with each kernel) will be shown during 500 milliseconds

M
Maksim Shabunin 已提交
64
-#  The tutorial code's is shown lines below. You can also download it from
65
    [here](https://github.com/opencv/opencv/tree/master/samples/cpp/tutorial_code/ImgTrans/filter2D_demo.cpp)
66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127
@code{.cpp}
#include "opencv2/imgproc.hpp"
#include "opencv2/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, 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;
}
@endcode
Explanation
-----------

M
Maksim Shabunin 已提交
128
-#  Load an image
129 130 131 132 133 134
    @code{.cpp}
    src = imread( argv[1] );

    if( !src.data )
      { return -1; }
    @endcode
M
Maksim Shabunin 已提交
135
-#  Create a window to display the result
136 137 138
    @code{.cpp}
    namedWindow( window_name, WINDOW_AUTOSIZE );
    @endcode
M
Maksim Shabunin 已提交
139
-#  Initialize the arguments for the linear filter
140 141 142 143 144
    @code{.cpp}
    anchor = Point( -1, -1 );
    delta = 0;
    ddepth = -1;
    @endcode
M
Maksim Shabunin 已提交
145
-#  Perform an infinite loop updating the kernel size and applying our linear filter to the input
146
    image. Let's analyze that more in detail:
M
Maksim Shabunin 已提交
147
-#  First we define the kernel our filter is going to use. Here it is:
148 149 150 151 152 153 154 155
    @code{.cpp}
    kernel_size = 3 + 2*( ind%5 );
    kernel = Mat::ones( kernel_size, kernel_size, CV_32F )/ (float)(kernel_size*kernel_size);
    @endcode
    The first line is to update the *kernel_size* to odd values in the range: \f$[3,11]\f$. The second
    line actually builds the kernel by setting its value to a matrix filled with \f$1's\f$ and
    normalizing it by dividing it between the number of elements.

M
Maksim Shabunin 已提交
156
-#  After setting the kernel, we can generate the filter by using the function @ref cv::filter2D :
157 158 159 160 161
    @code{.cpp}
    filter2D(src, dst, ddepth , kernel, anchor, delta, BORDER_DEFAULT );
    @endcode
    The arguments denote:

162 163 164
    -#  *src*: Source image
    -#  *dst*: Destination image
    -#  *ddepth*: The depth of *dst*. A negative value (such as \f$-1\f$) indicates that the depth is
165
        the same as the source.
166 167
    -#  *kernel*: The kernel to be scanned through the image
    -#  *anchor*: The position of the anchor relative to its kernel. The location *Point(-1, -1)*
168
        indicates the center by default.
169 170
    -#  *delta*: A value to be added to each pixel during the convolution. By default it is \f$0\f$
    -#  *BORDER_DEFAULT*: We let this value by default (more details in the following tutorial)
171

M
Maksim Shabunin 已提交
172
-#  Our program will effectuate a *while* loop, each 500 ms the kernel size of our filter will be
173 174 175 176 177
    updated in the range indicated.

Results
-------

M
Maksim Shabunin 已提交
178
-#  After compiling the code above, you can execute it giving as argument the path of an image. The
179 180 181
    result should be a window that shows an image blurred by a normalized filter. Each 0.5 seconds
    the kernel size should change, as can be seen in the series of snapshots below:

M
Maksim Shabunin 已提交
182
    ![](images/filter_2d_tutorial_result.jpg)