Doxygen tutorials: cpp done

This commit is contained in:
Maksim Shabunin
2014-11-28 16:21:28 +03:00
parent c5536534d8
commit 36a04ef8de
92 changed files with 2142 additions and 3691 deletions

View File

@@ -20,7 +20,7 @@ The *Canny Edge detector* was developed by John F. Canny in 1986. Also known to
### Steps
1. Filter out any noise. The Gaussian filter is used for this purpose. An example of a Gaussian
-# 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}
@@ -31,8 +31,8 @@ The *Canny Edge detector* was developed by John F. Canny in 1986. Also known to
2 & 4 & 5 & 4 & 2
\end{bmatrix}\f]
2. Find the intensity gradient of the image. For this, we follow a procedure analogous to Sobel:
1. Apply a pair of convolution masks (in \f$x\f$ and \f$y\f$ directions:
-# 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 \\
@@ -43,44 +43,44 @@ The *Canny Edge detector* was developed by John F. Canny in 1986. Also known to
+1 & +2 & +1
\end{bmatrix}\f]
2. Find the gradient strength and direction with:
-# 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)
3. *Non-maximum* suppression is applied. This removes pixels that are not considered to be part of
-# *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.
4. *Hysteresis*: The final step. Canny does use two thresholds (upper and lower):
-# *Hysteresis*: The final step. Canny does use two thresholds (upper and lower):
1. If a pixel gradient is higher than the *upper* threshold, the pixel is accepted as an edge
2. If a pixel gradient value is below the *lower* threshold, then it is rejected.
3. If the pixel gradient is between the two thresholds, then it will be accepted only if it is
-# 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.
5. For more details, you can always consult your favorite Computer Vision book.
-# For more details, you can always consult your favorite Computer Vision book.
Code
----
1. **What does this program do?**
-# **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.
2. The tutorial code's is shown lines below. You can also download it from
-# The tutorial code's is shown lines below. You can also download it from
[here](https://github.com/Itseez/opencv/tree/master/samples/cpp/tutorial_code/ImgTrans/CannyDetector_Demo.cpp)
@includelineno samples/cpp/tutorial_code/ImgTrans/CannyDetector_Demo.cpp
Explanation
-----------
1. Create some needed variables:
-# Create some needed variables:
@code{.cpp}
Mat src, src_gray;
Mat dst, detected_edges;
@@ -94,12 +94,12 @@ Explanation
@endcode
Note the following:
1. We establish a ratio of lower:upper threshold of 3:1 (with the variable *ratio*)
2. We set the kernel size of \f$3\f$ (for the Sobel operations to be performed internally by the
-# 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)
3. We set a maximum value for the lower Threshold of \f$100\f$.
-# We set a maximum value for the lower Threshold of \f$100\f$.
2. Loads the source image:
-# Loads the source image:
@code{.cpp}
/// Load an image
src = imread( argv[1] );
@@ -107,35 +107,35 @@ Explanation
if( !src.data )
{ return -1; }
@endcode
3. Create a matrix of the same type and size of *src* (to be *dst*)
-# Create a matrix of the same type and size of *src* (to be *dst*)
@code{.cpp}
dst.create( src.size(), src.type() );
@endcode
4. Convert the image to grayscale (using the function @ref cv::cvtColor :
-# Convert the image to grayscale (using the function @ref cv::cvtColor :
@code{.cpp}
cvtColor( src, src_gray, COLOR_BGR2GRAY );
@endcode
5. Create a window to display the results
-# Create a window to display the results
@code{.cpp}
namedWindow( window_name, WINDOW_AUTOSIZE );
@endcode
6. Create a Trackbar for the user to enter the lower threshold for our Canny detector:
-# Create a Trackbar for the user to enter the lower threshold for our Canny detector:
@code{.cpp}
createTrackbar( "Min Threshold:", window_name, &lowThreshold, max_lowThreshold, CannyThreshold );
@endcode
Observe the following:
1. The variable to be controlled by the Trackbar is *lowThreshold* with a limit of
-# The variable to be controlled by the Trackbar is *lowThreshold* with a limit of
*max_lowThreshold* (which we set to 100 previously)
2. Each time the Trackbar registers an action, the callback function *CannyThreshold* will be
-# Each time the Trackbar registers an action, the callback function *CannyThreshold* will be
invoked.
7. Let's check the *CannyThreshold* function, step by step:
1. First, we blur the image with a filter of kernel size 3:
-# Let's check the *CannyThreshold* function, step by step:
-# First, we blur the image with a filter of kernel size 3:
@code{.cpp}
blur( src_gray, detected_edges, Size(3,3) );
@endcode
2. Second, we apply the OpenCV function @ref cv::Canny :
-# Second, we apply the OpenCV function @ref cv::Canny :
@code{.cpp}
Canny( detected_edges, detected_edges, lowThreshold, lowThreshold*ratio, kernel_size );
@endcode
@@ -149,11 +149,11 @@ Explanation
- *kernel_size*: We defined it to be 3 (the size of the Sobel kernel to be used
internally)
8. We fill a *dst* image with zeros (meaning the image is completely black).
-# We fill a *dst* image with zeros (meaning the image is completely black).
@code{.cpp}
dst = Scalar::all(0);
@endcode
9. Finally, we will use the function @ref cv::Mat::copyTo to map only the areas of the image that are
-# 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).
@code{.cpp}
src.copyTo( dst, detected_edges);
@@ -163,20 +163,21 @@ Explanation
contours on a black background, the resulting *dst* will be black in all the area but the
detected edges.
10. We display our result:
-# We display our result:
@code{.cpp}
imshow( window_name, dst );
@endcode
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:
![image](images/Canny_Detector_Tutorial_Original_Image.jpg)
![](images/Canny_Detector_Tutorial_Original_Image.jpg)
- Moving the slider, trying different threshold, we obtain the following result:
![image](images/Canny_Detector_Tutorial_Result.jpg)
![](images/Canny_Detector_Tutorial_Result.jpg)
- Notice how the image is superposed to the black background on the edge regions.

View File

@@ -14,14 +14,14 @@ Theory
@note The explanation below belongs to the book **Learning OpenCV** by Bradski and Kaehler.
1. In our previous tutorial we learned to use convolution to operate on images. One problem that
-# 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?
2. What most of OpenCV functions do is to copy a given image onto another slightly larger image and
-# 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).
3. In this tutorial, we will briefly explore two ways of defining the extra padding (border) for an
-# 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$
@@ -33,91 +33,26 @@ Theory
Code
----
1. **What does this program do?**
-# **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:
1. *Constant value border*: Applies a padding of a constant value for the whole border.
-# *Constant value border*: Applies a padding of a constant value for the whole border.
This value will be updated randomly each 0.5 seconds.
2. *Replicated border*: The border will be replicated from the pixel values at the edges of
-# *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'
2. The tutorial code's is shown lines below. You can also download it from
-# The tutorial code's is shown lines below. You can also download it from
[here](https://github.com/Itseez/opencv/tree/master/samples/cpp/tutorial_code/ImgTrans/copyMakeBorder_demo.cpp)
@code{.cpp}
#include "opencv2/imgproc.hpp"
#include "opencv2/highgui.hpp"
#include <stdlib.h>
#include <stdio.h>
@includelineno samples/cpp/tutorial_code/ImgTrans/copyMakeBorder_demo.cpp
using namespace cv;
/// Global Variables
Mat src, dst;
int top, bottom, left, right;
int borderType;
Scalar value;
char* window_name = "copyMakeBorder Demo";
RNG rng(12345);
/* @function main */
int main( int argc, char** argv )
{
int c;
/// Load an image
src = imread( argv[1] );
if( !src.data )
{ return -1;
printf(" No data entered, please enter the path to an image file \n");
}
/// Brief how-to for this program
printf( "\n \t copyMakeBorder Demo: \n" );
printf( "\t -------------------- \n" );
printf( " ** Press 'c' to set the border to a random constant value \n");
printf( " ** Press 'r' to set the border to be replicated \n");
printf( " ** Press 'ESC' to exit the program \n");
/// Create window
namedWindow( window_name, WINDOW_AUTOSIZE );
/// Initialize arguments for the filter
top = (int) (0.05*src.rows); bottom = (int) (0.05*src.rows);
left = (int) (0.05*src.cols); right = (int) (0.05*src.cols);
dst = src;
imshow( window_name, dst );
while( true )
{
c = waitKey(500);
if( (char)c == 27 )
{ break; }
else if( (char)c == 'c' )
{ borderType = BORDER_CONSTANT; }
else if( (char)c == 'r' )
{ borderType = BORDER_REPLICATE; }
value = Scalar( rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255) );
copyMakeBorder( src, dst, top, bottom, left, right, borderType, value );
imshow( window_name, dst );
}
return 0;
}
@endcode
Explanation
-----------
1. First we declare the variables we are going to use:
-# First we declare the variables we are going to use:
@code{.cpp}
Mat src, dst;
int top, bottom, left, right;
@@ -129,7 +64,7 @@ Explanation
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.
2. As usual we load our source image *src*:
-# As usual we load our source image *src*:
@code{.cpp}
src = imread( argv[1] );
@@ -138,17 +73,17 @@ Explanation
printf(" No data entered, please enter the path to an image file \n");
}
@endcode
3. After giving a short intro of how to use the program, we create a window:
-# After giving a short intro of how to use the program, we create a window:
@code{.cpp}
namedWindow( window_name, WINDOW_AUTOSIZE );
@endcode
4. Now we initialize the argument that defines the size of the borders (*top*, *bottom*, *left* and
-# 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*.
@code{.cpp}
top = (int) (0.05*src.rows); bottom = (int) (0.05*src.rows);
left = (int) (0.05*src.cols); right = (int) (0.05*src.cols);
@endcode
5. The program begins a *while* loop. If the user presses 'c' or 'r', the *borderType* variable
-# The program begins a *while* loop. If the user presses 'c' or 'r', the *borderType* variable
takes the value of *BORDER_CONSTANT* or *BORDER_REPLICATE* respectively:
@code{.cpp}
while( true )
@@ -162,14 +97,14 @@ Explanation
else if( (char)c == 'r' )
{ borderType = BORDER_REPLICATE; }
@endcode
6. In each iteration (after 0.5 seconds), the variable *value* is updated...
-# In each iteration (after 0.5 seconds), the variable *value* is updated...
@code{.cpp}
value = Scalar( rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255) );
@endcode
with a random value generated by the **RNG** variable *rng*. This value is a number picked
randomly in the range \f$[0,255]\f$
7. Finally, we call the function @ref cv::copyMakeBorder to apply the respective padding:
-# Finally, we call the function @ref cv::copyMakeBorder to apply the respective padding:
@code{.cpp}
copyMakeBorder( src, dst, top, bottom, left, right, borderType, value );
@endcode
@@ -184,14 +119,15 @@ Explanation
-# *value*: If *borderType* is *BORDER_CONSTANT*, this is the value used to fill the border
pixels.
8. We display our output image in the image created previously
-# We display our output image in the image created previously
@code{.cpp}
imshow( window_name, dst );
@endcode
Results
-------
1. After compiling the code above, you can execute it giving as argument the path of an image. The
-# 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
@@ -203,4 +139,4 @@ Results
Below some screenshot showing how the border changes color and how the *BORDER_REPLICATE*
option looks:
![image](images/CopyMakeBorder_Tutorial_Results.jpg)
![](images/CopyMakeBorder_Tutorial_Results.jpg)

View File

@@ -23,18 +23,18 @@ In a very general sense, convolution is an operation between every part of an im
A kernel is essentially a fixed size array of numerical coefficeints along with an *anchor point* in
that array, which is tipically located at the center.
![image](images/filter_2d_tutorial_kernel_theory.png)
![](images/filter_2d_tutorial_kernel_theory.png)
### How does convolution with a kernel work?
Assume you want to know the resulting value of a particular location in the image. The value of the
convolution is calculated in the following way:
1. Place the kernel anchor on top of a determined pixel, with the rest of the kernel overlaying the
-# Place the kernel anchor on top of a determined pixel, with the rest of the kernel overlaying the
corresponding local pixels in the image.
2. Multiply the kernel coefficients by the corresponding image pixel values and sum the result.
3. Place the result to the location of the *anchor* in the input image.
4. Repeat the process for all pixels by scanning the kernel over the entire 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:
@@ -46,7 +46,7 @@ these operations.
Code
----
1. **What does this program do?**
-# **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:
@@ -61,7 +61,7 @@ Code
- The filter output (with each kernel) will be shown during 500 milliseconds
2. The tutorial code's is shown lines below. You can also download it from
-# The tutorial code's is shown lines below. You can also download it from
[here](https://github.com/Itseez/opencv/tree/master/samples/cpp/tutorial_code/ImgTrans/filter2D_demo.cpp)
@code{.cpp}
#include "opencv2/imgproc.hpp"
@@ -125,26 +125,26 @@ int main ( int argc, char** argv )
Explanation
-----------
1. Load an image
-# Load an image
@code{.cpp}
src = imread( argv[1] );
if( !src.data )
{ return -1; }
@endcode
2. Create a window to display the result
-# Create a window to display the result
@code{.cpp}
namedWindow( window_name, WINDOW_AUTOSIZE );
@endcode
3. Initialize the arguments for the linear filter
-# Initialize the arguments for the linear filter
@code{.cpp}
anchor = Point( -1, -1 );
delta = 0;
ddepth = -1;
@endcode
4. Perform an infinite loop updating the kernel size and applying our linear filter to the input
-# Perform an infinite loop updating the kernel size and applying our linear filter to the input
image. Let's analyze that more in detail:
5. First we define the kernel our filter is going to use. Here it is:
-# First we define the kernel our filter is going to use. Here it is:
@code{.cpp}
kernel_size = 3 + 2*( ind%5 );
kernel = Mat::ones( kernel_size, kernel_size, CV_32F )/ (float)(kernel_size*kernel_size);
@@ -153,7 +153,7 @@ Explanation
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.
6. After setting the kernel, we can generate the filter by using the function @ref cv::filter2D :
-# After setting the kernel, we can generate the filter by using the function @ref cv::filter2D :
@code{.cpp}
filter2D(src, dst, ddepth , kernel, anchor, delta, BORDER_DEFAULT );
@endcode
@@ -169,14 +169,14 @@ Explanation
-# *delta*: A value to be added to each pixel during the convolution. By default it is \f$0\f$
-# *BORDER_DEFAULT*: We let this value by default (more details in the following tutorial)
7. Our program will effectuate a *while* loop, each 500 ms the kernel size of our filter will be
-# Our program will effectuate a *while* loop, each 500 ms the kernel size of our filter will be
updated in the range indicated.
Results
-------
1. After compiling the code above, you can execute it giving as argument the path of an image. The
-# 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:
![image](images/filter_2d_tutorial_result.jpg)
![](images/filter_2d_tutorial_result.jpg)

View File

@@ -23,7 +23,7 @@ Theory
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:
![image](images/Hough_Circle_Tutorial_Theory_0.jpg)
![](images/Hough_Circle_Tutorial_Theory_0.jpg)
- 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
@@ -34,82 +34,35 @@ Theory
Code
----
1. **What does this program do?**
-# **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.
2. The sample code that we will explain can be downloaded from
|TutorialHoughCirclesSimpleDownload|_. A slightly fancier version (which shows trackbars for
changing the threshold values) can be found |TutorialHoughCirclesFancyDownload|_.
@code{.cpp}
#include "opencv2/highgui.hpp"
#include "opencv2/imgproc.hpp"
#include <iostream>
#include <stdio.h>
-# The sample code that we will explain can be downloaded from [here](https://github.com/Itseez/opencv/tree/master/samples/cpp/houghcircles.cpp).
A slightly fancier version (which shows trackbars for
changing the threshold values) can be found [here](https://github.com/Itseez/opencv/tree/master/samples/cpp/tutorial_code/ImgTrans/HoughCircle_Demo.cpp).
@includelineno samples/cpp/houghcircles.cpp
using namespace cv;
/* @function main */
int main(int argc, char** argv)
{
Mat src, src_gray;
/// Read the image
src = imread( argv[1], 1 );
if( !src.data )
{ return -1; }
/// Convert it to gray
cvtColor( src, src_gray, COLOR_BGR2GRAY );
/// Reduce the noise so we avoid false circle detection
GaussianBlur( src_gray, src_gray, Size(9, 9), 2, 2 );
vector<Vec3f> circles;
/// Apply the Hough Transform to find the circles
HoughCircles( src_gray, circles, HOUGH_GRADIENT, 1, src_gray.rows/8, 200, 100, 0, 0 );
/// Draw the circles detected
for( size_t i = 0; i < circles.size(); i++ )
{
Point center(cvRound(circles[i][0]), cvRound(circles[i][1]));
int radius = cvRound(circles[i][2]);
// circle center
circle( src, center, 3, Scalar(0,255,0), -1, 8, 0 );
// circle outline
circle( src, center, radius, Scalar(0,0,255), 3, 8, 0 );
}
/// Show your results
namedWindow( "Hough Circle Transform Demo", WINDOW_AUTOSIZE );
imshow( "Hough Circle Transform Demo", src );
waitKey(0);
return 0;
}
@endcode
Explanation
-----------
1. Load an image
-# Load an image
@code{.cpp}
src = imread( argv[1], 1 );
if( !src.data )
{ return -1; }
@endcode
2. Convert it to grayscale:
-# Convert it to grayscale:
@code{.cpp}
cvtColor( src, src_gray, COLOR_BGR2GRAY );
@endcode
3. Apply a Gaussian blur to reduce noise and avoid false circle detection:
-# Apply a Gaussian blur to reduce noise and avoid false circle detection:
@code{.cpp}
GaussianBlur( src_gray, src_gray, Size(9, 9), 2, 2 );
@endcode
4. Proceed to apply Hough Circle Transform:
-# Proceed to apply Hough Circle Transform:
@code{.cpp}
vector<Vec3f> circles;
@@ -129,7 +82,7 @@ Explanation
- *min_radius = 0*: Minimum radio to be detected. If unknown, put zero as default.
- *max_radius = 0*: Maximum radius to be detected. If unknown, put zero as default.
5. Draw the detected circles:
-# Draw the detected circles:
@code{.cpp}
for( size_t i = 0; i < circles.size(); i++ )
{
@@ -143,19 +96,19 @@ Explanation
@endcode
You can see that we will draw the circle(s) on red and the center(s) with a small green dot
6. Display the detected circle(s):
-# Display the detected circle(s):
@code{.cpp}
namedWindow( "Hough Circle Transform Demo", WINDOW_AUTOSIZE );
imshow( "Hough Circle Transform Demo", src );
@endcode
7. Wait for the user to exit the program
-# Wait for the user to exit the program
@code{.cpp}
waitKey(0);
@endcode
Result
------
The result of running the code above with a test image is shown below:
![image](images/Hough_Circle_Tutorial_Result.jpg)
![](images/Hough_Circle_Tutorial_Result.jpg)

View File

@@ -12,18 +12,22 @@ In this tutorial you will learn how to:
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.
@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?
1. As you know, a line in the image space can be expressed with two variables. For example:
-# 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$
![image](images/Hough_Lines_Tutorial_Theory_0.jpg)
![](images/Hough_Lines_Tutorial_Theory_0.jpg)
For Hough Transforms, we will express lines in the *Polar system*. Hence, a line equation can be
written as:
@@ -32,7 +36,7 @@ straight lines. \#. To apply the Transform, first an edge detection pre-processi
Arranging the terms: \f$r = x \cos \theta + y \sin \theta\f$
1. In general for each point \f$(x_{0}, y_{0})\f$, we can define the family of lines that goes through
-# 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]
@@ -40,30 +44,30 @@ Arranging the terms: \f$r = x \cos \theta + y \sin \theta\f$
Meaning that each pair \f$(r_{\theta},\theta)\f$ represents each line that passes by
\f$(x_{0}, y_{0})\f$.
2. If for a given \f$(x_{0}, y_{0})\f$ we plot the family of lines that goes through it, we get a
-# 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$):
![image](images/Hough_Lines_Tutorial_Theory_1.jpg)
![](images/Hough_Lines_Tutorial_Theory_1.jpg)
We consider only points such that \f$r > 0\f$ and \f$0< \theta < 2 \pi\f$.
3. We can do the same operation above for all the points in an image. If the curves of two
-# 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} = 9\f$, \f$y_{1} = 4\f$ and \f$x_{2} = 12\f$, \f$y_{2} = 3\f$, we get:
![image](images/Hough_Lines_Tutorial_Theory_2.jpg)
![](images/Hough_Lines_Tutorial_Theory_2.jpg)
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.
4. What does all the stuff above mean? It means that in general, a line can be *detected* by
-# 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.
5. This is what the Hough Line Transform does. It keeps track of the intersection between curves of
-# 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.
@@ -86,83 +90,20 @@ b. **The Probabilistic Hough Line Transform**
Code
----
1. **What does this program do?**
-# **What does this program do?**
- Loads an image
- Applies either a *Standard Hough Line Transform* or a *Probabilistic Line Transform*.
- Display the original image and the detected line in two windows.
2. The sample code that we will explain can be downloaded from here_. A slightly fancier version
-# The sample code that we will explain can be downloaded from [here](https://github.com/Itseez/opencv/tree/master/samples/cpp/houghlines.cpp). A slightly fancier version
(which shows both Hough standard and probabilistic with trackbars for changing the threshold
values) can be found here_.
@code{.cpp}
#include "opencv2/highgui.hpp"
#include "opencv2/imgproc.hpp"
values) can be found [here](https://github.com/Itseez/opencv/tree/master/samples/cpp/tutorial_code/ImgTrans/HoughLines_Demo.cpp).
@includelineno samples/cpp/houghlines.cpp
#include <iostream>
using namespace cv;
using namespace std;
void help()
{
cout << "\nThis program demonstrates line finding with the Hough transform.\n"
"Usage:\n"
"./houghlines <image_name>, Default is pic1.jpg\n" << endl;
}
int main(int argc, char** argv)
{
const char* filename = argc >= 2 ? argv[1] : "pic1.jpg";
Mat src = imread(filename, 0);
if(src.empty())
{
help();
cout << "can not open " << filename << endl;
return -1;
}
Mat dst, cdst;
Canny(src, dst, 50, 200, 3);
cvtColor(dst, cdst, COLOR_GRAY2BGR);
#if 0
vector<Vec2f> lines;
HoughLines(dst, lines, 1, CV_PI/180, 100, 0, 0 );
for( size_t i = 0; i < lines.size(); i++ )
{
float rho = lines[i][0], theta = lines[i][1];
Point pt1, pt2;
double a = cos(theta), b = sin(theta);
double x0 = a*rho, y0 = b*rho;
pt1.x = cvRound(x0 + 1000*(-b));
pt1.y = cvRound(y0 + 1000*(a));
pt2.x = cvRound(x0 - 1000*(-b));
pt2.y = cvRound(y0 - 1000*(a));
line( cdst, pt1, pt2, Scalar(0,0,255), 3, LINE_AA);
}
#else
vector<Vec4i> lines;
HoughLinesP(dst, lines, 1, CV_PI/180, 50, 50, 10 );
for( size_t i = 0; i < lines.size(); i++ )
{
Vec4i l = lines[i];
line( cdst, Point(l[0], l[1]), Point(l[2], l[3]), Scalar(0,0,255), 3, CV_AA);
}
#endif
imshow("source", src);
imshow("detected lines", cdst);
waitKey();
return 0;
}
@endcode
Explanation
-----------
1. Load an image
-# Load an image
@code{.cpp}
Mat src = imread(filename, 0);
if(src.empty())
@@ -172,14 +113,14 @@ Explanation
return -1;
}
@endcode
2. Detect the edges of the image by using a Canny detector
-# Detect the edges of the image by using a Canny detector
@code{.cpp}
Canny(src, dst, 50, 200, 3);
@endcode
Now we will apply the Hough Line Transform. We will explain how to use both OpenCV functions
available for this purpose:
3. **Standard Hough Line Transform**
-# **Standard Hough Line Transform**
-# First, you apply the Transform:
@code{.cpp}
vector<Vec2f> lines;
@@ -211,7 +152,7 @@ Explanation
line( cdst, pt1, pt2, Scalar(0,0,255), 3, LINE_AA);
}
@endcode
4. **Probabilistic Hough Line Transform**
-# **Probabilistic Hough Line Transform**
-# First you apply the transform:
@code{.cpp}
vector<Vec4i> lines;
@@ -239,15 +180,16 @@ Explanation
line( cdst, Point(l[0], l[1]), Point(l[2], l[3]), Scalar(0,0,255), 3, LINE_AA);
}
@endcode
5. Display the original image and the detected lines:
-# Display the original image and the detected lines:
@code{.cpp}
imshow("source", src);
imshow("detected lines", cdst);
@endcode
6. Wait until the user exits the program
-# Wait until the user exits the program
@code{.cpp}
waitKey();
@endcode
Result
------
@@ -258,11 +200,11 @@ Result
Using an input image such as:
![image](images/Hough_Lines_Tutorial_Original_Image.jpg)
![](images/Hough_Lines_Tutorial_Original_Image.jpg)
We get the following result by using the Probabilistic Hough Line Transform:
![image](images/Hough_Lines_Tutorial_Result.jpg)
![](images/Hough_Lines_Tutorial_Result.jpg)
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

View File

@@ -12,16 +12,16 @@ In this tutorial you will learn how to:
Theory
------
1. In the previous tutorial we learned how to use the *Sobel Operator*. It was based on the fact
-# In the previous tutorial we learned how to use the *Sobel Operator*. It was based on the fact
that in the edge area, the pixel intensity shows a "jump" or a high variation of intensity.
Getting the first derivative of the intensity, we observed that an edge is characterized by a
maximum, as it can be seen in the figure:
![image](images/Laplace_Operator_Tutorial_Theory_Previous.jpg)
![](images/Laplace_Operator_Tutorial_Theory_Previous.jpg)
2. And...what happens if we take the second derivative?
-# And...what happens if we take the second derivative?
![image](images/Laplace_Operator_Tutorial_Theory_ddIntensity.jpg)
![](images/Laplace_Operator_Tutorial_Theory_ddIntensity.jpg)
You can observe that the second derivative is zero! So, we can also use this criterion to
attempt to detect edges in an image. However, note that zeros will not only appear in edges
@@ -30,81 +30,34 @@ Theory
### Laplacian Operator
1. From the explanation above, we deduce that the second derivative can be used to *detect edges*.
-# From the explanation above, we deduce that the second derivative can be used to *detect edges*.
Since images are "*2D*", we would need to take the derivative in both dimensions. Here, the
Laplacian operator comes handy.
2. The *Laplacian operator* is defined by:
-# The *Laplacian operator* is defined by:
\f[Laplace(f) = \dfrac{\partial^{2} f}{\partial x^{2}} + \dfrac{\partial^{2} f}{\partial y^{2}}\f]
1. The Laplacian operator is implemented in OpenCV by the function @ref cv::Laplacian . In fact,
-# The Laplacian operator is implemented in OpenCV by the function @ref cv::Laplacian . In fact,
since the Laplacian uses the gradient of images, it calls internally the *Sobel* operator to
perform its computation.
Code
----
1. **What does this program do?**
-# **What does this program do?**
- Loads an image
- Remove noise by applying a Gaussian blur and then convert the original image to grayscale
- Applies a Laplacian operator to the grayscale image and stores the output image
- Display the result in a window
2. The tutorial code's is shown lines below. You can also download it from
-# The tutorial code's is shown lines below. You can also download it from
[here](https://github.com/Itseez/opencv/tree/master/samples/cpp/tutorial_code/ImgTrans/Laplace_Demo.cpp)
@code{.cpp}
#include "opencv2/imgproc.hpp"
#include "opencv2/highgui.hpp"
#include <stdlib.h>
#include <stdio.h>
@includelineno samples/cpp/tutorial_code/ImgTrans/Laplace_Demo.cpp
using namespace cv;
/* @function main */
int main( int argc, char** argv )
{
Mat src, src_gray, dst;
int kernel_size = 3;
int scale = 1;
int delta = 0;
int ddepth = CV_16S;
char* window_name = "Laplace Demo";
int c;
/// Load an image
src = imread( argv[1] );
if( !src.data )
{ return -1; }
/// Remove noise by blurring with a Gaussian filter
GaussianBlur( src, src, Size(3,3), 0, 0, BORDER_DEFAULT );
/// Convert the image to grayscale
cvtColor( src, src_gray, COLOR_RGB2GRAY );
/// Create window
namedWindow( window_name, WINDOW_AUTOSIZE );
/// Apply Laplace function
Mat abs_dst;
Laplacian( src_gray, dst, ddepth, kernel_size, scale, delta, BORDER_DEFAULT );
convertScaleAbs( dst, abs_dst );
/// Show what you got
imshow( window_name, abs_dst );
waitKey(0);
return 0;
}
@endcode
Explanation
-----------
1. Create some needed variables:
-# Create some needed variables:
@code{.cpp}
Mat src, src_gray, dst;
int kernel_size = 3;
@@ -113,22 +66,22 @@ Explanation
int ddepth = CV_16S;
char* window_name = "Laplace Demo";
@endcode
2. Loads the source image:
-# Loads the source image:
@code{.cpp}
src = imread( argv[1] );
if( !src.data )
{ return -1; }
@endcode
3. Apply a Gaussian blur to reduce noise:
-# Apply a Gaussian blur to reduce noise:
@code{.cpp}
GaussianBlur( src, src, Size(3,3), 0, 0, BORDER_DEFAULT );
@endcode
4. Convert the image to grayscale using @ref cv::cvtColor
-# Convert the image to grayscale using @ref cv::cvtColor
@code{.cpp}
cvtColor( src, src_gray, COLOR_RGB2GRAY );
@endcode
5. Apply the Laplacian operator to the grayscale image:
-# Apply the Laplacian operator to the grayscale image:
@code{.cpp}
Laplacian( src_gray, dst, ddepth, kernel_size, scale, delta, BORDER_DEFAULT );
@endcode
@@ -142,27 +95,26 @@ Explanation
this example.
- *scale*, *delta* and *BORDER_DEFAULT*: We leave them as default values.
6. Convert the output from the Laplacian operator to a *CV_8U* image:
-# Convert the output from the Laplacian operator to a *CV_8U* image:
@code{.cpp}
convertScaleAbs( dst, abs_dst );
@endcode
7. Display the result in a window:
-# Display the result in a window:
@code{.cpp}
imshow( window_name, abs_dst );
@endcode
Results
-------
1. After compiling the code above, we can run it giving as argument the path to an image. For
-# After compiling the code above, we can run it giving as argument the path to an image. For
example, using as an input:
![image](images/Laplace_Operator_Tutorial_Original_Image.jpg)
![](images/Laplace_Operator_Tutorial_Original_Image.jpg)
2. We obtain the following result. Notice how the trees and the silhouette of the cow are
-# We obtain the following result. Notice how the trees and the silhouette of the cow are
approximately well defined (except in areas in which the intensity are very similar, i.e. around
the cow's head). Also, note that the roof of the house behind the trees (right side) is
notoriously marked. This is due to the fact that the contrast is higher in that region.
![image](images/Laplace_Operator_Tutorial_Result.jpg)
![](images/Laplace_Operator_Tutorial_Result.jpg)

View File

@@ -33,146 +33,53 @@ Theory
What would happen? It is easily seen that the image would flip in the \f$x\f$ direction. For
instance, consider the input image:
![image](images/Remap_Tutorial_Theory_0.jpg)
![](images/Remap_Tutorial_Theory_0.jpg)
observe how the red circle changes positions with respect to x (considering \f$x\f$ the horizontal
direction):
![image](images/Remap_Tutorial_Theory_1.jpg)
![](images/Remap_Tutorial_Theory_1.jpg)
- In OpenCV, the function @ref cv::remap offers a simple remapping implementation.
Code
----
1. **What does this program do?**
-# **What does this program do?**
- Loads an image
- Each second, apply 1 of 4 different remapping processes to the image and display them
indefinitely in a window.
- Wait for the user to exit the program
2. The tutorial code's is shown lines below. You can also download it from
-# The tutorial code's is shown lines below. You can also download it from
[here](https://github.com/Itseez/opencv/tree/master/samples/cpp/tutorial_code/ImgTrans/Remap_Demo.cpp)
@code{.cpp}
#include "opencv2/highgui.hpp"
#include "opencv2/imgproc.hpp"
#include <iostream>
#include <stdio.h>
using namespace cv;
/// Global variables
Mat src, dst;
Mat map_x, map_y;
char* remap_window = "Remap demo";
int ind = 0;
/// Function Headers
void update_map( void );
/*
* @function main
*/
int main( int argc, char** argv )
{
/// Load the image
src = imread( argv[1], 1 );
/// Create dst, map_x and map_y with the same size as src:
dst.create( src.size(), src.type() );
map_x.create( src.size(), CV_32FC1 );
map_y.create( src.size(), CV_32FC1 );
/// Create window
namedWindow( remap_window, WINDOW_AUTOSIZE );
/// Loop
while( true )
{
/// Each 1 sec. Press ESC to exit the program
int c = waitKey( 1000 );
if( (char)c == 27 )
{ break; }
/// Update map_x & map_y. Then apply remap
update_map();
remap( src, dst, map_x, map_y, INTER_LINEAR, BORDER_CONSTANT, Scalar(0,0, 0) );
/// Display results
imshow( remap_window, dst );
}
return 0;
}
/*
* @function update_map
* @brief Fill the map_x and map_y matrices with 4 types of mappings
*/
void update_map( void )
{
ind = ind%4;
for( int j = 0; j < src.rows; j++ )
{ for( int i = 0; i < src.cols; i++ )
{
switch( ind )
{
case 0:
if( i > src.cols*0.25 && i < src.cols*0.75 && j > src.rows*0.25 && j < src.rows*0.75 )
{
map_x.at<float>(j,i) = 2*( i - src.cols*0.25 ) + 0.5 ;
map_y.at<float>(j,i) = 2*( j - src.rows*0.25 ) + 0.5 ;
}
else
{ map_x.at<float>(j,i) = 0 ;
map_y.at<float>(j,i) = 0 ;
}
break;
case 1:
map_x.at<float>(j,i) = i ;
map_y.at<float>(j,i) = src.rows - j ;
break;
case 2:
map_x.at<float>(j,i) = src.cols - i ;
map_y.at<float>(j,i) = j ;
break;
case 3:
map_x.at<float>(j,i) = src.cols - i ;
map_y.at<float>(j,i) = src.rows - j ;
break;
} // end of switch
}
}
ind++;
@endcode
}
@includelineno samples/cpp/tutorial_code/ImgTrans/Remap_Demo.cpp
Explanation
-----------
1. Create some variables we will use:
-# Create some variables we will use:
@code{.cpp}
Mat src, dst;
Mat map_x, map_y;
char* remap_window = "Remap demo";
int ind = 0;
@endcode
2. Load an image:
-# Load an image:
@code{.cpp}
src = imread( argv[1], 1 );
@endcode
3. Create the destination image and the two mapping matrices (for x and y )
-# Create the destination image and the two mapping matrices (for x and y )
@code{.cpp}
dst.create( src.size(), src.type() );
map_x.create( src.size(), CV_32FC1 );
map_y.create( src.size(), CV_32FC1 );
@endcode
4. Create a window to display results
-# Create a window to display results
@code{.cpp}
namedWindow( remap_window, WINDOW_AUTOSIZE );
@endcode
5. Establish a loop. Each 1000 ms we update our mapping matrices (*mat_x* and *mat_y*) and apply
-# Establish a loop. Each 1000 ms we update our mapping matrices (*mat_x* and *mat_y*) and apply
them to our source image:
@code{.cpp}
while( true )
@@ -205,14 +112,11 @@ Explanation
How do we update our mapping matrices *mat_x* and *mat_y*? Go on reading:
6. **Updating the mapping matrices:** We are going to perform 4 different mappings:
-# **Updating the mapping matrices:** We are going to perform 4 different mappings:
-# Reduce the picture to half its size and will display it in the middle:
\f[h(i,j) = ( 2*i - src.cols/2 + 0.5, 2*j - src.rows/2 + 0.5)\f]
for all pairs \f$(i,j)\f$ such that: \f$\dfrac{src.cols}{4}<i<\dfrac{3 \cdot src.cols}{4}\f$ and
\f$\dfrac{src.rows}{4}<j<\dfrac{3 \cdot src.rows}{4}\f$
-# Turn the image upside down: \f$h( i, j ) = (i, src.rows - j)\f$
-# Reflect the image from left to right: \f$h(i,j) = ( src.cols - i, j )\f$
-# Combination of b and c: \f$h(i,j) = ( src.cols - i, src.rows - j )\f$
@@ -254,26 +158,27 @@ for( int j = 0; j < src.rows; j++ )
ind++;
}
@endcode
Result
------
1. After compiling the code above, you can execute it giving as argument an image path. For
-# After compiling the code above, you can execute it giving as argument an image path. For
instance, by using the following image:
![image](images/Remap_Tutorial_Original_Image.jpg)
![](images/Remap_Tutorial_Original_Image.jpg)
2. This is the result of reducing it to half the size and centering it:
-# This is the result of reducing it to half the size and centering it:
![image](images/Remap_Tutorial_Result_0.jpg)
![](images/Remap_Tutorial_Result_0.jpg)
3. Turning it upside down:
-# Turning it upside down:
![image](images/Remap_Tutorial_Result_1.jpg)
![](images/Remap_Tutorial_Result_1.jpg)
4. Reflecting it in the x direction:
-# Reflecting it in the x direction:
![image](images/Remap_Tutorial_Result_2.jpg)
![](images/Remap_Tutorial_Result_2.jpg)
5. Reflecting it in both directions:
-# Reflecting it in both directions:
![image](images/Remap_Tutorial_Result_3.jpg)
![](images/Remap_Tutorial_Result_3.jpg)

View File

@@ -15,45 +15,45 @@ Theory
@note The explanation below belongs to the book **Learning OpenCV** by Bradski and Kaehler.
1. In the last two tutorials we have seen applicative examples of convolutions. One of the most
-# In the last two tutorials we have seen applicative examples of convolutions. One of the most
important convolutions is the computation of derivatives in an image (or an approximation to
them).
2. Why may be important the calculus of the derivatives in an image? Let's imagine we want to
-# Why may be important the calculus of the derivatives in an image? Let's imagine we want to
detect the *edges* present in the image. For instance:
![image](images/Sobel_Derivatives_Tutorial_Theory_0.jpg)
![](images/Sobel_Derivatives_Tutorial_Theory_0.jpg)
You can easily notice that in an *edge*, the pixel intensity *changes* in a notorious way. A
good way to express *changes* is by using *derivatives*. A high change in gradient indicates a
major change in the image.
3. To be more graphical, let's assume we have a 1D-image. An edge is shown by the "jump" in
-# To be more graphical, let's assume we have a 1D-image. An edge is shown by the "jump" in
intensity in the plot below:
![image](images/Sobel_Derivatives_Tutorial_Theory_Intensity_Function.jpg)
![](images/Sobel_Derivatives_Tutorial_Theory_Intensity_Function.jpg)
4. The edge "jump" can be seen more easily if we take the first derivative (actually, here appears
-# The edge "jump" can be seen more easily if we take the first derivative (actually, here appears
as a maximum)
![image](images/Sobel_Derivatives_Tutorial_Theory_dIntensity_Function.jpg)
![](images/Sobel_Derivatives_Tutorial_Theory_dIntensity_Function.jpg)
5. So, from the explanation above, we can deduce that a method to detect edges in an image can be
-# So, from the explanation above, we can deduce that a method to detect edges in an image can be
performed by locating pixel locations where the gradient is higher than its neighbors (or to
generalize, higher than a threshold).
6. More detailed explanation, please refer to **Learning OpenCV** by Bradski and Kaehler
-# More detailed explanation, please refer to **Learning OpenCV** by Bradski and Kaehler
### Sobel Operator
1. The Sobel Operator is a discrete differentiation operator. It computes an approximation of the
-# The Sobel Operator is a discrete differentiation operator. It computes an approximation of the
gradient of an image intensity function.
2. The Sobel Operator combines Gaussian smoothing and differentiation.
-# The Sobel Operator combines Gaussian smoothing and differentiation.
#### Formulation
Assuming that the image to be operated is \f$I\f$:
1. We calculate two derivatives:
1. **Horizontal changes**: This is computed by convolving \f$I\f$ with a kernel \f$G_{x}\f$ with odd
-# We calculate two derivatives:
-# **Horizontal changes**: This is computed by convolving \f$I\f$ with a kernel \f$G_{x}\f$ with odd
size. For example for a kernel size of 3, \f$G_{x}\f$ would be computed as:
\f[G_{x} = \begin{bmatrix}
@@ -62,7 +62,7 @@ Assuming that the image to be operated is \f$I\f$:
-1 & 0 & +1
\end{bmatrix} * I\f]
2. **Vertical changes**: This is computed by convolving \f$I\f$ with a kernel \f$G_{y}\f$ with odd
-# **Vertical changes**: This is computed by convolving \f$I\f$ with a kernel \f$G_{y}\f$ with odd
size. For example for a kernel size of 3, \f$G_{y}\f$ would be computed as:
\f[G_{y} = \begin{bmatrix}
@@ -71,7 +71,7 @@ Assuming that the image to be operated is \f$I\f$:
+1 & +2 & +1
\end{bmatrix} * I\f]
2. At each point of the image we calculate an approximation of the *gradient* in that point by
-# At each point of the image we calculate an approximation of the *gradient* in that point by
combining both results above:
\f[G = \sqrt{ G_{x}^{2} + G_{y}^{2} }\f]
@@ -83,7 +83,7 @@ Assuming that the image to be operated is \f$I\f$:
@note
When the size of the kernel is `3`, the Sobel kernel shown above may produce noticeable
inaccuracies (after all, Sobel is only an approximation of the derivative). OpenCV addresses
this inaccuracy for kernels of size 3 by using the :scharr:\`Scharr function. This is as fast
this inaccuracy for kernels of size 3 by using the @ref cv::Scharr function. This is as fast
but more accurate than the standar Sobel function. It implements the following kernels:
\f[G_{x} = \begin{bmatrix}
-3 & 0 & +3 \\
@@ -103,18 +103,18 @@ Assuming that the image to be operated is \f$I\f$:
Code
----
1. **What does this program do?**
-# **What does this program do?**
- Applies the *Sobel Operator* and generates as output an image with the detected *edges*
bright on a darker background.
2. The tutorial code's is shown lines below. You can also download it from
-# The tutorial code's is shown lines below. You can also download it from
[here](https://github.com/Itseez/opencv/tree/master/samples/cpp/tutorial_code/ImgTrans/Sobel_Demo.cpp)
@includelineno samples/cpp/tutorial_code/ImgTrans/Sobel_Demo.cpp
Explanation
-----------
1. First we declare the variables we are going to use:
-# First we declare the variables we are going to use:
@code{.cpp}
Mat src, src_gray;
Mat grad;
@@ -123,22 +123,22 @@ Explanation
int delta = 0;
int ddepth = CV_16S;
@endcode
2. As usual we load our source image *src*:
-# As usual we load our source image *src*:
@code{.cpp}
src = imread( argv[1] );
if( !src.data )
{ return -1; }
@endcode
3. First, we apply a @ref cv::GaussianBlur to our image to reduce the noise ( kernel size = 3 )
-# First, we apply a @ref cv::GaussianBlur to our image to reduce the noise ( kernel size = 3 )
@code{.cpp}
GaussianBlur( src, src, Size(3,3), 0, 0, BORDER_DEFAULT );
@endcode
4. Now we convert our filtered image to grayscale:
-# Now we convert our filtered image to grayscale:
@code{.cpp}
cvtColor( src, src_gray, COLOR_RGB2GRAY );
@endcode
5. Second, we calculate the "*derivatives*" in *x* and *y* directions. For this, we use the
-# Second, we calculate the "*derivatives*" in *x* and *y* directions. For this, we use the
function @ref cv::Sobel as shown below:
@code{.cpp}
Mat grad_x, grad_y;
@@ -161,23 +161,24 @@ Explanation
Notice that to calculate the gradient in *x* direction we use: \f$x_{order}= 1\f$ and
\f$y_{order} = 0\f$. We do analogously for the *y* direction.
6. We convert our partial results back to *CV_8U*:
-# We convert our partial results back to *CV_8U*:
@code{.cpp}
convertScaleAbs( grad_x, abs_grad_x );
convertScaleAbs( grad_y, abs_grad_y );
@endcode
7. Finally, we try to approximate the *gradient* by adding both directional gradients (note that
-# Finally, we try to approximate the *gradient* by adding both directional gradients (note that
this is not an exact calculation at all! but it is good for our purposes).
@code{.cpp}
addWeighted( abs_grad_x, 0.5, abs_grad_y, 0.5, 0, grad );
@endcode
8. Finally, we show our result:
-# Finally, we show our result:
@code{.cpp}
imshow( window_name, grad );
@endcode
Results
-------
1. Here is the output of applying our basic detector to *lena.jpg*:
-# Here is the output of applying our basic detector to *lena.jpg*:
![image](images/Sobel_Derivatives_Tutorial_Result.jpg)
![](images/Sobel_Derivatives_Tutorial_Result.jpg)

View File

@@ -6,17 +6,17 @@ Goal
In this tutorial you will learn how to:
a. Use the OpenCV function @ref cv::warpAffine to implement simple remapping routines.
b. Use the OpenCV function @ref cv::getRotationMatrix2D to obtain a \f$2 \times 3\f$ rotation matrix
- Use the OpenCV function @ref cv::warpAffine to implement simple remapping routines.
- Use the OpenCV function @ref cv::getRotationMatrix2D to obtain a \f$2 \times 3\f$ rotation matrix
Theory
------
### What is an Affine Transformation?
1. It is any transformation that can be expressed in the form of a *matrix multiplication* (linear
-# It is any transformation that can be expressed in the form of a *matrix multiplication* (linear
transformation) followed by a *vector addition* (translation).
2. From the above, We can use an Affine Transformation to express:
-# From the above, We can use an Affine Transformation to express:
-# Rotations (linear transformation)
-# Translations (vector addition)
@@ -25,24 +25,28 @@ Theory
you can see that, in essence, an Affine Transformation represents a **relation** between two
images.
3. The usual way to represent an Affine Transform is by using a \f$2 \times 3\f$ matrix.
-# The usual way to represent an Affine Transform is by using a \f$2 \times 3\f$ matrix.
\f[A = \begin{bmatrix}
\f[
A = \begin{bmatrix}
a_{00} & a_{01} \\
a_{10} & a_{11}
\end{bmatrix}_{2 \times 2}
B = \begin{bmatrix}
b_{00} \\
b_{10}
\end{bmatrix}_{2 \times 1}\f]\f[M = \begin{bmatrix}
\end{bmatrix}_{2 \times 1}
\f]
\f[
M = \begin{bmatrix}
A & B
\end{bmatrix}
=\f]
begin{bmatrix}
a_{00} & a_{01} & b_{00} \\ a_{10} & a_{11} & b_{10}
end{bmatrix}_{2 times 3}
=
\begin{bmatrix}
a_{00} & a_{01} & b_{00} \\
a_{10} & a_{11} & b_{10}
\end{bmatrix}_{2 \times 3}
\f]
Considering that we want to transform a 2D vector \f$X = \begin{bmatrix}x \\ y\end{bmatrix}\f$ by
using \f$A\f$ and \f$B\f$, we can do it equivalently with:
@@ -56,17 +60,17 @@ Theory
### How do we get an Affine Transformation?
1. Excellent question. We mentioned that an Affine Transformation is basically a **relation**
-# Excellent question. We mentioned that an Affine Transformation is basically a **relation**
between two images. The information about this relation can come, roughly, in two ways:
-# We know both \f$X\f$ and T and we also know that they are related. Then our job is to find \f$M\f$
-# We know \f$M\f$ and \f$X\f$. To obtain \f$T\f$ we only need to apply \f$T = M \cdot X\f$. Our information
for \f$M\f$ may be explicit (i.e. have the 2-by-3 matrix) or it can come as a geometric relation
between points.
2. Let's explain a little bit better (b). Since \f$M\f$ relates 02 images, we can analyze the simplest
-# Let's explain a little bit better (b). Since \f$M\f$ relates 02 images, we can analyze the simplest
case in which it relates three points in both images. Look at the figure below:
![image](images/Warp_Affine_Tutorial_Theory_0.jpg)
![](images/Warp_Affine_Tutorial_Theory_0.jpg)
the points 1, 2 and 3 (forming a triangle in image 1) are mapped into image 2, still forming a
triangle, but now they have changed notoriously. If we find the Affine Transformation with these
@@ -76,7 +80,7 @@ Theory
Code
----
1. **What does this program do?**
-# **What does this program do?**
- Loads an image
- Applies an Affine Transform to the image. This Transform is obtained from the relation
between three points. We use the function @ref cv::warpAffine for that purpose.
@@ -84,86 +88,14 @@ Code
the image center
- Waits until the user exits the program
2. The tutorial code's is shown lines below. You can also download it from
-# The tutorial code's is shown lines below. You can also download it from
[here](https://github.com/Itseez/opencv/tree/master/samples/cpp/tutorial_code/ImgTrans/Geometric_Transforms_Demo.cpp)
@code{.cpp}
#include "opencv2/highgui.hpp"
#include "opencv2/imgproc.hpp"
#include <iostream>
#include <stdio.h>
@includelineno samples/cpp/tutorial_code/ImgTrans/Geometric_Transforms_Demo.cpp
using namespace cv;
using namespace std;
/// Global variables
char* source_window = "Source image";
char* warp_window = "Warp";
char* warp_rotate_window = "Warp + Rotate";
/* @function main */
int main( int argc, char** argv )
{
Point2f srcTri[3];
Point2f dstTri[3];
Mat rot_mat( 2, 3, CV_32FC1 );
Mat warp_mat( 2, 3, CV_32FC1 );
Mat src, warp_dst, warp_rotate_dst;
/// Load the image
src = imread( argv[1], 1 );
/// Set the dst image the same type and size as src
warp_dst = Mat::zeros( src.rows, src.cols, src.type() );
/// Set your 3 points to calculate the Affine Transform
srcTri[0] = Point2f( 0,0 );
srcTri[1] = Point2f( src.cols - 1, 0 );
srcTri[2] = Point2f( 0, src.rows - 1 );
dstTri[0] = Point2f( src.cols*0.0, src.rows*0.33 );
dstTri[1] = Point2f( src.cols*0.85, src.rows*0.25 );
dstTri[2] = Point2f( src.cols*0.15, src.rows*0.7 );
/// Get the Affine Transform
warp_mat = getAffineTransform( srcTri, dstTri );
/// Apply the Affine Transform just found to the src image
warpAffine( src, warp_dst, warp_mat, warp_dst.size() );
/* Rotating the image after Warp */
/// Compute a rotation matrix with respect to the center of the image
Point center = Point( warp_dst.cols/2, warp_dst.rows/2 );
double angle = -50.0;
double scale = 0.6;
/// Get the rotation matrix with the specifications above
rot_mat = getRotationMatrix2D( center, angle, scale );
/// Rotate the warped image
warpAffine( warp_dst, warp_rotate_dst, rot_mat, warp_dst.size() );
/// Show what you got
namedWindow( source_window, WINDOW_AUTOSIZE );
imshow( source_window, src );
namedWindow( warp_window, WINDOW_AUTOSIZE );
imshow( warp_window, warp_dst );
namedWindow( warp_rotate_window, WINDOW_AUTOSIZE );
imshow( warp_rotate_window, warp_rotate_dst );
/// Wait until user exits the program
waitKey(0);
return 0;
}
@endcode
Explanation
-----------
1. Declare some variables we will use, such as the matrices to store our results and 2 arrays of
-# Declare some variables we will use, such as the matrices to store our results and 2 arrays of
points to store the 2D points that define our Affine Transform.
@code{.cpp}
Point2f srcTri[3];
@@ -173,15 +105,15 @@ Explanation
Mat warp_mat( 2, 3, CV_32FC1 );
Mat src, warp_dst, warp_rotate_dst;
@endcode
2. Load an image:
-# Load an image:
@code{.cpp}
src = imread( argv[1], 1 );
@endcode
3. Initialize the destination image as having the same size and type as the source:
-# Initialize the destination image as having the same size and type as the source:
@code{.cpp}
warp_dst = Mat::zeros( src.rows, src.cols, src.type() );
@endcode
4. **Affine Transform:** As we explained lines above, we need two sets of 3 points to derive the
-# **Affine Transform:** As we explained lines above, we need two sets of 3 points to derive the
affine transform relation. Take a look:
@code{.cpp}
srcTri[0] = Point2f( 0,0 );
@@ -196,14 +128,14 @@ Explanation
approximately the same as the ones depicted in the example figure (in the Theory section). You
may note that the size and orientation of the triangle defined by the 3 points change.
5. Armed with both sets of points, we calculate the Affine Transform by using OpenCV function @ref
-# Armed with both sets of points, we calculate the Affine Transform by using OpenCV function @ref
cv::getAffineTransform :
@code{.cpp}
warp_mat = getAffineTransform( srcTri, dstTri );
@endcode
We get as an output a \f$2 \times 3\f$ matrix (in this case **warp_mat**)
6. We apply the Affine Transform just found to the src image
-# We apply the Affine Transform just found to the src image
@code{.cpp}
warpAffine( src, warp_dst, warp_mat, warp_dst.size() );
@endcode
@@ -217,7 +149,7 @@ Explanation
We just got our first transformed image! We will display it in one bit. Before that, we also
want to rotate it...
7. **Rotate:** To rotate an image, we need to know two things:
-# **Rotate:** To rotate an image, we need to know two things:
-# The center with respect to which the image will rotate
-# The angle to be rotated. In OpenCV a positive angle is counter-clockwise
@@ -229,16 +161,16 @@ Explanation
double angle = -50.0;
double scale = 0.6;
@endcode
8. We generate the rotation matrix with the OpenCV function @ref cv::getRotationMatrix2D , which
-# We generate the rotation matrix with the OpenCV function @ref cv::getRotationMatrix2D , which
returns a \f$2 \times 3\f$ matrix (in this case *rot_mat*)
@code{.cpp}
rot_mat = getRotationMatrix2D( center, angle, scale );
@endcode
9. We now apply the found rotation to the output of our previous Transformation.
-# We now apply the found rotation to the output of our previous Transformation.
@code{.cpp}
warpAffine( warp_dst, warp_rotate_dst, rot_mat, warp_dst.size() );
@endcode
10. Finally, we display our results in two windows plus the original image for good measure:
-# Finally, we display our results in two windows plus the original image for good measure:
@code{.cpp}
namedWindow( source_window, WINDOW_AUTOSIZE );
imshow( source_window, src );
@@ -249,23 +181,24 @@ Explanation
namedWindow( warp_rotate_window, WINDOW_AUTOSIZE );
imshow( warp_rotate_window, warp_rotate_dst );
@endcode
11. We just have to wait until the user exits the program
-# We just have to wait until the user exits the program
@code{.cpp}
waitKey(0);
@endcode
Result
------
1. After compiling the code above, we can give it the path of an image as argument. For instance,
-# After compiling the code above, we can give it the path of an image as argument. For instance,
for a picture like:
![image](images/Warp_Affine_Tutorial_Original_Image.jpg)
![](images/Warp_Affine_Tutorial_Original_Image.jpg)
after applying the first Affine Transform we obtain:
![image](images/Warp_Affine_Tutorial_Result_Warp.jpg)
![](images/Warp_Affine_Tutorial_Result_Warp.jpg)
and finally, after applying a negative rotation (remember negative means clockwise) and a scale
factor, we get:
![image](images/Warp_Affine_Tutorial_Result_Warp_Rotate.jpg)
![](images/Warp_Affine_Tutorial_Result_Warp_Rotate.jpg)