init - 初始化项目
@@ -0,0 +1,138 @@
|
||||
Anisotropic image segmentation by a gradient structure tensor {#tutorial_anisotropic_image_segmentation_by_a_gst}
|
||||
==========================
|
||||
|
||||
@tableofcontents
|
||||
|
||||
@prev_tutorial{tutorial_motion_deblur_filter}
|
||||
@next_tutorial{tutorial_periodic_noise_removing_filter}
|
||||
|
||||
| | |
|
||||
| -: | :- |
|
||||
| Original author | Karpushin Vladislav |
|
||||
| Compatibility | OpenCV >= 3.0 |
|
||||
|
||||
Goal
|
||||
----
|
||||
|
||||
In this tutorial you will learn:
|
||||
|
||||
- what the gradient structure tensor is
|
||||
- how to estimate orientation and coherency of an anisotropic image by a gradient structure tensor
|
||||
- how to segment an anisotropic image with a single local orientation by a gradient structure tensor
|
||||
|
||||
Theory
|
||||
------
|
||||
|
||||
@note The explanation is based on the books @cite jahne2000computer, @cite bigun2006vision and @cite van1995estimators. Good physical explanation of a gradient structure tensor is given in @cite yang1996structure. Also, you can refer to a wikipedia page [Structure tensor].
|
||||
@note A anisotropic image on this page is a real world image.
|
||||
|
||||
### What is the gradient structure tensor?
|
||||
|
||||
In mathematics, the gradient structure tensor (also referred to as the second-moment matrix, the second order moment tensor, the inertia tensor, etc.) is a matrix derived from the gradient of a function. It summarizes the predominant directions of the gradient in a specified neighborhood of a point, and the degree to which those directions are coherent (coherency). The gradient structure tensor is widely used in image processing and computer vision for 2D/3D image segmentation, motion detection, adaptive filtration, local image features detection, etc.
|
||||
|
||||
Important features of anisotropic images include orientation and coherency of a local anisotropy. In this paper we will show how to estimate orientation and coherency, and how to segment an anisotropic image with a single local orientation by a gradient structure tensor.
|
||||
|
||||
The gradient structure tensor of an image is a 2x2 symmetric matrix. Eigenvectors of the gradient structure tensor indicate local orientation, whereas eigenvalues give coherency (a measure of anisotropism).
|
||||
|
||||
The gradient structure tensor \f$J\f$ of an image \f$Z\f$ can be written as:
|
||||
|
||||
\f[J = \begin{bmatrix}
|
||||
J_{11} & J_{12} \\
|
||||
J_{12} & J_{22}
|
||||
\end{bmatrix}\f]
|
||||
|
||||
where \f$J_{11} = M[Z_{x}^{2}]\f$, \f$J_{22} = M[Z_{y}^{2}]\f$, \f$J_{12} = M[Z_{x}Z_{y}]\f$ - components of the tensor, \f$M[]\f$ is a symbol of mathematical expectation (we can consider this operation as averaging in a window w), \f$Z_{x}\f$ and \f$Z_{y}\f$ are partial derivatives of an image \f$Z\f$ with respect to \f$x\f$ and \f$y\f$.
|
||||
|
||||
The eigenvalues of the tensor can be found in the below formula:
|
||||
\f[\lambda_{1,2} = \frac{1}{2} \left [ J_{11} + J_{22} \pm \sqrt{(J_{11} - J_{22})^{2} + 4J_{12}^{2}} \right ] \f]
|
||||
where \f$\lambda_1\f$ - largest eigenvalue, \f$\lambda_2\f$ - smallest eigenvalue.
|
||||
|
||||
### How to estimate orientation and coherency of an anisotropic image by gradient structure tensor?
|
||||
|
||||
The orientation of an anisotropic image:
|
||||
\f[\alpha = 0.5arctg\frac{2J_{12}}{J_{22} - J_{11}}\f]
|
||||
|
||||
Coherency:
|
||||
\f[C = \frac{\lambda_1 - \lambda_2}{\lambda_1 + \lambda_2}\f]
|
||||
|
||||
The coherency ranges from 0 to 1. For ideal local orientation (\f$\lambda_2\f$ = 0, \f$\lambda_1\f$ > 0) it is one, for an isotropic gray value structure (\f$\lambda_1\f$ = \f$\lambda_2\f$ \> 0) it is zero.
|
||||
|
||||
Source code
|
||||
-----------
|
||||
|
||||
You can find source code in the `samples/cpp/tutorial_code/ImgProc/anisotropic_image_segmentation/anisotropic_image_segmentation.cpp` of the OpenCV source code library.
|
||||
|
||||
@add_toggle_cpp
|
||||
@include cpp/tutorial_code/ImgProc/anisotropic_image_segmentation/anisotropic_image_segmentation.cpp
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_python
|
||||
@include samples/python/tutorial_code/imgProc/anisotropic_image_segmentation/anisotropic_image_segmentation.py
|
||||
@end_toggle
|
||||
|
||||
Explanation
|
||||
-----------
|
||||
An anisotropic image segmentation algorithm consists of a gradient structure tensor calculation, an orientation calculation, a coherency calculation and an orientation and coherency thresholding:
|
||||
|
||||
@add_toggle_cpp
|
||||
@snippet samples/cpp/tutorial_code/ImgProc/anisotropic_image_segmentation/anisotropic_image_segmentation.cpp main
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_python
|
||||
@snippet samples/python/tutorial_code/imgProc/anisotropic_image_segmentation/anisotropic_image_segmentation.py main
|
||||
@end_toggle
|
||||
|
||||
A function calcGST() calculates orientation and coherency by using a gradient structure tensor. An input parameter w defines a window size:
|
||||
|
||||
@add_toggle_cpp
|
||||
@snippet samples/cpp/tutorial_code/ImgProc/anisotropic_image_segmentation/anisotropic_image_segmentation.cpp calcGST
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_python
|
||||
@snippet samples/python/tutorial_code/imgProc/anisotropic_image_segmentation/anisotropic_image_segmentation.py calcGST
|
||||
@end_toggle
|
||||
|
||||
|
||||
The below code applies a thresholds LowThr and HighThr to image orientation and a threshold C_Thr to image coherency calculated by the previous function. LowThr and HighThr define orientation range:
|
||||
|
||||
@add_toggle_cpp
|
||||
@snippet samples/cpp/tutorial_code/ImgProc/anisotropic_image_segmentation/anisotropic_image_segmentation.cpp thresholding
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_python
|
||||
@snippet samples/python/tutorial_code/imgProc/anisotropic_image_segmentation/anisotropic_image_segmentation.py thresholding
|
||||
@end_toggle
|
||||
|
||||
|
||||
And finally we combine thresholding results:
|
||||
|
||||
@add_toggle_cpp
|
||||
@snippet samples/cpp/tutorial_code/ImgProc/anisotropic_image_segmentation/anisotropic_image_segmentation.cpp combining
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_python
|
||||
@snippet samples/python/tutorial_code/imgProc/anisotropic_image_segmentation/anisotropic_image_segmentation.py combining
|
||||
@end_toggle
|
||||
|
||||
|
||||
Result
|
||||
------
|
||||
|
||||
Below you can see the real anisotropic image with single direction:
|
||||

|
||||
|
||||
Below you can see the orientation and coherency of the anisotropic image:
|
||||

|
||||

|
||||
|
||||
Below you can see the segmentation result:
|
||||

|
||||
|
||||
The result has been computed with w = 52, C_Thr = 0.43, LowThr = 35, HighThr = 57. We can see that the algorithm selected only the areas with one single direction.
|
||||
|
||||
References
|
||||
------
|
||||
- [Structure tensor] - structure tensor description on the wikipedia
|
||||
|
||||
<!-- invisible references list -->
|
||||
[Structure tensor]: https://en.wikipedia.org/wiki/Structure_tensor
|
||||
|
After Width: | Height: | Size: 20 KiB |
|
After Width: | Height: | Size: 40 KiB |
|
After Width: | Height: | Size: 19 KiB |
|
After Width: | Height: | Size: 32 KiB |
@@ -0,0 +1,270 @@
|
||||
Basic Drawing {#tutorial_basic_geometric_drawing}
|
||||
=============
|
||||
|
||||
@tableofcontents
|
||||
|
||||
@next_tutorial{tutorial_random_generator_and_text}
|
||||
|
||||
| | |
|
||||
| -: | :- |
|
||||
| Original author | Ana Huamán |
|
||||
| Compatibility | OpenCV >= 3.0 |
|
||||
|
||||
Goals
|
||||
-----
|
||||
|
||||
In this tutorial you will learn how to:
|
||||
|
||||
- Draw a **line** by using the OpenCV function **line()**
|
||||
- Draw an **ellipse** by using the OpenCV function **ellipse()**
|
||||
- Draw a **rectangle** by using the OpenCV function **rectangle()**
|
||||
- Draw a **circle** by using the OpenCV function **circle()**
|
||||
- Draw a **filled polygon** by using the OpenCV function **fillPoly()**
|
||||
|
||||
@add_toggle_cpp
|
||||
OpenCV Theory
|
||||
-------------
|
||||
|
||||
For this tutorial, we will heavily use two structures: @ref cv::Point and @ref cv::Scalar :
|
||||
|
||||
### Point
|
||||
|
||||
It represents a 2D point, specified by its image coordinates \f$x\f$ and \f$y\f$. We can define it as:
|
||||
@code{.cpp}
|
||||
Point pt;
|
||||
pt.x = 10;
|
||||
pt.y = 8;
|
||||
@endcode
|
||||
or
|
||||
@code{.cpp}
|
||||
Point pt = Point(10, 8);
|
||||
@endcode
|
||||
### Scalar
|
||||
|
||||
- Represents a 4-element vector. The type Scalar is widely used in OpenCV for passing pixel
|
||||
values.
|
||||
- In this tutorial, we will use it extensively to represent BGR color values (3 parameters). It is
|
||||
not necessary to define the last argument if it is not going to be used.
|
||||
- Let's see an example, if we are asked for a color argument and we give:
|
||||
@code{.cpp}
|
||||
Scalar( a, b, c )
|
||||
@endcode
|
||||
We would be defining a BGR color such as: *Blue = a*, *Green = b* and *Red = c*
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_java
|
||||
OpenCV Theory
|
||||
-------------
|
||||
|
||||
For this tutorial, we will heavily use two structures: @ref cv::Point and @ref cv::Scalar :
|
||||
|
||||
### Point
|
||||
|
||||
It represents a 2D point, specified by its image coordinates \f$x\f$ and \f$y\f$. We can define it as:
|
||||
@code{.java}
|
||||
Point pt = new Point();
|
||||
pt.x = 10;
|
||||
pt.y = 8;
|
||||
@endcode
|
||||
or
|
||||
@code{.java}
|
||||
Point pt = new Point(10, 8);
|
||||
@endcode
|
||||
### Scalar
|
||||
|
||||
- Represents a 4-element vector. The type Scalar is widely used in OpenCV for passing pixel
|
||||
values.
|
||||
- In this tutorial, we will use it extensively to represent BGR color values (3 parameters). It is
|
||||
not necessary to define the last argument if it is not going to be used.
|
||||
- Let's see an example, if we are asked for a color argument and we give:
|
||||
@code{.java}
|
||||
Scalar( a, b, c )
|
||||
@endcode
|
||||
We would be defining a BGR color such as: *Blue = a*, *Green = b* and *Red = c*
|
||||
@end_toggle
|
||||
|
||||
Code
|
||||
----
|
||||
|
||||
@add_toggle_cpp
|
||||
- This code is in your OpenCV sample folder. Otherwise you can grab it from
|
||||
[here](https://raw.githubusercontent.com/opencv/opencv/master/samples/cpp/tutorial_code/ImgProc/basic_drawing/Drawing_1.cpp)
|
||||
@include samples/cpp/tutorial_code/ImgProc/basic_drawing/Drawing_1.cpp
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_java
|
||||
- This code is in your OpenCV sample folder. Otherwise you can grab it from
|
||||
[here](https://raw.githubusercontent.com/opencv/opencv/master/samples/java/tutorial_code/ImgProc/BasicGeometricDrawing/BasicGeometricDrawing.java)
|
||||
@include samples/java/tutorial_code/ImgProc/BasicGeometricDrawing/BasicGeometricDrawing.java
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_python
|
||||
- This code is in your OpenCV sample folder. Otherwise you can grab it from
|
||||
[here](https://raw.githubusercontent.com/opencv/opencv/master/samples/python/tutorial_code/imgProc/BasicGeometricDrawing/basic_geometric_drawing.py)
|
||||
@include samples/python/tutorial_code/imgProc/BasicGeometricDrawing/basic_geometric_drawing.py
|
||||
@end_toggle
|
||||
|
||||
Explanation
|
||||
-----------
|
||||
|
||||
Since we plan to draw two examples (an atom and a rook), we have to create two images and two
|
||||
windows to display them.
|
||||
@add_toggle_cpp
|
||||
@snippet cpp/tutorial_code/ImgProc/basic_drawing/Drawing_1.cpp create_images
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_java
|
||||
@snippet java/tutorial_code/ImgProc/BasicGeometricDrawing/BasicGeometricDrawing.java create_images
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_python
|
||||
@snippet python/tutorial_code/imgProc/BasicGeometricDrawing/basic_geometric_drawing.py create_images
|
||||
@end_toggle
|
||||
|
||||
We created functions to draw different geometric shapes. For instance, to draw the atom we used
|
||||
**MyEllipse** and **MyFilledCircle**:
|
||||
@add_toggle_cpp
|
||||
@snippet cpp/tutorial_code/ImgProc/basic_drawing/Drawing_1.cpp draw_atom
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_java
|
||||
@snippet java/tutorial_code/ImgProc/BasicGeometricDrawing/BasicGeometricDrawing.java draw_atom
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_python
|
||||
@snippet python/tutorial_code/imgProc/BasicGeometricDrawing/basic_geometric_drawing.py draw_atom
|
||||
@end_toggle
|
||||
|
||||
And to draw the rook we employed **MyLine**, **rectangle** and a **MyPolygon**:
|
||||
@add_toggle_cpp
|
||||
@snippet cpp/tutorial_code/ImgProc/basic_drawing/Drawing_1.cpp draw_rook
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_java
|
||||
@snippet java/tutorial_code/ImgProc/BasicGeometricDrawing/BasicGeometricDrawing.java draw_rook
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_python
|
||||
@snippet python/tutorial_code/imgProc/BasicGeometricDrawing/basic_geometric_drawing.py draw_rook
|
||||
@end_toggle
|
||||
|
||||
|
||||
Let's check what is inside each of these functions:
|
||||
@add_toggle_cpp
|
||||
@end_toggle
|
||||
|
||||
<H4>MyLine</H4>
|
||||
@add_toggle_cpp
|
||||
@snippet cpp/tutorial_code/ImgProc/basic_drawing/Drawing_1.cpp my_line
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_java
|
||||
@snippet java/tutorial_code/ImgProc/BasicGeometricDrawing/BasicGeometricDrawing.java my_line
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_python
|
||||
@snippet python/tutorial_code/imgProc/BasicGeometricDrawing/basic_geometric_drawing.py my_line
|
||||
@end_toggle
|
||||
|
||||
- As we can see, **MyLine** just call the function **line()** , which does the following:
|
||||
- Draw a line from Point **start** to Point **end**
|
||||
- The line is displayed in the image **img**
|
||||
- The line color is defined by <B>( 0, 0, 0 )</B> which is the RGB value correspondent
|
||||
to **Black**
|
||||
- The line thickness is set to **thickness** (in this case 2)
|
||||
- The line is a 8-connected one (**lineType** = 8)
|
||||
|
||||
<H4>MyEllipse</H4>
|
||||
@add_toggle_cpp
|
||||
@snippet cpp/tutorial_code/ImgProc/basic_drawing/Drawing_1.cpp my_ellipse
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_java
|
||||
@snippet java/tutorial_code/ImgProc/BasicGeometricDrawing/BasicGeometricDrawing.java my_ellipse
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_python
|
||||
@snippet python/tutorial_code/imgProc/BasicGeometricDrawing/basic_geometric_drawing.py my_ellipse
|
||||
@end_toggle
|
||||
|
||||
- From the code above, we can observe that the function **ellipse()** draws an ellipse such
|
||||
that:
|
||||
|
||||
- The ellipse is displayed in the image **img**
|
||||
- The ellipse center is located in the point <B>(w/2, w/2)</B> and is enclosed in a box
|
||||
of size <B>(w/4, w/16)</B>
|
||||
- The ellipse is rotated **angle** degrees
|
||||
- The ellipse extends an arc between **0** and **360** degrees
|
||||
- The color of the figure will be <B>( 255, 0, 0 )</B> which means blue in BGR value.
|
||||
- The ellipse's **thickness** is 2.
|
||||
|
||||
<H4>MyFilledCircle</H4>
|
||||
@add_toggle_cpp
|
||||
@snippet cpp/tutorial_code/ImgProc/basic_drawing/Drawing_1.cpp my_filled_circle
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_java
|
||||
@snippet java/tutorial_code/ImgProc/BasicGeometricDrawing/BasicGeometricDrawing.java my_filled_circle
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_python
|
||||
@snippet python/tutorial_code/imgProc/BasicGeometricDrawing/basic_geometric_drawing.py my_filled_circle
|
||||
@end_toggle
|
||||
|
||||
- Similar to the ellipse function, we can observe that *circle* receives as arguments:
|
||||
|
||||
- The image where the circle will be displayed (**img**)
|
||||
- The center of the circle denoted as the point **center**
|
||||
- The radius of the circle: **w/32**
|
||||
- The color of the circle: <B>( 0, 0, 255 )</B> which means *Red* in BGR
|
||||
- Since **thickness** = -1, the circle will be drawn filled.
|
||||
|
||||
<H4>MyPolygon</H4>
|
||||
@add_toggle_cpp
|
||||
@snippet cpp/tutorial_code/ImgProc/basic_drawing/Drawing_1.cpp my_polygon
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_java
|
||||
@snippet java/tutorial_code/ImgProc/BasicGeometricDrawing/BasicGeometricDrawing.java my_polygon
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_python
|
||||
@snippet python/tutorial_code/imgProc/BasicGeometricDrawing/basic_geometric_drawing.py my_polygon
|
||||
@end_toggle
|
||||
|
||||
- To draw a filled polygon we use the function **fillPoly()** . We note that:
|
||||
|
||||
- The polygon will be drawn on **img**
|
||||
- The vertices of the polygon are the set of points in **ppt**
|
||||
- The color of the polygon is defined by <B>( 255, 255, 255 )</B>, which is the BGR
|
||||
value for *white*
|
||||
|
||||
<H4>rectangle</H4>
|
||||
@add_toggle_cpp
|
||||
@snippet cpp/tutorial_code/ImgProc/basic_drawing/Drawing_1.cpp rectangle
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_java
|
||||
@snippet java/tutorial_code/ImgProc/BasicGeometricDrawing/BasicGeometricDrawing.java rectangle
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_python
|
||||
@snippet python/tutorial_code/imgProc/BasicGeometricDrawing/basic_geometric_drawing.py rectangle
|
||||
@end_toggle
|
||||
|
||||
- Finally we have the @ref cv::rectangle function (we did not create a special function for
|
||||
this guy). We note that:
|
||||
|
||||
- The rectangle will be drawn on **rook_image**
|
||||
- Two opposite vertices of the rectangle are defined by <B>( 0, 7*w/8 )</B>
|
||||
and <B>( w, w )</B>
|
||||
- The color of the rectangle is given by <B>( 0, 255, 255 )</B> which is the BGR value
|
||||
for *yellow*
|
||||
- Since the thickness value is given by **FILLED (-1)**, the rectangle will be filled.
|
||||
|
||||
Result
|
||||
------
|
||||
|
||||
Compiling and running your program should give you a result like this:
|
||||
|
||||

|
||||
|
After Width: | Height: | Size: 16 KiB |
@@ -0,0 +1,294 @@
|
||||
Eroding and Dilating {#tutorial_erosion_dilatation}
|
||||
====================
|
||||
|
||||
@tableofcontents
|
||||
|
||||
@prev_tutorial{tutorial_gausian_median_blur_bilateral_filter}
|
||||
@next_tutorial{tutorial_opening_closing_hats}
|
||||
|
||||
| | |
|
||||
| -: | :- |
|
||||
| Original author | Ana Huamán |
|
||||
| Compatibility | OpenCV >= 3.0 |
|
||||
|
||||
Goal
|
||||
----
|
||||
|
||||
In this tutorial you will learn how to:
|
||||
|
||||
- Apply two very common morphological operators: Erosion and Dilation. For this purpose, you will use
|
||||
the following OpenCV functions:
|
||||
- @ref cv::erode
|
||||
- @ref cv::dilate
|
||||
|
||||
@note The explanation below belongs to the book **Learning OpenCV** by Bradski and Kaehler.
|
||||
|
||||
Morphological Operations
|
||||
------------------------
|
||||
|
||||
- In short: A set of operations that process images based on shapes. Morphological operations
|
||||
apply a *structuring element* to an input image and generate an output image.
|
||||
- The most basic morphological operations are: Erosion and Dilation. They have a wide array of
|
||||
uses, i.e. :
|
||||
- Removing noise
|
||||
- Isolation of individual elements and joining disparate elements in an image.
|
||||
- Finding of intensity bumps or holes in an image
|
||||
- We will explain dilation and erosion briefly, using the following image as an example:
|
||||
|
||||

|
||||
|
||||
### Dilation
|
||||
|
||||
- This operations consists of convolving an image \f$A\f$ with some kernel (\f$B\f$), which can have any
|
||||
shape or size, usually a square or circle.
|
||||
- The kernel \f$B\f$ has a defined *anchor point*, usually being the center of the kernel.
|
||||
- As the kernel \f$B\f$ is scanned over the image, we compute the maximal pixel value overlapped by
|
||||
\f$B\f$ and replace the image pixel in the anchor point position with that maximal value. As you can
|
||||
deduce, this maximizing operation causes bright regions within an image to "grow" (therefore the
|
||||
name *dilation*).
|
||||
- The dilatation operation is: \f$\texttt{dst} (x,y) = \max _{(x',y'): \, \texttt{element} (x',y') \ne0 } \texttt{src} (x+x',y+y')\f$
|
||||
|
||||
- Take the above image as an example. Applying dilation we can get:
|
||||
|
||||

|
||||
|
||||
- The bright area of the letter dilates around the black regions of the background.
|
||||
|
||||
### Erosion
|
||||
|
||||
- This operation is the sister of dilation. It computes a local minimum over the
|
||||
area of given kernel.
|
||||
- As the kernel \f$B\f$ is scanned over the image, we compute the minimal pixel value overlapped by
|
||||
\f$B\f$ and replace the image pixel under the anchor point with that minimal value.
|
||||
- The erosion operation is: \f$\texttt{dst} (x,y) = \min _{(x',y'): \, \texttt{element} (x',y') \ne0 } \texttt{src} (x+x',y+y')\f$
|
||||
- Analagously to the example for dilation, we can apply the erosion operator to the original image
|
||||
(shown above). You can see in the result below that the bright areas of the image get thinner,
|
||||
whereas the dark zones gets bigger.
|
||||
|
||||

|
||||
|
||||
Code
|
||||
----
|
||||
|
||||
@add_toggle_cpp
|
||||
This tutorial's code is shown below. You can also download it
|
||||
[here](https://github.com/opencv/opencv/tree/master/samples/cpp/tutorial_code/ImgProc/Morphology_1.cpp)
|
||||
@include samples/cpp/tutorial_code/ImgProc/Morphology_1.cpp
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_java
|
||||
This tutorial's code is shown below. You can also download it
|
||||
[here](https://github.com/opencv/opencv/tree/master/samples/java/tutorial_code/ImgProc/erosion_dilatation/MorphologyDemo1.java)
|
||||
@include samples/java/tutorial_code/ImgProc/erosion_dilatation/MorphologyDemo1.java
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_python
|
||||
This tutorial's code is shown below. You can also download it
|
||||
[here](https://github.com/opencv/opencv/tree/master/samples/python/tutorial_code/imgProc/erosion_dilatation/morphology_1.py)
|
||||
@include samples/python/tutorial_code/imgProc/erosion_dilatation/morphology_1.py
|
||||
@end_toggle
|
||||
|
||||
Explanation
|
||||
-----------
|
||||
|
||||
@add_toggle_cpp
|
||||
Most of the material shown here is trivial (if you have any doubt, please refer to the tutorials in
|
||||
previous sections). Let's check the general structure of the C++ program:
|
||||
|
||||
@snippet cpp/tutorial_code/ImgProc/Morphology_1.cpp main
|
||||
|
||||
-# Load an image (can be BGR or grayscale)
|
||||
-# Create two windows (one for dilation output, the other for erosion)
|
||||
-# Create a set of two Trackbars for each operation:
|
||||
- The first trackbar "Element" returns either **erosion_elem** or **dilation_elem**
|
||||
- The second trackbar "Kernel size" return **erosion_size** or **dilation_size** for the
|
||||
corresponding operation.
|
||||
-# Call once erosion and dilation to show the initial image.
|
||||
|
||||
|
||||
Every time we move any slider, the user's function **Erosion** or **Dilation** will be
|
||||
called and it will update the output image based on the current trackbar values.
|
||||
|
||||
Let's analyze these two functions:
|
||||
|
||||
#### The erosion function
|
||||
|
||||
@snippet cpp/tutorial_code/ImgProc/Morphology_1.cpp erosion
|
||||
|
||||
The function that performs the *erosion* operation is @ref cv::erode . As we can see, it
|
||||
receives three arguments:
|
||||
- *src*: The source image
|
||||
- *erosion_dst*: The output image
|
||||
- *element*: This is the kernel we will use to perform the operation. If we do not
|
||||
specify, the default is a simple `3x3` matrix. Otherwise, we can specify its
|
||||
shape. For this, we need to use the function cv::getStructuringElement :
|
||||
@snippet cpp/tutorial_code/ImgProc/Morphology_1.cpp kernel
|
||||
|
||||
We can choose any of three shapes for our kernel:
|
||||
|
||||
- Rectangular box: MORPH_RECT
|
||||
- Cross: MORPH_CROSS
|
||||
- Ellipse: MORPH_ELLIPSE
|
||||
|
||||
Then, we just have to specify the size of our kernel and the *anchor point*. If not
|
||||
specified, it is assumed to be in the center.
|
||||
|
||||
That is all. We are ready to perform the erosion of our image.
|
||||
|
||||
#### The dilation function
|
||||
|
||||
The code is below. As you can see, it is completely similar to the snippet of code for **erosion**.
|
||||
Here we also have the option of defining our kernel, its anchor point and the size of the operator
|
||||
to be used.
|
||||
@snippet cpp/tutorial_code/ImgProc/Morphology_1.cpp dilation
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_java
|
||||
Most of the material shown here is trivial (if you have any doubt, please refer to the tutorials in
|
||||
previous sections). Let's check however the general structure of the java class. There are 4 main
|
||||
parts in the java class:
|
||||
|
||||
- the class constructor which setups the window that will be filled with window components
|
||||
- the `addComponentsToPane` method, which fills out the window
|
||||
- the `update` method, which determines what happens when the user changes any value
|
||||
- the `main` method, which is the entry point of the program
|
||||
|
||||
In this tutorial we will focus on the `addComponentsToPane` and `update` methods. However, for completion the
|
||||
steps followed in the constructor are:
|
||||
|
||||
-# Load an image (can be BGR or grayscale)
|
||||
-# Create a window
|
||||
-# Add various control components with `addComponentsToPane`
|
||||
-# show the window
|
||||
|
||||
The components were added by the following method:
|
||||
|
||||
@snippet java/tutorial_code/ImgProc/erosion_dilatation/MorphologyDemo1.java components
|
||||
|
||||
In short we
|
||||
|
||||
-# create a panel for the sliders
|
||||
-# create a combo box for the element types
|
||||
-# create a slider for the kernel size
|
||||
-# create a combo box for the morphology function to use (erosion or dilation)
|
||||
|
||||
The action and state changed listeners added call at the end the `update` method which updates
|
||||
the image based on the current slider values. So every time we move any slider, the `update` method is triggered.
|
||||
|
||||
#### Updating the image
|
||||
|
||||
To update the image we used the following implementation:
|
||||
|
||||
@snippet java/tutorial_code/ImgProc/erosion_dilatation/MorphologyDemo1.java update
|
||||
|
||||
In other words we
|
||||
|
||||
-# get the structuring element the user chose
|
||||
-# execute the **erosion** or **dilation** function based on `doErosion`
|
||||
-# reload the image with the morphology applied
|
||||
-# repaint the frame
|
||||
|
||||
Let's analyze the `erode` and `dilate` methods:
|
||||
|
||||
#### The erosion method
|
||||
|
||||
@snippet java/tutorial_code/ImgProc/erosion_dilatation/MorphologyDemo1.java erosion
|
||||
|
||||
The function that performs the *erosion* operation is @ref cv::erode . As we can see, it
|
||||
receives three arguments:
|
||||
- *src*: The source image
|
||||
- *erosion_dst*: The output image
|
||||
- *element*: This is the kernel we will use to perform the operation. For specifying the shape, we need to use
|
||||
the function cv::getStructuringElement :
|
||||
@snippet java/tutorial_code/ImgProc/erosion_dilatation/MorphologyDemo1.java kernel
|
||||
|
||||
We can choose any of three shapes for our kernel:
|
||||
|
||||
- Rectangular box: CV_SHAPE_RECT
|
||||
- Cross: CV_SHAPE_CROSS
|
||||
- Ellipse: CV_SHAPE_ELLIPSE
|
||||
|
||||
Together with the shape we specify the size of our kernel and the *anchor point*. If the anchor point is not
|
||||
specified, it is assumed to be in the center.
|
||||
|
||||
That is all. We are ready to perform the erosion of our image.
|
||||
|
||||
#### The dilation function
|
||||
|
||||
The code is below. As you can see, it is completely similar to the snippet of code for **erosion**.
|
||||
Here we also have the option of defining our kernel, its anchor point and the size of the operator
|
||||
to be used.
|
||||
@snippet java/tutorial_code/ImgProc/erosion_dilatation/MorphologyDemo1.java dilation
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_python
|
||||
Most of the material shown here is trivial (if you have any doubt, please refer to the tutorials in
|
||||
previous sections). Let's check the general structure of the python script:
|
||||
|
||||
@snippet python/tutorial_code/imgProc/erosion_dilatation/morphology_1.py main
|
||||
|
||||
-# Load an image (can be BGR or grayscale)
|
||||
-# Create two windows (one for erosion output, the other for dilation) with a set of trackbars each
|
||||
- The first trackbar "Element" returns the value for the morphological type that will be mapped
|
||||
(1 = rectangle, 2 = cross, 3 = ellipse)
|
||||
- The second trackbar "Kernel size" returns the size of the element for the
|
||||
corresponding operation
|
||||
-# Call once erosion and dilation to show the initial image
|
||||
|
||||
Every time we move any slider, the user's function **erosion** or **dilation** will be
|
||||
called and it will update the output image based on the current trackbar values.
|
||||
|
||||
Let's analyze these two functions:
|
||||
|
||||
#### The erosion function
|
||||
|
||||
@snippet python/tutorial_code/imgProc/erosion_dilatation/morphology_1.py erosion
|
||||
|
||||
The function that performs the *erosion* operation is @ref cv::erode . As we can see, it
|
||||
receives two arguments and returns the processed image:
|
||||
- *src*: The source image
|
||||
- *element*: The kernel we will use to perform the operation. We can specify its
|
||||
shape by using the function cv::getStructuringElement :
|
||||
@snippet python/tutorial_code/imgProc/erosion_dilatation/morphology_1.py kernel
|
||||
|
||||
We can choose any of three shapes for our kernel:
|
||||
|
||||
- Rectangular box: MORPH_RECT
|
||||
- Cross: MORPH_CROSS
|
||||
- Ellipse: MORPH_ELLIPSE
|
||||
|
||||
Then, we just have to specify the size of our kernel and the *anchor point*. If the anchor point not
|
||||
specified, it is assumed to be in the center.
|
||||
|
||||
That is all. We are ready to perform the erosion of our image.
|
||||
|
||||
#### The dilation function
|
||||
|
||||
The code is below. As you can see, it is completely similar to the snippet of code for **erosion**.
|
||||
Here we also have the option of defining our kernel, its anchor point and the size of the operator
|
||||
to be used.
|
||||
|
||||
@snippet python/tutorial_code/imgProc/erosion_dilatation/morphology_1.py dilation
|
||||
@end_toggle
|
||||
|
||||
@note Additionally, there are further parameters that allow you to perform multiple erosions/dilations
|
||||
(iterations) at once and also set the border type and value. However, We haven't used those
|
||||
in this simple tutorial. You can check out the reference for more details.
|
||||
|
||||
Results
|
||||
-------
|
||||
|
||||
Compile the code above and execute it (or run the script if using python) with an image as argument.
|
||||
If you do not provide an image as argument the default sample image
|
||||
([LinuxLogo.jpg](https://github.com/opencv/opencv/tree/master/samples/data/LinuxLogo.jpg)) will be used.
|
||||
|
||||
For instance, using this image:
|
||||
|
||||

|
||||
|
||||
We get the results below. Varying the indices in the Trackbars give different output images,
|
||||
naturally. Try them out! You can even try to add a third Trackbar to control the number of
|
||||
iterations.
|
||||
|
||||

|
||||
(depending on the programming language the output might vary a little or be only 1 window)
|
||||
|
After Width: | Height: | Size: 26 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 923 B |
|
After Width: | Height: | Size: 844 B |
|
After Width: | Height: | Size: 1.1 KiB |
@@ -0,0 +1,235 @@
|
||||
Smoothing Images {#tutorial_gausian_median_blur_bilateral_filter}
|
||||
================
|
||||
|
||||
@tableofcontents
|
||||
|
||||
@prev_tutorial{tutorial_random_generator_and_text}
|
||||
@next_tutorial{tutorial_erosion_dilatation}
|
||||
|
||||
| | |
|
||||
| -: | :- |
|
||||
| Original author | Ana Huamán |
|
||||
| Compatibility | OpenCV >= 3.0 |
|
||||
|
||||
Goal
|
||||
----
|
||||
|
||||
In this tutorial you will learn how to apply diverse linear filters to smooth images using OpenCV
|
||||
functions such as:
|
||||
|
||||
- **blur()**
|
||||
- **GaussianBlur()**
|
||||
- **medianBlur()**
|
||||
- **bilateralFilter()**
|
||||
|
||||
Theory
|
||||
------
|
||||
|
||||
@note The explanation below belongs to the book [Computer Vision: Algorithms and
|
||||
Applications](http://szeliski.org/Book/) by Richard Szeliski and to *LearningOpenCV*
|
||||
|
||||
- *Smoothing*, also called *blurring*, is a simple and frequently used image processing
|
||||
operation.
|
||||
- There are many reasons for smoothing. In this tutorial we will focus on smoothing in order to
|
||||
reduce noise (other uses will be seen in the following tutorials).
|
||||
- To perform a smoothing operation we will apply a *filter* to our image. The most common type
|
||||
of filters are *linear*, in which an output pixel's value (i.e. \f$g(i,j)\f$) is determined as a
|
||||
weighted sum of input pixel values (i.e. \f$f(i+k,j+l)\f$) :
|
||||
|
||||
\f[g(i,j) = \sum_{k,l} f(i+k, j+l) h(k,l)\f]
|
||||
|
||||
\f$h(k,l)\f$ is called the *kernel*, which is nothing more than the coefficients of the filter.
|
||||
|
||||
It helps to visualize a *filter* as a window of coefficients sliding across the image.
|
||||
|
||||
- There are many kind of filters, here we will mention the most used:
|
||||
|
||||
### Normalized Box Filter
|
||||
|
||||
- This filter is the simplest of all! Each output pixel is the *mean* of its kernel neighbors (
|
||||
all of them contribute with equal weights)
|
||||
- The kernel is below:
|
||||
|
||||
\f[K = \dfrac{1}{K_{width} \cdot K_{height}} \begin{bmatrix}
|
||||
1 & 1 & 1 & ... & 1 \\
|
||||
1 & 1 & 1 & ... & 1 \\
|
||||
. & . & . & ... & 1 \\
|
||||
. & . & . & ... & 1 \\
|
||||
1 & 1 & 1 & ... & 1
|
||||
\end{bmatrix}\f]
|
||||
|
||||
### Gaussian Filter
|
||||
|
||||
- Probably the most useful filter (although not the fastest). Gaussian filtering is done by
|
||||
convolving each point in the input array with a *Gaussian kernel* and then summing them all to
|
||||
produce the output array.
|
||||
- Just to make the picture clearer, remember how a 1D Gaussian kernel look like?
|
||||
|
||||

|
||||
|
||||
Assuming that an image is 1D, you can notice that the pixel located in the middle would have the
|
||||
biggest weight. The weight of its neighbors decreases as the spatial distance between them and
|
||||
the center pixel increases.
|
||||
|
||||
@note
|
||||
Remember that a 2D Gaussian can be represented as :
|
||||
\f[G_{0}(x, y) = A e^{ \dfrac{ -(x - \mu_{x})^{2} }{ 2\sigma^{2}_{x} } + \dfrac{ -(y - \mu_{y})^{2} }{ 2\sigma^{2}_{y} } }\f]
|
||||
where \f$\mu\f$ is the mean (the peak) and \f$\sigma^{2}\f$ represents the variance (per each of the
|
||||
variables \f$x\f$ and \f$y\f$)
|
||||
|
||||
### Median Filter
|
||||
|
||||
The median filter run through each element of the signal (in this case the image) and replace each
|
||||
pixel with the **median** of its neighboring pixels (located in a square neighborhood around the
|
||||
evaluated pixel).
|
||||
|
||||
### Bilateral Filter
|
||||
|
||||
- So far, we have explained some filters which main goal is to *smooth* an input image. However,
|
||||
sometimes the filters do not only dissolve the noise, but also smooth away the *edges*. To avoid
|
||||
this (at certain extent at least), we can use a bilateral filter.
|
||||
- In an analogous way as the Gaussian filter, the bilateral filter also considers the neighboring
|
||||
pixels with weights assigned to each of them. These weights have two components, the first of
|
||||
which is the same weighting used by the Gaussian filter. The second component takes into account
|
||||
the difference in intensity between the neighboring pixels and the evaluated one.
|
||||
- For a more detailed explanation you can check [this
|
||||
link](http://homepages.inf.ed.ac.uk/rbf/CVonline/LOCAL_COPIES/MANDUCHI1/Bilateral_Filtering.html)
|
||||
|
||||
Code
|
||||
----
|
||||
|
||||
- **What does this program do?**
|
||||
- Loads an image
|
||||
- Applies 4 different kinds of filters (explained in Theory) and show the filtered images
|
||||
sequentially
|
||||
|
||||
@add_toggle_cpp
|
||||
- **Downloadable code**: Click
|
||||
[here](https://raw.githubusercontent.com/opencv/opencv/master/samples/cpp/tutorial_code/ImgProc/Smoothing/Smoothing.cpp)
|
||||
|
||||
- **Code at glance:**
|
||||
@include samples/cpp/tutorial_code/ImgProc/Smoothing/Smoothing.cpp
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_java
|
||||
- **Downloadable code**: Click
|
||||
[here](https://raw.githubusercontent.com/opencv/opencv/master/samples/java/tutorial_code/ImgProc/Smoothing/Smoothing.java)
|
||||
|
||||
- **Code at glance:**
|
||||
@include samples/java/tutorial_code/ImgProc/Smoothing/Smoothing.java
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_python
|
||||
- **Downloadable code**: Click
|
||||
[here](https://raw.githubusercontent.com/opencv/opencv/master/samples/python/tutorial_code/imgProc/Smoothing/smoothing.py)
|
||||
|
||||
- **Code at glance:**
|
||||
@include samples/python/tutorial_code/imgProc/Smoothing/smoothing.py
|
||||
@end_toggle
|
||||
|
||||
Explanation
|
||||
-----------
|
||||
|
||||
Let's check the OpenCV functions that involve only the smoothing procedure, since the rest is
|
||||
already known by now.
|
||||
|
||||
#### Normalized Block Filter:
|
||||
|
||||
- OpenCV offers the function **blur()** to perform smoothing with this filter.
|
||||
We specify 4 arguments (more details, check the Reference):
|
||||
- *src*: Source image
|
||||
- *dst*: Destination image
|
||||
- *Size( w, h )*: Defines the size of the kernel to be used ( of width *w* pixels and height
|
||||
*h* pixels)
|
||||
- *Point(-1, -1)*: Indicates where the anchor point (the pixel evaluated) is located with
|
||||
respect to the neighborhood. If there is a negative value, then the center of the kernel is
|
||||
considered the anchor point.
|
||||
|
||||
@add_toggle_cpp
|
||||
@snippet cpp/tutorial_code/ImgProc/Smoothing/Smoothing.cpp blur
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_java
|
||||
@snippet samples/java/tutorial_code/ImgProc/Smoothing/Smoothing.java blur
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_python
|
||||
@snippet samples/python/tutorial_code/imgProc/Smoothing/smoothing.py blur
|
||||
@end_toggle
|
||||
|
||||
#### Gaussian Filter:
|
||||
|
||||
- It is performed by the function **GaussianBlur()** :
|
||||
Here we use 4 arguments (more details, check the OpenCV reference):
|
||||
- *src*: Source image
|
||||
- *dst*: Destination image
|
||||
- *Size(w, h)*: The size of the kernel to be used (the neighbors to be considered). \f$w\f$ and
|
||||
\f$h\f$ have to be odd and positive numbers otherwise the size will be calculated using the
|
||||
\f$\sigma_{x}\f$ and \f$\sigma_{y}\f$ arguments.
|
||||
- \f$\sigma_{x}\f$: The standard deviation in x. Writing \f$0\f$ implies that \f$\sigma_{x}\f$ is
|
||||
calculated using kernel size.
|
||||
- \f$\sigma_{y}\f$: The standard deviation in y. Writing \f$0\f$ implies that \f$\sigma_{y}\f$ is
|
||||
calculated using kernel size.
|
||||
|
||||
@add_toggle_cpp
|
||||
@snippet cpp/tutorial_code/ImgProc/Smoothing/Smoothing.cpp gaussianblur
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_java
|
||||
@snippet samples/java/tutorial_code/ImgProc/Smoothing/Smoothing.java gaussianblur
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_python
|
||||
@snippet samples/python/tutorial_code/imgProc/Smoothing/smoothing.py gaussianblur
|
||||
@end_toggle
|
||||
|
||||
#### Median Filter:
|
||||
|
||||
- This filter is provided by the **medianBlur()** function:
|
||||
We use three arguments:
|
||||
- *src*: Source image
|
||||
- *dst*: Destination image, must be the same type as *src*
|
||||
- *i*: Size of the kernel (only one because we use a square window). Must be odd.
|
||||
|
||||
@add_toggle_cpp
|
||||
@snippet cpp/tutorial_code/ImgProc/Smoothing/Smoothing.cpp medianblur
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_java
|
||||
@snippet samples/java/tutorial_code/ImgProc/Smoothing/Smoothing.java medianblur
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_python
|
||||
@snippet samples/python/tutorial_code/imgProc/Smoothing/smoothing.py medianblur
|
||||
@end_toggle
|
||||
|
||||
#### Bilateral Filter
|
||||
|
||||
- Provided by OpenCV function **bilateralFilter()**
|
||||
We use 5 arguments:
|
||||
- *src*: Source image
|
||||
- *dst*: Destination image
|
||||
- *d*: The diameter of each pixel neighborhood.
|
||||
- \f$\sigma_{Color}\f$: Standard deviation in the color space.
|
||||
- \f$\sigma_{Space}\f$: Standard deviation in the coordinate space (in pixel terms)
|
||||
|
||||
@add_toggle_cpp
|
||||
@snippet cpp/tutorial_code/ImgProc/Smoothing/Smoothing.cpp bilateralfilter
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_java
|
||||
@snippet samples/java/tutorial_code/ImgProc/Smoothing/Smoothing.java bilateralfilter
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_python
|
||||
@snippet samples/python/tutorial_code/imgProc/Smoothing/smoothing.py bilateralfilter
|
||||
@end_toggle
|
||||
|
||||
Results
|
||||
-------
|
||||
|
||||
- The code opens an image (in this case [lena.jpg](https://raw.githubusercontent.com/opencv/opencv/master/samples/data/lena.jpg))
|
||||
and display it under the effects of the 4 filters explained.
|
||||
- Here is a snapshot of the image smoothed using *medianBlur*:
|
||||
|
||||

|
||||
|
After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 4.4 KiB |
@@ -0,0 +1,296 @@
|
||||
Back Projection {#tutorial_back_projection}
|
||||
===============
|
||||
|
||||
@tableofcontents
|
||||
|
||||
@prev_tutorial{tutorial_histogram_comparison}
|
||||
@next_tutorial{tutorial_template_matching}
|
||||
|
||||
| | |
|
||||
| -: | :- |
|
||||
| Original author | Ana Huamán |
|
||||
| Compatibility | OpenCV >= 3.0 |
|
||||
|
||||
Goal
|
||||
----
|
||||
|
||||
In this tutorial you will learn:
|
||||
|
||||
- What is Back Projection and why it is useful
|
||||
- How to use the OpenCV function @ref cv::calcBackProject to calculate Back Projection
|
||||
- How to mix different channels of an image by using the OpenCV function @ref cv::mixChannels
|
||||
|
||||
Theory
|
||||
------
|
||||
|
||||
### What is Back Projection?
|
||||
|
||||
- Back Projection is a way of recording how well the pixels of a given image fit the distribution
|
||||
of pixels in a histogram model.
|
||||
- To make it simpler: For Back Projection, you calculate the histogram model of a feature and then
|
||||
use it to find this feature in an image.
|
||||
- Application example: If you have a histogram of flesh color (say, a Hue-Saturation histogram ),
|
||||
then you can use it to find flesh color areas in an image:
|
||||
|
||||
### How does it work?
|
||||
|
||||
- We explain this by using the skin example:
|
||||
- Let's say you have gotten a skin histogram (Hue-Saturation) based on the image below. The
|
||||
histogram besides is going to be our *model histogram* (which we know represents a sample of
|
||||
skin tonality). You applied some mask to capture only the histogram of the skin area:
|
||||

|
||||

|
||||
|
||||
- Now, let's imagine that you get another hand image (Test Image) like the one below: (with its
|
||||
respective histogram):
|
||||

|
||||

|
||||
|
||||
|
||||
- What we want to do is to use our *model histogram* (that we know represents a skin tonality) to
|
||||
detect skin areas in our Test Image. Here are the steps
|
||||
-# In each pixel of our Test Image (i.e. \f$p(i,j)\f$ ), collect the data and find the
|
||||
correspondent bin location for that pixel (i.e. \f$( h_{i,j}, s_{i,j} )\f$ ).
|
||||
-# Lookup the *model histogram* in the correspondent bin - \f$( h_{i,j}, s_{i,j} )\f$ - and read
|
||||
the bin value.
|
||||
-# Store this bin value in a new image (*BackProjection*). Also, you may consider to normalize
|
||||
the *model histogram* first, so the output for the Test Image can be visible for you.
|
||||
-# Applying the steps above, we get the following BackProjection image for our Test Image:
|
||||
|
||||

|
||||
|
||||
-# In terms of statistics, the values stored in *BackProjection* represent the *probability*
|
||||
that a pixel in *Test Image* belongs to a skin area, based on the *model histogram* that we
|
||||
use. For instance in our Test image, the brighter areas are more probable to be skin area
|
||||
(as they actually are), whereas the darker areas have less probability (notice that these
|
||||
"dark" areas belong to surfaces that have some shadow on it, which in turns affects the
|
||||
detection).
|
||||
|
||||
Code
|
||||
----
|
||||
|
||||
- **What does this program do?**
|
||||
- Loads an image
|
||||
- Convert the original to HSV format and separate only *Hue* channel to be used for the
|
||||
Histogram (using the OpenCV function @ref cv::mixChannels )
|
||||
- Let the user to enter the number of bins to be used in the calculation of the histogram.
|
||||
- Calculate the histogram (and update it if the bins change) and the backprojection of the
|
||||
same image.
|
||||
- Display the backprojection and the histogram in windows.
|
||||
|
||||
@add_toggle_cpp
|
||||
- **Downloadable code**:
|
||||
- Click
|
||||
[here](https://github.com/opencv/opencv/tree/master/samples/cpp/tutorial_code/Histograms_Matching/calcBackProject_Demo1.cpp)
|
||||
for the basic version (explained in this tutorial).
|
||||
- For stuff slightly fancier (using H-S histograms and floodFill to define a mask for the
|
||||
skin area) you can check the [improved
|
||||
demo](https://github.com/opencv/opencv/tree/master/samples/cpp/tutorial_code/Histograms_Matching/calcBackProject_Demo2.cpp)
|
||||
- ...or you can always check out the classical
|
||||
[camshiftdemo](https://github.com/opencv/opencv/tree/master/samples/cpp/camshiftdemo.cpp)
|
||||
in samples.
|
||||
|
||||
- **Code at glance:**
|
||||
@include samples/cpp/tutorial_code/Histograms_Matching/calcBackProject_Demo1.cpp
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_java
|
||||
- **Downloadable code**:
|
||||
- Click
|
||||
[here](https://github.com/opencv/opencv/tree/master/samples/java/tutorial_code/Histograms_Matching/back_projection/CalcBackProjectDemo1.java)
|
||||
for the basic version (explained in this tutorial).
|
||||
- For stuff slightly fancier (using H-S histograms and floodFill to define a mask for the
|
||||
skin area) you can check the [improved
|
||||
demo](https://github.com/opencv/opencv/tree/master/samples/java/tutorial_code/Histograms_Matching/back_projection/CalcBackProjectDemo2.java)
|
||||
- ...or you can always check out the classical
|
||||
[camshiftdemo](https://github.com/opencv/opencv/tree/master/samples/cpp/camshiftdemo.cpp)
|
||||
in samples.
|
||||
|
||||
- **Code at glance:**
|
||||
@include samples/java/tutorial_code/Histograms_Matching/back_projection/CalcBackProjectDemo1.java
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_python
|
||||
- **Downloadable code**:
|
||||
- Click
|
||||
[here](https://github.com/opencv/opencv/tree/master/samples/python/tutorial_code/Histograms_Matching/back_projection/calcBackProject_Demo1.py)
|
||||
for the basic version (explained in this tutorial).
|
||||
- For stuff slightly fancier (using H-S histograms and floodFill to define a mask for the
|
||||
skin area) you can check the [improved
|
||||
demo](https://github.com/opencv/opencv/tree/master/samples/python/tutorial_code/Histograms_Matching/back_projection/calcBackProject_Demo2.py)
|
||||
- ...or you can always check out the classical
|
||||
[camshiftdemo](https://github.com/opencv/opencv/tree/master/samples/cpp/camshiftdemo.cpp)
|
||||
in samples.
|
||||
|
||||
- **Code at glance:**
|
||||
@include samples/python/tutorial_code/Histograms_Matching/back_projection/calcBackProject_Demo1.py
|
||||
@end_toggle
|
||||
|
||||
Explanation
|
||||
-----------
|
||||
|
||||
- Read the input image:
|
||||
|
||||
@add_toggle_cpp
|
||||
@snippet samples/cpp/tutorial_code/Histograms_Matching/calcBackProject_Demo1.cpp Read the image
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_java
|
||||
@snippet samples/java/tutorial_code/Histograms_Matching/back_projection/CalcBackProjectDemo1.java Read the image
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_python
|
||||
@snippet samples/python/tutorial_code/Histograms_Matching/back_projection/calcBackProject_Demo1.py Read the image
|
||||
@end_toggle
|
||||
|
||||
- Transform it to HSV format:
|
||||
|
||||
@add_toggle_cpp
|
||||
@snippet samples/cpp/tutorial_code/Histograms_Matching/calcBackProject_Demo1.cpp Transform it to HSV
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_java
|
||||
@snippet samples/java/tutorial_code/Histograms_Matching/back_projection/CalcBackProjectDemo1.java Transform it to HSV
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_python
|
||||
@snippet samples/python/tutorial_code/Histograms_Matching/back_projection/calcBackProject_Demo1.py Transform it to HSV
|
||||
@end_toggle
|
||||
|
||||
- For this tutorial, we will use only the Hue value for our 1-D histogram (check out the fancier
|
||||
code in the links above if you want to use the more standard H-S histogram, which yields better
|
||||
results):
|
||||
|
||||
@add_toggle_cpp
|
||||
@snippet samples/cpp/tutorial_code/Histograms_Matching/calcBackProject_Demo1.cpp Use only the Hue value
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_java
|
||||
@snippet samples/java/tutorial_code/Histograms_Matching/back_projection/CalcBackProjectDemo1.java Use only the Hue value
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_python
|
||||
@snippet samples/python/tutorial_code/Histograms_Matching/back_projection/calcBackProject_Demo1.py Use only the Hue value
|
||||
@end_toggle
|
||||
|
||||
- as you see, we use the function @ref cv::mixChannels to get only the channel 0 (Hue) from
|
||||
the hsv image. It gets the following parameters:
|
||||
- **&hsv:** The source array from which the channels will be copied
|
||||
- **1:** The number of source arrays
|
||||
- **&hue:** The destination array of the copied channels
|
||||
- **1:** The number of destination arrays
|
||||
- **ch[] = {0,0}:** The array of index pairs indicating how the channels are copied. In this
|
||||
case, the Hue(0) channel of &hsv is being copied to the 0 channel of &hue (1-channel)
|
||||
- **1:** Number of index pairs
|
||||
|
||||
- Create a Trackbar for the user to enter the bin values. Any change on the Trackbar means a call
|
||||
to the **Hist_and_Backproj** callback function.
|
||||
|
||||
@add_toggle_cpp
|
||||
@snippet samples/cpp/tutorial_code/Histograms_Matching/calcBackProject_Demo1.cpp Create Trackbar to enter the number of bins
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_java
|
||||
@snippet samples/java/tutorial_code/Histograms_Matching/back_projection/CalcBackProjectDemo1.java Create Trackbar to enter the number of bins
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_python
|
||||
@snippet samples/python/tutorial_code/Histograms_Matching/back_projection/calcBackProject_Demo1.py Create Trackbar to enter the number of bins
|
||||
@end_toggle
|
||||
|
||||
- Show the image and wait for the user to exit the program:
|
||||
|
||||
@add_toggle_cpp
|
||||
@snippet samples/cpp/tutorial_code/Histograms_Matching/calcBackProject_Demo1.cpp Show the image
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_java
|
||||
@snippet samples/java/tutorial_code/Histograms_Matching/back_projection/CalcBackProjectDemo1.java Show the image
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_python
|
||||
@snippet samples/python/tutorial_code/Histograms_Matching/back_projection/calcBackProject_Demo1.py Show the image
|
||||
@end_toggle
|
||||
|
||||
- **Hist_and_Backproj function:** Initialize the arguments needed for @ref cv::calcHist . The
|
||||
number of bins comes from the Trackbar:
|
||||
|
||||
@add_toggle_cpp
|
||||
@snippet samples/cpp/tutorial_code/Histograms_Matching/calcBackProject_Demo1.cpp initialize
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_java
|
||||
@snippet samples/java/tutorial_code/Histograms_Matching/back_projection/CalcBackProjectDemo1.java initialize
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_python
|
||||
@snippet samples/python/tutorial_code/Histograms_Matching/back_projection/calcBackProject_Demo1.py initialize
|
||||
@end_toggle
|
||||
|
||||
- Calculate the Histogram and normalize it to the range \f$[0,255]\f$
|
||||
|
||||
@add_toggle_cpp
|
||||
@snippet samples/cpp/tutorial_code/Histograms_Matching/calcBackProject_Demo1.cpp Get the Histogram and normalize it
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_java
|
||||
@snippet samples/java/tutorial_code/Histograms_Matching/back_projection/CalcBackProjectDemo1.java Get the Histogram and normalize it
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_python
|
||||
@snippet samples/python/tutorial_code/Histograms_Matching/back_projection/calcBackProject_Demo1.py Get the Histogram and normalize it
|
||||
@end_toggle
|
||||
|
||||
- Get the Backprojection of the same image by calling the function @ref cv::calcBackProject
|
||||
|
||||
@add_toggle_cpp
|
||||
@snippet samples/cpp/tutorial_code/Histograms_Matching/calcBackProject_Demo1.cpp Get Backprojection
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_java
|
||||
@snippet samples/java/tutorial_code/Histograms_Matching/back_projection/CalcBackProjectDemo1.java Get Backprojection
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_python
|
||||
@snippet samples/python/tutorial_code/Histograms_Matching/back_projection/calcBackProject_Demo1.py Get Backprojection
|
||||
@end_toggle
|
||||
|
||||
- all the arguments are known (the same as used to calculate the histogram), only we add the
|
||||
backproj matrix, which will store the backprojection of the source image (&hue)
|
||||
|
||||
- Display backproj:
|
||||
|
||||
@add_toggle_cpp
|
||||
@snippet samples/cpp/tutorial_code/Histograms_Matching/calcBackProject_Demo1.cpp Draw the backproj
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_java
|
||||
@snippet samples/java/tutorial_code/Histograms_Matching/back_projection/CalcBackProjectDemo1.java Draw the backproj
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_python
|
||||
@snippet samples/python/tutorial_code/Histograms_Matching/back_projection/calcBackProject_Demo1.py Draw the backproj
|
||||
@end_toggle
|
||||
|
||||
- Draw the 1-D Hue histogram of the image:
|
||||
|
||||
@add_toggle_cpp
|
||||
@snippet samples/cpp/tutorial_code/Histograms_Matching/calcBackProject_Demo1.cpp Draw the histogram
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_java
|
||||
@snippet samples/java/tutorial_code/Histograms_Matching/back_projection/CalcBackProjectDemo1.java Draw the histogram
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_python
|
||||
@snippet samples/python/tutorial_code/Histograms_Matching/back_projection/calcBackProject_Demo1.py Draw the histogram
|
||||
@end_toggle
|
||||
|
||||
Results
|
||||
-------
|
||||
|
||||
Here are the output by using a sample image ( guess what? Another hand ). You can play with the
|
||||
bin values and you will observe how it affects the results:
|
||||

|
||||

|
||||

|
||||
|
After Width: | Height: | Size: 5.2 KiB |
|
After Width: | Height: | Size: 3.2 KiB |
|
After Width: | Height: | Size: 7.6 KiB |
|
After Width: | Height: | Size: 8.9 KiB |
|
After Width: | Height: | Size: 8.8 KiB |
|
After Width: | Height: | Size: 8.4 KiB |
|
After Width: | Height: | Size: 8.6 KiB |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 5.4 KiB |
|
After Width: | Height: | Size: 2.0 KiB |
|
After Width: | Height: | Size: 7.4 KiB |
@@ -0,0 +1,297 @@
|
||||
Histogram Calculation {#tutorial_histogram_calculation}
|
||||
=====================
|
||||
|
||||
@tableofcontents
|
||||
|
||||
@prev_tutorial{tutorial_histogram_equalization}
|
||||
@next_tutorial{tutorial_histogram_comparison}
|
||||
|
||||
| | |
|
||||
| -: | :- |
|
||||
| Original author | Ana Huamán |
|
||||
| Compatibility | OpenCV >= 3.0 |
|
||||
|
||||
Goal
|
||||
----
|
||||
|
||||
In this tutorial you will learn how to:
|
||||
|
||||
- Use the OpenCV function @ref cv::split to divide an image into its correspondent planes.
|
||||
- To calculate histograms of arrays of images by using the OpenCV function @ref cv::calcHist
|
||||
- To normalize an array by using the function @ref cv::normalize
|
||||
|
||||
@note In the last tutorial (@ref tutorial_histogram_equalization) we talked about a particular kind of
|
||||
histogram called *Image histogram*. Now we will considerate it in its more general concept. Read on!
|
||||
|
||||
### What are histograms?
|
||||
|
||||
- Histograms are collected *counts* of data organized into a set of predefined *bins*
|
||||
- When we say *data* we are not restricting it to be intensity values (as we saw in the previous
|
||||
Tutorial @ref tutorial_histogram_equalization). The data collected can be whatever feature you find
|
||||
useful to describe your image.
|
||||
- Let's see an example. Imagine that a Matrix contains information of an image (i.e. intensity in
|
||||
the range \f$0-255\f$):
|
||||
|
||||

|
||||
|
||||
- What happens if we want to *count* this data in an organized way? Since we know that the *range*
|
||||
of information value for this case is 256 values, we can segment our range in subparts (called
|
||||
**bins**) like:
|
||||
|
||||
\f[\begin{array}{l}
|
||||
[0, 255] = { [0, 15] \cup [16, 31] \cup ....\cup [240,255] } \\
|
||||
range = { bin_{1} \cup bin_{2} \cup ....\cup bin_{n = 15} }
|
||||
\end{array}\f]
|
||||
|
||||
and we can keep count of the number of pixels that fall in the range of each \f$bin_{i}\f$. Applying
|
||||
this to the example above we get the image below ( axis x represents the bins and axis y the
|
||||
number of pixels in each of them).
|
||||
|
||||

|
||||
|
||||
- This was just a simple example of how an histogram works and why it is useful. An histogram can
|
||||
keep count not only of color intensities, but of whatever image features that we want to measure
|
||||
(i.e. gradients, directions, etc).
|
||||
- Let's identify some parts of the histogram:
|
||||
-# **dims**: The number of parameters you want to collect data of. In our example, **dims = 1**
|
||||
because we are only counting the intensity values of each pixel (in a greyscale image).
|
||||
-# **bins**: It is the number of **subdivisions** in each dim. In our example, **bins = 16**
|
||||
-# **range**: The limits for the values to be measured. In this case: **range = [0,255]**
|
||||
- What if you want to count two features? In this case your resulting histogram would be a 3D plot
|
||||
(in which x and y would be \f$bin_{x}\f$ and \f$bin_{y}\f$ for each feature and z would be the number of
|
||||
counts for each combination of \f$(bin_{x}, bin_{y})\f$. The same would apply for more features (of
|
||||
course it gets trickier).
|
||||
|
||||
### What OpenCV offers you
|
||||
|
||||
For simple purposes, OpenCV implements the function @ref cv::calcHist , which calculates the
|
||||
histogram of a set of arrays (usually images or image planes). It can operate with up to 32
|
||||
dimensions. We will see it in the code below!
|
||||
|
||||
Code
|
||||
----
|
||||
|
||||
- **What does this program do?**
|
||||
- Loads an image
|
||||
- Splits the image into its R, G and B planes using the function @ref cv::split
|
||||
- Calculate the Histogram of each 1-channel plane by calling the function @ref cv::calcHist
|
||||
- Plot the three histograms in a window
|
||||
|
||||
@add_toggle_cpp
|
||||
- **Downloadable code**: Click
|
||||
[here](https://github.com/opencv/opencv/tree/master/samples/cpp/tutorial_code/Histograms_Matching/calcHist_Demo.cpp)
|
||||
|
||||
- **Code at glance:**
|
||||
@include samples/cpp/tutorial_code/Histograms_Matching/calcHist_Demo.cpp
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_java
|
||||
- **Downloadable code**: Click
|
||||
[here](https://github.com/opencv/opencv/tree/master/samples/java/tutorial_code/Histograms_Matching/histogram_calculation/CalcHistDemo.java)
|
||||
|
||||
- **Code at glance:**
|
||||
@include samples/java/tutorial_code/Histograms_Matching/histogram_calculation/CalcHistDemo.java
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_python
|
||||
- **Downloadable code**: Click
|
||||
[here](https://github.com/opencv/opencv/tree/master/samples/python/tutorial_code/Histograms_Matching/histogram_calculation/calcHist_Demo.py)
|
||||
|
||||
- **Code at glance:**
|
||||
@include samples/python/tutorial_code/Histograms_Matching/histogram_calculation/calcHist_Demo.py
|
||||
@end_toggle
|
||||
|
||||
Explanation
|
||||
-----------
|
||||
|
||||
- Load the source image
|
||||
|
||||
@add_toggle_cpp
|
||||
@snippet samples/cpp/tutorial_code/Histograms_Matching/calcHist_Demo.cpp Load image
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_java
|
||||
@snippet samples/java/tutorial_code/Histograms_Matching/histogram_calculation/CalcHistDemo.java Load image
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_python
|
||||
@snippet samples/python/tutorial_code/Histograms_Matching/histogram_calculation/calcHist_Demo.py Load image
|
||||
@end_toggle
|
||||
|
||||
- Separate the source image in its three R,G and B planes. For this we use the OpenCV function
|
||||
@ref cv::split :
|
||||
|
||||
@add_toggle_cpp
|
||||
@snippet samples/cpp/tutorial_code/Histograms_Matching/calcHist_Demo.cpp Separate the image in 3 places ( B, G and R )
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_java
|
||||
@snippet samples/java/tutorial_code/Histograms_Matching/histogram_calculation/CalcHistDemo.java Separate the image in 3 places ( B, G and R )
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_python
|
||||
@snippet samples/python/tutorial_code/Histograms_Matching/histogram_calculation/calcHist_Demo.py Separate the image in 3 places ( B, G and R )
|
||||
@end_toggle
|
||||
our input is the image to be divided (this case with three channels) and the output is a vector
|
||||
of Mat )
|
||||
|
||||
- Now we are ready to start configuring the **histograms** for each plane. Since we are working
|
||||
with the B, G and R planes, we know that our values will range in the interval \f$[0,255]\f$
|
||||
|
||||
- Establish the number of bins (5, 10...):
|
||||
|
||||
@add_toggle_cpp
|
||||
@snippet samples/cpp/tutorial_code/Histograms_Matching/calcHist_Demo.cpp Establish the number of bins
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_java
|
||||
@snippet samples/java/tutorial_code/Histograms_Matching/histogram_calculation/CalcHistDemo.java Establish the number of bins
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_python
|
||||
@snippet samples/python/tutorial_code/Histograms_Matching/histogram_calculation/calcHist_Demo.py Establish the number of bins
|
||||
@end_toggle
|
||||
|
||||
- Set the range of values (as we said, between 0 and 255 )
|
||||
|
||||
@add_toggle_cpp
|
||||
@snippet samples/cpp/tutorial_code/Histograms_Matching/calcHist_Demo.cpp Set the ranges ( for B,G,R) )
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_java
|
||||
@snippet samples/java/tutorial_code/Histograms_Matching/histogram_calculation/CalcHistDemo.java Set the ranges ( for B,G,R) )
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_python
|
||||
@snippet samples/python/tutorial_code/Histograms_Matching/histogram_calculation/calcHist_Demo.py Set the ranges ( for B,G,R) )
|
||||
@end_toggle
|
||||
|
||||
- We want our bins to have the same size (uniform) and to clear the histograms in the
|
||||
beginning, so:
|
||||
|
||||
@add_toggle_cpp
|
||||
@snippet samples/cpp/tutorial_code/Histograms_Matching/calcHist_Demo.cpp Set histogram param
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_java
|
||||
@snippet samples/java/tutorial_code/Histograms_Matching/histogram_calculation/CalcHistDemo.java Set histogram param
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_python
|
||||
@snippet samples/python/tutorial_code/Histograms_Matching/histogram_calculation/calcHist_Demo.py Set histogram param
|
||||
@end_toggle
|
||||
|
||||
- We proceed to calculate the histograms by using the OpenCV function @ref cv::calcHist :
|
||||
|
||||
@add_toggle_cpp
|
||||
@snippet samples/cpp/tutorial_code/Histograms_Matching/calcHist_Demo.cpp Compute the histograms
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_java
|
||||
@snippet samples/java/tutorial_code/Histograms_Matching/histogram_calculation/CalcHistDemo.java Compute the histograms
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_python
|
||||
@snippet samples/python/tutorial_code/Histograms_Matching/histogram_calculation/calcHist_Demo.py Compute the histograms
|
||||
@end_toggle
|
||||
|
||||
- where the arguments are (**C++ code**):
|
||||
- **&bgr_planes[0]:** The source array(s)
|
||||
- **1**: The number of source arrays (in this case we are using 1. We can enter here also
|
||||
a list of arrays )
|
||||
- **0**: The channel (*dim*) to be measured. In this case it is just the intensity (each
|
||||
array is single-channel) so we just write 0.
|
||||
- **Mat()**: A mask to be used on the source array ( zeros indicating pixels to be ignored
|
||||
). If not defined it is not used
|
||||
- **b_hist**: The Mat object where the histogram will be stored
|
||||
- **1**: The histogram dimensionality.
|
||||
- **histSize:** The number of bins per each used dimension
|
||||
- **histRange:** The range of values to be measured per each dimension
|
||||
- **uniform** and **accumulate**: The bin sizes are the same and the histogram is cleared
|
||||
at the beginning.
|
||||
|
||||
- Create an image to display the histograms:
|
||||
|
||||
@add_toggle_cpp
|
||||
@snippet samples/cpp/tutorial_code/Histograms_Matching/calcHist_Demo.cpp Draw the histograms for B, G and R
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_java
|
||||
@snippet samples/java/tutorial_code/Histograms_Matching/histogram_calculation/CalcHistDemo.java Draw the histograms for B, G and R
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_python
|
||||
@snippet samples/python/tutorial_code/Histograms_Matching/histogram_calculation/calcHist_Demo.py Draw the histograms for B, G and R
|
||||
@end_toggle
|
||||
|
||||
- Notice that before drawing, we first @ref cv::normalize the histogram so its values fall in the
|
||||
range indicated by the parameters entered:
|
||||
|
||||
@add_toggle_cpp
|
||||
@snippet samples/cpp/tutorial_code/Histograms_Matching/calcHist_Demo.cpp Normalize the result to ( 0, histImage.rows )
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_java
|
||||
@snippet samples/java/tutorial_code/Histograms_Matching/histogram_calculation/CalcHistDemo.java Normalize the result to ( 0, histImage.rows )
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_python
|
||||
@snippet samples/python/tutorial_code/Histograms_Matching/histogram_calculation/calcHist_Demo.py Normalize the result to ( 0, histImage.rows )
|
||||
@end_toggle
|
||||
|
||||
- this function receives these arguments (**C++ code**):
|
||||
- **b_hist:** Input array
|
||||
- **b_hist:** Output normalized array (can be the same)
|
||||
- **0** and **histImage.rows**: For this example, they are the lower and upper limits to
|
||||
normalize the values of **r_hist**
|
||||
- **NORM_MINMAX:** Argument that indicates the type of normalization (as described above, it
|
||||
adjusts the values between the two limits set before)
|
||||
- **-1:** Implies that the output normalized array will be the same type as the input
|
||||
- **Mat():** Optional mask
|
||||
|
||||
- Observe that to access the bin (in this case in this 1D-Histogram):
|
||||
|
||||
@add_toggle_cpp
|
||||
@snippet samples/cpp/tutorial_code/Histograms_Matching/calcHist_Demo.cpp Draw for each channel
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_java
|
||||
@snippet samples/java/tutorial_code/Histograms_Matching/histogram_calculation/CalcHistDemo.java Draw for each channel
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_python
|
||||
@snippet samples/python/tutorial_code/Histograms_Matching/histogram_calculation/calcHist_Demo.py Draw for each channel
|
||||
@end_toggle
|
||||
we use the expression (**C++ code**):
|
||||
@code{.cpp}
|
||||
b_hist.at<float>(i)
|
||||
@endcode
|
||||
where \f$i\f$ indicates the dimension. If it were a 2D-histogram we would use something like:
|
||||
@code{.cpp}
|
||||
b_hist.at<float>( i, j )
|
||||
@endcode
|
||||
|
||||
- Finally we display our histograms and wait for the user to exit:
|
||||
|
||||
@add_toggle_cpp
|
||||
@snippet samples/cpp/tutorial_code/Histograms_Matching/calcHist_Demo.cpp Display
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_java
|
||||
@snippet samples/java/tutorial_code/Histograms_Matching/histogram_calculation/CalcHistDemo.java Display
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_python
|
||||
@snippet samples/python/tutorial_code/Histograms_Matching/histogram_calculation/calcHist_Demo.py Display
|
||||
@end_toggle
|
||||
|
||||
Result
|
||||
------
|
||||
|
||||
-# Using as input argument an image like the one shown below:
|
||||
|
||||

|
||||
|
||||
-# Produces the following histogram:
|
||||
|
||||

|
||||
|
After Width: | Height: | Size: 19 KiB |
|
After Width: | Height: | Size: 30 KiB |
|
After Width: | Height: | Size: 62 KiB |
|
After Width: | Height: | Size: 9.3 KiB |
@@ -0,0 +1,196 @@
|
||||
Histogram Comparison {#tutorial_histogram_comparison}
|
||||
====================
|
||||
|
||||
@tableofcontents
|
||||
|
||||
@prev_tutorial{tutorial_histogram_calculation}
|
||||
@next_tutorial{tutorial_back_projection}
|
||||
|
||||
| | |
|
||||
| -: | :- |
|
||||
| Original author | Ana Huamán |
|
||||
| Compatibility | OpenCV >= 3.0 |
|
||||
|
||||
Goal
|
||||
----
|
||||
|
||||
In this tutorial you will learn how to:
|
||||
|
||||
- Use the function @ref cv::compareHist to get a numerical parameter that express how well two
|
||||
histograms match with each other.
|
||||
- Use different metrics to compare histograms
|
||||
|
||||
Theory
|
||||
------
|
||||
|
||||
- To compare two histograms ( \f$H_{1}\f$ and \f$H_{2}\f$ ), first we have to choose a *metric*
|
||||
(\f$d(H_{1}, H_{2})\f$) to express how well both histograms match.
|
||||
- OpenCV implements the function @ref cv::compareHist to perform a comparison. It also offers 4
|
||||
different metrics to compute the matching:
|
||||
-# **Correlation ( CV_COMP_CORREL )**
|
||||
\f[d(H_1,H_2) = \frac{\sum_I (H_1(I) - \bar{H_1}) (H_2(I) - \bar{H_2})}{\sqrt{\sum_I(H_1(I) - \bar{H_1})^2 \sum_I(H_2(I) - \bar{H_2})^2}}\f]
|
||||
where
|
||||
\f[\bar{H_k} = \frac{1}{N} \sum _J H_k(J)\f]
|
||||
and \f$N\f$ is the total number of histogram bins.
|
||||
|
||||
-# **Chi-Square ( CV_COMP_CHISQR )**
|
||||
\f[d(H_1,H_2) = \sum _I \frac{\left(H_1(I)-H_2(I)\right)^2}{H_1(I)}\f]
|
||||
|
||||
-# **Intersection ( method=CV_COMP_INTERSECT )**
|
||||
\f[d(H_1,H_2) = \sum _I \min (H_1(I), H_2(I))\f]
|
||||
|
||||
-# **Bhattacharyya distance ( CV_COMP_BHATTACHARYYA )**
|
||||
\f[d(H_1,H_2) = \sqrt{1 - \frac{1}{\sqrt{\bar{H_1} \bar{H_2} N^2}} \sum_I \sqrt{H_1(I) \cdot H_2(I)}}\f]
|
||||
|
||||
Code
|
||||
----
|
||||
|
||||
- **What does this program do?**
|
||||
- Loads a *base image* and 2 *test images* to be compared with it.
|
||||
- Generate 1 image that is the lower half of the *base image*
|
||||
- Convert the images to HSV format
|
||||
- Calculate the H-S histogram for all the images and normalize them in order to compare them.
|
||||
- Compare the histogram of the *base image* with respect to the 2 test histograms, the
|
||||
histogram of the lower half base image and with the same base image histogram.
|
||||
- Display the numerical matching parameters obtained.
|
||||
|
||||
@add_toggle_cpp
|
||||
- **Downloadable code**: Click
|
||||
[here](https://github.com/opencv/opencv/tree/master/samples/cpp/tutorial_code/Histograms_Matching/compareHist_Demo.cpp)
|
||||
|
||||
- **Code at glance:**
|
||||
@include samples/cpp/tutorial_code/Histograms_Matching/compareHist_Demo.cpp
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_java
|
||||
- **Downloadable code**: Click
|
||||
[here](https://github.com/opencv/opencv/tree/master/samples/java/tutorial_code/Histograms_Matching/histogram_comparison/CompareHistDemo.java)
|
||||
|
||||
- **Code at glance:**
|
||||
@include samples/java/tutorial_code/Histograms_Matching/histogram_comparison/CompareHistDemo.java
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_python
|
||||
- **Downloadable code**: Click
|
||||
[here](https://github.com/opencv/opencv/tree/master/samples/python/tutorial_code/Histograms_Matching/histogram_comparison/compareHist_Demo.py)
|
||||
|
||||
- **Code at glance:**
|
||||
@include samples/python/tutorial_code/Histograms_Matching/histogram_comparison/compareHist_Demo.py
|
||||
@end_toggle
|
||||
|
||||
Explanation
|
||||
-----------
|
||||
|
||||
- Load the base image (src_base) and the other two test images:
|
||||
|
||||
@add_toggle_cpp
|
||||
@snippet samples/cpp/tutorial_code/Histograms_Matching/compareHist_Demo.cpp Load three images with different environment settings
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_java
|
||||
@snippet samples/java/tutorial_code/Histograms_Matching/histogram_comparison/CompareHistDemo.java Load three images with different environment settings
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_python
|
||||
@snippet samples/python/tutorial_code/Histograms_Matching/histogram_comparison/compareHist_Demo.py Load three images with different environment settings
|
||||
@end_toggle
|
||||
|
||||
- Convert them to HSV format:
|
||||
|
||||
@add_toggle_cpp
|
||||
@snippet samples/cpp/tutorial_code/Histograms_Matching/compareHist_Demo.cpp Convert to HSV
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_java
|
||||
@snippet samples/java/tutorial_code/Histograms_Matching/histogram_comparison/CompareHistDemo.java Convert to HSV
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_python
|
||||
@snippet samples/python/tutorial_code/Histograms_Matching/histogram_comparison/compareHist_Demo.py Convert to HSV
|
||||
@end_toggle
|
||||
|
||||
- Also, create an image of half the base image (in HSV format):
|
||||
|
||||
@add_toggle_cpp
|
||||
@snippet samples/cpp/tutorial_code/Histograms_Matching/compareHist_Demo.cpp Convert to HSV half
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_java
|
||||
@snippet samples/java/tutorial_code/Histograms_Matching/histogram_comparison/CompareHistDemo.java Convert to HSV half
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_python
|
||||
@snippet samples/python/tutorial_code/Histograms_Matching/histogram_comparison/compareHist_Demo.py Convert to HSV half
|
||||
@end_toggle
|
||||
|
||||
- Initialize the arguments to calculate the histograms (bins, ranges and channels H and S ).
|
||||
|
||||
@add_toggle_cpp
|
||||
@snippet samples/cpp/tutorial_code/Histograms_Matching/compareHist_Demo.cpp Using 50 bins for hue and 60 for saturation
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_java
|
||||
@snippet samples/java/tutorial_code/Histograms_Matching/histogram_comparison/CompareHistDemo.java Using 50 bins for hue and 60 for saturation
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_python
|
||||
@snippet samples/python/tutorial_code/Histograms_Matching/histogram_comparison/compareHist_Demo.py Using 50 bins for hue and 60 for saturation
|
||||
@end_toggle
|
||||
|
||||
- Calculate the Histograms for the base image, the 2 test images and the half-down base image:
|
||||
|
||||
@add_toggle_cpp
|
||||
@snippet samples/cpp/tutorial_code/Histograms_Matching/compareHist_Demo.cpp Calculate the histograms for the HSV images
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_java
|
||||
@snippet samples/java/tutorial_code/Histograms_Matching/histogram_comparison/CompareHistDemo.java Calculate the histograms for the HSV images
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_python
|
||||
@snippet samples/python/tutorial_code/Histograms_Matching/histogram_comparison/compareHist_Demo.py Calculate the histograms for the HSV images
|
||||
@end_toggle
|
||||
|
||||
- Apply sequentially the 4 comparison methods between the histogram of the base image (hist_base)
|
||||
and the other histograms:
|
||||
|
||||
@add_toggle_cpp
|
||||
@snippet samples/cpp/tutorial_code/Histograms_Matching/compareHist_Demo.cpp Apply the histogram comparison methods
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_java
|
||||
@snippet samples/java/tutorial_code/Histograms_Matching/histogram_comparison/CompareHistDemo.java Apply the histogram comparison methods
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_python
|
||||
@snippet samples/python/tutorial_code/Histograms_Matching/histogram_comparison/compareHist_Demo.py Apply the histogram comparison methods
|
||||
@end_toggle
|
||||
|
||||
Results
|
||||
-------
|
||||
|
||||
-# We use as input the following images:
|
||||

|
||||

|
||||

|
||||
where the first one is the base (to be compared to the others), the other 2 are the test images.
|
||||
We will also compare the first image with respect to itself and with respect of half the base
|
||||
image.
|
||||
|
||||
-# We should expect a perfect match when we compare the base image histogram with itself. Also,
|
||||
compared with the histogram of half the base image, it should present a high match since both
|
||||
are from the same source. For the other two test images, we can observe that they have very
|
||||
different lighting conditions, so the matching should not be very good:
|
||||
|
||||
-# Here the numeric results we got with OpenCV 3.4.1:
|
||||
*Method* | Base - Base | Base - Half | Base - Test 1 | Base - Test 2
|
||||
----------------- | ------------ | ------------ | -------------- | ---------------
|
||||
*Correlation* | 1.000000 | 0.880438 | 0.20457 | 0.0664547
|
||||
*Chi-square* | 0.000000 | 4.6834 | 2697.98 | 4763.8
|
||||
*Intersection* | 18.8947 | 13.022 | 5.44085 | 2.58173
|
||||
*Bhattacharyya* | 0.000000 | 0.237887 | 0.679826 | 0.874173
|
||||
For the *Correlation* and *Intersection* methods, the higher the metric, the more accurate the
|
||||
match. As we can see, the match *base-base* is the highest of all as expected. Also we can observe
|
||||
that the match *base-half* is the second best match (as we predicted). For the other two metrics,
|
||||
the less the result, the better the match. We can observe that the matches between the test 1 and
|
||||
test 2 with respect to the base are worse, which again, was expected.
|
||||
|
After Width: | Height: | Size: 8.6 KiB |
|
After Width: | Height: | Size: 5.3 KiB |
|
After Width: | Height: | Size: 3.4 KiB |
@@ -0,0 +1,200 @@
|
||||
Histogram Equalization {#tutorial_histogram_equalization}
|
||||
======================
|
||||
|
||||
@tableofcontents
|
||||
|
||||
@prev_tutorial{tutorial_warp_affine}
|
||||
@next_tutorial{tutorial_histogram_calculation}
|
||||
|
||||
| | |
|
||||
| -: | :- |
|
||||
| Original author | Ana Huamán |
|
||||
| Compatibility | OpenCV >= 3.0 |
|
||||
|
||||
Goal
|
||||
----
|
||||
|
||||
In this tutorial you will learn:
|
||||
|
||||
- What an image histogram is and why it is useful
|
||||
- To equalize histograms of images by using the OpenCV function @ref cv::equalizeHist
|
||||
|
||||
Theory
|
||||
------
|
||||
|
||||
### What is an Image Histogram?
|
||||
|
||||
- It is a graphical representation of the intensity distribution of an image.
|
||||
- It quantifies the number of pixels for each intensity value considered.
|
||||
|
||||

|
||||
|
||||
### What is Histogram Equalization?
|
||||
|
||||
- It is a method that improves the contrast in an image, in order to stretch out the intensity
|
||||
range (see also the corresponding <a href="https://en.wikipedia.org/wiki/Histogram_equalization">Wikipedia entry</a>).
|
||||
- To make it clearer, from the image above, you can see that the pixels seem clustered around the
|
||||
middle of the available range of intensities. What Histogram Equalization does is to *stretch
|
||||
out* this range. Take a look at the figure below: The green circles indicate the
|
||||
*underpopulated* intensities. After applying the equalization, we get an histogram like the
|
||||
figure in the center. The resulting image is shown in the picture at right.
|
||||
|
||||

|
||||
|
||||
### How does it work?
|
||||
|
||||
- Equalization implies *mapping* one distribution (the given histogram) to another distribution (a
|
||||
wider and more uniform distribution of intensity values) so the intensity values are spread
|
||||
over the whole range.
|
||||
- To accomplish the equalization effect, the remapping should be the *cumulative distribution
|
||||
function (cdf)* (more details, refer to *Learning OpenCV*). For the histogram \f$H(i)\f$, its
|
||||
*cumulative distribution* \f$H^{'}(i)\f$ is:
|
||||
|
||||
\f[H^{'}(i) = \sum_{0 \le j < i} H(j)\f]
|
||||
|
||||
To use this as a remapping function, we have to normalize \f$H^{'}(i)\f$ such that the maximum value
|
||||
is 255 ( or the maximum value for the intensity of the image ). From the example above, the
|
||||
cumulative function is:
|
||||
|
||||

|
||||
|
||||
- Finally, we use a simple remapping procedure to obtain the intensity values of the equalized
|
||||
image:
|
||||
|
||||
\f[equalized( x, y ) = H^{'}( src(x,y) )\f]
|
||||
|
||||
Code
|
||||
----
|
||||
|
||||
- **What does this program do?**
|
||||
- Loads an image
|
||||
- Convert the original image to grayscale
|
||||
- Equalize the Histogram by using the OpenCV function @ref cv::equalizeHist
|
||||
- Display the source and equalized images in a window.
|
||||
|
||||
@add_toggle_cpp
|
||||
- **Downloadable code**: Click
|
||||
[here](https://github.com/opencv/opencv/tree/master/samples/cpp/tutorial_code/Histograms_Matching/EqualizeHist_Demo.cpp)
|
||||
|
||||
- **Code at glance:**
|
||||
@include samples/cpp/tutorial_code/Histograms_Matching/EqualizeHist_Demo.cpp
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_java
|
||||
- **Downloadable code**: Click
|
||||
[here](https://github.com/opencv/opencv/tree/master/samples/java/tutorial_code/Histograms_Matching/histogram_equalization/EqualizeHistDemo.java)
|
||||
|
||||
- **Code at glance:**
|
||||
@include samples/java/tutorial_code/Histograms_Matching/histogram_equalization/EqualizeHistDemo.java
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_python
|
||||
- **Downloadable code**: Click
|
||||
[here](https://github.com/opencv/opencv/tree/master/samples/python/tutorial_code/Histograms_Matching/histogram_equalization/EqualizeHist_Demo.py)
|
||||
|
||||
- **Code at glance:**
|
||||
@include samples/python/tutorial_code/Histograms_Matching/histogram_equalization/EqualizeHist_Demo.py
|
||||
@end_toggle
|
||||
|
||||
Explanation
|
||||
-----------
|
||||
|
||||
- Load the source image:
|
||||
|
||||
@add_toggle_cpp
|
||||
@snippet samples/cpp/tutorial_code/Histograms_Matching/EqualizeHist_Demo.cpp Load image
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_java
|
||||
@snippet samples/java/tutorial_code/Histograms_Matching/histogram_equalization/EqualizeHistDemo.java Load image
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_python
|
||||
@snippet samples/python/tutorial_code/Histograms_Matching/histogram_equalization/EqualizeHist_Demo.py Load image
|
||||
@end_toggle
|
||||
|
||||
- Convert it to grayscale:
|
||||
|
||||
@add_toggle_cpp
|
||||
@snippet samples/cpp/tutorial_code/Histograms_Matching/EqualizeHist_Demo.cpp Convert to grayscale
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_java
|
||||
@snippet samples/java/tutorial_code/Histograms_Matching/histogram_equalization/EqualizeHistDemo.java Convert to grayscale
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_python
|
||||
@snippet samples/python/tutorial_code/Histograms_Matching/histogram_equalization/EqualizeHist_Demo.py Convert to grayscale
|
||||
@end_toggle
|
||||
|
||||
- Apply histogram equalization with the function @ref cv::equalizeHist :
|
||||
|
||||
@add_toggle_cpp
|
||||
@snippet samples/cpp/tutorial_code/Histograms_Matching/EqualizeHist_Demo.cpp Apply Histogram Equalization
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_java
|
||||
@snippet samples/java/tutorial_code/Histograms_Matching/histogram_equalization/EqualizeHistDemo.java Apply Histogram Equalization
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_python
|
||||
@snippet samples/python/tutorial_code/Histograms_Matching/histogram_equalization/EqualizeHist_Demo.py Apply Histogram Equalization
|
||||
@end_toggle
|
||||
As it can be easily seen, the only arguments are the original image and the output (equalized)
|
||||
image.
|
||||
|
||||
- Display both images (original and equalized):
|
||||
|
||||
@add_toggle_cpp
|
||||
@snippet samples/cpp/tutorial_code/Histograms_Matching/EqualizeHist_Demo.cpp Display results
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_java
|
||||
@snippet samples/java/tutorial_code/Histograms_Matching/histogram_equalization/EqualizeHistDemo.java Display results
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_python
|
||||
@snippet samples/python/tutorial_code/Histograms_Matching/histogram_equalization/EqualizeHist_Demo.py Display results
|
||||
@end_toggle
|
||||
|
||||
- Wait until user exists the program
|
||||
|
||||
@add_toggle_cpp
|
||||
@snippet samples/cpp/tutorial_code/Histograms_Matching/EqualizeHist_Demo.cpp Wait until user exits the program
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_java
|
||||
@snippet samples/java/tutorial_code/Histograms_Matching/histogram_equalization/EqualizeHistDemo.java Wait until user exits the program
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_python
|
||||
@snippet samples/python/tutorial_code/Histograms_Matching/histogram_equalization/EqualizeHist_Demo.py Wait until user exits the program
|
||||
@end_toggle
|
||||
|
||||
Results
|
||||
-------
|
||||
|
||||
-# To appreciate better the results of equalization, let's introduce an image with not much
|
||||
contrast, such as:
|
||||
|
||||

|
||||
|
||||
which, by the way, has this histogram:
|
||||
|
||||

|
||||
|
||||
notice that the pixels are clustered around the center of the histogram.
|
||||
|
||||
-# After applying the equalization with our program, we get this result:
|
||||
|
||||

|
||||
|
||||
this image has certainly more contrast. Check out its new histogram like this:
|
||||
|
||||

|
||||
|
||||
Notice how the number of pixels is more distributed through the intensity range.
|
||||
|
||||
@note
|
||||
Are you wondering how did we draw the Histogram figures shown above? Check out the following
|
||||
tutorial!
|
||||
|
After Width: | Height: | Size: 7.2 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 4.9 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 3.6 KiB |
|
After Width: | Height: | Size: 8.0 KiB |
|
After Width: | Height: | Size: 9.6 KiB |
|
After Width: | Height: | Size: 8.2 KiB |
|
After Width: | Height: | Size: 9.6 KiB |
|
After Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 22 KiB |
|
After Width: | Height: | Size: 77 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 840 B |
|
After Width: | Height: | Size: 7.9 KiB |
|
After Width: | Height: | Size: 20 KiB |
|
After Width: | Height: | Size: 16 KiB |
@@ -0,0 +1,327 @@
|
||||
Template Matching {#tutorial_template_matching}
|
||||
=================
|
||||
|
||||
@tableofcontents
|
||||
|
||||
@prev_tutorial{tutorial_back_projection}
|
||||
@next_tutorial{tutorial_find_contours}
|
||||
|
||||
| | |
|
||||
| -: | :- |
|
||||
| Original author | Ana Huamán |
|
||||
| Compatibility | OpenCV >= 3.0 |
|
||||
|
||||
Goal
|
||||
----
|
||||
|
||||
In this tutorial you will learn how to:
|
||||
|
||||
- Use the OpenCV function **matchTemplate()** to search for matches between an image patch and
|
||||
an input image
|
||||
- Use the OpenCV function **minMaxLoc()** to find the maximum and minimum values (as well as
|
||||
their positions) in a given array.
|
||||
|
||||
Theory
|
||||
------
|
||||
|
||||
### What is template matching?
|
||||
|
||||
Template matching is a technique for finding areas of an image that match (are similar) to a
|
||||
template image (patch).
|
||||
|
||||
While the patch must be a rectangle it may be that not all of the
|
||||
rectangle is relevant. In such a case, a mask can be used to isolate the portion of the patch
|
||||
that should be used to find the match.
|
||||
|
||||
### How does it work?
|
||||
|
||||
- We need two primary components:
|
||||
|
||||
-# **Source image (I):** The image in which we expect to find a match to the template image
|
||||
-# **Template image (T):** The patch image which will be compared to the source image
|
||||
|
||||
our goal is to detect the highest matching area:
|
||||
|
||||

|
||||
|
||||
- To identify the matching area, we have to *compare* the template image against the source image
|
||||
by sliding it:
|
||||
|
||||

|
||||
|
||||
- By **sliding**, we mean moving the patch one pixel at a time (left to right, up to down). At
|
||||
each location, a metric is calculated so it represents how "good" or "bad" the match at that
|
||||
location is (or how similar the patch is to that particular area of the source image).
|
||||
- For each location of **T** over **I**, you *store* the metric in the *result matrix* **R**.
|
||||
Each location \f$(x,y)\f$ in **R** contains the match metric:
|
||||
|
||||

|
||||
|
||||
the image above is the result **R** of sliding the patch with a metric **TM_CCORR_NORMED**.
|
||||
The brightest locations indicate the highest matches. As you can see, the location marked by the
|
||||
red circle is probably the one with the highest value, so that location (the rectangle formed by
|
||||
that point as a corner and width and height equal to the patch image) is considered the match.
|
||||
- In practice, we locate the highest value (or lower, depending of the type of matching method) in
|
||||
the *R* matrix, using the function **minMaxLoc()**
|
||||
|
||||
### How does the mask work?
|
||||
- If masking is needed for the match, three components are required:
|
||||
|
||||
-# **Source image (I):** The image in which we expect to find a match to the template image
|
||||
-# **Template image (T):** The patch image which will be compared to the source image
|
||||
-# **Mask image (M):** The mask, a grayscale image that masks the template
|
||||
|
||||
|
||||
- Only two matching methods currently accept a mask: TM_SQDIFF and TM_CCORR_NORMED (see
|
||||
below for explanation of all the matching methods available in opencv).
|
||||
|
||||
|
||||
- The mask must have the same dimensions as the template
|
||||
|
||||
|
||||
- The mask should have a CV_8U or CV_32F depth and the same number of channels
|
||||
as the template image. In CV_8U case, the mask values are treated as binary,
|
||||
i.e. zero and non-zero. In CV_32F case, the values should fall into [0..1]
|
||||
range and the template pixels will be multiplied by the corresponding mask pixel
|
||||
values. Since the input images in the sample have the CV_8UC3 type, the mask
|
||||
is also read as color image.
|
||||
|
||||

|
||||
|
||||
### Which are the matching methods available in OpenCV?
|
||||
|
||||
Good question. OpenCV implements Template matching in the function **matchTemplate()**. The
|
||||
available methods are 6:
|
||||
|
||||
-# **method=TM_SQDIFF**
|
||||
|
||||
\f[R(x,y)= \sum _{x',y'} (T(x',y')-I(x+x',y+y'))^2\f]
|
||||
|
||||
-# **method=TM_SQDIFF_NORMED**
|
||||
|
||||
\f[R(x,y)= \frac{\sum_{x',y'} (T(x',y')-I(x+x',y+y'))^2}{\sqrt{\sum_{x',y'}T(x',y')^2 \cdot \sum_{x',y'} I(x+x',y+y')^2}}\f]
|
||||
|
||||
-# **method=TM_CCORR**
|
||||
|
||||
\f[R(x,y)= \sum _{x',y'} (T(x',y') \cdot I(x+x',y+y'))\f]
|
||||
|
||||
-# **method=TM_CCORR_NORMED**
|
||||
|
||||
\f[R(x,y)= \frac{\sum_{x',y'} (T(x',y') \cdot I(x+x',y+y'))}{\sqrt{\sum_{x',y'}T(x',y')^2 \cdot \sum_{x',y'} I(x+x',y+y')^2}}\f]
|
||||
|
||||
-# **method=TM_CCOEFF**
|
||||
|
||||
\f[R(x,y)= \sum _{x',y'} (T'(x',y') \cdot I'(x+x',y+y'))\f]
|
||||
|
||||
where
|
||||
|
||||
\f[\begin{array}{l} T'(x',y')=T(x',y') - 1/(w \cdot h) \cdot \sum _{x'',y''} T(x'',y'') \\ I'(x+x',y+y')=I(x+x',y+y') - 1/(w \cdot h) \cdot \sum _{x'',y''} I(x+x'',y+y'') \end{array}\f]
|
||||
|
||||
-# **method=TM_CCOEFF_NORMED**
|
||||
|
||||
\f[R(x,y)= \frac{ \sum_{x',y'} (T'(x',y') \cdot I'(x+x',y+y')) }{ \sqrt{\sum_{x',y'}T'(x',y')^2 \cdot \sum_{x',y'} I'(x+x',y+y')^2} }\f]
|
||||
|
||||
Code
|
||||
----
|
||||
|
||||
- **What does this program do?**
|
||||
- Loads an input image, an image patch (*template*), and optionally a mask
|
||||
- Perform a template matching procedure by using the OpenCV function **matchTemplate()**
|
||||
with any of the 6 matching methods described before. The user can choose the method by
|
||||
entering its selection in the Trackbar. If a mask is supplied, it will only be used for
|
||||
the methods that support masking
|
||||
- Normalize the output of the matching procedure
|
||||
- Localize the location with higher matching probability
|
||||
- Draw a rectangle around the area corresponding to the highest match
|
||||
|
||||
@add_toggle_cpp
|
||||
|
||||
- **Downloadable code**: Click
|
||||
[here](https://github.com/opencv/opencv/tree/master/samples/cpp/tutorial_code/Histograms_Matching/MatchTemplate_Demo.cpp)
|
||||
- **Code at glance:**
|
||||
@include samples/cpp/tutorial_code/Histograms_Matching/MatchTemplate_Demo.cpp
|
||||
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_java
|
||||
|
||||
- **Downloadable code**: Click
|
||||
[here](https://github.com/opencv/opencv/tree/master/samples/java/tutorial_code/ImgProc/tutorial_template_matching/MatchTemplateDemo.java)
|
||||
- **Code at glance:**
|
||||
@include samples/java/tutorial_code/ImgProc/tutorial_template_matching/MatchTemplateDemo.java
|
||||
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_python
|
||||
|
||||
- **Downloadable code**: Click
|
||||
[here](https://github.com/opencv/opencv/tree/master/samples/python/tutorial_code/imgProc/match_template/match_template.py)
|
||||
- **Code at glance:**
|
||||
@include samples/python/tutorial_code/imgProc/match_template/match_template.py
|
||||
|
||||
@end_toggle
|
||||
|
||||
Explanation
|
||||
-----------
|
||||
|
||||
- Declare some global variables, such as the image, template and result matrices, as well as the
|
||||
match method and the window names:
|
||||
|
||||
@add_toggle_cpp
|
||||
@snippet samples/cpp/tutorial_code/Histograms_Matching/MatchTemplate_Demo.cpp declare
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_java
|
||||
@snippet samples/java/tutorial_code/ImgProc/tutorial_template_matching/MatchTemplateDemo.java declare
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_python
|
||||
@snippet samples/python/tutorial_code/imgProc/match_template/match_template.py global_variables
|
||||
@end_toggle
|
||||
|
||||
- Load the source image, template, and optionally, if supported for the matching method, a mask:
|
||||
|
||||
@add_toggle_cpp
|
||||
@snippet samples/cpp/tutorial_code/Histograms_Matching/MatchTemplate_Demo.cpp load_image
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_java
|
||||
@snippet samples/java/tutorial_code/ImgProc/tutorial_template_matching/MatchTemplateDemo.java load_image
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_python
|
||||
@snippet samples/python/tutorial_code/imgProc/match_template/match_template.py load_image
|
||||
@end_toggle
|
||||
|
||||
- Create the Trackbar to enter the kind of matching method to be used. When a change is detected
|
||||
the callback function is called.
|
||||
|
||||
@add_toggle_cpp
|
||||
@snippet samples/cpp/tutorial_code/Histograms_Matching/MatchTemplate_Demo.cpp create_trackbar
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_java
|
||||
@snippet samples/java/tutorial_code/ImgProc/tutorial_template_matching/MatchTemplateDemo.java create_trackbar
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_python
|
||||
@snippet samples/python/tutorial_code/imgProc/match_template/match_template.py create_trackbar
|
||||
@end_toggle
|
||||
|
||||
- Let's check out the callback function. First, it makes a copy of the source image:
|
||||
|
||||
@add_toggle_cpp
|
||||
@snippet samples/cpp/tutorial_code/Histograms_Matching/MatchTemplate_Demo.cpp copy_source
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_java
|
||||
@snippet samples/java/tutorial_code/ImgProc/tutorial_template_matching/MatchTemplateDemo.java copy_source
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_python
|
||||
@snippet samples/python/tutorial_code/imgProc/match_template/match_template.py copy_source
|
||||
@end_toggle
|
||||
|
||||
- Perform the template matching operation. The arguments are naturally the input image **I**,
|
||||
the template **T**, the result **R** and the match_method (given by the Trackbar),
|
||||
and optionally the mask image **M**.
|
||||
|
||||
@add_toggle_cpp
|
||||
@snippet samples/cpp/tutorial_code/Histograms_Matching/MatchTemplate_Demo.cpp match_template
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_java
|
||||
@snippet samples/java/tutorial_code/ImgProc/tutorial_template_matching/MatchTemplateDemo.java match_template
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_python
|
||||
@snippet samples/python/tutorial_code/imgProc/match_template/match_template.py match_template
|
||||
@end_toggle
|
||||
|
||||
- We normalize the results:
|
||||
|
||||
@add_toggle_cpp
|
||||
@snippet samples/cpp/tutorial_code/Histograms_Matching/MatchTemplate_Demo.cpp normalize
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_java
|
||||
@snippet samples/java/tutorial_code/ImgProc/tutorial_template_matching/MatchTemplateDemo.java normalize
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_python
|
||||
@snippet samples/python/tutorial_code/imgProc/match_template/match_template.py normalize
|
||||
@end_toggle
|
||||
|
||||
- We localize the minimum and maximum values in the result matrix **R** by using **minMaxLoc()**.
|
||||
|
||||
@add_toggle_cpp
|
||||
@snippet samples/cpp/tutorial_code/Histograms_Matching/MatchTemplate_Demo.cpp best_match
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_java
|
||||
@snippet samples/java/tutorial_code/ImgProc/tutorial_template_matching/MatchTemplateDemo.java best_match
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_python
|
||||
@snippet samples/python/tutorial_code/imgProc/match_template/match_template.py best_match
|
||||
@end_toggle
|
||||
|
||||
- For the first two methods ( TM_SQDIFF and MT_SQDIFF_NORMED ) the best match are the lowest
|
||||
values. For all the others, higher values represent better matches. So, we save the
|
||||
corresponding value in the **matchLoc** variable:
|
||||
|
||||
@add_toggle_cpp
|
||||
@snippet samples/cpp/tutorial_code/Histograms_Matching/MatchTemplate_Demo.cpp match_loc
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_java
|
||||
@snippet samples/java/tutorial_code/ImgProc/tutorial_template_matching/MatchTemplateDemo.java match_loc
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_python
|
||||
@snippet samples/python/tutorial_code/imgProc/match_template/match_template.py match_loc
|
||||
@end_toggle
|
||||
|
||||
- Display the source image and the result matrix. Draw a rectangle around the highest possible
|
||||
matching area:
|
||||
|
||||
@add_toggle_cpp
|
||||
@snippet samples/cpp/tutorial_code/Histograms_Matching/MatchTemplate_Demo.cpp imshow
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_java
|
||||
@snippet samples/java/tutorial_code/ImgProc/tutorial_template_matching/MatchTemplateDemo.java imshow
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_python
|
||||
@snippet samples/python/tutorial_code/imgProc/match_template/match_template.py imshow
|
||||
@end_toggle
|
||||
|
||||
Results
|
||||
-------
|
||||
|
||||
-# Testing our program with an input image such as:
|
||||
|
||||

|
||||
|
||||
and a template image:
|
||||
|
||||

|
||||
|
||||
-# Generate the following result matrices (first row are the standard methods SQDIFF, CCORR and
|
||||
CCOEFF, second row are the same methods in its normalized version). In the first column, the
|
||||
darkest is the better match, for the other two columns, the brighter a location, the higher the
|
||||
match.
|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||
|
||||
-# The right match is shown below (black rectangle around the face of the guy at the right). Notice
|
||||
that CCORR and CCDEFF gave erroneous best matches, however their normalized version did it
|
||||
right, this may be due to the fact that we are only considering the "highest match" and not the
|
||||
other possible high matches.
|
||||
|
||||

|
||||
84
doc/tutorials/imgproc/hitOrMiss/hitOrMiss.markdown
Normal file
@@ -0,0 +1,84 @@
|
||||
Hit-or-Miss {#tutorial_hitOrMiss}
|
||||
=================================
|
||||
|
||||
@tableofcontents
|
||||
|
||||
@prev_tutorial{tutorial_opening_closing_hats}
|
||||
@next_tutorial{tutorial_morph_lines_detection}
|
||||
|
||||
| | |
|
||||
| -: | :- |
|
||||
| Original author | Lorena García |
|
||||
| Compatibility | OpenCV >= 3.0 |
|
||||
|
||||
Goal
|
||||
----
|
||||
|
||||
In this tutorial you will learn how to find a given configuration or pattern in a binary image by using the Hit-or-Miss transform (also known as Hit-and-Miss transform).
|
||||
This transform is also the basis of more advanced morphological operations such as thinning or pruning.
|
||||
|
||||
We will use the OpenCV function **morphologyEx()** .
|
||||
|
||||
Hit-or-Miss theory
|
||||
-------------------
|
||||
|
||||
Morphological operators process images based on their shape. These operators apply one or more *structuring elements* to an input image to obtain the output image.
|
||||
The two basic morphological operations are the *erosion* and the *dilation*. The combination of these two operations generate advanced morphological transformations such as *opening*, *closing*, or *top-hat* transform.
|
||||
To know more about these and other basic morphological operations refer to previous tutorials (@ref tutorial_erosion_dilatation "Eroding and Dilating") and (@ref tutorial_opening_closing_hats "More Morphology Transformations").
|
||||
|
||||
The Hit-or-Miss transformation is useful to find patterns in binary images. In particular, it finds those pixels whose neighbourhood matches the shape of a first structuring element \f$B_1\f$
|
||||
while not matching the shape of a second structuring element \f$B_2\f$ at the same time. Mathematically, the operation applied to an image \f$A\f$ can be expressed as follows:
|
||||
\f[
|
||||
A\circledast B = (A\ominus B_1) \cap (A^c\ominus B_2)
|
||||
\f]
|
||||
|
||||
Therefore, the hit-or-miss operation comprises three steps:
|
||||
1. Erode image \f$A\f$ with structuring element \f$B_1\f$.
|
||||
2. Erode the complement of image \f$A\f$ (\f$A^c\f$) with structuring element \f$B_2\f$.
|
||||
3. AND results from step 1 and step 2.
|
||||
|
||||
The structuring elements \f$B_1\f$ and \f$B_2\f$ can be combined into a single element \f$B\f$. Let's see an example:
|
||||

|
||||
|
||||
In this case, we are looking for a pattern in which the central pixel belongs to the background while the north, south, east, and west pixels belong to the foreground. The rest of pixels in the neighbourhood can be of any kind, we don't care about them. Now, let's apply this kernel to an input image:
|
||||
|
||||

|
||||

|
||||
|
||||
You can see that the pattern is found in just one location within the image.
|
||||
|
||||
|
||||
Code
|
||||
----
|
||||
|
||||
The code corresponding to the previous example is shown below.
|
||||
|
||||
@add_toggle_cpp
|
||||
You can also download it from
|
||||
[here](https://raw.githubusercontent.com/opencv/opencv/master/samples/cpp/tutorial_code/ImgProc/HitMiss/HitMiss.cpp)
|
||||
@include samples/cpp/tutorial_code/ImgProc/HitMiss/HitMiss.cpp
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_java
|
||||
You can also download it from
|
||||
[here](https://raw.githubusercontent.com/opencv/opencv/master/samples/java/tutorial_code/ImgProc/HitMiss/HitMiss.java)
|
||||
@include samples/java/tutorial_code/ImgProc/HitMiss/HitMiss.java
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_python
|
||||
You can also download it from
|
||||
[here](https://raw.githubusercontent.com/opencv/opencv/master/samples/python/tutorial_code/imgProc/HitMiss/hit_miss.py)
|
||||
@include samples/python/tutorial_code/imgProc/HitMiss/hit_miss.py
|
||||
@end_toggle
|
||||
|
||||
As you can see, it is as simple as using the function **morphologyEx()** with the operation type **MORPH_HITMISS** and the chosen kernel.
|
||||
|
||||
Other examples
|
||||
--------------
|
||||
|
||||
Here you can find the output results of applying different kernels to the same input image used before:
|
||||
|
||||

|
||||

|
||||
|
||||
Now try your own patterns!
|
||||
BIN
doc/tutorials/imgproc/hitOrMiss/images/hitmiss_example2.png
Normal file
|
After Width: | Height: | Size: 22 KiB |
BIN
doc/tutorials/imgproc/hitOrMiss/images/hitmiss_example3.png
Normal file
|
After Width: | Height: | Size: 22 KiB |
BIN
doc/tutorials/imgproc/hitOrMiss/images/hitmiss_input.png
Normal file
|
After Width: | Height: | Size: 24 KiB |
BIN
doc/tutorials/imgproc/hitOrMiss/images/hitmiss_kernels.png
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
doc/tutorials/imgproc/hitOrMiss/images/hitmiss_output.png
Normal file
|
After Width: | Height: | Size: 18 KiB |
@@ -0,0 +1,174 @@
|
||||
Canny Edge Detector {#tutorial_canny_detector}
|
||||
===================
|
||||
|
||||
@tableofcontents
|
||||
|
||||
@prev_tutorial{tutorial_laplace_operator}
|
||||
@next_tutorial{tutorial_hough_lines}
|
||||
|
||||
| | |
|
||||
| -: | :- |
|
||||
| Original author | Ana Huamán |
|
||||
| Compatibility | OpenCV >= 3.0 |
|
||||
|
||||
Goal
|
||||
----
|
||||
|
||||
In this tutorial you will learn how to:
|
||||
|
||||
- Use the OpenCV function @ref cv::Canny to implement the Canny Edge Detector.
|
||||
|
||||
Theory
|
||||
------
|
||||
|
||||
The *Canny Edge detector* @cite Canny86 was developed by John F. Canny in 1986. Also known to many as the
|
||||
*optimal detector*, the Canny algorithm aims to satisfy three main criteria:
|
||||
- **Low error rate:** Meaning a good detection of only existent edges.
|
||||
- **Good localization:** The distance between edge pixels detected and real edge pixels have
|
||||
to be minimized.
|
||||
- **Minimal response:** Only one detector response per edge.
|
||||
|
||||
### Steps
|
||||
|
||||
-# Filter out any noise. The Gaussian filter is used for this purpose. An example of a Gaussian
|
||||
kernel of \f$size = 5\f$ that might be used is shown below:
|
||||
|
||||
\f[K = \dfrac{1}{159}\begin{bmatrix}
|
||||
2 & 4 & 5 & 4 & 2 \\
|
||||
4 & 9 & 12 & 9 & 4 \\
|
||||
5 & 12 & 15 & 12 & 5 \\
|
||||
4 & 9 & 12 & 9 & 4 \\
|
||||
2 & 4 & 5 & 4 & 2
|
||||
\end{bmatrix}\f]
|
||||
|
||||
-# Find the intensity gradient of the image. For this, we follow a procedure analogous to Sobel:
|
||||
-# Apply a pair of convolution masks (in \f$x\f$ and \f$y\f$ directions:
|
||||
\f[G_{x} = \begin{bmatrix}
|
||||
-1 & 0 & +1 \\
|
||||
-2 & 0 & +2 \\
|
||||
-1 & 0 & +1
|
||||
\end{bmatrix}\f]\f[G_{y} = \begin{bmatrix}
|
||||
-1 & -2 & -1 \\
|
||||
0 & 0 & 0 \\
|
||||
+1 & +2 & +1
|
||||
\end{bmatrix}\f]
|
||||
|
||||
-# Find the gradient strength and direction with:
|
||||
\f[\begin{array}{l}
|
||||
G = \sqrt{ G_{x}^{2} + G_{y}^{2} } \\
|
||||
\theta = \arctan(\dfrac{ G_{y} }{ G_{x} })
|
||||
\end{array}\f]
|
||||
The direction is rounded to one of four possible angles (namely 0, 45, 90 or 135)
|
||||
|
||||
-# *Non-maximum* suppression is applied. This removes pixels that are not considered to be part of
|
||||
an edge. Hence, only thin lines (candidate edges) will remain.
|
||||
-# *Hysteresis*: The final step. Canny does use two thresholds (upper and lower):
|
||||
|
||||
-# If a pixel gradient is higher than the *upper* threshold, the pixel is accepted as an edge
|
||||
-# If a pixel gradient value is below the *lower* threshold, then it is rejected.
|
||||
-# If the pixel gradient is between the two thresholds, then it will be accepted only if it is
|
||||
connected to a pixel that is above the *upper* threshold.
|
||||
|
||||
Canny recommended a *upper*:*lower* ratio between 2:1 and 3:1.
|
||||
|
||||
-# For more details, you can always consult your favorite Computer Vision book.
|
||||
|
||||
Code
|
||||
----
|
||||
|
||||
@add_toggle_cpp
|
||||
- The tutorial code's is shown lines below. You can also download it from
|
||||
[here](https://github.com/opencv/opencv/tree/master/samples/cpp/tutorial_code/ImgTrans/CannyDetector_Demo.cpp)
|
||||
@include samples/cpp/tutorial_code/ImgTrans/CannyDetector_Demo.cpp
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_java
|
||||
- The tutorial code's is shown lines below. You can also download it from
|
||||
[here](https://github.com/opencv/opencv/tree/master/samples/java/tutorial_code/ImgTrans/canny_detector/CannyDetectorDemo.java)
|
||||
@include samples/java/tutorial_code/ImgTrans/canny_detector/CannyDetectorDemo.java
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_python
|
||||
- The tutorial code's is shown lines below. You can also download it from
|
||||
[here](https://github.com/opencv/opencv/tree/master/samples/python/tutorial_code/ImgTrans/canny_detector/CannyDetector_Demo.py)
|
||||
@include samples/python/tutorial_code/ImgTrans/canny_detector/CannyDetector_Demo.py
|
||||
@end_toggle
|
||||
|
||||
- **What does this program do?**
|
||||
- Asks the user to enter a numerical value to set the lower threshold for our *Canny Edge
|
||||
Detector* (by means of a Trackbar).
|
||||
- Applies the *Canny Detector* and generates a **mask** (bright lines representing the edges
|
||||
on a black background).
|
||||
- Applies the mask obtained on the original image and display it in a window.
|
||||
|
||||
Explanation (C++ code)
|
||||
----------------------
|
||||
|
||||
-# Create some needed variables:
|
||||
@snippet cpp/tutorial_code/ImgTrans/CannyDetector_Demo.cpp variables
|
||||
|
||||
Note the following:
|
||||
|
||||
-# We establish a ratio of lower:upper threshold of 3:1 (with the variable *ratio*).
|
||||
-# We set the kernel size of \f$3\f$ (for the Sobel operations to be performed internally by the
|
||||
Canny function).
|
||||
-# We set a maximum value for the lower Threshold of \f$100\f$.
|
||||
|
||||
-# Loads the source image:
|
||||
@snippet cpp/tutorial_code/ImgTrans/CannyDetector_Demo.cpp load
|
||||
|
||||
-# Create a matrix of the same type and size of *src* (to be *dst*):
|
||||
@snippet cpp/tutorial_code/ImgTrans/CannyDetector_Demo.cpp create_mat
|
||||
-# Convert the image to grayscale (using the function @ref cv::cvtColor ):
|
||||
@snippet cpp/tutorial_code/ImgTrans/CannyDetector_Demo.cpp convert_to_gray
|
||||
-# Create a window to display the results:
|
||||
@snippet cpp/tutorial_code/ImgTrans/CannyDetector_Demo.cpp create_window
|
||||
-# Create a Trackbar for the user to enter the lower threshold for our Canny detector:
|
||||
@snippet cpp/tutorial_code/ImgTrans/CannyDetector_Demo.cpp create_trackbar
|
||||
Observe the following:
|
||||
|
||||
-# The variable to be controlled by the Trackbar is *lowThreshold* with a limit of
|
||||
*max_lowThreshold* (which we set to 100 previously)
|
||||
-# Each time the Trackbar registers an action, the callback function *CannyThreshold* will be
|
||||
invoked.
|
||||
|
||||
-# Let's check the *CannyThreshold* function, step by step:
|
||||
-# First, we blur the image with a filter of kernel size 3:
|
||||
@snippet cpp/tutorial_code/ImgTrans/CannyDetector_Demo.cpp reduce_noise
|
||||
-# Second, we apply the OpenCV function @ref cv::Canny :
|
||||
@snippet cpp/tutorial_code/ImgTrans/CannyDetector_Demo.cpp canny
|
||||
where the arguments are:
|
||||
|
||||
- *detected_edges*: Source image, grayscale
|
||||
- *detected_edges*: Output of the detector (can be the same as the input)
|
||||
- *lowThreshold*: The value entered by the user moving the Trackbar
|
||||
- *highThreshold*: Set in the program as three times the lower threshold (following
|
||||
Canny's recommendation)
|
||||
- *kernel_size*: We defined it to be 3 (the size of the Sobel kernel to be used
|
||||
internally)
|
||||
|
||||
-# We fill a *dst* image with zeros (meaning the image is completely black).
|
||||
@snippet cpp/tutorial_code/ImgTrans/CannyDetector_Demo.cpp fill
|
||||
-# Finally, we will use the function @ref cv::Mat::copyTo to map only the areas of the image that are
|
||||
identified as edges (on a black background).
|
||||
@ref cv::Mat::copyTo copy the *src* image onto *dst*. However, it will only copy the pixels in the
|
||||
locations where they have non-zero values. Since the output of the Canny detector is the edge
|
||||
contours on a black background, the resulting *dst* will be black in all the area but the
|
||||
detected edges.
|
||||
@snippet cpp/tutorial_code/ImgTrans/CannyDetector_Demo.cpp copyto
|
||||
-# We display our result:
|
||||
@snippet cpp/tutorial_code/ImgTrans/CannyDetector_Demo.cpp display
|
||||
|
||||
Result
|
||||
------
|
||||
|
||||
- After compiling the code above, we can run it giving as argument the path to an image. For
|
||||
example, using as an input the following image:
|
||||
|
||||

|
||||
|
||||
- Moving the slider, trying different threshold, we obtain the following result:
|
||||
|
||||

|
||||
|
||||
- Notice how the image is superposed to the black background on the edge regions.
|
||||
|
After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 28 KiB |
@@ -0,0 +1,243 @@
|
||||
Adding borders to your images {#tutorial_copyMakeBorder}
|
||||
=============================
|
||||
|
||||
@tableofcontents
|
||||
|
||||
@prev_tutorial{tutorial_filter_2d}
|
||||
@next_tutorial{tutorial_sobel_derivatives}
|
||||
|
||||
| | |
|
||||
| -: | :- |
|
||||
| Original author | Ana Huamán |
|
||||
| Compatibility | OpenCV >= 3.0 |
|
||||
|
||||
Goal
|
||||
----
|
||||
|
||||
In this tutorial you will learn how to:
|
||||
|
||||
- Use the OpenCV function **copyMakeBorder()** to set the borders (extra padding to your
|
||||
image).
|
||||
|
||||
Theory
|
||||
------
|
||||
|
||||
@note The explanation below belongs to the book **Learning OpenCV** by Bradski and Kaehler.
|
||||
|
||||
-# In our previous tutorial we learned to use convolution to operate on images. One problem that
|
||||
naturally arises is how to handle the boundaries. How can we convolve them if the evaluated
|
||||
points are at the edge of the image?
|
||||
-# What most of OpenCV functions do is to copy a given image onto another slightly larger image and
|
||||
then automatically pads the boundary (by any of the methods explained in the sample code just
|
||||
below). This way, the convolution can be performed over the needed pixels without problems (the
|
||||
extra padding is cut after the operation is done).
|
||||
-# In this tutorial, we will briefly explore two ways of defining the extra padding (border) for an
|
||||
image:
|
||||
|
||||
-# **BORDER_CONSTANT**: Pad the image with a constant value (i.e. black or \f$0\f$
|
||||
-# **BORDER_REPLICATE**: The row or column at the very edge of the original is replicated to
|
||||
the extra border.
|
||||
|
||||
This will be seen more clearly in the Code section.
|
||||
|
||||
- **What does this program do?**
|
||||
- Load an image
|
||||
- Let the user choose what kind of padding use in the input image. There are two options:
|
||||
|
||||
-# *Constant value border*: Applies a padding of a constant value for the whole border.
|
||||
This value will be updated randomly each 0.5 seconds.
|
||||
-# *Replicated border*: The border will be replicated from the pixel values at the edges of
|
||||
the original image.
|
||||
|
||||
The user chooses either option by pressing 'c' (constant) or 'r' (replicate)
|
||||
- The program finishes when the user presses 'ESC'
|
||||
|
||||
Code
|
||||
----
|
||||
|
||||
The tutorial code's is shown lines below.
|
||||
|
||||
@add_toggle_cpp
|
||||
You can also download it from
|
||||
[here](https://raw.githubusercontent.com/opencv/opencv/master/samples/cpp/tutorial_code/ImgTrans/copyMakeBorder_demo.cpp)
|
||||
@include samples/cpp/tutorial_code/ImgTrans/copyMakeBorder_demo.cpp
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_java
|
||||
You can also download it from
|
||||
[here](https://raw.githubusercontent.com/opencv/opencv/master/samples/java/tutorial_code/ImgTrans/MakeBorder/CopyMakeBorder.java)
|
||||
@include samples/java/tutorial_code/ImgTrans/MakeBorder/CopyMakeBorder.java
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_python
|
||||
You can also download it from
|
||||
[here](https://raw.githubusercontent.com/opencv/opencv/master/samples/python/tutorial_code/ImgTrans/MakeBorder/copy_make_border.py)
|
||||
@include samples/python/tutorial_code/ImgTrans/MakeBorder/copy_make_border.py
|
||||
@end_toggle
|
||||
|
||||
Explanation
|
||||
-----------
|
||||
|
||||
#### Declare the variables
|
||||
|
||||
First we declare the variables we are going to use:
|
||||
|
||||
@add_toggle_cpp
|
||||
@snippet cpp/tutorial_code/ImgTrans/copyMakeBorder_demo.cpp variables
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_java
|
||||
@snippet java/tutorial_code/ImgTrans/MakeBorder/CopyMakeBorder.java variables
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_python
|
||||
@snippet python/tutorial_code/ImgTrans/MakeBorder/copy_make_border.py variables
|
||||
@end_toggle
|
||||
|
||||
Especial attention deserves the variable *rng* which is a random number generator. We use it to
|
||||
generate the random border color, as we will see soon.
|
||||
|
||||
#### Load an image
|
||||
|
||||
As usual we load our source image *src*:
|
||||
|
||||
@add_toggle_cpp
|
||||
@snippet cpp/tutorial_code/ImgTrans/copyMakeBorder_demo.cpp load
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_java
|
||||
@snippet java/tutorial_code/ImgTrans/MakeBorder/CopyMakeBorder.java load
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_python
|
||||
@snippet python/tutorial_code/ImgTrans/MakeBorder/copy_make_border.py load
|
||||
@end_toggle
|
||||
|
||||
#### Create a window
|
||||
|
||||
After giving a short intro of how to use the program, we create a window:
|
||||
|
||||
@add_toggle_cpp
|
||||
@snippet cpp/tutorial_code/ImgTrans/copyMakeBorder_demo.cpp create_window
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_java
|
||||
@snippet java/tutorial_code/ImgTrans/MakeBorder/CopyMakeBorder.java create_window
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_python
|
||||
@snippet python/tutorial_code/ImgTrans/MakeBorder/copy_make_border.py create_window
|
||||
@end_toggle
|
||||
|
||||
#### Initialize arguments
|
||||
|
||||
Now we initialize the argument that defines the size of the borders (*top*, *bottom*, *left* and
|
||||
*right*). We give them a value of 5% the size of *src*.
|
||||
|
||||
@add_toggle_cpp
|
||||
@snippet cpp/tutorial_code/ImgTrans/copyMakeBorder_demo.cpp init_arguments
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_java
|
||||
@snippet java/tutorial_code/ImgTrans/MakeBorder/CopyMakeBorder.java init_arguments
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_python
|
||||
@snippet python/tutorial_code/ImgTrans/MakeBorder/copy_make_border.py init_arguments
|
||||
@end_toggle
|
||||
|
||||
#### Loop
|
||||
|
||||
The program runs in an infinite loop while the key **ESC** isn't pressed.
|
||||
If the user presses '**c**' or '**r**', the *borderType* variable
|
||||
takes the value of *BORDER_CONSTANT* or *BORDER_REPLICATE* respectively:
|
||||
|
||||
@add_toggle_cpp
|
||||
@snippet cpp/tutorial_code/ImgTrans/copyMakeBorder_demo.cpp check_keypress
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_java
|
||||
@snippet java/tutorial_code/ImgTrans/MakeBorder/CopyMakeBorder.java check_keypress
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_python
|
||||
@snippet python/tutorial_code/ImgTrans/MakeBorder/copy_make_border.py check_keypress
|
||||
@end_toggle
|
||||
|
||||
#### Random color
|
||||
|
||||
In each iteration (after 0.5 seconds), the random border color (*value*) is updated...
|
||||
|
||||
@add_toggle_cpp
|
||||
@snippet cpp/tutorial_code/ImgTrans/copyMakeBorder_demo.cpp update_value
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_java
|
||||
@snippet java/tutorial_code/ImgTrans/MakeBorder/CopyMakeBorder.java update_value
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_python
|
||||
@snippet python/tutorial_code/ImgTrans/MakeBorder/copy_make_border.py update_value
|
||||
@end_toggle
|
||||
|
||||
This value is a set of three numbers picked randomly in the range \f$[0,255]\f$.
|
||||
|
||||
#### Form a border around the image
|
||||
|
||||
Finally, we call the function **copyMakeBorder()** to apply the respective padding:
|
||||
|
||||
@add_toggle_cpp
|
||||
@snippet cpp/tutorial_code/ImgTrans/copyMakeBorder_demo.cpp copymakeborder
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_java
|
||||
@snippet java/tutorial_code/ImgTrans/MakeBorder/CopyMakeBorder.java copymakeborder
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_python
|
||||
@snippet python/tutorial_code/ImgTrans/MakeBorder/copy_make_border.py copymakeborder
|
||||
@end_toggle
|
||||
|
||||
- The arguments are:
|
||||
|
||||
-# *src*: Source image
|
||||
-# *dst*: Destination image
|
||||
-# *top*, *bottom*, *left*, *right*: Length in pixels of the borders at each side of the image.
|
||||
We define them as being 5% of the original size of the image.
|
||||
-# *borderType*: Define what type of border is applied. It can be constant or replicate for
|
||||
this example.
|
||||
-# *value*: If *borderType* is *BORDER_CONSTANT*, this is the value used to fill the border
|
||||
pixels.
|
||||
|
||||
#### Display the results
|
||||
|
||||
We display our output image in the image created previously
|
||||
|
||||
@add_toggle_cpp
|
||||
@snippet cpp/tutorial_code/ImgTrans/copyMakeBorder_demo.cpp display
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_java
|
||||
@snippet java/tutorial_code/ImgTrans/MakeBorder/CopyMakeBorder.java display
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_python
|
||||
@snippet python/tutorial_code/ImgTrans/MakeBorder/copy_make_border.py display
|
||||
@end_toggle
|
||||
|
||||
Results
|
||||
-------
|
||||
|
||||
-# After compiling the code above, you can execute it giving as argument the path of an image. The
|
||||
result should be:
|
||||
|
||||
- By default, it begins with the border set to BORDER_CONSTANT. Hence, a succession of random
|
||||
colored borders will be shown.
|
||||
- If you press 'r', the border will become a replica of the edge pixels.
|
||||
- If you press 'c', the random colored borders will appear again
|
||||
- If you press 'ESC' the program will exit.
|
||||
|
||||
Below some screenshot showing how the border changes color and how the *BORDER_REPLICATE*
|
||||
option looks:
|
||||
|
||||

|
||||
|
After Width: | Height: | Size: 20 KiB |
@@ -0,0 +1,177 @@
|
||||
Image Segmentation with Distance Transform and Watershed Algorithm {#tutorial_distance_transform}
|
||||
=============
|
||||
|
||||
@tableofcontents
|
||||
|
||||
@prev_tutorial{tutorial_point_polygon_test}
|
||||
@next_tutorial{tutorial_out_of_focus_deblur_filter}
|
||||
|
||||
| | |
|
||||
| -: | :- |
|
||||
| Original author | Theodore Tsesmelis |
|
||||
| Compatibility | OpenCV >= 3.0 |
|
||||
|
||||
Goal
|
||||
----
|
||||
|
||||
In this tutorial you will learn how to:
|
||||
|
||||
- Use the OpenCV function @ref cv::filter2D in order to perform some laplacian filtering for image sharpening
|
||||
- Use the OpenCV function @ref cv::distanceTransform in order to obtain the derived representation of a binary image, where the value of each pixel is replaced by its distance to the nearest background pixel
|
||||
- Use the OpenCV function @ref cv::watershed in order to isolate objects in the image from the background
|
||||
|
||||
Theory
|
||||
------
|
||||
|
||||
Code
|
||||
----
|
||||
|
||||
@add_toggle_cpp
|
||||
This tutorial code's is shown lines below. You can also download it from
|
||||
[here](https://github.com/opencv/opencv/tree/master/samples/cpp/tutorial_code/ImgTrans/imageSegmentation.cpp).
|
||||
@include samples/cpp/tutorial_code/ImgTrans/imageSegmentation.cpp
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_java
|
||||
This tutorial code's is shown lines below. You can also download it from
|
||||
[here](https://github.com/opencv/opencv/tree/master/samples/java/tutorial_code/ImgTrans/distance_transformation/ImageSegmentationDemo.java)
|
||||
@include samples/java/tutorial_code/ImgTrans/distance_transformation/ImageSegmentationDemo.java
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_python
|
||||
This tutorial code's is shown lines below. You can also download it from
|
||||
[here](https://github.com/opencv/opencv/tree/master/samples/python/tutorial_code/ImgTrans/distance_transformation/imageSegmentation.py)
|
||||
@include samples/python/tutorial_code/ImgTrans/distance_transformation/imageSegmentation.py
|
||||
@end_toggle
|
||||
|
||||
Explanation / Result
|
||||
--------------------
|
||||
|
||||
- Load the source image and check if it is loaded without any problem, then show it:
|
||||
|
||||
@add_toggle_cpp
|
||||
@snippet samples/cpp/tutorial_code/ImgTrans/imageSegmentation.cpp load_image
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_java
|
||||
@snippet samples/java/tutorial_code/ImgTrans/distance_transformation/ImageSegmentationDemo.java load_image
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_python
|
||||
@snippet samples/python/tutorial_code/ImgTrans/distance_transformation/imageSegmentation.py load_image
|
||||
@end_toggle
|
||||
|
||||

|
||||
|
||||
- Then if we have an image with a white background, it is good to transform it to black. This will help us to discriminate the foreground objects easier when we will apply the Distance Transform:
|
||||
|
||||
@add_toggle_cpp
|
||||
@snippet samples/cpp/tutorial_code/ImgTrans/imageSegmentation.cpp black_bg
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_java
|
||||
@snippet samples/java/tutorial_code/ImgTrans/distance_transformation/ImageSegmentationDemo.java black_bg
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_python
|
||||
@snippet samples/python/tutorial_code/ImgTrans/distance_transformation/imageSegmentation.py black_bg
|
||||
@end_toggle
|
||||
|
||||

|
||||
|
||||
- Afterwards we will sharpen our image in order to acute the edges of the foreground objects. We will apply a laplacian filter with a quite strong filter (an approximation of second derivative):
|
||||
|
||||
@add_toggle_cpp
|
||||
@snippet samples/cpp/tutorial_code/ImgTrans/imageSegmentation.cpp sharp
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_java
|
||||
@snippet samples/java/tutorial_code/ImgTrans/distance_transformation/ImageSegmentationDemo.java sharp
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_python
|
||||
@snippet samples/python/tutorial_code/ImgTrans/distance_transformation/imageSegmentation.py sharp
|
||||
@end_toggle
|
||||
|
||||

|
||||

|
||||
|
||||
- Now we transform our new sharpened source image to a grayscale and a binary one, respectively:
|
||||
|
||||
@add_toggle_cpp
|
||||
@snippet samples/cpp/tutorial_code/ImgTrans/imageSegmentation.cpp bin
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_java
|
||||
@snippet samples/java/tutorial_code/ImgTrans/distance_transformation/ImageSegmentationDemo.java bin
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_python
|
||||
@snippet samples/python/tutorial_code/ImgTrans/distance_transformation/imageSegmentation.py bin
|
||||
@end_toggle
|
||||
|
||||

|
||||
|
||||
- We are ready now to apply the Distance Transform on the binary image. Moreover, we normalize the output image in order to be able visualize and threshold the result:
|
||||
|
||||
@add_toggle_cpp
|
||||
@snippet samples/cpp/tutorial_code/ImgTrans/imageSegmentation.cpp dist
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_java
|
||||
@snippet samples/java/tutorial_code/ImgTrans/distance_transformation/ImageSegmentationDemo.java dist
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_python
|
||||
@snippet samples/python/tutorial_code/ImgTrans/distance_transformation/imageSegmentation.py dist
|
||||
@end_toggle
|
||||
|
||||

|
||||
|
||||
- We threshold the *dist* image and then perform some morphology operation (i.e. dilation) in order to extract the peaks from the above image:
|
||||
|
||||
@add_toggle_cpp
|
||||
@snippet samples/cpp/tutorial_code/ImgTrans/imageSegmentation.cpp peaks
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_java
|
||||
@snippet samples/java/tutorial_code/ImgTrans/distance_transformation/ImageSegmentationDemo.java peaks
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_python
|
||||
@snippet samples/python/tutorial_code/ImgTrans/distance_transformation/imageSegmentation.py peaks
|
||||
@end_toggle
|
||||
|
||||

|
||||
|
||||
- From each blob then we create a seed/marker for the watershed algorithm with the help of the @ref cv::findContours function:
|
||||
|
||||
@add_toggle_cpp
|
||||
@snippet samples/cpp/tutorial_code/ImgTrans/imageSegmentation.cpp seeds
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_java
|
||||
@snippet samples/java/tutorial_code/ImgTrans/distance_transformation/ImageSegmentationDemo.java seeds
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_python
|
||||
@snippet samples/python/tutorial_code/ImgTrans/distance_transformation/imageSegmentation.py seeds
|
||||
@end_toggle
|
||||
|
||||

|
||||
|
||||
- Finally, we can apply the watershed algorithm, and visualize the result:
|
||||
|
||||
@add_toggle_cpp
|
||||
@snippet samples/cpp/tutorial_code/ImgTrans/imageSegmentation.cpp watershed
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_java
|
||||
@snippet samples/java/tutorial_code/ImgTrans/distance_transformation/ImageSegmentationDemo.java watershed
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_python
|
||||
@snippet samples/python/tutorial_code/ImgTrans/distance_transformation/imageSegmentation.py watershed
|
||||
@end_toggle
|
||||
|
||||

|
||||
|
After Width: | Height: | Size: 32 KiB |
|
After Width: | Height: | Size: 48 KiB |
|
After Width: | Height: | Size: 20 KiB |
|
After Width: | Height: | Size: 41 KiB |
|
After Width: | Height: | Size: 47 KiB |
|
After Width: | Height: | Size: 22 KiB |
|
After Width: | Height: | Size: 22 KiB |
|
After Width: | Height: | Size: 50 KiB |
|
After Width: | Height: | Size: 46 KiB |
183
doc/tutorials/imgproc/imgtrans/filter_2d/filter_2d.markdown
Normal file
@@ -0,0 +1,183 @@
|
||||
Making your own linear filters! {#tutorial_filter_2d}
|
||||
===============================
|
||||
|
||||
@tableofcontents
|
||||
|
||||
@prev_tutorial{tutorial_threshold_inRange}
|
||||
@next_tutorial{tutorial_copyMakeBorder}
|
||||
|
||||
| | |
|
||||
| -: | :- |
|
||||
| Original author | Ana Huamán |
|
||||
| Compatibility | OpenCV >= 3.0 |
|
||||
|
||||
Goal
|
||||
----
|
||||
|
||||
In this tutorial you will learn how to:
|
||||
|
||||
- Use the OpenCV function **filter2D()** to create your own linear filters.
|
||||
|
||||
Theory
|
||||
------
|
||||
|
||||
@note The explanation below belongs to the book **Learning OpenCV** by Bradski and Kaehler.
|
||||
|
||||
### Correlation
|
||||
|
||||
In a very general sense, correlation 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 coefficients along with an *anchor point* in
|
||||
that array, which is typically located at the center.
|
||||
|
||||

|
||||
|
||||
### How does correlation with a kernel work?
|
||||
|
||||
Assume you want to know the resulting value of a particular location in the image. The value of the
|
||||
correlation is calculated in the following way:
|
||||
|
||||
-# Place the kernel anchor on top of a determined pixel, with the rest of the kernel overlaying the
|
||||
corresponding local pixels in the image.
|
||||
-# 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.
|
||||
|
||||
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 **filter2D()** so you do not have to code all
|
||||
these operations.
|
||||
|
||||
### What does this program do?
|
||||
- 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
|
||||
|
||||
Code
|
||||
----
|
||||
|
||||
The tutorial code's is shown in the lines below.
|
||||
|
||||
@add_toggle_cpp
|
||||
You can also download it from
|
||||
[here](https://raw.githubusercontent.com/opencv/opencv/master/samples/cpp/tutorial_code/ImgTrans/filter2D_demo.cpp)
|
||||
@include cpp/tutorial_code/ImgTrans/filter2D_demo.cpp
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_java
|
||||
You can also download it from
|
||||
[here](https://raw.githubusercontent.com/opencv/opencv/master/samples/java/tutorial_code/ImgTrans/Filter2D/Filter2D_Demo.java)
|
||||
@include java/tutorial_code/ImgTrans/Filter2D/Filter2D_Demo.java
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_python
|
||||
You can also download it from
|
||||
[here](https://raw.githubusercontent.com/opencv/opencv/master/samples/python/tutorial_code/ImgTrans/Filter2D/filter2D.py)
|
||||
@include python/tutorial_code/ImgTrans/Filter2D/filter2D.py
|
||||
@end_toggle
|
||||
|
||||
Explanation
|
||||
-----------
|
||||
|
||||
#### Load an image
|
||||
|
||||
@add_toggle_cpp
|
||||
@snippet cpp/tutorial_code/ImgTrans/filter2D_demo.cpp load
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_java
|
||||
@snippet java/tutorial_code/ImgTrans/Filter2D/Filter2D_Demo.java load
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_python
|
||||
@snippet python/tutorial_code/ImgTrans/Filter2D/filter2D.py load
|
||||
@end_toggle
|
||||
|
||||
#### Initialize the arguments
|
||||
|
||||
@add_toggle_cpp
|
||||
@snippet cpp/tutorial_code/ImgTrans/filter2D_demo.cpp init_arguments
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_java
|
||||
@snippet java/tutorial_code/ImgTrans/Filter2D/Filter2D_Demo.java init_arguments
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_python
|
||||
@snippet python/tutorial_code/ImgTrans/Filter2D/filter2D.py init_arguments
|
||||
@end_toggle
|
||||
|
||||
##### Loop
|
||||
|
||||
Perform an infinite loop updating the kernel size and applying our linear filter to the input
|
||||
image. Let's analyze that more in detail:
|
||||
|
||||
- First we define the kernel our filter is going to use. Here it is:
|
||||
|
||||
@add_toggle_cpp
|
||||
@snippet cpp/tutorial_code/ImgTrans/filter2D_demo.cpp update_kernel
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_java
|
||||
@snippet java/tutorial_code/ImgTrans/Filter2D/Filter2D_Demo.java update_kernel
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_python
|
||||
@snippet python/tutorial_code/ImgTrans/Filter2D/filter2D.py update_kernel
|
||||
@end_toggle
|
||||
|
||||
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.
|
||||
|
||||
- After setting the kernel, we can generate the filter by using the function **filter2D()** :
|
||||
|
||||
@add_toggle_cpp
|
||||
@snippet cpp/tutorial_code/ImgTrans/filter2D_demo.cpp apply_filter
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_java
|
||||
@snippet java/tutorial_code/ImgTrans/Filter2D/Filter2D_Demo.java apply_filter
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_python
|
||||
@snippet python/tutorial_code/ImgTrans/Filter2D/filter2D.py apply_filter
|
||||
@end_toggle
|
||||
|
||||
- The arguments denote:
|
||||
- *src*: Source image
|
||||
- *dst*: Destination image
|
||||
- *ddepth*: The depth of *dst*. A negative value (such as \f$-1\f$) indicates that the depth is
|
||||
the same as the source.
|
||||
- *kernel*: The kernel to be scanned through the image
|
||||
- *anchor*: The position of the anchor relative to its kernel. The location *Point(-1, -1)*
|
||||
indicates the center by default.
|
||||
- *delta*: A value to be added to each pixel during the correlation. By default it is \f$0\f$
|
||||
- *BORDER_DEFAULT*: We let this value by default (more details in the following tutorial)
|
||||
|
||||
- Our program will effectuate a *while* loop, each 500 ms the kernel size of our filter will be
|
||||
updated in the range indicated.
|
||||
|
||||
Results
|
||||
-------
|
||||
|
||||
-# After compiling the code above, you can execute it giving as argument the path of an image. The
|
||||
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:
|
||||
|
||||

|
||||
|
After Width: | Height: | Size: 3.4 KiB |
|
After Width: | Height: | Size: 15 KiB |
@@ -0,0 +1,182 @@
|
||||
Hough Circle Transform {#tutorial_hough_circle}
|
||||
======================
|
||||
|
||||
@tableofcontents
|
||||
|
||||
@prev_tutorial{tutorial_hough_lines}
|
||||
@next_tutorial{tutorial_remap}
|
||||
|
||||
| | |
|
||||
| -: | :- |
|
||||
| Original author | Ana Huamán |
|
||||
| Compatibility | OpenCV >= 3.0 |
|
||||
|
||||
Goal
|
||||
----
|
||||
|
||||
In this tutorial you will learn how to:
|
||||
|
||||
- Use the OpenCV function **HoughCircles()** to detect circles in an image.
|
||||
|
||||
Theory
|
||||
------
|
||||
|
||||
### Hough Circle Transform
|
||||
|
||||
- The Hough Circle Transform works in a *roughly* analogous way to the Hough Line Transform
|
||||
explained in the previous tutorial.
|
||||
- In the line detection case, a line was defined by two parameters \f$(r, \theta)\f$. In the circle
|
||||
case, we need three parameters to define a circle:
|
||||
|
||||
\f[C : ( x_{center}, y_{center}, r )\f]
|
||||
|
||||
where \f$(x_{center}, y_{center})\f$ define the center position (green point) and \f$r\f$ is the radius,
|
||||
which allows us to completely define a circle, as it can be seen below:
|
||||
|
||||

|
||||
|
||||
- For sake of efficiency, OpenCV implements a detection method slightly trickier than the standard
|
||||
Hough Transform: *The Hough gradient method*, which is made up of two main stages. The first
|
||||
stage involves edge detection and finding the possible circle centers and the second stage finds
|
||||
the best radius for each candidate center. For more details, please check the book *Learning
|
||||
OpenCV* or your favorite Computer Vision bibliography
|
||||
|
||||
#### What does this program do?
|
||||
- Loads an image and blur it to reduce the noise
|
||||
- Applies the *Hough Circle Transform* to the blurred image .
|
||||
- Display the detected circle in a window.
|
||||
|
||||
Code
|
||||
----
|
||||
|
||||
@add_toggle_cpp
|
||||
The sample code that we will explain can be downloaded from
|
||||
[here](https://raw.githubusercontent.com/opencv/opencv/master/samples/cpp/tutorial_code/ImgTrans/houghcircles.cpp).
|
||||
A slightly fancier version (which shows trackbars for changing the threshold values) can be found
|
||||
[here](https://raw.githubusercontent.com/opencv/opencv/master/samples/cpp/tutorial_code/ImgTrans/HoughCircle_Demo.cpp).
|
||||
@include samples/cpp/tutorial_code/ImgTrans/houghcircles.cpp
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_java
|
||||
The sample code that we will explain can be downloaded from
|
||||
[here](https://raw.githubusercontent.com/opencv/opencv/master/samples/java/tutorial_code/ImgTrans/HoughCircle/HoughCircles.java).
|
||||
@include samples/java/tutorial_code/ImgTrans/HoughCircle/HoughCircles.java
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_python
|
||||
The sample code that we will explain can be downloaded from
|
||||
[here](https://raw.githubusercontent.com/opencv/opencv/master/samples/python/tutorial_code/ImgTrans/HoughCircle/hough_circle.py).
|
||||
@include samples/python/tutorial_code/ImgTrans/HoughCircle/hough_circle.py
|
||||
@end_toggle
|
||||
|
||||
Explanation
|
||||
-----------
|
||||
|
||||
The image we used can be found [here](https://raw.githubusercontent.com/opencv/opencv/master/samples/data/smarties.png)
|
||||
|
||||
#### Load an image:
|
||||
|
||||
@add_toggle_cpp
|
||||
@snippet samples/cpp/tutorial_code/ImgTrans/houghcircles.cpp load
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_java
|
||||
@snippet samples/python/tutorial_code/ImgTrans/HoughCircle/hough_circle.py load
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_python
|
||||
@snippet samples/java/tutorial_code/ImgTrans/HoughCircle/HoughCircles.java load
|
||||
@end_toggle
|
||||
|
||||
#### Convert it to grayscale:
|
||||
|
||||
@add_toggle_cpp
|
||||
@snippet samples/cpp/tutorial_code/ImgTrans/houghcircles.cpp convert_to_gray
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_java
|
||||
@snippet samples/python/tutorial_code/ImgTrans/HoughCircle/hough_circle.py convert_to_gray
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_python
|
||||
@snippet samples/java/tutorial_code/ImgTrans/HoughCircle/HoughCircles.java convert_to_gray
|
||||
@end_toggle
|
||||
|
||||
#### Apply a Median blur to reduce noise and avoid false circle detection:
|
||||
|
||||
@add_toggle_cpp
|
||||
@snippet samples/cpp/tutorial_code/ImgTrans/houghcircles.cpp reduce_noise
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_java
|
||||
@snippet samples/python/tutorial_code/ImgTrans/HoughCircle/hough_circle.py reduce_noise
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_python
|
||||
@snippet samples/java/tutorial_code/ImgTrans/HoughCircle/HoughCircles.java reduce_noise
|
||||
@end_toggle
|
||||
|
||||
#### Proceed to apply Hough Circle Transform:
|
||||
|
||||
@add_toggle_cpp
|
||||
@snippet samples/cpp/tutorial_code/ImgTrans/houghcircles.cpp houghcircles
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_java
|
||||
@snippet samples/python/tutorial_code/ImgTrans/HoughCircle/hough_circle.py houghcircles
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_python
|
||||
@snippet samples/java/tutorial_code/ImgTrans/HoughCircle/HoughCircles.java houghcircles
|
||||
@end_toggle
|
||||
|
||||
- with the arguments:
|
||||
|
||||
- *gray*: Input image (grayscale).
|
||||
- *circles*: A vector that stores sets of 3 values: \f$x_{c}, y_{c}, r\f$ for each detected
|
||||
circle.
|
||||
- *HOUGH_GRADIENT*: Define the detection method. Currently this is the only one available in
|
||||
OpenCV.
|
||||
- *dp = 1*: The inverse ratio of resolution.
|
||||
- *min_dist = gray.rows/16*: Minimum distance between detected centers.
|
||||
- *param_1 = 200*: Upper threshold for the internal Canny edge detector.
|
||||
- *param_2* = 100\*: Threshold for center detection.
|
||||
- *min_radius = 0*: Minimum radius to be detected. If unknown, put zero as default.
|
||||
- *max_radius = 0*: Maximum radius to be detected. If unknown, put zero as default.
|
||||
|
||||
#### Draw the detected circles:
|
||||
|
||||
@add_toggle_cpp
|
||||
@snippet samples/cpp/tutorial_code/ImgTrans/houghcircles.cpp draw
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_java
|
||||
@snippet samples/python/tutorial_code/ImgTrans/HoughCircle/hough_circle.py draw
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_python
|
||||
@snippet samples/java/tutorial_code/ImgTrans/HoughCircle/HoughCircles.java draw
|
||||
@end_toggle
|
||||
|
||||
You can see that we will draw the circle(s) on red and the center(s) with a small green dot
|
||||
|
||||
#### Display the detected circle(s) and wait for the user to exit the program:
|
||||
|
||||
@add_toggle_cpp
|
||||
@snippet samples/cpp/tutorial_code/ImgTrans/houghcircles.cpp display
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_java
|
||||
@snippet samples/python/tutorial_code/ImgTrans/HoughCircle/hough_circle.py display
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_python
|
||||
@snippet samples/java/tutorial_code/ImgTrans/HoughCircle/HoughCircles.java display
|
||||
@end_toggle
|
||||
|
||||
Result
|
||||
------
|
||||
|
||||
The result of running the code above with a test image is shown below:
|
||||
|
||||

|
||||
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 77 KiB |
|
After Width: | Height: | Size: 7.1 KiB |
289
doc/tutorials/imgproc/imgtrans/hough_lines/hough_lines.markdown
Normal file
@@ -0,0 +1,289 @@
|
||||
Hough Line Transform {#tutorial_hough_lines}
|
||||
====================
|
||||
|
||||
@tableofcontents
|
||||
|
||||
@prev_tutorial{tutorial_canny_detector}
|
||||
@next_tutorial{tutorial_hough_circle}
|
||||
|
||||
| | |
|
||||
| -: | :- |
|
||||
| Original author | Ana Huamán |
|
||||
| Compatibility | OpenCV >= 3.0 |
|
||||
|
||||
Goal
|
||||
----
|
||||
|
||||
In this tutorial you will learn how to:
|
||||
|
||||
- Use the OpenCV functions **HoughLines()** and **HoughLinesP()** to detect lines in an
|
||||
image.
|
||||
|
||||
Theory
|
||||
------
|
||||
|
||||
@note The explanation below belongs to the book **Learning OpenCV** by Bradski and Kaehler.
|
||||
|
||||
Hough Line Transform
|
||||
--------------------
|
||||
|
||||
-# The Hough Line Transform is a transform used to detect straight lines.
|
||||
-# To apply the Transform, first an edge detection pre-processing is desirable.
|
||||
|
||||
### How does it work?
|
||||
|
||||
-# As you know, a line in the image space can be expressed with two variables. For example:
|
||||
|
||||
-# In the **Cartesian coordinate system:** Parameters: \f$(m,b)\f$.
|
||||
-# In the **Polar coordinate system:** Parameters: \f$(r,\theta)\f$
|
||||
|
||||

|
||||
|
||||
For Hough Transforms, we will express lines in the *Polar system*. Hence, a line equation can be
|
||||
written as:
|
||||
|
||||
\f[y = \left ( -\dfrac{\cos \theta}{\sin \theta} \right ) x + \left ( \dfrac{r}{\sin \theta} \right )\f]
|
||||
|
||||
Arranging the terms: \f$r = x \cos \theta + y \sin \theta\f$
|
||||
|
||||
-# In general for each point \f$(x_{0}, y_{0})\f$, we can define the family of lines that goes through
|
||||
that point as:
|
||||
|
||||
\f[r_{\theta} = x_{0} \cdot \cos \theta + y_{0} \cdot \sin \theta\f]
|
||||
|
||||
Meaning that each pair \f$(r_{\theta},\theta)\f$ represents each line that passes by
|
||||
\f$(x_{0}, y_{0})\f$.
|
||||
|
||||
-# If for a given \f$(x_{0}, y_{0})\f$ we plot the family of lines that goes through it, we get a
|
||||
sinusoid. For instance, for \f$x_{0} = 8\f$ and \f$y_{0} = 6\f$ we get the following plot (in a plane
|
||||
\f$\theta\f$ - \f$r\f$):
|
||||
|
||||

|
||||
|
||||
We consider only points such that \f$r > 0\f$ and \f$0< \theta < 2 \pi\f$.
|
||||
|
||||
-# We can do the same operation above for all the points in an image. If the curves of two
|
||||
different points intersect in the plane \f$\theta\f$ - \f$r\f$, that means that both points belong to a
|
||||
same line. For instance, following with the example above and drawing the plot for two more
|
||||
points: \f$x_{1} = 4\f$, \f$y_{1} = 9\f$ and \f$x_{2} = 12\f$, \f$y_{2} = 3\f$, we get:
|
||||
|
||||

|
||||
|
||||
The three plots intersect in one single point \f$(0.925, 9.6)\f$, these coordinates are the
|
||||
parameters (\f$\theta, r\f$) or the line in which \f$(x_{0}, y_{0})\f$, \f$(x_{1}, y_{1})\f$ and
|
||||
\f$(x_{2}, y_{2})\f$ lay.
|
||||
|
||||
-# What does all the stuff above mean? It means that in general, a line can be *detected* by
|
||||
finding the number of intersections between curves.The more curves intersecting means that the
|
||||
line represented by that intersection have more points. In general, we can define a *threshold*
|
||||
of the minimum number of intersections needed to *detect* a line.
|
||||
-# This is what the Hough Line Transform does. It keeps track of the intersection between curves of
|
||||
every point in the image. If the number of intersections is above some *threshold*, then it
|
||||
declares it as a line with the parameters \f$(\theta, r_{\theta})\f$ of the intersection point.
|
||||
|
||||
### Standard and Probabilistic Hough Line Transform
|
||||
|
||||
OpenCV implements two kind of Hough Line Transforms:
|
||||
|
||||
a. **The Standard Hough Transform**
|
||||
|
||||
- It consists in pretty much what we just explained in the previous section. It gives you as
|
||||
result a vector of couples \f$(\theta, r_{\theta})\f$
|
||||
- In OpenCV it is implemented with the function **HoughLines()**
|
||||
|
||||
b. **The Probabilistic Hough Line Transform**
|
||||
|
||||
- A more efficient implementation of the Hough Line Transform. It gives as output the extremes
|
||||
of the detected lines \f$(x_{0}, y_{0}, x_{1}, y_{1})\f$
|
||||
- In OpenCV it is implemented with the function **HoughLinesP()**
|
||||
|
||||
### What does this program do?
|
||||
- Loads an image
|
||||
- Applies a *Standard Hough Line Transform* and a *Probabilistic Line Transform*.
|
||||
- Display the original image and the detected line in three windows.
|
||||
|
||||
Code
|
||||
----
|
||||
|
||||
@add_toggle_cpp
|
||||
The sample code that we will explain can be downloaded from
|
||||
[here](https://raw.githubusercontent.com/opencv/opencv/master/samples/cpp/tutorial_code/ImgTrans/houghlines.cpp).
|
||||
A slightly fancier version (which shows both Hough standard and probabilistic
|
||||
with trackbars for changing the threshold values) can be found
|
||||
[here](https://raw.githubusercontent.com/opencv/opencv/master/samples/cpp/tutorial_code/ImgTrans/HoughLines_Demo.cpp).
|
||||
@include samples/cpp/tutorial_code/ImgTrans/houghlines.cpp
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_java
|
||||
The sample code that we will explain can be downloaded from
|
||||
[here](https://raw.githubusercontent.com/opencv/opencv/master/samples/java/tutorial_code/ImgTrans/HoughLine/HoughLines.java).
|
||||
@include samples/java/tutorial_code/ImgTrans/HoughLine/HoughLines.java
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_python
|
||||
The sample code that we will explain can be downloaded from
|
||||
[here](https://raw.githubusercontent.com/opencv/opencv/master/samples/python/tutorial_code/ImgTrans/HoughLine/hough_lines.py).
|
||||
@include samples/python/tutorial_code/ImgTrans/HoughLine/hough_lines.py
|
||||
@end_toggle
|
||||
|
||||
Explanation
|
||||
-----------
|
||||
|
||||
#### Load an image:
|
||||
|
||||
@add_toggle_cpp
|
||||
@snippet samples/cpp/tutorial_code/ImgTrans/houghlines.cpp load
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_java
|
||||
@snippet samples/java/tutorial_code/ImgTrans/HoughLine/HoughLines.java load
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_python
|
||||
@snippet samples/python/tutorial_code/ImgTrans/HoughLine/hough_lines.py load
|
||||
@end_toggle
|
||||
|
||||
#### Detect the edges of the image by using a Canny detector:
|
||||
|
||||
@add_toggle_cpp
|
||||
@snippet samples/cpp/tutorial_code/ImgTrans/houghlines.cpp edge_detection
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_java
|
||||
@snippet samples/java/tutorial_code/ImgTrans/HoughLine/HoughLines.java edge_detection
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_python
|
||||
@snippet samples/python/tutorial_code/ImgTrans/HoughLine/hough_lines.py edge_detection
|
||||
@end_toggle
|
||||
|
||||
Now we will apply the Hough Line Transform. We will explain how to use both OpenCV functions
|
||||
available for this purpose.
|
||||
|
||||
#### Standard Hough Line Transform:
|
||||
First, you apply the Transform:
|
||||
|
||||
@add_toggle_cpp
|
||||
@snippet samples/cpp/tutorial_code/ImgTrans/houghlines.cpp hough_lines
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_java
|
||||
@snippet samples/java/tutorial_code/ImgTrans/HoughLine/HoughLines.java hough_lines
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_python
|
||||
@snippet samples/python/tutorial_code/ImgTrans/HoughLine/hough_lines.py hough_lines
|
||||
@end_toggle
|
||||
|
||||
- with the following arguments:
|
||||
|
||||
- *dst*: Output of the edge detector. It should be a grayscale image (although in fact it
|
||||
is a binary one)
|
||||
- *lines*: A vector that will store the parameters \f$(r,\theta)\f$ of the detected lines
|
||||
- *rho* : The resolution of the parameter \f$r\f$ in pixels. We use **1** pixel.
|
||||
- *theta*: The resolution of the parameter \f$\theta\f$ in radians. We use **1 degree**
|
||||
(CV_PI/180)
|
||||
- *threshold*: The minimum number of intersections to "*detect*" a line
|
||||
- *srn* and *stn*: Default parameters to zero. Check OpenCV reference for more info.
|
||||
|
||||
And then you display the result by drawing the lines.
|
||||
@add_toggle_cpp
|
||||
@snippet samples/cpp/tutorial_code/ImgTrans/houghlines.cpp draw_lines
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_java
|
||||
@snippet samples/java/tutorial_code/ImgTrans/HoughLine/HoughLines.java draw_lines
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_python
|
||||
@snippet samples/python/tutorial_code/ImgTrans/HoughLine/hough_lines.py draw_lines
|
||||
@end_toggle
|
||||
|
||||
#### Probabilistic Hough Line Transform
|
||||
First you apply the transform:
|
||||
|
||||
@add_toggle_cpp
|
||||
@snippet samples/cpp/tutorial_code/ImgTrans/houghlines.cpp hough_lines_p
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_java
|
||||
@snippet samples/java/tutorial_code/ImgTrans/HoughLine/HoughLines.java hough_lines_p
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_python
|
||||
@snippet samples/python/tutorial_code/ImgTrans/HoughLine/hough_lines.py hough_lines_p
|
||||
@end_toggle
|
||||
|
||||
- with the arguments:
|
||||
|
||||
- *dst*: Output of the edge detector. It should be a grayscale image (although in fact it
|
||||
is a binary one)
|
||||
- *lines*: A vector that will store the parameters
|
||||
\f$(x_{start}, y_{start}, x_{end}, y_{end})\f$ of the detected lines
|
||||
- *rho* : The resolution of the parameter \f$r\f$ in pixels. We use **1** pixel.
|
||||
- *theta*: The resolution of the parameter \f$\theta\f$ in radians. We use **1 degree**
|
||||
(CV_PI/180)
|
||||
- *threshold*: The minimum number of intersections to "*detect*" a line
|
||||
- *minLineLength*: The minimum number of points that can form a line. Lines with less than
|
||||
this number of points are disregarded.
|
||||
- *maxLineGap*: The maximum gap between two points to be considered in the same line.
|
||||
|
||||
And then you display the result by drawing the lines.
|
||||
|
||||
@add_toggle_cpp
|
||||
@snippet samples/cpp/tutorial_code/ImgTrans/houghlines.cpp draw_lines_p
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_java
|
||||
@snippet samples/java/tutorial_code/ImgTrans/HoughLine/HoughLines.java draw_lines_p
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_python
|
||||
@snippet samples/python/tutorial_code/ImgTrans/HoughLine/hough_lines.py draw_lines_p
|
||||
@end_toggle
|
||||
|
||||
#### Display the original image and the detected lines:
|
||||
|
||||
@add_toggle_cpp
|
||||
@snippet samples/cpp/tutorial_code/ImgTrans/houghlines.cpp imshow
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_java
|
||||
@snippet samples/java/tutorial_code/ImgTrans/HoughLine/HoughLines.java imshow
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_python
|
||||
@snippet samples/python/tutorial_code/ImgTrans/HoughLine/hough_lines.py imshow
|
||||
@end_toggle
|
||||
|
||||
#### Wait until the user exits the program
|
||||
|
||||
@add_toggle_cpp
|
||||
@snippet samples/cpp/tutorial_code/ImgTrans/houghlines.cpp exit
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_java
|
||||
@snippet samples/java/tutorial_code/ImgTrans/HoughLine/HoughLines.java exit
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_python
|
||||
@snippet samples/python/tutorial_code/ImgTrans/HoughLine/hough_lines.py exit
|
||||
@end_toggle
|
||||
|
||||
Result
|
||||
------
|
||||
|
||||
@note
|
||||
The results below are obtained using the slightly fancier version we mentioned in the *Code*
|
||||
section. It still implements the same stuff as above, only adding the Trackbar for the
|
||||
Threshold.
|
||||
|
||||
Using an input image such as a [sudoku image](https://raw.githubusercontent.com/opencv/opencv/master/samples/data/sudoku.png).
|
||||
We get the following result by using the Standard Hough Line Transform:
|
||||

|
||||
And by using the Probabilistic Hough Line Transform:
|
||||

|
||||
|
||||
You may observe that the number of lines detected vary while you change the *threshold*. The
|
||||
explanation is sort of evident: If you establish a higher threshold, fewer lines will be detected
|
||||
(since you will need more points to declare a line detected).
|
||||
|
After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 24 KiB |
|
After Width: | Height: | Size: 5.4 KiB |
|
After Width: | Height: | Size: 6.3 KiB |
|
After Width: | Height: | Size: 7.8 KiB |
|
After Width: | Height: | Size: 66 KiB |
|
After Width: | Height: | Size: 61 KiB |
|
After Width: | Height: | Size: 22 KiB |
|
After Width: | Height: | Size: 23 KiB |
|
After Width: | Height: | Size: 9.2 KiB |