Commit 36a04ef8 authored by Maksim Shabunin's avatar Maksim Shabunin

Doxygen tutorials: cpp done

parent c5536534
...@@ -824,3 +824,11 @@ ...@@ -824,3 +824,11 @@
journal = {Machine learning}, journal = {Machine learning},
volume = {10} volume = {10}
} }
@inproceedings{vacavant2013benchmark,
title={A benchmark dataset for outdoor foreground/background extraction},
author={Vacavant, Antoine and Chateau, Thierry and Wilhelm, Alexis and Lequi{\`e}vre, Laurent},
booktitle={Computer Vision-ACCV 2012 Workshops},
pages={291--300},
year={2013},
organization={Springer}
}
...@@ -96,7 +96,7 @@ on how to do this you can find in the @ref tutorial_file_input_output_with_xml_y ...@@ -96,7 +96,7 @@ on how to do this you can find in the @ref tutorial_file_input_output_with_xml_y
Explanation Explanation
----------- -----------
1. **Read the settings.** -# **Read the settings.**
@code{.cpp} @code{.cpp}
Settings s; Settings s;
const string inputSettingsFile = argc > 1 ? argv[1] : "default.xml"; const string inputSettingsFile = argc > 1 ? argv[1] : "default.xml";
...@@ -119,7 +119,7 @@ Explanation ...@@ -119,7 +119,7 @@ Explanation
additional post-processing function that checks validity of the input. Only if all inputs are additional post-processing function that checks validity of the input. Only if all inputs are
good then *goodInput* variable will be true. good then *goodInput* variable will be true.
2. **Get next input, if it fails or we have enough of them - calibrate**. After this we have a big -# **Get next input, if it fails or we have enough of them - calibrate**. After this we have a big
loop where we do the following operations: get the next image from the image list, camera or loop where we do the following operations: get the next image from the image list, camera or
video file. If this fails or we have enough images then we run the calibration process. In case video file. If this fails or we have enough images then we run the calibration process. In case
of image we step out of the loop and otherwise the remaining frames will be undistorted (if the of image we step out of the loop and otherwise the remaining frames will be undistorted (if the
...@@ -151,7 +151,7 @@ Explanation ...@@ -151,7 +151,7 @@ Explanation
@endcode @endcode
For some cameras we may need to flip the input image. Here we do this too. For some cameras we may need to flip the input image. Here we do this too.
3. **Find the pattern in the current input**. The formation of the equations I mentioned above aims -# **Find the pattern in the current input**. The formation of the equations I mentioned above aims
to finding major patterns in the input: in case of the chessboard this are corners of the to finding major patterns in the input: in case of the chessboard this are corners of the
squares and for the circles, well, the circles themselves. The position of these will form the squares and for the circles, well, the circles themselves. The position of these will form the
result which will be written into the *pointBuf* vector. result which will be written into the *pointBuf* vector.
...@@ -212,7 +212,7 @@ Explanation ...@@ -212,7 +212,7 @@ Explanation
drawChessboardCorners( view, s.boardSize, Mat(pointBuf), found ); drawChessboardCorners( view, s.boardSize, Mat(pointBuf), found );
} }
@endcode @endcode
4. **Show state and result to the user, plus command line control of the application**. This part -# **Show state and result to the user, plus command line control of the application**. This part
shows text output on the image. shows text output on the image.
@code{.cpp} @code{.cpp}
//----------------------------- Output Text ------------------------------------------------ //----------------------------- Output Text ------------------------------------------------
...@@ -263,7 +263,7 @@ Explanation ...@@ -263,7 +263,7 @@ Explanation
imagePoints.clear(); imagePoints.clear();
} }
@endcode @endcode
5. **Show the distortion removal for the images too**. When you work with an image list it is not -# **Show the distortion removal for the images too**. When you work with an image list it is not
possible to remove the distortion inside the loop. Therefore, you must do this after the loop. possible to remove the distortion inside the loop. Therefore, you must do this after the loop.
Taking advantage of this now I'll expand the @ref cv::undistort function, which is in fact first Taking advantage of this now I'll expand the @ref cv::undistort function, which is in fact first
calls @ref cv::initUndistortRectifyMap to find transformation matrices and then performs calls @ref cv::initUndistortRectifyMap to find transformation matrices and then performs
...@@ -291,6 +291,7 @@ Explanation ...@@ -291,6 +291,7 @@ Explanation
} }
} }
@endcode @endcode
The calibration and save The calibration and save
------------------------ ------------------------
...@@ -419,6 +420,7 @@ double rms = calibrateCamera(objectPoints, imagePoints, imageSize, cameraMatrix, ...@@ -419,6 +420,7 @@ double rms = calibrateCamera(objectPoints, imagePoints, imageSize, cameraMatrix,
return std::sqrt(totalErr/totalPoints); // calculate the arithmetical mean return std::sqrt(totalErr/totalPoints); // calculate the arithmetical mean
} }
@endcode @endcode
Results Results
------- -------
...@@ -444,21 +446,21 @@ images/CameraCalibration/VID5/xx8.jpg ...@@ -444,21 +446,21 @@ images/CameraCalibration/VID5/xx8.jpg
Then passed `images/CameraCalibration/VID5/VID5.XML` as an input in the configuration file. Here's a Then passed `images/CameraCalibration/VID5/VID5.XML` as an input in the configuration file. Here's a
chessboard pattern found during the runtime of the application: chessboard pattern found during the runtime of the application:
![image](images/fileListImage.jpg) ![](images/fileListImage.jpg)
After applying the distortion removal we get: After applying the distortion removal we get:
![image](images/fileListImageUnDist.jpg) ![](images/fileListImageUnDist.jpg)
The same works for [this asymmetrical circle pattern ](acircles_pattern.png) by setting the input The same works for [this asymmetrical circle pattern ](acircles_pattern.png) by setting the input
width to 4 and height to 11. This time I've used a live camera feed by specifying its ID ("1") for width to 4 and height to 11. This time I've used a live camera feed by specifying its ID ("1") for
the input. Here's, how a detected pattern should look: the input. Here's, how a detected pattern should look:
![image](images/asymetricalPattern.jpg) ![](images/asymetricalPattern.jpg)
In both cases in the specified output XML/YAML file you'll find the camera and distortion In both cases in the specified output XML/YAML file you'll find the camera and distortion
coefficients matrices: coefficients matrices:
@code{.cpp} @code{.xml}
<Camera_Matrix type_id="opencv-matrix"> <Camera_Matrix type_id="opencv-matrix">
<rows>3</rows> <rows>3</rows>
<cols>3</cols> <cols>3</cols>
......
...@@ -73,7 +73,7 @@ int main( int argc, char** argv ) ...@@ -73,7 +73,7 @@ int main( int argc, char** argv )
Explanation Explanation
----------- -----------
1. Since we are going to perform: -# Since we are going to perform:
\f[g(x) = (1 - \alpha)f_{0}(x) + \alpha f_{1}(x)\f] \f[g(x) = (1 - \alpha)f_{0}(x) + \alpha f_{1}(x)\f]
...@@ -87,7 +87,7 @@ Explanation ...@@ -87,7 +87,7 @@ Explanation
Since we are *adding* *src1* and *src2*, they both have to be of the same size (width and Since we are *adding* *src1* and *src2*, they both have to be of the same size (width and
height) and type. height) and type.
2. Now we need to generate the `g(x)` image. For this, the function add_weighted:addWeighted comes quite handy: -# Now we need to generate the `g(x)` image. For this, the function add_weighted:addWeighted comes quite handy:
@code{.cpp} @code{.cpp}
beta = ( 1.0 - alpha ); beta = ( 1.0 - alpha );
addWeighted( src1, alpha, src2, beta, 0.0, dst); addWeighted( src1, alpha, src2, beta, 0.0, dst);
...@@ -96,9 +96,9 @@ Explanation ...@@ -96,9 +96,9 @@ Explanation
\f[dst = \alpha \cdot src1 + \beta \cdot src2 + \gamma\f] \f[dst = \alpha \cdot src1 + \beta \cdot src2 + \gamma\f]
In this case, `gamma` is the argument \f$0.0\f$ in the code above. In this case, `gamma` is the argument \f$0.0\f$ in the code above.
3. Create windows, show the images and wait for the user to end the program. -# Create windows, show the images and wait for the user to end the program.
Result Result
------ ------
![image](images/Adding_Images_Tutorial_Result_Big.jpg) ![](images/Adding_Images_Tutorial_Result_Big.jpg)
...@@ -52,7 +52,7 @@ Code ...@@ -52,7 +52,7 @@ Code
Explanation Explanation
----------- -----------
1. Since we plan to draw two examples (an atom and a rook), we have to create 02 images and two -# Since we plan to draw two examples (an atom and a rook), we have to create 02 images and two
windows to display them. windows to display them.
@code{.cpp} @code{.cpp}
/// Windows names /// Windows names
...@@ -63,7 +63,7 @@ Explanation ...@@ -63,7 +63,7 @@ Explanation
Mat atom_image = Mat::zeros( w, w, CV_8UC3 ); Mat atom_image = Mat::zeros( w, w, CV_8UC3 );
Mat rook_image = Mat::zeros( w, w, CV_8UC3 ); Mat rook_image = Mat::zeros( w, w, CV_8UC3 );
@endcode @endcode
2. We created functions to draw different geometric shapes. For instance, to draw the atom we used -# We created functions to draw different geometric shapes. For instance, to draw the atom we used
*MyEllipse* and *MyFilledCircle*: *MyEllipse* and *MyFilledCircle*:
@code{.cpp} @code{.cpp}
/// 1. Draw a simple atom: /// 1. Draw a simple atom:
...@@ -77,7 +77,7 @@ Explanation ...@@ -77,7 +77,7 @@ Explanation
/// 1.b. Creating circles /// 1.b. Creating circles
MyFilledCircle( atom_image, Point( w/2.0, w/2.0) ); MyFilledCircle( atom_image, Point( w/2.0, w/2.0) );
@endcode @endcode
3. And to draw the rook we employed *MyLine*, *rectangle* and a *MyPolygon*: -# And to draw the rook we employed *MyLine*, *rectangle* and a *MyPolygon*:
@code{.cpp} @code{.cpp}
/// 2. Draw a rook /// 2. Draw a rook
...@@ -98,7 +98,7 @@ Explanation ...@@ -98,7 +98,7 @@ Explanation
MyLine( rook_image, Point( w/2, 7*w/8 ), Point( w/2, w ) ); MyLine( rook_image, Point( w/2, 7*w/8 ), Point( w/2, w ) );
MyLine( rook_image, Point( 3*w/4, 7*w/8 ), Point( 3*w/4, w ) ); MyLine( rook_image, Point( 3*w/4, 7*w/8 ), Point( 3*w/4, w ) );
@endcode @endcode
4. Let's check what is inside each of these functions: -# Let's check what is inside each of these functions:
- *MyLine* - *MyLine*
@code{.cpp} @code{.cpp}
void MyLine( Mat img, Point start, Point end ) void MyLine( Mat img, Point start, Point end )
...@@ -240,5 +240,5 @@ Result ...@@ -240,5 +240,5 @@ Result
Compiling and running your program should give you a result like this: Compiling and running your program should give you a result like this:
![image](images/Drawing_1_Tutorial_Result_0.png) ![](images/Drawing_1_Tutorial_Result_0.png)
...@@ -101,16 +101,16 @@ int main( int argc, char** argv ) ...@@ -101,16 +101,16 @@ int main( int argc, char** argv )
Explanation Explanation
----------- -----------
1. We begin by creating parameters to save \f$\alpha\f$ and \f$\beta\f$ to be entered by the user: -# We begin by creating parameters to save \f$\alpha\f$ and \f$\beta\f$ to be entered by the user:
@code{.cpp} @code{.cpp}
double alpha; double alpha;
int beta; int beta;
@endcode @endcode
2. We load an image using @ref cv::imread and save it in a Mat object: -# We load an image using @ref cv::imread and save it in a Mat object:
@code{.cpp} @code{.cpp}
Mat image = imread( argv[1] ); Mat image = imread( argv[1] );
@endcode @endcode
3. Now, since we will make some transformations to this image, we need a new Mat object to store -# Now, since we will make some transformations to this image, we need a new Mat object to store
it. Also, we want this to have the following features: it. Also, we want this to have the following features:
- Initial pixel values equal to zero - Initial pixel values equal to zero
...@@ -121,7 +121,7 @@ Explanation ...@@ -121,7 +121,7 @@ Explanation
We observe that @ref cv::Mat::zeros returns a Matlab-style zero initializer based on We observe that @ref cv::Mat::zeros returns a Matlab-style zero initializer based on
*image.size()* and *image.type()* *image.size()* and *image.type()*
4. Now, to perform the operation \f$g(i,j) = \alpha \cdot f(i,j) + \beta\f$ we will access to each -# Now, to perform the operation \f$g(i,j) = \alpha \cdot f(i,j) + \beta\f$ we will access to each
pixel in image. Since we are operating with RGB images, we will have three values per pixel (R, pixel in image. Since we are operating with RGB images, we will have three values per pixel (R,
G and B), so we will also access them separately. Here is the piece of code: G and B), so we will also access them separately. Here is the piece of code:
@code{.cpp} @code{.cpp}
...@@ -141,7 +141,7 @@ Explanation ...@@ -141,7 +141,7 @@ Explanation
integers (if \f$\alpha\f$ is float), we use cv::saturate_cast to make sure the integers (if \f$\alpha\f$ is float), we use cv::saturate_cast to make sure the
values are valid. values are valid.
5. Finally, we create windows and show the images, the usual way. -# Finally, we create windows and show the images, the usual way.
@code{.cpp} @code{.cpp}
namedWindow("Original Image", 1); namedWindow("Original Image", 1);
namedWindow("New Image", 1); namedWindow("New Image", 1);
...@@ -166,7 +166,7 @@ Result ...@@ -166,7 +166,7 @@ Result
- Running our code and using \f$\alpha = 2.2\f$ and \f$\beta = 50\f$ - Running our code and using \f$\alpha = 2.2\f$ and \f$\beta = 50\f$
@code{.bash} @code{.bash}
\f$ ./BasicLinearTransforms lena.jpg $ ./BasicLinearTransforms lena.jpg
Basic Linear Transforms Basic Linear Transforms
------------------------- -------------------------
* Enter the alpha value [1.0-3.0]: 2.2 * Enter the alpha value [1.0-3.0]: 2.2
...@@ -175,4 +175,4 @@ Result ...@@ -175,4 +175,4 @@ Result
- We get this: - We get this:
![image](images/Basic_Linear_Transform_Tutorial_Result_big.jpg) ![](images/Basic_Linear_Transform_Tutorial_Result_big.jpg)
...@@ -22,10 +22,14 @@ OpenCV source code library. ...@@ -22,10 +22,14 @@ OpenCV source code library.
Here's a sample usage of @ref cv::dft() : Here's a sample usage of @ref cv::dft() :
@includelineno cpp/tutorial_code/core/discrete_fourier_transform/discrete_fourier_transform.cpp @dontinclude cpp/tutorial_code/core/discrete_fourier_transform/discrete_fourier_transform.cpp
@until highgui.hpp
lines @skipline iostream
1-4, 6, 20-21, 24-79 @skip main
@until {
@skip filename
@until return 0;
@until }
Explanation Explanation
----------- -----------
...@@ -52,7 +56,7 @@ Fourier Transform too needs to be of a discrete type resulting in a Discrete Fou ...@@ -52,7 +56,7 @@ Fourier Transform too needs to be of a discrete type resulting in a Discrete Fou
(*DFT*). You'll want to use this whenever you need to determine the structure of an image from a (*DFT*). You'll want to use this whenever you need to determine the structure of an image from a
geometrical point of view. Here are the steps to follow (in case of a gray scale input image *I*): geometrical point of view. Here are the steps to follow (in case of a gray scale input image *I*):
1. **Expand the image to an optimal size**. The performance of a DFT is dependent of the image -# **Expand the image to an optimal size**. The performance of a DFT is dependent of the image
size. It tends to be the fastest for image sizes that are multiple of the numbers two, three and size. It tends to be the fastest for image sizes that are multiple of the numbers two, three and
five. Therefore, to achieve maximal performance it is generally a good idea to pad border values five. Therefore, to achieve maximal performance it is generally a good idea to pad border values
to the image to get a size with such traits. The @ref cv::getOptimalDFTSize() returns this to the image to get a size with such traits. The @ref cv::getOptimalDFTSize() returns this
...@@ -66,7 +70,7 @@ geometrical point of view. Here are the steps to follow (in case of a gray scale ...@@ -66,7 +70,7 @@ geometrical point of view. Here are the steps to follow (in case of a gray scale
@endcode @endcode
The appended pixels are initialized with zero. The appended pixels are initialized with zero.
2. **Make place for both the complex and the real values**. The result of a Fourier Transform is -# **Make place for both the complex and the real values**. The result of a Fourier Transform is
complex. This implies that for each image value the result is two image values (one per complex. This implies that for each image value the result is two image values (one per
component). Moreover, the frequency domains range is much larger than its spatial counterpart. component). Moreover, the frequency domains range is much larger than its spatial counterpart.
Therefore, we store these usually at least in a *float* format. Therefore we'll convert our Therefore, we store these usually at least in a *float* format. Therefore we'll convert our
...@@ -76,12 +80,12 @@ geometrical point of view. Here are the steps to follow (in case of a gray scale ...@@ -76,12 +80,12 @@ geometrical point of view. Here are the steps to follow (in case of a gray scale
Mat complexI; Mat complexI;
merge(planes, 2, complexI); // Add to the expanded another plane with zeros merge(planes, 2, complexI); // Add to the expanded another plane with zeros
@endcode @endcode
3. **Make the Discrete Fourier Transform**. It's possible an in-place calculation (same input as -# **Make the Discrete Fourier Transform**. It's possible an in-place calculation (same input as
output): output):
@code{.cpp} @code{.cpp}
dft(complexI, complexI); // this way the result may fit in the source matrix dft(complexI, complexI); // this way the result may fit in the source matrix
@endcode @endcode
4. **Transform the real and complex values to magnitude**. A complex number has a real (*Re*) and a -# **Transform the real and complex values to magnitude**. A complex number has a real (*Re*) and a
complex (imaginary - *Im*) part. The results of a DFT are complex numbers. The magnitude of a complex (imaginary - *Im*) part. The results of a DFT are complex numbers. The magnitude of a
DFT is: DFT is:
...@@ -93,7 +97,7 @@ geometrical point of view. Here are the steps to follow (in case of a gray scale ...@@ -93,7 +97,7 @@ geometrical point of view. Here are the steps to follow (in case of a gray scale
magnitude(planes[0], planes[1], planes[0]);// planes[0] = magnitude magnitude(planes[0], planes[1], planes[0]);// planes[0] = magnitude
Mat magI = planes[0]; Mat magI = planes[0];
@endcode @endcode
5. **Switch to a logarithmic scale**. It turns out that the dynamic range of the Fourier -# **Switch to a logarithmic scale**. It turns out that the dynamic range of the Fourier
coefficients is too large to be displayed on the screen. We have some small and some high coefficients is too large to be displayed on the screen. We have some small and some high
changing values that we can't observe like this. Therefore the high values will all turn out as changing values that we can't observe like this. Therefore the high values will all turn out as
white points, while the small ones as black. To use the gray scale values to for visualization white points, while the small ones as black. To use the gray scale values to for visualization
...@@ -106,7 +110,7 @@ geometrical point of view. Here are the steps to follow (in case of a gray scale ...@@ -106,7 +110,7 @@ geometrical point of view. Here are the steps to follow (in case of a gray scale
magI += Scalar::all(1); // switch to logarithmic scale magI += Scalar::all(1); // switch to logarithmic scale
log(magI, magI); log(magI, magI);
@endcode @endcode
6. **Crop and rearrange**. Remember, that at the first step, we expanded the image? Well, it's time -# **Crop and rearrange**. Remember, that at the first step, we expanded the image? Well, it's time
to throw away the newly introduced values. For visualization purposes we may also rearrange the to throw away the newly introduced values. For visualization purposes we may also rearrange the
quadrants of the result, so that the origin (zero, zero) corresponds with the image center. quadrants of the result, so that the origin (zero, zero) corresponds with the image center.
@code{.cpp} @code{.cpp}
...@@ -128,13 +132,14 @@ geometrical point of view. Here are the steps to follow (in case of a gray scale ...@@ -128,13 +132,14 @@ geometrical point of view. Here are the steps to follow (in case of a gray scale
q2.copyTo(q1); q2.copyTo(q1);
tmp.copyTo(q2); tmp.copyTo(q2);
@endcode @endcode
7. **Normalize**. This is done again for visualization purposes. We now have the magnitudes, -# **Normalize**. This is done again for visualization purposes. We now have the magnitudes,
however this are still out of our image display range of zero to one. We normalize our values to however this are still out of our image display range of zero to one. We normalize our values to
this range using the @ref cv::normalize() function. this range using the @ref cv::normalize() function.
@code{.cpp} @code{.cpp}
normalize(magI, magI, 0, 1, NORM_MINMAX); // Transform the matrix with float values into a normalize(magI, magI, 0, 1, NORM_MINMAX); // Transform the matrix with float values into a
// viewable image form (float between values 0 and 1). // viewable image form (float between values 0 and 1).
@endcode @endcode
Result Result
------ ------
...@@ -147,13 +152,12 @@ image about a text. ...@@ -147,13 +152,12 @@ image about a text.
In case of the horizontal text: In case of the horizontal text:
![image](images/result_normal.jpg) ![](images/result_normal.jpg)
In case of a rotated text: In case of a rotated text:
![image](images/result_rotated.jpg) ![](images/result_rotated.jpg)
You can see that the most influential components of the frequency domain (brightest dots on the You can see that the most influential components of the frequency domain (brightest dots on the
magnitude image) follow the geometric rotation of objects on the image. From this we may calculate magnitude image) follow the geometric rotation of objects on the image. From this we may calculate
the offset and perform an image rotation to correct eventual miss alignments. the offset and perform an image rotation to correct eventual miss alignments.
...@@ -22,10 +22,12 @@ library. ...@@ -22,10 +22,12 @@ library.
Here's a sample code of how to achieve all the stuff enumerated at the goal list. Here's a sample code of how to achieve all the stuff enumerated at the goal list.
@includelineno cpp/tutorial_code/core/file_input_output/file_input_output.cpp @dontinclude cpp/tutorial_code/core/file_input_output/file_input_output.cpp
lines @until std;
1-7, 21-154 @skip class MyData
@until return 0;
@until }
Explanation Explanation
----------- -----------
...@@ -36,7 +38,7 @@ structures you may serialize: *mappings* (like the STL map) and *element sequenc ...@@ -36,7 +38,7 @@ structures you may serialize: *mappings* (like the STL map) and *element sequenc
vector). The difference between these is that in a map every element has a unique name through what vector). The difference between these is that in a map every element has a unique name through what
you may access it. For sequences you need to go through them to query a specific item. you may access it. For sequences you need to go through them to query a specific item.
1. **XML/YAML File Open and Close.** Before you write any content to such file you need to open it -# **XML/YAML File Open and Close.** Before you write any content to such file you need to open it
and at the end to close it. The XML/YAML data structure in OpenCV is @ref cv::FileStorage . To and at the end to close it. The XML/YAML data structure in OpenCV is @ref cv::FileStorage . To
specify that this structure to which file binds on your hard drive you can use either its specify that this structure to which file binds on your hard drive you can use either its
constructor or the *open()* function of this: constructor or the *open()* function of this:
...@@ -56,7 +58,7 @@ you may access it. For sequences you need to go through them to query a specific ...@@ -56,7 +58,7 @@ you may access it. For sequences you need to go through them to query a specific
@code{.cpp} @code{.cpp}
fs.release(); // explicit close fs.release(); // explicit close
@endcode @endcode
2. **Input and Output of text and numbers.** The data structure uses the same \<\< output operator -# **Input and Output of text and numbers.** The data structure uses the same \<\< output operator
that the STL library. For outputting any type of data structure we need first to specify its that the STL library. For outputting any type of data structure we need first to specify its
name. We do this by just simply printing out the name of this. For basic types you may follow name. We do this by just simply printing out the name of this. For basic types you may follow
this with the print of the value : this with the print of the value :
...@@ -70,7 +72,7 @@ you may access it. For sequences you need to go through them to query a specific ...@@ -70,7 +72,7 @@ you may access it. For sequences you need to go through them to query a specific
fs["iterationNr"] >> itNr; fs["iterationNr"] >> itNr;
itNr = (int) fs["iterationNr"]; itNr = (int) fs["iterationNr"];
@endcode @endcode
3. **Input/Output of OpenCV Data structures.** Well these behave exactly just as the basic C++ -# **Input/Output of OpenCV Data structures.** Well these behave exactly just as the basic C++
types: types:
@code{.cpp} @code{.cpp}
Mat R = Mat_<uchar >::eye (3, 3), Mat R = Mat_<uchar >::eye (3, 3),
...@@ -82,7 +84,7 @@ you may access it. For sequences you need to go through them to query a specific ...@@ -82,7 +84,7 @@ you may access it. For sequences you need to go through them to query a specific
fs["R"] >> R; // Read cv::Mat fs["R"] >> R; // Read cv::Mat
fs["T"] >> T; fs["T"] >> T;
@endcode @endcode
4. **Input/Output of vectors (arrays) and associative maps.** As I mentioned beforehand, we can -# **Input/Output of vectors (arrays) and associative maps.** As I mentioned beforehand, we can
output maps and sequences (array, vector) too. Again we first print the name of the variable and output maps and sequences (array, vector) too. Again we first print the name of the variable and
then we have to specify if our output is either a sequence or map. then we have to specify if our output is either a sequence or map.
...@@ -121,7 +123,7 @@ you may access it. For sequences you need to go through them to query a specific ...@@ -121,7 +123,7 @@ you may access it. For sequences you need to go through them to query a specific
cout << "Two " << (int)(n["Two"]) << "; "; cout << "Two " << (int)(n["Two"]) << "; ";
cout << "One " << (int)(n["One"]) << endl << endl; cout << "One " << (int)(n["One"]) << endl << endl;
@endcode @endcode
5. **Read and write your own data structures.** Suppose you have a data structure such as: -# **Read and write your own data structures.** Suppose you have a data structure such as:
@code{.cpp} @code{.cpp}
class MyData class MyData
{ {
...@@ -180,6 +182,7 @@ you may access it. For sequences you need to go through them to query a specific ...@@ -180,6 +182,7 @@ you may access it. For sequences you need to go through them to query a specific
fs["NonExisting"] >> m; // Do not add a fs << "NonExisting" << m command for this to work fs["NonExisting"] >> m; // Do not add a fs << "NonExisting" << m command for this to work
cout << endl << "NonExisting = " << endl << m << endl; cout << endl << "NonExisting = " << endl << m << endl;
@endcode @endcode
Result Result
------ ------
...@@ -270,4 +273,3 @@ here](https://www.youtube.com/watch?v=A4yqVnByMMM) . ...@@ -270,4 +273,3 @@ here](https://www.youtube.com/watch?v=A4yqVnByMMM) .
<iframe title="File Input and Output using XML and YAML files in OpenCV" width="560" height="349" src="http://www.youtube.com/embed/A4yqVnByMMM?rel=0&loop=1" frameborder="0" allowfullscreen align="middle"></iframe> <iframe title="File Input and Output using XML and YAML files in OpenCV" width="560" height="349" src="http://www.youtube.com/embed/A4yqVnByMMM?rel=0&loop=1" frameborder="0" allowfullscreen align="middle"></iframe>
</div> </div>
\endhtmlonly \endhtmlonly
...@@ -59,10 +59,10 @@ how_to_scan_images imageName.jpg intValueToReduce [G] ...@@ -59,10 +59,10 @@ how_to_scan_images imageName.jpg intValueToReduce [G]
The final argument is optional. If given the image will be loaded in gray scale format, otherwise The final argument is optional. If given the image will be loaded in gray scale format, otherwise
the RGB color way is used. The first thing is to calculate the lookup table. the RGB color way is used. The first thing is to calculate the lookup table.
@includelineno cpp/tutorial_code/core/how_to_scan_images/how_to_scan_images.cpp @dontinclude cpp/tutorial_code/core/how_to_scan_images/how_to_scan_images.cpp
lines @skip int divideWith
49-61 @until table[i]
Here we first use the C++ *stringstream* class to convert the third command line argument from text Here we first use the C++ *stringstream* class to convert the third command line argument from text
to an integer format. Then we use a simple look and the upper formula to calculate the lookup table. to an integer format. Then we use a simple look and the upper formula to calculate the lookup table.
...@@ -88,26 +88,12 @@ As you could already read in my @ref tutorial_mat_the_basic_image_container tuto ...@@ -88,26 +88,12 @@ As you could already read in my @ref tutorial_mat_the_basic_image_container tuto
depends of the color system used. More accurately, it depends from the number of channels used. In depends of the color system used. More accurately, it depends from the number of channels used. In
case of a gray scale image we have something like: case of a gray scale image we have something like:
\f[\newcommand{\tabItG}[1] { \textcolor{black}{#1} \cellcolor[gray]{0.8}} ![](tutorial_how_matrix_stored_1.png)
\begin{tabular} {ccccc}
~ & \multicolumn{1}{c}{Column 0} & \multicolumn{1}{c}{Column 1} & \multicolumn{1}{c}{Column ...} & \multicolumn{1}{c}{Column m}\\
Row 0 & \tabItG{0,0} & \tabItG{0,1} & \tabItG{...} & \tabItG{0, m} \\
Row 1 & \tabItG{1,0} & \tabItG{1,1} & \tabItG{...} & \tabItG{1, m} \\
Row ... & \tabItG{...,0} & \tabItG{...,1} & \tabItG{...} & \tabItG{..., m} \\
Row n & \tabItG{n,0} & \tabItG{n,1} & \tabItG{n,...} & \tabItG{n, m} \\
\end{tabular}\f]
For multichannel images the columns contain as many sub columns as the number of channels. For For multichannel images the columns contain as many sub columns as the number of channels. For
example in case of an RGB color system: example in case of an RGB color system:
\f[\newcommand{\tabIt}[1] { \textcolor{yellow}{#1} \cellcolor{blue} & \textcolor{black}{#1} \cellcolor{green} & \textcolor{black}{#1} \cellcolor{red}} ![](tutorial_how_matrix_stored_2.png)
\begin{tabular} {ccccccccccccc}
~ & \multicolumn{3}{c}{Column 0} & \multicolumn{3}{c}{Column 1} & \multicolumn{3}{c}{Column ...} & \multicolumn{3}{c}{Column m}\\
Row 0 & \tabIt{0,0} & \tabIt{0,1} & \tabIt{...} & \tabIt{0, m} \\
Row 1 & \tabIt{1,0} & \tabIt{1,1} & \tabIt{...} & \tabIt{1, m} \\
Row ... & \tabIt{...,0} & \tabIt{...,1} & \tabIt{...} & \tabIt{..., m} \\
Row n & \tabIt{n,0} & \tabIt{n,1} & \tabIt{n,...} & \tabIt{n, m} \\
\end{tabular}\f]
Note that the order of the channels is inverse: BGR instead of RGB. Because in many cases the memory Note that the order of the channels is inverse: BGR instead of RGB. Because in many cases the memory
is large enough to store the rows in a successive fashion the rows may follow one after another, is large enough to store the rows in a successive fashion the rows may follow one after another,
...@@ -121,10 +107,9 @@ The efficient way ...@@ -121,10 +107,9 @@ The efficient way
When it comes to performance you cannot beat the classic C style operator[] (pointer) access. When it comes to performance you cannot beat the classic C style operator[] (pointer) access.
Therefore, the most efficient method we can recommend for making the assignment is: Therefore, the most efficient method we can recommend for making the assignment is:
@includelineno cpp/tutorial_code/core/how_to_scan_images/how_to_scan_images.cpp @skip Mat& ScanImageAndReduceC
@until return
lines @until }
126-153
Here we basically just acquire a pointer to the start of each row and go through it until it ends. Here we basically just acquire a pointer to the start of each row and go through it until it ends.
In the special case that the matrix is stored in a continues manner we only need to request the In the special case that the matrix is stored in a continues manner we only need to request the
...@@ -156,10 +141,9 @@ considered a safer way as it takes over these tasks from the user. All you need ...@@ -156,10 +141,9 @@ considered a safer way as it takes over these tasks from the user. All you need
begin and the end of the image matrix and then just increase the begin iterator until you reach the begin and the end of the image matrix and then just increase the begin iterator until you reach the
end. To acquire the value *pointed* by the iterator use the \* operator (add it before it). end. To acquire the value *pointed* by the iterator use the \* operator (add it before it).
@includelineno cpp/tutorial_code/core/how_to_scan_images/how_to_scan_images.cpp @skip ScanImageAndReduceIterator
@until return
lines @until }
155-183
In case of color images we have three uchar items per column. This may be considered a short vector In case of color images we have three uchar items per column. This may be considered a short vector
of uchar items, that has been baptized in OpenCV with the *Vec3b* name. To access the n-th sub of uchar items, that has been baptized in OpenCV with the *Vec3b* name. To access the n-th sub
...@@ -177,10 +161,9 @@ what type we are looking at the image. It's no different here as you need manual ...@@ -177,10 +161,9 @@ what type we are looking at the image. It's no different here as you need manual
type to use at the automatic lookup. You can observe this in case of the gray scale images for the type to use at the automatic lookup. You can observe this in case of the gray scale images for the
following source code (the usage of the + @ref cv::at() function): following source code (the usage of the + @ref cv::at() function):
@includelineno cpp/tutorial_code/core/how_to_scan_images/how_to_scan_images.cpp @skip ScanImageAndReduceRandomAccess
@until return
lines @until }
185-217
The functions takes your input type and coordinates and calculates on the fly the address of the The functions takes your input type and coordinates and calculates on the fly the address of the
queried item. Then returns a reference to that. This may be a constant when you *get* the value and queried item. Then returns a reference to that. This may be a constant when you *get* the value and
...@@ -209,17 +192,14 @@ OpenCV has a function that makes the modification without the need from you to w ...@@ -209,17 +192,14 @@ OpenCV has a function that makes the modification without the need from you to w
the image. We use the @ref cv::LUT() function of the core module. First we build a Mat type of the the image. We use the @ref cv::LUT() function of the core module. First we build a Mat type of the
lookup table: lookup table:
@includelineno cpp/tutorial_code/core/how_to_scan_images/how_to_scan_images.cpp @dontinclude cpp/tutorial_code/core/how_to_scan_images/how_to_scan_images.cpp
lines @skip Mat lookUpTable
108-111 @until p[i] = table[i]
Finally call the function (I is our input image and J the output one): Finally call the function (I is our input image and J the output one):
@includelineno cpp/tutorial_code/core/how_to_scan_images/how_to_scan_images.cpp @skipline LUT
lines
116
Performance Difference Performance Difference
---------------------- ----------------------
......
...@@ -23,7 +23,7 @@ download it from [here](samples/cpp/tutorial_code/core/ippasync/ippasync_sample. ...@@ -23,7 +23,7 @@ download it from [here](samples/cpp/tutorial_code/core/ippasync/ippasync_sample.
Explanation Explanation
----------- -----------
1. Create parameters for OpenCV: -# Create parameters for OpenCV:
@code{.cpp} @code{.cpp}
VideoCapture cap; VideoCapture cap;
Mat image, gray, result; Mat image, gray, result;
...@@ -36,7 +36,7 @@ Explanation ...@@ -36,7 +36,7 @@ Explanation
hppStatus sts; hppStatus sts;
hppiVirtualMatrix * virtMatrix; hppiVirtualMatrix * virtMatrix;
@endcode @endcode
2. Load input image or video. How to open and read video stream you can see in the -# Load input image or video. How to open and read video stream you can see in the
@ref tutorial_video_input_psnr_ssim tutorial. @ref tutorial_video_input_psnr_ssim tutorial.
@code{.cpp} @code{.cpp}
if( useCamera ) if( useCamera )
...@@ -56,7 +56,7 @@ Explanation ...@@ -56,7 +56,7 @@ Explanation
return -1; return -1;
} }
@endcode @endcode
3. Create accelerator instance using -# Create accelerator instance using
[hppCreateInstance](http://software.intel.com/en-us/node/501686): [hppCreateInstance](http://software.intel.com/en-us/node/501686):
@code{.cpp} @code{.cpp}
accelType = sAccel == "cpu" ? HPP_ACCEL_TYPE_CPU: accelType = sAccel == "cpu" ? HPP_ACCEL_TYPE_CPU:
...@@ -67,12 +67,12 @@ Explanation ...@@ -67,12 +67,12 @@ Explanation
sts = hppCreateInstance(accelType, 0, &accel); sts = hppCreateInstance(accelType, 0, &accel);
CHECK_STATUS(sts, "hppCreateInstance"); CHECK_STATUS(sts, "hppCreateInstance");
@endcode @endcode
4. Create an array of virtual matrices using -# Create an array of virtual matrices using
[hppiCreateVirtualMatrices](http://software.intel.com/en-us/node/501700) function. [hppiCreateVirtualMatrices](http://software.intel.com/en-us/node/501700) function.
@code{.cpp} @code{.cpp}
virtMatrix = hppiCreateVirtualMatrices(accel, 1); virtMatrix = hppiCreateVirtualMatrices(accel, 1);
@endcode @endcode
5. Prepare a matrix for input and output data: -# Prepare a matrix for input and output data:
@code{.cpp} @code{.cpp}
cap >> image; cap >> image;
if(image.empty()) if(image.empty())
...@@ -82,7 +82,7 @@ Explanation ...@@ -82,7 +82,7 @@ Explanation
result.create( image.rows, image.cols, CV_8U); result.create( image.rows, image.cols, CV_8U);
@endcode @endcode
6. Convert Mat to [hppiMatrix](http://software.intel.com/en-us/node/501660) using @ref cv::hpp::getHpp -# Convert Mat to [hppiMatrix](http://software.intel.com/en-us/node/501660) using @ref cv::hpp::getHpp
and call [hppiSobel](http://software.intel.com/en-us/node/474701) function. and call [hppiSobel](http://software.intel.com/en-us/node/474701) function.
@code{.cpp} @code{.cpp}
//convert Mat to hppiMatrix //convert Mat to hppiMatrix
...@@ -104,14 +104,14 @@ Explanation ...@@ -104,14 +104,14 @@ Explanation
HPP_DATA_TYPE_16S data type for source matrix with HPP_DATA_TYPE_8U type. You should check HPP_DATA_TYPE_16S data type for source matrix with HPP_DATA_TYPE_8U type. You should check
hppStatus after each call IPP Async function. hppStatus after each call IPP Async function.
7. Create windows and show the images, the usual way. -# Create windows and show the images, the usual way.
@code{.cpp} @code{.cpp}
imshow("image", image); imshow("image", image);
imshow("rez", result); imshow("rez", result);
waitKey(15); waitKey(15);
@endcode @endcode
8. Delete hpp matrices. -# Delete hpp matrices.
@code{.cpp} @code{.cpp}
sts = hppiFreeMatrix(src); sts = hppiFreeMatrix(src);
CHECK_DEL_STATUS(sts,"hppiFreeMatrix"); CHECK_DEL_STATUS(sts,"hppiFreeMatrix");
...@@ -119,7 +119,7 @@ Explanation ...@@ -119,7 +119,7 @@ Explanation
sts = hppiFreeMatrix(dst); sts = hppiFreeMatrix(dst);
CHECK_DEL_STATUS(sts,"hppiFreeMatrix"); CHECK_DEL_STATUS(sts,"hppiFreeMatrix");
@endcode @endcode
9. Delete virtual matrices and accelerator instance. -# Delete virtual matrices and accelerator instance.
@code{.cpp} @code{.cpp}
if (virtMatrix) if (virtMatrix)
{ {
...@@ -140,4 +140,4 @@ Result ...@@ -140,4 +140,4 @@ Result
After compiling the code above we can execute it giving an image or video path and accelerator type After compiling the code above we can execute it giving an image or video path and accelerator type
as an argument. For this tutorial we use baboon.png image as input. The result is below. as an argument. For this tutorial we use baboon.png image as input. The result is below.
![image](images/How_To_Use_IPPA_Result.jpg) ![](images/How_To_Use_IPPA_Result.jpg)
...@@ -93,20 +93,18 @@ To further help on seeing the difference the programs supports two modes: one mi ...@@ -93,20 +93,18 @@ To further help on seeing the difference the programs supports two modes: one mi
one pure C++. If you define the *DEMO_MIXED_API_USE* you'll end up using the first. The program one pure C++. If you define the *DEMO_MIXED_API_USE* you'll end up using the first. The program
separates the color planes, does some modifications on them and in the end merge them back together. separates the color planes, does some modifications on them and in the end merge them back together.
@includelineno @dontinclude cpp/tutorial_code/core/interoperability_with_OpenCV_1/interoperability_with_OpenCV_1.cpp
cpp/tutorial_code/core/interoperability_with_OpenCV_1/interoperability_with_OpenCV_1.cpp @until namespace cv
@skip ifdef
lines @until endif
1-10, 23-26, 29-46 @skip main
@until endif
Here you can observe that with the new structure we have no pointer problems, although it is Here you can observe that with the new structure we have no pointer problems, although it is
possible to use the old functions and in the end just transform the result to a *Mat* object. possible to use the old functions and in the end just transform the result to a *Mat* object.
@includelineno @skip convert image
cpp/tutorial_code/core/interoperability_with_OpenCV_1/interoperability_with_OpenCV_1.cpp @until split
lines
48-53
Because, we want to mess around with the images luma component we first convert from the default RGB Because, we want to mess around with the images luma component we first convert from the default RGB
to the YUV color space and then split the result up into separate planes. Here the program splits: to the YUV color space and then split the result up into separate planes. Here the program splits:
...@@ -116,11 +114,8 @@ image some Gaussian noise and then mix together the channels according to some f ...@@ -116,11 +114,8 @@ image some Gaussian noise and then mix together the channels according to some f
The scanning version looks like: The scanning version looks like:
@includelineno @skip #if 1
cpp/tutorial_code/core/interoperability_with_OpenCV_1/interoperability_with_OpenCV_1.cpp @until #else
lines
57-77
Here you can observe that we may go through all the pixels of an image in three fashions: an Here you can observe that we may go through all the pixels of an image in three fashions: an
iterator, a C pointer and an individual element access style. You can read a more in-depth iterator, a C pointer and an individual element access style. You can read a more in-depth
...@@ -128,26 +123,20 @@ description of these in the @ref tutorial_how_to_scan_images tutorial. Convertin ...@@ -128,26 +123,20 @@ description of these in the @ref tutorial_how_to_scan_images tutorial. Convertin
names is easy. Just remove the cv prefix and use the new *Mat* data structure. Here's an example of names is easy. Just remove the cv prefix and use the new *Mat* data structure. Here's an example of
this by using the weighted addition function: this by using the weighted addition function:
@includelineno @until planes[0]
cpp/tutorial_code/core/interoperability_with_OpenCV_1/interoperability_with_OpenCV_1.cpp @until endif
lines
81-113
As you may observe the *planes* variable is of type *Mat*. However, converting from *Mat* to As you may observe the *planes* variable is of type *Mat*. However, converting from *Mat* to
*IplImage* is easy and made automatically with a simple assignment operator. *IplImage* is easy and made automatically with a simple assignment operator.
@includelineno @skip merge(planes
cpp/tutorial_code/core/interoperability_with_OpenCV_1/interoperability_with_OpenCV_1.cpp @until #endif
lines
117-129
The new *imshow* highgui function accepts both the *Mat* and *IplImage* data structures. Compile and The new *imshow* highgui function accepts both the *Mat* and *IplImage* data structures. Compile and
run the program and if the first image below is your input you may get either the first or second as run the program and if the first image below is your input you may get either the first or second as
output: output:
![image](images/outputInteropOpenCV1.jpg) ![](images/outputInteropOpenCV1.jpg)
You may observe a runtime instance of this on the [YouTube You may observe a runtime instance of this on the [YouTube
here](https://www.youtube.com/watch?v=qckm-zvo31w) and you can [download the source code from here here](https://www.youtube.com/watch?v=qckm-zvo31w) and you can [download the source code from here
......
...@@ -130,7 +130,7 @@ difference. ...@@ -130,7 +130,7 @@ difference.
For example: For example:
![image](images/resultMatMaskFilter2D.png) ![](images/resultMatMaskFilter2D.png)
You can download this source code from [here You can download this source code from [here
](samples/cpp/tutorial_code/core/mat_mask_operations/mat_mask_operations.cpp) or look in the ](samples/cpp/tutorial_code/core/mat_mask_operations/mat_mask_operations.cpp) or look in the
......
...@@ -9,7 +9,7 @@ computed tomography, and magnetic resonance imaging to name a few. In every case ...@@ -9,7 +9,7 @@ computed tomography, and magnetic resonance imaging to name a few. In every case
see are images. However, when transforming this to our digital devices what we record are numerical see are images. However, when transforming this to our digital devices what we record are numerical
values for each of the points of the image. values for each of the points of the image.
![image](images/MatBasicImageForComputer.jpg) ![](images/MatBasicImageForComputer.jpg)
For example in the above image you can see that the mirror of the car is nothing more than a matrix For example in the above image you can see that the mirror of the car is nothing more than a matrix
containing all the intensity values of the pixel points. How we get and store the pixels values may containing all the intensity values of the pixel points. How we get and store the pixels values may
...@@ -144,18 +144,18 @@ file by using the @ref cv::imwrite() function. However, for debugging purposes i ...@@ -144,18 +144,18 @@ file by using the @ref cv::imwrite() function. However, for debugging purposes i
convenient to see the actual values. You can do this using the \<\< operator of *Mat*. Be aware that convenient to see the actual values. You can do this using the \<\< operator of *Mat*. Be aware that
this only works for two dimensional matrices. this only works for two dimensional matrices.
@dontinclude cpp/tutorial_code/core/mat_the_basic_image_container/mat_the_basic_image_container.cpp
Although *Mat* works really well as an image container, it is also a general matrix class. Although *Mat* works really well as an image container, it is also a general matrix class.
Therefore, it is possible to create and manipulate multidimensional matrices. You can create a Mat Therefore, it is possible to create and manipulate multidimensional matrices. You can create a Mat
object in multiple ways: object in multiple ways:
- @ref cv::Mat::Mat Constructor - @ref cv::Mat::Mat Constructor
@includelineno @skip Mat M(2
cpp/tutorial_code/core/mat_the_basic_image_container/mat_the_basic_image_container.cpp @until cout
lines 27-28
![image](images/MatBasicContainerOut1.png) ![](images/MatBasicContainerOut1.png)
For two dimensional and multichannel images we first define their size: row and column count wise. For two dimensional and multichannel images we first define their size: row and column count wise.
...@@ -173,11 +173,8 @@ object in multiple ways: ...@@ -173,11 +173,8 @@ object in multiple ways:
- Use C/C++ arrays and initialize via constructor - Use C/C++ arrays and initialize via constructor
@includelineno @skip int sz
cpp/tutorial_code/core/mat_the_basic_image_container/mat_the_basic_image_container.cpp @until Mat L
lines
35-36
The upper example shows how to create a matrix with more than two dimensions. Specify its The upper example shows how to create a matrix with more than two dimensions. Specify its
dimension, then pass a pointer containing the size for each dimension and the rest remains the dimension, then pass a pointer containing the size for each dimension and the rest remains the
...@@ -188,14 +185,14 @@ object in multiple ways: ...@@ -188,14 +185,14 @@ object in multiple ways:
IplImage* img = cvLoadImage("greatwave.png", 1); IplImage* img = cvLoadImage("greatwave.png", 1);
Mat mtx(img); // convert IplImage* -> Mat Mat mtx(img); // convert IplImage* -> Mat
@endcode @endcode
- @ref cv::Mat::create function:
@includelineno
cpp/tutorial_code/core/mat_the_basic_image_container/mat_the_basic_image_container.cpp
lines 31-32 - @ref cv::Mat::create function:
@code
M.create(4,4, CV_8UC(2));
cout << "M = "<< endl << " " << M << endl << endl;
@endcode
![image](images/MatBasicContainerOut2.png) ![](images/MatBasicContainerOut2.png)
You cannot initialize the matrix values with this construction. It will only reallocate its matrix You cannot initialize the matrix values with this construction. It will only reallocate its matrix
data memory if the new size will not fit into the old one. data memory if the new size will not fit into the old one.
...@@ -203,41 +200,31 @@ object in multiple ways: ...@@ -203,41 +200,31 @@ object in multiple ways:
- MATLAB style initializer: @ref cv::Mat::zeros , @ref cv::Mat::ones , @ref cv::Mat::eye . Specify size and - MATLAB style initializer: @ref cv::Mat::zeros , @ref cv::Mat::ones , @ref cv::Mat::eye . Specify size and
data type to use: data type to use:
@includelineno @skip Mat E
cpp/tutorial_code/core/mat_the_basic_image_container/mat_the_basic_image_container.cpp @until cout
lines
40-47
![image](images/MatBasicContainerOut3.png) ![](images/MatBasicContainerOut3.png)
- For small matrices you may use comma separated initializers: - For small matrices you may use comma separated initializers:
@includelineno @skip Mat C
cpp/tutorial_code/core/mat_the_basic_image_container/mat_the_basic_image_container.cpp @until cout
lines 50-51 ![](images/MatBasicContainerOut6.png)
![image](images/MatBasicContainerOut6.png)
- Create a new header for an existing *Mat* object and @ref cv::Mat::clone or @ref cv::Mat::copyTo it. - Create a new header for an existing *Mat* object and @ref cv::Mat::clone or @ref cv::Mat::copyTo it.
@includelineno @skip Mat RowClone
cpp/tutorial_code/core/mat_the_basic_image_container/mat_the_basic_image_container.cpp @until cout
lines 53-54
![image](images/MatBasicContainerOut7.png) ![](images/MatBasicContainerOut7.png)
@note @note
You can fill out a matrix with random values using the @ref cv::randu() function. You need to You can fill out a matrix with random values using the @ref cv::randu() function. You need to
give the lower and upper value for the random values: give the lower and upper value for the random values:
@skip Mat R
@until randu
@includelineno
cpp/tutorial_code/core/mat_the_basic_image_container/mat_the_basic_image_container.cpp
lines
57-58
Output formatting Output formatting
----------------- -----------------
...@@ -246,54 +233,26 @@ In the above examples you could see the default formatting option. OpenCV, howev ...@@ -246,54 +233,26 @@ In the above examples you could see the default formatting option. OpenCV, howev
format your matrix output: format your matrix output:
- Default - Default
@skipline (default)
@includelineno ![](images/MatBasicContainerOut8.png)
cpp/tutorial_code/core/mat_the_basic_image_container/mat_the_basic_image_container.cpp
lines
61
![image](images/MatBasicContainerOut8.png)
- Python - Python
@skipline (python)
@includelineno ![](images/MatBasicContainerOut16.png)
cpp/tutorial_code/core/mat_the_basic_image_container/mat_the_basic_image_container.cpp
lines
62
![image](images/MatBasicContainerOut16.png)
- Comma separated values (CSV) - Comma separated values (CSV)
@skipline (csv)
@includelineno ![](images/MatBasicContainerOut10.png)
cpp/tutorial_code/core/mat_the_basic_image_container/mat_the_basic_image_container.cpp
lines
64
![image](images/MatBasicContainerOut10.png)
- Numpy - Numpy
@code
@includelineno cout << "R (numpy) = " << endl << format(R, Formatter::FMT_NUMPY ) << endl << endl;
cpp/tutorial_code/core/mat_the_basic_image_container/mat_the_basic_image_container.cpp @endcode
![](images/MatBasicContainerOut9.png)
lines
63
![image](images/MatBasicContainerOut9.png)
- C - C
@skipline (c)
@includelineno ![](images/MatBasicContainerOut11.png)
cpp/tutorial_code/core/mat_the_basic_image_container/mat_the_basic_image_container.cpp
lines
65
![image](images/MatBasicContainerOut11.png)
Output of other common items Output of other common items
---------------------------- ----------------------------
...@@ -301,44 +260,24 @@ Output of other common items ...@@ -301,44 +260,24 @@ Output of other common items
OpenCV offers support for output of other common OpenCV data structures too via the \<\< operator: OpenCV offers support for output of other common OpenCV data structures too via the \<\< operator:
- 2D Point - 2D Point
@skip Point2f P
@includelineno @until cout
cpp/tutorial_code/core/mat_the_basic_image_container/mat_the_basic_image_container.cpp ![](images/MatBasicContainerOut12.png)
lines
67-68
![image](images/MatBasicContainerOut12.png)
- 3D Point - 3D Point
@skip Point3f P3f
@includelineno @until cout
cpp/tutorial_code/core/mat_the_basic_image_container/mat_the_basic_image_container.cpp ![](images/MatBasicContainerOut13.png)
lines
70-71
![image](images/MatBasicContainerOut13.png)
- std::vector via cv::Mat - std::vector via cv::Mat
@skip vector<float> v
@includelineno @until cout
cpp/tutorial_code/core/mat_the_basic_image_container/mat_the_basic_image_container.cpp ![](images/MatBasicContainerOut14.png)
lines
74-77
![image](images/MatBasicContainerOut14.png)
- std::vector of points - std::vector of points
@skip vector<Point2f> vPoints
@includelineno @until cout
cpp/tutorial_code/core/mat_the_basic_image_container/mat_the_basic_image_container.cpp ![](images/MatBasicContainerOut15.png)
lines
79-83
![image](images/MatBasicContainerOut15.png)
Most of the samples here have been included in a small console application. You can download it from Most of the samples here have been included in a small console application. You can download it from
[here](samples/cpp/tutorial_code/core/mat_the_basic_image_container/mat_the_basic_image_container.cpp) [here](samples/cpp/tutorial_code/core/mat_the_basic_image_container/mat_the_basic_image_container.cpp)
......
...@@ -25,7 +25,7 @@ Code ...@@ -25,7 +25,7 @@ Code
Explanation Explanation
----------- -----------
1. Let's start by checking out the *main* function. We observe that first thing we do is creating a -# Let's start by checking out the *main* function. We observe that first thing we do is creating a
*Random Number Generator* object (RNG): *Random Number Generator* object (RNG):
@code{.cpp} @code{.cpp}
RNG rng( 0xFFFFFFFF ); RNG rng( 0xFFFFFFFF );
...@@ -33,7 +33,7 @@ Explanation ...@@ -33,7 +33,7 @@ Explanation
RNG implements a random number generator. In this example, *rng* is a RNG element initialized RNG implements a random number generator. In this example, *rng* is a RNG element initialized
with the value *0xFFFFFFFF* with the value *0xFFFFFFFF*
2. Then we create a matrix initialized to *zeros* (which means that it will appear as black), -# Then we create a matrix initialized to *zeros* (which means that it will appear as black),
specifying its height, width and its type: specifying its height, width and its type:
@code{.cpp} @code{.cpp}
/// Initialize a matrix filled with zeros /// Initialize a matrix filled with zeros
...@@ -42,7 +42,7 @@ Explanation ...@@ -42,7 +42,7 @@ Explanation
/// Show it in a window during DELAY ms /// Show it in a window during DELAY ms
imshow( window_name, image ); imshow( window_name, image );
@endcode @endcode
3. Then we proceed to draw crazy stuff. After taking a look at the code, you can see that it is -# Then we proceed to draw crazy stuff. After taking a look at the code, you can see that it is
mainly divided in 8 sections, defined as functions: mainly divided in 8 sections, defined as functions:
@code{.cpp} @code{.cpp}
/// Now, let's draw some lines /// Now, let's draw some lines
...@@ -79,7 +79,7 @@ Explanation ...@@ -79,7 +79,7 @@ Explanation
All of these functions follow the same pattern, so we will analyze only a couple of them, since All of these functions follow the same pattern, so we will analyze only a couple of them, since
the same explanation applies for all. the same explanation applies for all.
4. Checking out the function **Drawing_Random_Lines**: -# Checking out the function **Drawing_Random_Lines**:
@code{.cpp} @code{.cpp}
int Drawing_Random_Lines( Mat image, char* window_name, RNG rng ) int Drawing_Random_Lines( Mat image, char* window_name, RNG rng )
{ {
...@@ -133,11 +133,11 @@ Explanation ...@@ -133,11 +133,11 @@ Explanation
are used as the *R*, *G* and *B* parameters for the line color. Hence, the color of the are used as the *R*, *G* and *B* parameters for the line color. Hence, the color of the
lines will be random too! lines will be random too!
5. The explanation above applies for the other functions generating circles, ellipses, polygones, -# The explanation above applies for the other functions generating circles, ellipses, polygones,
etc. The parameters such as *center* and *vertices* are also generated randomly. etc. The parameters such as *center* and *vertices* are also generated randomly.
6. Before finishing, we also should take a look at the functions *Display_Random_Text* and -# Before finishing, we also should take a look at the functions *Display_Random_Text* and
*Displaying_Big_End*, since they both have a few interesting features: *Displaying_Big_End*, since they both have a few interesting features:
7. **Display_Random_Text:** -# **Display_Random_Text:**
@code{.cpp} @code{.cpp}
int Displaying_Random_Text( Mat image, char* window_name, RNG rng ) int Displaying_Random_Text( Mat image, char* window_name, RNG rng )
{ {
...@@ -178,7 +178,7 @@ Explanation ...@@ -178,7 +178,7 @@ Explanation
As a result, we will get (analagously to the other drawing functions) **NUMBER** texts over our As a result, we will get (analagously to the other drawing functions) **NUMBER** texts over our
image, in random locations. image, in random locations.
8. **Displaying_Big_End** -# **Displaying_Big_End**
@code{.cpp} @code{.cpp}
int Displaying_Big_End( Mat image, char* window_name, RNG rng ) int Displaying_Big_End( Mat image, char* window_name, RNG rng )
{ {
...@@ -222,28 +222,28 @@ Result ...@@ -222,28 +222,28 @@ Result
As you just saw in the Code section, the program will sequentially execute diverse drawing As you just saw in the Code section, the program will sequentially execute diverse drawing
functions, which will produce: functions, which will produce:
1. First a random set of *NUMBER* lines will appear on screen such as it can be seen in this -# First a random set of *NUMBER* lines will appear on screen such as it can be seen in this
screenshot: screenshot:
![image](images/Drawing_2_Tutorial_Result_0.jpg) ![](images/Drawing_2_Tutorial_Result_0.jpg)
2. Then, a new set of figures, these time *rectangles* will follow. -# Then, a new set of figures, these time *rectangles* will follow.
3. Now some ellipses will appear, each of them with random position, size, thickness and arc -# Now some ellipses will appear, each of them with random position, size, thickness and arc
length: length:
![image](images/Drawing_2_Tutorial_Result_2.jpg) ![](images/Drawing_2_Tutorial_Result_2.jpg)
4. Now, *polylines* with 03 segments will appear on screen, again in random configurations. -# Now, *polylines* with 03 segments will appear on screen, again in random configurations.
![image](images/Drawing_2_Tutorial_Result_3.jpg) ![](images/Drawing_2_Tutorial_Result_3.jpg)
5. Filled polygons (in this example triangles) will follow. -# Filled polygons (in this example triangles) will follow.
6. The last geometric figure to appear: circles! -# The last geometric figure to appear: circles!
![image](images/Drawing_2_Tutorial_Result_5.jpg) ![](images/Drawing_2_Tutorial_Result_5.jpg)
7. Near the end, the text *"Testing Text Rendering"* will appear in a variety of fonts, sizes, -# Near the end, the text *"Testing Text Rendering"* will appear in a variety of fonts, sizes,
colors and positions. colors and positions.
8. And the big end (which by the way expresses a big truth too): -# And the big end (which by the way expresses a big truth too):
![image](images/Drawing_2_Tutorial_Result_big.jpg) ![](images/Drawing_2_Tutorial_Result_big.jpg)
...@@ -4,10 +4,10 @@ AKAZE local features matching {#tutorial_akaze_matching} ...@@ -4,10 +4,10 @@ AKAZE local features matching {#tutorial_akaze_matching}
Introduction Introduction
------------ ------------
In this tutorial we will learn how to use [AKAZE]_ local features to detect and match keypoints on In this tutorial we will learn how to use AKAZE @cite ANB13 local features to detect and match keypoints on
two images. two images.
We will find keypoints on a pair of images with given homography matrix, match them and count the We will find keypoints on a pair of images with given homography matrix, match them and count the
number of inliers (i. e. matches that fit in the given homography). number of inliers (i. e. matches that fit in the given homography).
You can find expanded version of this example here: You can find expanded version of this example here:
...@@ -18,7 +18,7 @@ Data ...@@ -18,7 +18,7 @@ Data
We are going to use images 1 and 3 from *Graffity* sequence of Oxford dataset. We are going to use images 1 and 3 from *Graffity* sequence of Oxford dataset.
![image](images/graf.png) ![](images/graf.png)
Homography is given by a 3 by 3 matrix: Homography is given by a 3 by 3 matrix:
@code{.none} @code{.none}
...@@ -35,92 +35,92 @@ You can find the images (*graf1.png*, *graf3.png*) and homography (*H1to3p.xml*) ...@@ -35,92 +35,92 @@ You can find the images (*graf1.png*, *graf3.png*) and homography (*H1to3p.xml*)
### Explanation ### Explanation
1. **Load images and homography** -# **Load images and homography**
@code{.cpp} @code{.cpp}
Mat img1 = imread("graf1.png", IMREAD_GRAYSCALE); Mat img1 = imread("graf1.png", IMREAD_GRAYSCALE);
Mat img2 = imread("graf3.png", IMREAD_GRAYSCALE); Mat img2 = imread("graf3.png", IMREAD_GRAYSCALE);
Mat homography; Mat homography;
FileStorage fs("H1to3p.xml", FileStorage::READ); FileStorage fs("H1to3p.xml", FileStorage::READ);
fs.getFirstTopLevelNode() >> homography; fs.getFirstTopLevelNode() >> homography;
@endcode @endcode
We are loading grayscale images here. Homography is stored in the xml created with FileStorage. We are loading grayscale images here. Homography is stored in the xml created with FileStorage.
1. **Detect keypoints and compute descriptors using AKAZE** -# **Detect keypoints and compute descriptors using AKAZE**
@code{.cpp} @code{.cpp}
vector<KeyPoint> kpts1, kpts2; vector<KeyPoint> kpts1, kpts2;
Mat desc1, desc2; Mat desc1, desc2;
AKAZE akaze; AKAZE akaze;
akaze(img1, noArray(), kpts1, desc1); akaze(img1, noArray(), kpts1, desc1);
akaze(img2, noArray(), kpts2, desc2); akaze(img2, noArray(), kpts2, desc2);
@endcode @endcode
We create AKAZE object and use it's *operator()* functionality. Since we don't need the *mask* We create AKAZE object and use it's *operator()* functionality. Since we don't need the *mask*
parameter, *noArray()* is used. parameter, *noArray()* is used.
1. **Use brute-force matcher to find 2-nn matches** -# **Use brute-force matcher to find 2-nn matches**
@code{.cpp} @code{.cpp}
BFMatcher matcher(NORM_HAMMING); BFMatcher matcher(NORM_HAMMING);
vector< vector<DMatch> > nn_matches; vector< vector<DMatch> > nn_matches;
matcher.knnMatch(desc1, desc2, nn_matches, 2); matcher.knnMatch(desc1, desc2, nn_matches, 2);
@endcode @endcode
We use Hamming distance, because AKAZE uses binary descriptor by default. We use Hamming distance, because AKAZE uses binary descriptor by default.
1. **Use 2-nn matches to find correct keypoint matches** -# **Use 2-nn matches to find correct keypoint matches**
@code{.cpp} @code{.cpp}
for(size_t i = 0; i < nn_matches.size(); i++) { for(size_t i = 0; i < nn_matches.size(); i++) {
DMatch first = nn_matches[i][0]; DMatch first = nn_matches[i][0];
float dist1 = nn_matches[i][0].distance; float dist1 = nn_matches[i][0].distance;
float dist2 = nn_matches[i][1].distance; float dist2 = nn_matches[i][1].distance;
if(dist1 < nn_match_ratio * dist2) { if(dist1 < nn_match_ratio * dist2) {
matched1.push_back(kpts1[first.queryIdx]); matched1.push_back(kpts1[first.queryIdx]);
matched2.push_back(kpts2[first.trainIdx]); matched2.push_back(kpts2[first.trainIdx]);
}
} }
} @endcode
@endcode If the closest match is *ratio* closer than the second closest one, then the match is correct.
If the closest match is *ratio* closer than the second closest one, then the match is correct.
-# **Check if our matches fit in the homography model**
1. **Check if our matches fit in the homography model** @code{.cpp}
@code{.cpp} for(int i = 0; i < matched1.size(); i++) {
for(int i = 0; i < matched1.size(); i++) { Mat col = Mat::ones(3, 1, CV_64F);
Mat col = Mat::ones(3, 1, CV_64F); col.at<double>(0) = matched1[i].pt.x;
col.at<double>(0) = matched1[i].pt.x; col.at<double>(1) = matched1[i].pt.y;
col.at<double>(1) = matched1[i].pt.y;
col = homography * col;
col = homography * col; col /= col.at<double>(2);
col /= col.at<double>(2); float dist = sqrt( pow(col.at<double>(0) - matched2[i].pt.x, 2) +
float dist = sqrt( pow(col.at<double>(0) - matched2[i].pt.x, 2) + pow(col.at<double>(1) - matched2[i].pt.y, 2));
pow(col.at<double>(1) - matched2[i].pt.y, 2));
if(dist < inlier_threshold) {
if(dist < inlier_threshold) { int new_i = inliers1.size();
int new_i = inliers1.size(); inliers1.push_back(matched1[i]);
inliers1.push_back(matched1[i]); inliers2.push_back(matched2[i]);
inliers2.push_back(matched2[i]); good_matches.push_back(DMatch(new_i, new_i, 0));
good_matches.push_back(DMatch(new_i, new_i, 0)); }
} }
} @endcode
@endcode If the distance from first keypoint's projection to the second keypoint is less than threshold,
If the distance from first keypoint's projection to the second keypoint is less than threshold, then it it fits in the homography.
then it it fits in the homography.
We create a new set of matches for the inliers, because it is required by the drawing function. We create a new set of matches for the inliers, because it is required by the drawing function.
1. **Output results** -# **Output results**
@code{.cpp} @code{.cpp}
Mat res; Mat res;
drawMatches(img1, inliers1, img2, inliers2, good_matches, res); drawMatches(img1, inliers1, img2, inliers2, good_matches, res);
imwrite("res.png", res); imwrite("res.png", res);
... ...
@endcode @endcode
Here we save the resulting image and print some statistics. Here we save the resulting image and print some statistics.
### Results ### Results
Found matches Found matches
------------- -------------
![image](images/res.png) ![](images/res.png)
A-KAZE Matching Results A-KAZE Matching Results
----------------------- -----------------------
......
...@@ -152,8 +152,9 @@ A-KAZE Matching Results ...@@ -152,8 +152,9 @@ A-KAZE Matching Results
-------------------------- --------------------------
.. code-block:: none .. code-block:: none
Keypoints 1: 2943
Keypoints 2: 3511 Keypoints 1 2943
Matches: 447 Keypoints 2 3511
Inliers: 308 Matches 447
Inlier Ratio: 0.689038 Inliers 308
Inlier Ratio 0.689038
...@@ -11,16 +11,17 @@ The algorithm is as follows: ...@@ -11,16 +11,17 @@ The algorithm is as follows:
- Detect and describe keypoints on the first frame, manually set object boundaries - Detect and describe keypoints on the first frame, manually set object boundaries
- For every next frame: - For every next frame:
1. Detect and describe keypoints -# Detect and describe keypoints
2. Match them using bruteforce matcher -# Match them using bruteforce matcher
3. Estimate homography transformation using RANSAC -# Estimate homography transformation using RANSAC
4. Filter inliers from all the matches -# Filter inliers from all the matches
5. Apply homography transformation to the bounding box to find the object -# Apply homography transformation to the bounding box to find the object
6. Draw bounding box and inliers, compute inlier ratio as evaluation metric -# Draw bounding box and inliers, compute inlier ratio as evaluation metric
![image](images/frame.png) ![](images/frame.png)
### Data Data
----
To do the tracking we need a video and object position on the first frame. To do the tracking we need a video and object position on the first frame.
...@@ -31,14 +32,16 @@ To run the code you have to specify input and output video path and object bound ...@@ -31,14 +32,16 @@ To run the code you have to specify input and output video path and object bound
@code{.none} @code{.none}
./planar_tracking blais.mp4 result.avi blais_bb.xml.gz ./planar_tracking blais.mp4 result.avi blais_bb.xml.gz
@endcode @endcode
### Source Code
Source Code
-----------
@includelineno cpp/tutorial_code/features2D/AKAZE_tracking/planar_tracking.cpp @includelineno cpp/tutorial_code/features2D/AKAZE_tracking/planar_tracking.cpp
### Explanation Explanation
-----------
Tracker class ### Tracker class
-------------
This class implements algorithm described abobve using given feature detector and descriptor This class implements algorithm described abobve using given feature detector and descriptor
matcher. matcher.
...@@ -63,62 +66,60 @@ matcher. ...@@ -63,62 +66,60 @@ matcher.
- **Processing frames** - **Processing frames**
1. Locate keypoints and compute descriptors -# Locate keypoints and compute descriptors
@code{.cpp} @code{.cpp}
(*detector)(frame, noArray(), kp, desc); (*detector)(frame, noArray(), kp, desc);
@endcode @endcode
To find matches between frames we have to locate the keypoints first.
In this tutorial detectors are set up to find about 1000 keypoints on each frame.
1. Use 2-nn matcher to find correspondences To find matches between frames we have to locate the keypoints first.
@code{.cpp}
matcher->knnMatch(first_desc, desc, matches, 2);
for(unsigned i = 0; i < matches.size(); i++) {
if(matches[i][0].distance < nn_match_ratio * matches[i][1].distance) {
matched1.push_back(first_kp[matches[i][0].queryIdx]);
matched2.push_back( kp[matches[i][0].trainIdx]);
}
}
@endcode
If the closest match is *nn_match_ratio* closer than the second closest one, then it's a
match.
2. Use *RANSAC* to estimate homography transformation In this tutorial detectors are set up to find about 1000 keypoints on each frame.
@code{.cpp}
homography = findHomography(Points(matched1), Points(matched2),
RANSAC, ransac_thresh, inlier_mask);
@endcode
If there are at least 4 matches we can use random sample consensus to estimate image
transformation.
3. Save the inliers -# Use 2-nn matcher to find correspondences
@code{.cpp} @code{.cpp}
for(unsigned i = 0; i < matched1.size(); i++) { matcher->knnMatch(first_desc, desc, matches, 2);
if(inlier_mask.at<uchar>(i)) { for(unsigned i = 0; i < matches.size(); i++) {
int new_i = static_cast<int>(inliers1.size()); if(matches[i][0].distance < nn_match_ratio * matches[i][1].distance) {
inliers1.push_back(matched1[i]); matched1.push_back(first_kp[matches[i][0].queryIdx]);
inliers2.push_back(matched2[i]); matched2.push_back( kp[matches[i][0].trainIdx]);
inlier_matches.push_back(DMatch(new_i, new_i, 0)); }
} }
} @endcode
@endcode If the closest match is *nn_match_ratio* closer than the second closest one, then it's a
match.
Since *findHomography* computes the inliers we only have to save the chosen points and
matches. -# Use *RANSAC* to estimate homography transformation
@code{.cpp}
homography = findHomography(Points(matched1), Points(matched2),
RANSAC, ransac_thresh, inlier_mask);
@endcode
If there are at least 4 matches we can use random sample consensus to estimate image
transformation.
-# Save the inliers
@code{.cpp}
for(unsigned i = 0; i < matched1.size(); i++) {
if(inlier_mask.at<uchar>(i)) {
int new_i = static_cast<int>(inliers1.size());
inliers1.push_back(matched1[i]);
inliers2.push_back(matched2[i]);
inlier_matches.push_back(DMatch(new_i, new_i, 0));
}
}
@endcode
Since *findHomography* computes the inliers we only have to save the chosen points and
matches.
4. Project object bounding box -# Project object bounding box
@code{.cpp} @code{.cpp}
perspectiveTransform(object_bb, new_bb, homography); perspectiveTransform(object_bb, new_bb, homography);
@endcode @endcode
If there is a reasonable number of inliers we can use estimated transformation to locate the
object.
### Results If there is a reasonable number of inliers we can use estimated transformation to locate the
object.
Results
-------
You can watch the resulting [video on youtube](http://www.youtube.com/watch?v=LWY-w8AGGhE). You can watch the resulting [video on youtube](http://www.youtube.com/watch?v=LWY-w8AGGhE).
...@@ -129,6 +130,7 @@ Inliers 410 ...@@ -129,6 +130,7 @@ Inliers 410
Inlier ratio 0.58 Inlier ratio 0.58
Keypoints 1117 Keypoints 1117
@endcode @endcode
*ORB* statistics: *ORB* statistics:
@code{.none} @code{.none}
Matches 504 Matches 504
......
...@@ -87,4 +87,4 @@ Result ...@@ -87,4 +87,4 @@ Result
Here is the result after applying the BruteForce matcher between the two original images: Here is the result after applying the BruteForce matcher between the two original images:
![image](images/Feature_Description_BruteForce_Result.jpg) ![](images/Feature_Description_BruteForce_Result.jpg)
...@@ -79,10 +79,10 @@ Explanation ...@@ -79,10 +79,10 @@ Explanation
Result Result
------ ------
1. Here is the result of the feature detection applied to the first image: -# Here is the result of the feature detection applied to the first image:
![image](images/Feature_Detection_Result_a.jpg) ![](images/Feature_Detection_Result_a.jpg)
2. And here is the result for the second image: -# And here is the result for the second image:
![image](images/Feature_Detection_Result_b.jpg) ![](images/Feature_Detection_Result_b.jpg)
...@@ -130,10 +130,10 @@ Explanation ...@@ -130,10 +130,10 @@ Explanation
Result Result
------ ------
1. Here is the result of the feature detection applied to the first image: -# Here is the result of the feature detection applied to the first image:
![image](images/Featur_FlannMatcher_Result.jpg) ![](images/Featur_FlannMatcher_Result.jpg)
2. Additionally, we get as console output the keypoints filtered: -# Additionally, we get as console output the keypoints filtered:
![image](images/Feature_FlannMatcher_Keypoints_Result.jpg) ![](images/Feature_FlannMatcher_Keypoints_Result.jpg)
...@@ -134,8 +134,8 @@ Explanation ...@@ -134,8 +134,8 @@ Explanation
Result Result
------ ------
1. And here is the result for the detected object (highlighted in green) -# And here is the result for the detected object (highlighted in green)
![image](images/Feature_Homography_Result.jpg) ![](images/Feature_Homography_Result.jpg)
...@@ -122,9 +122,9 @@ Explanation ...@@ -122,9 +122,9 @@ Explanation
Result Result
------ ------
![image](images/Corner_Subpixeles_Original_Image.jpg) ![](images/Corner_Subpixeles_Original_Image.jpg)
Here is the result: Here is the result:
![image](images/Corner_Subpixeles_Result.jpg) ![](images/Corner_Subpixeles_Result.jpg)
...@@ -30,7 +30,7 @@ Explanation ...@@ -30,7 +30,7 @@ Explanation
Result Result
------ ------
![image](images/My_Harris_corner_detector_Result.jpg) ![](images/My_Harris_corner_detector_Result.jpg)
![image](images/My_Shi_Tomasi_corner_detector_Result.jpg) ![](images/My_Shi_Tomasi_corner_detector_Result.jpg)
...@@ -111,5 +111,5 @@ Explanation ...@@ -111,5 +111,5 @@ Explanation
Result Result
------ ------
![image](images/Feature_Detection_Result_a.jpg) ![](images/Feature_Detection_Result_a.jpg)
...@@ -201,9 +201,9 @@ Result ...@@ -201,9 +201,9 @@ Result
The original image: The original image:
![image](images/Harris_Detector_Original_Image.jpg) ![](images/Harris_Detector_Original_Image.jpg)
The detected corners are surrounded by a small black circle The detected corners are surrounded by a small black circle
![image](images/Harris_Detector_Result.jpg) ![](images/Harris_Detector_Result.jpg)
General tutorials {#tutorial_table_of_content_general}
=================
These tutorials are the bottom of the iceberg as they link together multiple of the modules
presented above in order to solve complex problems.
...@@ -24,28 +24,45 @@ The source code ...@@ -24,28 +24,45 @@ The source code
You may also find the source code and these video file in the You may also find the source code and these video file in the
`samples/cpp/tutorial_code/gpu/gpu-basics-similarity/gpu-basics-similarity` folder of the OpenCV `samples/cpp/tutorial_code/gpu/gpu-basics-similarity/gpu-basics-similarity` folder of the OpenCV
source library or download it from here source library or download it from [here](samples/cpp/tutorial_code/gpu/gpu-basics-similarity/gpu-basics-similarity.cpp).
\<../../../../samples/cpp/tutorial_code/gpu/gpu-basics-similarity/gpu-basics-similarity.cpp\>. The The full source code is quite long (due to the controlling of the application via the command line
full source code is quite long (due to the controlling of the application via the command line
arguments and performance measurement). Therefore, to avoid cluttering up these sections with those arguments and performance measurement). Therefore, to avoid cluttering up these sections with those
you'll find here only the functions itself. you'll find here only the functions itself.
The PSNR returns a float number, that if the two inputs are similar between 30 and 50 (higher is The PSNR returns a float number, that if the two inputs are similar between 30 and 50 (higher is
better). better).
@includelineno samples/cpp/tutorial_code/gpu/gpu-basics-similarity/gpu-basics-similarity.cpp @dontinclude samples/cpp/tutorial_code/gpu/gpu-basics-similarity/gpu-basics-similarity.cpp
lines @skip struct BufferPSNR
165-210, 18-23, 210-235 @until };
@skip double getPSNR(
@until return psnr;
@until }
@until }
@skip double getPSNR_CUDA(
@until return psnr;
@until }
@until }
The SSIM returns the MSSIM of the images. This is too a float number between zero and one (higher is The SSIM returns the MSSIM of the images. This is too a float number between zero and one (higher is
better), however we have one for each channel. Therefore, we return a *Scalar* OpenCV data better), however we have one for each channel. Therefore, we return a *Scalar* OpenCV data
structure: structure:
@includelineno samples/cpp/tutorial_code/gpu/gpu-basics-similarity/gpu-basics-similarity.cpp @dontinclude samples/cpp/tutorial_code/gpu/gpu-basics-similarity/gpu-basics-similarity.cpp
@skip struct BufferMSSIM
@until };
lines @skip Scalar getMSSIM(
235-355, 26-42, 357- @until return mssim;
@until }
@skip Scalar getMSSIM_CUDA_optimized(
@until return mssim;
@until }
How to do it? - The GPU How to do it? - The GPU
----------------------- -----------------------
...@@ -124,7 +141,7 @@ The reason for this is that you're throwing out on the window the price for memo ...@@ -124,7 +141,7 @@ The reason for this is that you're throwing out on the window the price for memo
data transfer. And on the GPU this is damn high. Another possibility for optimization is to data transfer. And on the GPU this is damn high. Another possibility for optimization is to
introduce asynchronous OpenCV GPU calls too with the help of the @ref cv::cuda::Stream. introduce asynchronous OpenCV GPU calls too with the help of the @ref cv::cuda::Stream.
1. Memory allocation on the GPU is considerable. Therefore, if it’s possible allocate new memory as -# Memory allocation on the GPU is considerable. Therefore, if it’s possible allocate new memory as
few times as possible. If you create a function what you intend to call multiple times it is a few times as possible. If you create a function what you intend to call multiple times it is a
good idea to allocate any local parameters for the function only once, during the first call. To good idea to allocate any local parameters for the function only once, during the first call. To
do this you create a data structure containing all the local variables you will use. For do this you create a data structure containing all the local variables you will use. For
...@@ -148,7 +165,7 @@ introduce asynchronous OpenCV GPU calls too with the help of the @ref cv::cuda:: ...@@ -148,7 +165,7 @@ introduce asynchronous OpenCV GPU calls too with the help of the @ref cv::cuda::
Now you access these local parameters as: *b.gI1*, *b.buf* and so on. The GpuMat will only Now you access these local parameters as: *b.gI1*, *b.buf* and so on. The GpuMat will only
reallocate itself on a new call if the new matrix size is different from the previous one. reallocate itself on a new call if the new matrix size is different from the previous one.
2. Avoid unnecessary function data transfers. Any small data transfer will be significant one once -# Avoid unnecessary function data transfers. Any small data transfer will be significant one once
you go to the GPU. Therefore, if possible make all calculations in-place (in other words do not you go to the GPU. Therefore, if possible make all calculations in-place (in other words do not
create new memory objects - for reasons explained at the previous point). For example, although create new memory objects - for reasons explained at the previous point). For example, although
expressing arithmetical operations may be easier to express in one line formulas, it will be expressing arithmetical operations may be easier to express in one line formulas, it will be
...@@ -164,7 +181,7 @@ introduce asynchronous OpenCV GPU calls too with the help of the @ref cv::cuda:: ...@@ -164,7 +181,7 @@ introduce asynchronous OpenCV GPU calls too with the help of the @ref cv::cuda::
gpu::multiply(b.mu1_mu2, 2, b.t1); //b.t1 = 2 * b.mu1_mu2 + C1; gpu::multiply(b.mu1_mu2, 2, b.t1); //b.t1 = 2 * b.mu1_mu2 + C1;
gpu::add(b.t1, C1, b.t1); gpu::add(b.t1, C1, b.t1);
@endcode @endcode
3. Use asynchronous calls (the @ref cv::cuda::Stream ). By default whenever you call a gpu function -# Use asynchronous calls (the @ref cv::cuda::Stream ). By default whenever you call a gpu function
it will wait for the call to finish and return with the result afterwards. However, it is it will wait for the call to finish and return with the result afterwards. However, it is
possible to make asynchronous calls, meaning it will call for the operation execution, make the possible to make asynchronous calls, meaning it will call for the operation execution, make the
costly data allocations for the algorithm and return back right away. Now you can call another costly data allocations for the algorithm and return back right away. Now you can call another
...@@ -189,7 +206,7 @@ Result and conclusion ...@@ -189,7 +206,7 @@ Result and conclusion
--------------------- ---------------------
On an Intel P8700 laptop CPU paired with a low end NVidia GT220M here are the performance numbers: On an Intel P8700 laptop CPU paired with a low end NVidia GT220M here are the performance numbers:
@code{.cpp} @code
Time of PSNR CPU (averaged for 10 runs): 41.4122 milliseconds. With result of: 19.2506 Time of PSNR CPU (averaged for 10 runs): 41.4122 milliseconds. With result of: 19.2506
Time of PSNR GPU (averaged for 10 runs): 158.977 milliseconds. With result of: 19.2506 Time of PSNR GPU (averaged for 10 runs): 158.977 milliseconds. With result of: 19.2506
Initial call GPU optimized: 31.3418 milliseconds. With result of: 19.2506 Initial call GPU optimized: 31.3418 milliseconds. With result of: 19.2506
......
...@@ -94,9 +94,8 @@ Below is the output of the program. Use the first image as the input. For the DE ...@@ -94,9 +94,8 @@ Below is the output of the program. Use the first image as the input. For the DE
the SRTM file located at the USGS here. the SRTM file located at the USGS here.
[<http://dds.cr.usgs.gov/srtm/version2_1/SRTM1/Region_04/N37W123.hgt.zip>](http://dds.cr.usgs.gov/srtm/version2_1/SRTM1/Region_04/N37W123.hgt.zip) [<http://dds.cr.usgs.gov/srtm/version2_1/SRTM1/Region_04/N37W123.hgt.zip>](http://dds.cr.usgs.gov/srtm/version2_1/SRTM1/Region_04/N37W123.hgt.zip)
![image](images/output.jpg) ![](images/gdal_output.jpg)
![image](images/heat-map.jpg) ![](images/gdal_heat-map.jpg)
![image](images/flood-zone.jpg)
![](images/gdal_flood-zone.jpg)
...@@ -106,8 +106,8 @@ Results ...@@ -106,8 +106,8 @@ Results
Below is the output of the program. Use the first image as the input. For the DEM model, download the SRTM file located at the USGS here. `http://dds.cr.usgs.gov/srtm/version2_1/SRTM1/Region_04/N37W123.hgt.zip <http://dds.cr.usgs.gov/srtm/version2_1/SRTM1/Region_04/N37W123.hgt.zip>`_ Below is the output of the program. Use the first image as the input. For the DEM model, download the SRTM file located at the USGS here. `http://dds.cr.usgs.gov/srtm/version2_1/SRTM1/Region_04/N37W123.hgt.zip <http://dds.cr.usgs.gov/srtm/version2_1/SRTM1/Region_04/N37W123.hgt.zip>`_
.. image:: images/output.jpg .. image:: images/gdal_output.jpg
.. image:: images/heat-map.jpg .. image:: images/gdal_heat-map.jpg
.. image:: images/flood-zone.jpg .. image:: images/gdal_flood-zone.jpg
...@@ -7,7 +7,7 @@ Adding a Trackbar to our applications! {#tutorial_trackbar} ...@@ -7,7 +7,7 @@ Adding a Trackbar to our applications! {#tutorial_trackbar}
- Well, it is time to use some fancy GUI tools. OpenCV provides some GUI utilities (*highgui.h*) - Well, it is time to use some fancy GUI tools. OpenCV provides some GUI utilities (*highgui.h*)
for you. An example of this is a **Trackbar** for you. An example of this is a **Trackbar**
![image](images/Adding_Trackbars_Tutorial_Trackbar.png) ![](images/Adding_Trackbars_Tutorial_Trackbar.png)
- In this tutorial we will just modify our two previous programs so that they get the input - In this tutorial we will just modify our two previous programs so that they get the input
information from the trackbar. information from the trackbar.
...@@ -88,16 +88,16 @@ Explanation ...@@ -88,16 +88,16 @@ Explanation
We only analyze the code that is related to Trackbar: We only analyze the code that is related to Trackbar:
1. First, we load 02 images, which are going to be blended. -# First, we load 02 images, which are going to be blended.
@code{.cpp} @code{.cpp}
src1 = imread("../../images/LinuxLogo.jpg"); src1 = imread("../../images/LinuxLogo.jpg");
src2 = imread("../../images/WindowsLogo.jpg"); src2 = imread("../../images/WindowsLogo.jpg");
@endcode @endcode
2. To create a trackbar, first we have to create the window in which it is going to be located. So: -# To create a trackbar, first we have to create the window in which it is going to be located. So:
@code{.cpp} @code{.cpp}
namedWindow("Linear Blend", 1); namedWindow("Linear Blend", 1);
@endcode @endcode
3. Now we can create the Trackbar: -# Now we can create the Trackbar:
@code{.cpp} @code{.cpp}
createTrackbar( TrackbarName, "Linear Blend", &alpha_slider, alpha_slider_max, on_trackbar ); createTrackbar( TrackbarName, "Linear Blend", &alpha_slider, alpha_slider_max, on_trackbar );
@endcode @endcode
...@@ -110,7 +110,7 @@ We only analyze the code that is related to Trackbar: ...@@ -110,7 +110,7 @@ We only analyze the code that is related to Trackbar:
- The numerical value of Trackbar is stored in **alpha_slider** - The numerical value of Trackbar is stored in **alpha_slider**
- Whenever the user moves the Trackbar, the callback function **on_trackbar** is called - Whenever the user moves the Trackbar, the callback function **on_trackbar** is called
4. Finally, we have to define the callback function **on_trackbar** -# Finally, we have to define the callback function **on_trackbar**
@code{.cpp} @code{.cpp}
void on_trackbar( int, void* ) void on_trackbar( int, void* )
{ {
...@@ -133,10 +133,10 @@ Result ...@@ -133,10 +133,10 @@ Result
- Our program produces the following output: - Our program produces the following output:
![image](images/Adding_Trackbars_Tutorial_Result_0.jpg) ![](images/Adding_Trackbars_Tutorial_Result_0.jpg)
- As a manner of practice, you can also add 02 trackbars for the program made in - As a manner of practice, you can also add 02 trackbars for the program made in
@ref tutorial_basic_linear_transform. One trackbar to set \f$\alpha\f$ and another for \f$\beta\f$. The output might @ref tutorial_basic_linear_transform. One trackbar to set \f$\alpha\f$ and another for \f$\beta\f$. The output might
look like: look like:
![image](images/Adding_Trackbars_Tutorial_Result_1.jpg) ![](images/Adding_Trackbars_Tutorial_Result_1.jpg)
...@@ -25,10 +25,14 @@ version of it ](samples/cpp/tutorial_code/HighGUI/video-input-psnr-ssim/video/Me ...@@ -25,10 +25,14 @@ version of it ](samples/cpp/tutorial_code/HighGUI/video-input-psnr-ssim/video/Me
You may also find the source code and these video file in the You may also find the source code and these video file in the
`samples/cpp/tutorial_code/HighGUI/video-input-psnr-ssim/` folder of the OpenCV source library. `samples/cpp/tutorial_code/HighGUI/video-input-psnr-ssim/` folder of the OpenCV source library.
@includelineno cpp/tutorial_code/HighGUI/video-input-psnr-ssim/video-input-psnr-ssim.cpp @dontinclude cpp/tutorial_code/HighGUI/video-input-psnr-ssim/video-input-psnr-ssim.cpp
lines @until Scalar getMSSIM
1-15, 29-31, 33-208 @skip main
@until {
@skip if
@until return mssim;
@until }
How to read a video stream (online-camera or offline-file)? How to read a video stream (online-camera or offline-file)?
----------------------------------------------------------- -----------------------------------------------------------
...@@ -243,10 +247,9 @@ for each frame, and the SSIM only for the frames where the PSNR falls below an i ...@@ -243,10 +247,9 @@ for each frame, and the SSIM only for the frames where the PSNR falls below an i
visualization purpose we show both images in an OpenCV window and print the PSNR and MSSIM values to visualization purpose we show both images in an OpenCV window and print the PSNR and MSSIM values to
the console. Expect to see something like: the console. Expect to see something like:
![image](images/outputVideoInput.png) ![](images/outputVideoInput.png)
You may observe a runtime instance of this on the [YouTube You may observe a runtime instance of this on the [YouTube here](https://www.youtube.com/watch?v=iOcNljutOgg).
here](https://www.youtube.com/watch?v=iOcNljutOgg).
\htmlonly \htmlonly
<div align="center"> <div align="center">
......
...@@ -47,7 +47,7 @@ somehow longer and includes names such as *XVID*, *DIVX*, *H264* or *LAGS* (*Lag ...@@ -47,7 +47,7 @@ somehow longer and includes names such as *XVID*, *DIVX*, *H264* or *LAGS* (*Lag
Codec*). The full list of codecs you may use on a system depends on just what one you have Codec*). The full list of codecs you may use on a system depends on just what one you have
installed. installed.
![image](images/videoFileStructure.png) ![](images/videoFileStructure.png)
As you can see things can get really complicated with videos. However, OpenCV is mainly a computer As you can see things can get really complicated with videos. However, OpenCV is mainly a computer
vision library, not a video stream, codec and write one. Therefore, the developers tried to keep vision library, not a video stream, codec and write one. Therefore, the developers tried to keep
...@@ -75,7 +75,7 @@ const string source = argv[1]; // the source file name ...@@ -75,7 +75,7 @@ const string source = argv[1]; // the source file name
string::size_type pAt = source.find_last_of('.'); // Find extension point string::size_type pAt = source.find_last_of('.'); // Find extension point
const string NAME = source.substr(0, pAt) + argv[2][0] + ".avi"; // Form the new name with container const string NAME = source.substr(0, pAt) + argv[2][0] + ".avi"; // Form the new name with container
@endcode @endcode
1. The codec to use for the video track. Now all the video codecs have a unique short name of -# The codec to use for the video track. Now all the video codecs have a unique short name of
maximum four characters. Hence, the *XVID*, *DIVX* or *H264* names. This is called a four maximum four characters. Hence, the *XVID*, *DIVX* or *H264* names. This is called a four
character code. You may also ask this from an input video by using its *get* function. Because character code. You may also ask this from an input video by using its *get* function. Because
the *get* function is a general function it always returns double values. A double value is the *get* function is a general function it always returns double values. A double value is
...@@ -109,13 +109,13 @@ const string NAME = source.substr(0, pAt) + argv[2][0] + ".avi"; // Form the n ...@@ -109,13 +109,13 @@ const string NAME = source.substr(0, pAt) + argv[2][0] + ".avi"; // Form the n
If you pass for this argument minus one than a window will pop up at runtime that contains all If you pass for this argument minus one than a window will pop up at runtime that contains all
the codec installed on your system and ask you to select the one to use: the codec installed on your system and ask you to select the one to use:
![image](images/videoCompressSelect.png) ![](images/videoCompressSelect.png)
2. The frame per second for the output video. Again, here I keep the input videos frame per second -# The frame per second for the output video. Again, here I keep the input videos frame per second
by using the *get* function. by using the *get* function.
3. The size of the frames for the output video. Here too I keep the input videos frame size per -# The size of the frames for the output video. Here too I keep the input videos frame size per
second by using the *get* function. second by using the *get* function.
4. The final argument is an optional one. By default is true and says that the output will be a -# The final argument is an optional one. By default is true and says that the output will be a
colorful one (so for write you will send three channel images). To create a gray scale video colorful one (so for write you will send three channel images). To create a gray scale video
pass a false parameter here. pass a false parameter here.
...@@ -148,7 +148,7 @@ merge(spl, res); ...@@ -148,7 +148,7 @@ merge(spl, res);
Put all this together and you'll get the upper source code, whose runtime result will show something Put all this together and you'll get the upper source code, whose runtime result will show something
around the idea: around the idea:
![image](images/resultOutputWideoWrite.png) ![](images/resultOutputWideoWrite.png)
You may observe a runtime instance of this on the [YouTube You may observe a runtime instance of this on the [YouTube
here](https://www.youtube.com/watch?v=jpBwHxsl1_0). here](https://www.youtube.com/watch?v=jpBwHxsl1_0).
......
...@@ -28,7 +28,7 @@ Morphological Operations ...@@ -28,7 +28,7 @@ Morphological Operations
- Finding of intensity bumps or holes 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: - We will explain dilation and erosion briefly, using the following image as an example:
![image](images/Morphology_1_Tutorial_Theory_Original_Image.png) ![](images/Morphology_1_Tutorial_Theory_Original_Image.png)
### Dilation ### Dilation
...@@ -40,7 +40,7 @@ Morphological Operations ...@@ -40,7 +40,7 @@ Morphological Operations
deduce, this maximizing operation causes bright regions within an image to "grow" (therefore the deduce, this maximizing operation causes bright regions within an image to "grow" (therefore the
name *dilation*). Take as an example the image above. Applying dilation we can get: name *dilation*). Take as an example the image above. Applying dilation we can get:
![image](images/Morphology_1_Tutorial_Theory_Dilation.png) ![](images/Morphology_1_Tutorial_Theory_Dilation.png)
The background (bright) dilates around the black regions of the letter. The background (bright) dilates around the black regions of the letter.
...@@ -54,7 +54,7 @@ The background (bright) dilates around the black regions of the letter. ...@@ -54,7 +54,7 @@ The background (bright) dilates around the black regions of the letter.
(shown above). You can see in the result below that the bright areas of the image (the (shown above). You can see in the result below that the bright areas of the image (the
background, apparently), get thinner, whereas the dark zones (the "writing") gets bigger. background, apparently), get thinner, whereas the dark zones (the "writing") gets bigger.
![image](images/Morphology_1_Tutorial_Theory_Erosion.png) ![](images/Morphology_1_Tutorial_Theory_Erosion.png)
Code Code
---- ----
...@@ -66,7 +66,7 @@ This tutorial code's is shown lines below. You can also download it from ...@@ -66,7 +66,7 @@ This tutorial code's is shown lines below. You can also download it from
Explanation Explanation
----------- -----------
1. Most of the stuff shown is known by you (if you have any doubt, please refer to the tutorials in -# Most of the stuff shown is known by you (if you have any doubt, please refer to the tutorials in
previous sections). Let's check the general structure of the program: previous sections). Let's check the general structure of the program:
- Load an image (can be RGB or grayscale) - Load an image (can be RGB or grayscale)
...@@ -80,7 +80,7 @@ Explanation ...@@ -80,7 +80,7 @@ Explanation
Let's analyze these two functions: Let's analyze these two functions:
2. **erosion:** -# **erosion:**
@code{.cpp} @code{.cpp}
/* @function Erosion */ /* @function Erosion */
void Erosion( int, void* ) void Erosion( int, void* )
...@@ -124,7 +124,7 @@ Explanation ...@@ -124,7 +124,7 @@ Explanation
(iterations) at once. We are not using it in this simple tutorial, though. You can check out the (iterations) at once. We are not using it in this simple tutorial, though. You can check out the
Reference for more details. Reference for more details.
3. **dilation:** -# **dilation:**
The code is below. As you can see, it is completely similar to the snippet of code for **erosion**. 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 Here we also have the option of defining our kernel, its anchor point and the size of the operator
...@@ -152,10 +152,10 @@ Results ...@@ -152,10 +152,10 @@ Results
Compile the code above and execute it with an image as argument. For instance, using this image: Compile the code above and execute it with an image as argument. For instance, using this image:
![image](images/Morphology_1_Tutorial_Original_Image.jpg) ![](images/Morphology_1_Tutorial_Original_Image.jpg)
We get the results below. Varying the indices in the Trackbars give different output images, 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 naturally. Try them out! You can even try to add a third Trackbar to control the number of
iterations. iterations.
![image](images/Morphology_1_Result.jpg) ![](images/Morphology_1_Result.jpg)
...@@ -56,17 +56,15 @@ enumeratevisibleitemswithsquare ...@@ -56,17 +56,15 @@ enumeratevisibleitemswithsquare
produce the output array. produce the output array.
- Just to make the picture clearer, remember how a 1D Gaussian kernel look like? - Just to make the picture clearer, remember how a 1D Gaussian kernel look like?
![image](images/Smoothing_Tutorial_theory_gaussian_0.jpg) ![](images/Smoothing_Tutorial_theory_gaussian_0.jpg)
Assuming that an image is 1D, you can notice that the pixel located in the middle would have the 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 biggest weight. The weight of its neighbors decreases as the spatial distance between them and
the center pixel increases. the center pixel increases.
@note @note
Remember that a 2D Gaussian can be represented as : 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] \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\f$ represents the variance (per each of the where \f$\mu\f$ is the mean (the peak) and \f$\sigma\f$ represents the variance (per each of the
variables \f$x\f$ and \f$y\f$) variables \f$x\f$ and \f$y\f$)
...@@ -188,12 +186,13 @@ int display_dst( int delay ); ...@@ -188,12 +186,13 @@ int display_dst( int delay );
return 0; return 0;
} }
@endcode @endcode
Explanation Explanation
----------- -----------
1. Let's check the OpenCV functions that involve only the smoothing procedure, since the rest is -# Let's check the OpenCV functions that involve only the smoothing procedure, since the rest is
already known by now. already known by now.
2. **Normalized Block Filter:** -# **Normalized Block Filter:**
OpenCV offers the function @ref cv::blur to perform smoothing with this filter. OpenCV offers the function @ref cv::blur to perform smoothing with this filter.
@code{.cpp} @code{.cpp}
...@@ -211,7 +210,7 @@ Explanation ...@@ -211,7 +210,7 @@ Explanation
respect to the neighborhood. If there is a negative value, then the center of the kernel is respect to the neighborhood. If there is a negative value, then the center of the kernel is
considered the anchor point. considered the anchor point.
3. **Gaussian Filter:** -# **Gaussian Filter:**
It is performed by the function @ref cv::GaussianBlur : It is performed by the function @ref cv::GaussianBlur :
@code{.cpp} @code{.cpp}
...@@ -231,7 +230,7 @@ Explanation ...@@ -231,7 +230,7 @@ Explanation
- \f$\sigma_{y}\f$: The standard deviation in y. Writing \f$0\f$ implies that \f$\sigma_{y}\f$ is - \f$\sigma_{y}\f$: The standard deviation in y. Writing \f$0\f$ implies that \f$\sigma_{y}\f$ is
calculated using kernel size. calculated using kernel size.
4. **Median Filter:** -# **Median Filter:**
This filter is provided by the @ref cv::medianBlur function: This filter is provided by the @ref cv::medianBlur function:
@code{.cpp} @code{.cpp}
...@@ -245,7 +244,7 @@ Explanation ...@@ -245,7 +244,7 @@ Explanation
- *dst*: Destination image, must be the same type as *src* - *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. - *i*: Size of the kernel (only one because we use a square window). Must be odd.
5. **Bilateral Filter** -# **Bilateral Filter**
Provided by OpenCV function @ref cv::bilateralFilter Provided by OpenCV function @ref cv::bilateralFilter
@code{.cpp} @code{.cpp}
...@@ -268,6 +267,4 @@ Results ...@@ -268,6 +267,4 @@ Results
filters explained. filters explained.
- Here is a snapshot of the image smoothed using *medianBlur*: - Here is a snapshot of the image smoothed using *medianBlur*:
![image](images/Smoothing_Tutorial_Result_Median_Filter.jpg) ![](images/Smoothing_Tutorial_Result_Median_Filter.jpg)
...@@ -28,17 +28,14 @@ Theory ...@@ -28,17 +28,14 @@ Theory
- Let's say you have gotten a skin histogram (Hue-Saturation) based on the image below. The - 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 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: skin tonality). You applied some mask to capture only the histogram of the skin area:
![T0](images/Back_Projection_Theory0.jpg)
------ ------ ![T1](images/Back_Projection_Theory1.jpg)
|T0| |T1|
------ ------
- Now, let's imagine that you get another hand image (Test Image) like the one below: (with its - Now, let's imagine that you get another hand image (Test Image) like the one below: (with its
respective histogram): respective histogram):
![T2](images/Back_Projection_Theory2.jpg)
![T3](images/Back_Projection_Theory3.jpg)
------ ------
|T2| |T3|
------ ------
- What we want to do is to use our *model histogram* (that we know represents a skin tonality) to - 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 detect skin areas in our Test Image. Here are the steps
...@@ -50,7 +47,7 @@ Theory ...@@ -50,7 +47,7 @@ Theory
the *model histogram* first, so the output for the Test Image can be visible for you. 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: -# Applying the steps above, we get the following BackProjection image for our Test Image:
![image](images/Back_Projection_Theory4.jpg) ![](images/Back_Projection_Theory4.jpg)
-# In terms of statistics, the values stored in *BackProjection* represent the *probability* -# 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 that a pixel in *Test Image* belongs to a skin area, based on the *model histogram* that we
...@@ -83,98 +80,23 @@ Code ...@@ -83,98 +80,23 @@ Code
in samples. in samples.
- **Code at glance:** - **Code at glance:**
@code{.cpp} @includelineno samples/cpp/tutorial_code/Histograms_Matching/calcBackProject_Demo1.cpp
#include "opencv2/imgproc.hpp"
#include "opencv2/highgui.hpp"
#include <iostream>
using namespace cv;
using namespace std;
/// Global Variables
Mat src; Mat hsv; Mat hue;
int bins = 25;
/// Function Headers
void Hist_and_Backproj(int, void* );
/* @function main */
int main( int argc, char** argv )
{
/// Read the image
src = imread( argv[1], 1 );
/// Transform it to HSV
cvtColor( src, hsv, COLOR_BGR2HSV );
/// Use only the Hue value
hue.create( hsv.size(), hsv.depth() );
int ch[] = { 0, 0 };
mixChannels( &hsv, 1, &hue, 1, ch, 1 );
/// Create Trackbar to enter the number of bins
char* window_image = "Source image";
namedWindow( window_image, WINDOW_AUTOSIZE );
createTrackbar("* Hue bins: ", window_image, &bins, 180, Hist_and_Backproj );
Hist_and_Backproj(0, 0);
/// Show the image
imshow( window_image, src );
/// Wait until user exits the program
waitKey(0);
return 0;
}
/*
* @function Hist_and_Backproj
* @brief Callback to Trackbar
*/
void Hist_and_Backproj(int, void* )
{
MatND hist;
int histSize = MAX( bins, 2 );
float hue_range[] = { 0, 180 };
const float* ranges = { hue_range };
/// Get the Histogram and normalize it
calcHist( &hue, 1, 0, Mat(), hist, 1, &histSize, &ranges, true, false );
normalize( hist, hist, 0, 255, NORM_MINMAX, -1, Mat() );
/// Get Backprojection
MatND backproj;
calcBackProject( &hue, 1, 0, hist, backproj, &ranges, 1, true );
/// Draw the backproj
imshow( "BackProj", backproj );
/// Draw the histogram
int w = 400; int h = 400;
int bin_w = cvRound( (double) w / histSize );
Mat histImg = Mat::zeros( w, h, CV_8UC3 );
for( int i = 0; i < bins; i ++ )
{ rectangle( histImg, Point( i*bin_w, h ), Point( (i+1)*bin_w, h - cvRound( hist.at<float>(i)*h/255.0 ) ), Scalar( 0, 0, 255 ), -1 ); }
imshow( "Histogram", histImg );
}
@endcode
Explanation Explanation
----------- -----------
1. Declare the matrices to store our images and initialize the number of bins to be used by our -# Declare the matrices to store our images and initialize the number of bins to be used by our
histogram: histogram:
@code{.cpp} @code{.cpp}
Mat src; Mat hsv; Mat hue; Mat src; Mat hsv; Mat hue;
int bins = 25; int bins = 25;
@endcode @endcode
2. Read the input image and transform it to HSV format: -# Read the input image and transform it to HSV format:
@code{.cpp} @code{.cpp}
src = imread( argv[1], 1 ); src = imread( argv[1], 1 );
cvtColor( src, hsv, COLOR_BGR2HSV ); cvtColor( src, hsv, COLOR_BGR2HSV );
@endcode @endcode
3. For this tutorial, we will use only the Hue value for our 1-D histogram (check out the fancier -# 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 code in the links above if you want to use the more standard H-S histogram, which yields better
results): results):
@code{.cpp} @code{.cpp}
...@@ -182,7 +104,7 @@ Explanation ...@@ -182,7 +104,7 @@ Explanation
int ch[] = { 0, 0 }; int ch[] = { 0, 0 };
mixChannels( &hsv, 1, &hue, 1, ch, 1 ); mixChannels( &hsv, 1, &hue, 1, ch, 1 );
@endcode @endcode
as you see, we use the function :mix_channels:mixChannels to get only the channel 0 (Hue) from 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: the hsv image. It gets the following parameters:
- **&hsv:** The source array from which the channels will be copied - **&hsv:** The source array from which the channels will be copied
...@@ -193,7 +115,7 @@ Explanation ...@@ -193,7 +115,7 @@ Explanation
case, the Hue(0) channel of &hsv is being copied to the 0 channel of &hue (1-channel) case, the Hue(0) channel of &hsv is being copied to the 0 channel of &hue (1-channel)
- **1:** Number of index pairs - **1:** Number of index pairs
4. Create a Trackbar for the user to enter the bin values. Any change on the Trackbar means a call -# 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. to the **Hist_and_Backproj** callback function.
@code{.cpp} @code{.cpp}
char* window_image = "Source image"; char* window_image = "Source image";
...@@ -201,14 +123,14 @@ Explanation ...@@ -201,14 +123,14 @@ Explanation
createTrackbar("* Hue bins: ", window_image, &bins, 180, Hist_and_Backproj ); createTrackbar("* Hue bins: ", window_image, &bins, 180, Hist_and_Backproj );
Hist_and_Backproj(0, 0); Hist_and_Backproj(0, 0);
@endcode @endcode
5. Show the image and wait for the user to exit the program: -# Show the image and wait for the user to exit the program:
@code{.cpp} @code{.cpp}
imshow( window_image, src ); imshow( window_image, src );
waitKey(0); waitKey(0);
return 0; return 0;
@endcode @endcode
6. **Hist_and_Backproj function:** Initialize the arguments needed for @ref cv::calcHist . The -# **Hist_and_Backproj function:** Initialize the arguments needed for @ref cv::calcHist . The
number of bins comes from the Trackbar: number of bins comes from the Trackbar:
@code{.cpp} @code{.cpp}
void Hist_and_Backproj(int, void* ) void Hist_and_Backproj(int, void* )
...@@ -218,12 +140,12 @@ Explanation ...@@ -218,12 +140,12 @@ Explanation
float hue_range[] = { 0, 180 }; float hue_range[] = { 0, 180 };
const float* ranges = { hue_range }; const float* ranges = { hue_range };
@endcode @endcode
7. Calculate the Histogram and normalize it to the range \f$[0,255]\f$ -# Calculate the Histogram and normalize it to the range \f$[0,255]\f$
@code{.cpp} @code{.cpp}
calcHist( &hue, 1, 0, Mat(), hist, 1, &histSize, &ranges, true, false ); calcHist( &hue, 1, 0, Mat(), hist, 1, &histSize, &ranges, true, false );
normalize( hist, hist, 0, 255, NORM_MINMAX, -1, Mat() ); normalize( hist, hist, 0, 255, NORM_MINMAX, -1, Mat() );
@endcode @endcode
8. Get the Backprojection of the same image by calling the function @ref cv::calcBackProject -# Get the Backprojection of the same image by calling the function @ref cv::calcBackProject
@code{.cpp} @code{.cpp}
MatND backproj; MatND backproj;
calcBackProject( &hue, 1, 0, hist, backproj, &ranges, 1, true ); calcBackProject( &hue, 1, 0, hist, backproj, &ranges, 1, true );
...@@ -231,11 +153,11 @@ Explanation ...@@ -231,11 +153,11 @@ Explanation
all the arguments are known (the same as used to calculate the histogram), only we add the 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) backproj matrix, which will store the backprojection of the source image (&hue)
9. Display backproj: -# Display backproj:
@code{.cpp} @code{.cpp}
imshow( "BackProj", backproj ); imshow( "BackProj", backproj );
@endcode @endcode
10. Draw the 1-D Hue histogram of the image: -# Draw the 1-D Hue histogram of the image:
@code{.cpp} @code{.cpp}
int w = 400; int h = 400; int w = 400; int h = 400;
int bin_w = cvRound( (double) w / histSize ); int bin_w = cvRound( (double) w / histSize );
...@@ -246,12 +168,12 @@ Explanation ...@@ -246,12 +168,12 @@ Explanation
imshow( "Histogram", histImg ); imshow( "Histogram", histImg );
@endcode @endcode
Results Results
------- -------
1. Here are the output by using a sample image ( guess what? Another hand ). You can play with the 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: bin values and you will observe how it affects the results:
![R0](images/Back_Projection1_Source_Image.jpg)
------ ------ ------ ![R1](images/Back_Projection1_Histogram.jpg)
|R0| |R1| |R2| ![R2](images/Back_Projection1_BackProj.jpg)
------ ------ ------
...@@ -21,7 +21,7 @@ histogram called *Image histogram*. Now we will considerate it in its more gener ...@@ -21,7 +21,7 @@ histogram called *Image histogram*. Now we will considerate it in its more gener
- Let's see an example. Imagine that a Matrix contains information of an image (i.e. intensity in - Let's see an example. Imagine that a Matrix contains information of an image (i.e. intensity in
the range \f$0-255\f$): the range \f$0-255\f$):
![image](images/Histogram_Calculation_Theory_Hist0.jpg) ![](images/Histogram_Calculation_Theory_Hist0.jpg)
- What happens if we want to *count* this data in an organized way? Since we know that the *range* - 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 of information value for this case is 256 values, we can segment our range in subparts (called
...@@ -36,7 +36,7 @@ histogram called *Image histogram*. Now we will considerate it in its more gener ...@@ -36,7 +36,7 @@ histogram called *Image histogram*. Now we will considerate it in its more gener
this to the example above we get the image below ( axis x represents the bins and axis y the 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). number of pixels in each of them).
![image](images/Histogram_Calculation_Theory_Hist1.jpg) ![](images/Histogram_Calculation_Theory_Hist1.jpg)
- This was just a simple example of how an histogram works and why it is useful. An histogram can - 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 keep count not only of color intensities, but of whatever image features that we want to measure
...@@ -73,18 +73,18 @@ Code ...@@ -73,18 +73,18 @@ Code
Explanation Explanation
----------- -----------
1. Create the necessary matrices: -# Create the necessary matrices:
@code{.cpp} @code{.cpp}
Mat src, dst; Mat src, dst;
@endcode @endcode
2. Load the source image -# Load the source image
@code{.cpp} @code{.cpp}
src = imread( argv[1], 1 ); src = imread( argv[1], 1 );
if( !src.data ) if( !src.data )
{ return -1; } { return -1; }
@endcode @endcode
3. Separate the source image in its three R,G and B planes. For this we use the OpenCV function -# Separate the source image in its three R,G and B planes. For this we use the OpenCV function
@ref cv::split : @ref cv::split :
@code{.cpp} @code{.cpp}
vector<Mat> bgr_planes; vector<Mat> bgr_planes;
...@@ -93,7 +93,7 @@ Explanation ...@@ -93,7 +93,7 @@ Explanation
our input is the image to be divided (this case with three channels) and the output is a vector our input is the image to be divided (this case with three channels) and the output is a vector
of Mat ) of Mat )
4. Now we are ready to start configuring the **histograms** for each plane. Since we are working -# 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$ with the B, G and R planes, we know that our values will range in the interval \f$[0,255]\f$
-# Establish number of bins (5, 10...): -# Establish number of bins (5, 10...):
@code{.cpp} @code{.cpp}
...@@ -137,7 +137,7 @@ Explanation ...@@ -137,7 +137,7 @@ Explanation
- **uniform** and **accumulate**: The bin sizes are the same and the histogram is cleared - **uniform** and **accumulate**: The bin sizes are the same and the histogram is cleared
at the beginning. at the beginning.
5. Create an image to display the histograms: -# Create an image to display the histograms:
@code{.cpp} @code{.cpp}
// Draw the histograms for R, G and B // Draw the histograms for R, G and B
int hist_w = 512; int hist_h = 400; int hist_w = 512; int hist_h = 400;
...@@ -145,7 +145,7 @@ Explanation ...@@ -145,7 +145,7 @@ Explanation
Mat histImage( hist_h, hist_w, CV_8UC3, Scalar( 0,0,0) ); Mat histImage( hist_h, hist_w, CV_8UC3, Scalar( 0,0,0) );
@endcode @endcode
6. Notice that before drawing, we first @ref cv::normalize the histogram so its values fall in the -# Notice that before drawing, we first @ref cv::normalize the histogram so its values fall in the
range indicated by the parameters entered: range indicated by the parameters entered:
@code{.cpp} @code{.cpp}
/// Normalize the result to [ 0, histImage.rows ] /// Normalize the result to [ 0, histImage.rows ]
...@@ -164,7 +164,7 @@ Explanation ...@@ -164,7 +164,7 @@ Explanation
- **-1:** Implies that the output normalized array will be the same type as the input - **-1:** Implies that the output normalized array will be the same type as the input
- **Mat():** Optional mask - **Mat():** Optional mask
7. Finally, observe that to access the bin (in this case in this 1D-Histogram): -# Finally, observe that to access the bin (in this case in this 1D-Histogram):
@code{.cpp} @code{.cpp}
/// Draw for each channel /// Draw for each channel
for( int i = 1; i < histSize; i++ ) for( int i = 1; i < histSize; i++ )
...@@ -189,7 +189,7 @@ Explanation ...@@ -189,7 +189,7 @@ Explanation
b_hist.at<float>( i, j ) b_hist.at<float>( i, j )
@endcode @endcode
8. Finally we display our histograms and wait for the user to exit: -# Finally we display our histograms and wait for the user to exit:
@code{.cpp} @code{.cpp}
namedWindow("calcHist Demo", WINDOW_AUTOSIZE ); namedWindow("calcHist Demo", WINDOW_AUTOSIZE );
imshow("calcHist Demo", histImage ); imshow("calcHist Demo", histImage );
...@@ -202,10 +202,10 @@ Explanation ...@@ -202,10 +202,10 @@ Explanation
Result Result
------ ------
1. Using as input argument an image like the shown below: -# Using as input argument an image like the shown below:
![image](images/Histogram_Calculation_Original_Image.jpg) ![](images/Histogram_Calculation_Original_Image.jpg)
2. Produces the following histogram: -# Produces the following histogram:
![image](images/Histogram_Calculation_Result.jpg) ![](images/Histogram_Calculation_Result.jpg)
...@@ -18,25 +18,18 @@ Theory ...@@ -18,25 +18,18 @@ Theory
- OpenCV implements the function @ref cv::compareHist to perform a comparison. It also offers 4 - OpenCV implements the function @ref cv::compareHist to perform a comparison. It also offers 4
different metrics to compute the matching: different metrics to compute the matching:
-# **Correlation ( CV_COMP_CORREL )** -# **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] \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 where
\f[\bar{H_k} = \frac{1}{N} \sum _J H_k(J)\f] \f[\bar{H_k} = \frac{1}{N} \sum _J H_k(J)\f]
and \f$N\f$ is the total number of histogram bins. and \f$N\f$ is the total number of histogram bins.
-# **Chi-Square ( CV_COMP_CHISQR )** -# **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] \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 )** -# **Intersection ( method=CV_COMP_INTERSECT )**
\f[d(H_1,H_2) = \sum _I \min (H_1(I), H_2(I))\f] \f[d(H_1,H_2) = \sum _I \min (H_1(I), H_2(I))\f]
-# **Bhattacharyya distance ( CV_COMP_BHATTACHARYYA )** -# **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] \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 Code
...@@ -59,7 +52,7 @@ Code ...@@ -59,7 +52,7 @@ Code
Explanation Explanation
----------- -----------
1. Declare variables such as the matrices to store the base image and the two other images to -# Declare variables such as the matrices to store the base image and the two other images to
compare ( RGB and HSV ) compare ( RGB and HSV )
@code{.cpp} @code{.cpp}
Mat src_base, hsv_base; Mat src_base, hsv_base;
...@@ -67,7 +60,7 @@ Explanation ...@@ -67,7 +60,7 @@ Explanation
Mat src_test2, hsv_test2; Mat src_test2, hsv_test2;
Mat hsv_half_down; Mat hsv_half_down;
@endcode @endcode
2. Load the base image (src_base) and the other two test images: -# Load the base image (src_base) and the other two test images:
@code{.cpp} @code{.cpp}
if( argc < 4 ) if( argc < 4 )
{ printf("** Error. Usage: ./compareHist_Demo <image_settings0> <image_setting1> <image_settings2>\n"); { printf("** Error. Usage: ./compareHist_Demo <image_settings0> <image_setting1> <image_settings2>\n");
...@@ -78,17 +71,17 @@ Explanation ...@@ -78,17 +71,17 @@ Explanation
src_test1 = imread( argv[2], 1 ); src_test1 = imread( argv[2], 1 );
src_test2 = imread( argv[3], 1 ); src_test2 = imread( argv[3], 1 );
@endcode @endcode
3. Convert them to HSV format: -# Convert them to HSV format:
@code{.cpp} @code{.cpp}
cvtColor( src_base, hsv_base, COLOR_BGR2HSV ); cvtColor( src_base, hsv_base, COLOR_BGR2HSV );
cvtColor( src_test1, hsv_test1, COLOR_BGR2HSV ); cvtColor( src_test1, hsv_test1, COLOR_BGR2HSV );
cvtColor( src_test2, hsv_test2, COLOR_BGR2HSV ); cvtColor( src_test2, hsv_test2, COLOR_BGR2HSV );
@endcode @endcode
4. Also, create an image of half the base image (in HSV format): -# Also, create an image of half the base image (in HSV format):
@code{.cpp} @code{.cpp}
hsv_half_down = hsv_base( Range( hsv_base.rows/2, hsv_base.rows - 1 ), Range( 0, hsv_base.cols - 1 ) ); hsv_half_down = hsv_base( Range( hsv_base.rows/2, hsv_base.rows - 1 ), Range( 0, hsv_base.cols - 1 ) );
@endcode @endcode
5. Initialize the arguments to calculate the histograms (bins, ranges and channels H and S ). -# Initialize the arguments to calculate the histograms (bins, ranges and channels H and S ).
@code{.cpp} @code{.cpp}
int h_bins = 50; int s_bins = 60; int h_bins = 50; int s_bins = 60;
int histSize[] = { h_bins, s_bins }; int histSize[] = { h_bins, s_bins };
...@@ -100,14 +93,14 @@ Explanation ...@@ -100,14 +93,14 @@ Explanation
int channels[] = { 0, 1 }; int channels[] = { 0, 1 };
@endcode @endcode
6. Create the MatND objects to store the histograms: -# Create the MatND objects to store the histograms:
@code{.cpp} @code{.cpp}
MatND hist_base; MatND hist_base;
MatND hist_half_down; MatND hist_half_down;
MatND hist_test1; MatND hist_test1;
MatND hist_test2; MatND hist_test2;
@endcode @endcode
7. Calculate the Histograms for the base image, the 2 test images and the half-down base image: -# Calculate the Histograms for the base image, the 2 test images and the half-down base image:
@code{.cpp} @code{.cpp}
calcHist( &hsv_base, 1, channels, Mat(), hist_base, 2, histSize, ranges, true, false ); calcHist( &hsv_base, 1, channels, Mat(), hist_base, 2, histSize, ranges, true, false );
normalize( hist_base, hist_base, 0, 1, NORM_MINMAX, -1, Mat() ); normalize( hist_base, hist_base, 0, 1, NORM_MINMAX, -1, Mat() );
...@@ -121,7 +114,7 @@ Explanation ...@@ -121,7 +114,7 @@ Explanation
calcHist( &hsv_test2, 1, channels, Mat(), hist_test2, 2, histSize, ranges, true, false ); calcHist( &hsv_test2, 1, channels, Mat(), hist_test2, 2, histSize, ranges, true, false );
normalize( hist_test2, hist_test2, 0, 1, NORM_MINMAX, -1, Mat() ); normalize( hist_test2, hist_test2, 0, 1, NORM_MINMAX, -1, Mat() );
@endcode @endcode
8. Apply sequentially the 4 comparison methods between the histogram of the base image (hist_base) -# Apply sequentially the 4 comparison methods between the histogram of the base image (hist_base)
and the other histograms: and the other histograms:
@code{.cpp} @code{.cpp}
for( int i = 0; i < 4; i++ ) for( int i = 0; i < 4; i++ )
...@@ -134,34 +127,32 @@ Explanation ...@@ -134,34 +127,32 @@ Explanation
printf( " Method [%d] Perfect, Base-Half, Base-Test(1), Base-Test(2) : %f, %f, %f, %f \n", i, base_base, base_half , base_test1, base_test2 ); printf( " Method [%d] Perfect, Base-Half, Base-Test(1), Base-Test(2) : %f, %f, %f, %f \n", i, base_base, base_half , base_test1, base_test2 );
} }
@endcode @endcode
Results Results
------- -------
1. We use as input the following images: -# We use as input the following images:
![Base_0](images/Histogram_Comparison_Source_0.jpg)
----------- ----------- ----------- ![Test_1](images/Histogram_Comparison_Source_1.jpg)
|Base_0| |Test_1| |Test_2| ![Test_2](images/Histogram_Comparison_Source_2.jpg)
----------- ----------- -----------
where the first one is the base (to be compared to the others), the other 2 are the test 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 We will also compare the first image with respect to itself and with respect of half the base
image. image.
2. We should expect a perfect match when we compare the base image histogram with itself. Also, -# 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 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 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: different lighting conditions, so the matching should not be very good:
3. Here the numeric results:
-# Here the numeric results:
*Method* Base - Base Base - Half Base - Test 1 Base - Test 2 *Method* | Base - Base | Base - Half | Base - Test 1 | Base - Test 2
----------------- ------------- ------------- --------------- --------------- ----------------- | ------------ | ------------ | -------------- | ---------------
*Correlation* 1.000000 0.930766 0.182073 0.120447 *Correlation* | 1.000000 | 0.930766 | 0.182073 | 0.120447
*Chi-square* 0.000000 4.940466 21.184536 49.273437 *Chi-square* | 0.000000 | 4.940466 | 21.184536 | 49.273437
*Intersection* 24.391548 14.959809 3.889029 5.775088 *Intersection* | 24.391548 | 14.959809 | 3.889029 | 5.775088
*Bhattacharyya* 0.000000 0.222609 0.646576 0.801869 *Bhattacharyya* | 0.000000 | 0.222609 | 0.646576 | 0.801869
For the *Correlation* and *Intersection* methods, the higher the metric, the more accurate the
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
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,
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
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.
test 2 with respect to the base are worse, which again, was expected.
...@@ -17,7 +17,7 @@ Theory ...@@ -17,7 +17,7 @@ Theory
- It is a graphical representation of the intensity distribution of an image. - It is a graphical representation of the intensity distribution of an image.
- It quantifies the number of pixels for each intensity value considered. - It quantifies the number of pixels for each intensity value considered.
![image](images/Histogram_Equalization_Theory_0.jpg) ![](images/Histogram_Equalization_Theory_0.jpg)
### What is Histogram Equalization? ### What is Histogram Equalization?
...@@ -29,7 +29,7 @@ Theory ...@@ -29,7 +29,7 @@ Theory
*underpopulated* intensities. After applying the equalization, we get an histogram like 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. figure in the center. The resulting image is shown in the picture at right.
![image](images/Histogram_Equalization_Theory_1.jpg) ![](images/Histogram_Equalization_Theory_1.jpg)
### How does it work? ### How does it work?
...@@ -46,7 +46,7 @@ Theory ...@@ -46,7 +46,7 @@ Theory
is 255 ( or the maximum value for the intensity of the image ). From the example above, the is 255 ( or the maximum value for the intensity of the image ). From the example above, the
cumulative function is: cumulative function is:
![image](images/Histogram_Equalization_Theory_2.jpg) ![](images/Histogram_Equalization_Theory_2.jpg)
- Finally, we use a simple remapping procedure to obtain the intensity values of the equalized - Finally, we use a simple remapping procedure to obtain the intensity values of the equalized
image: image:
...@@ -69,14 +69,14 @@ Code ...@@ -69,14 +69,14 @@ Code
Explanation Explanation
----------- -----------
1. Declare the source and destination images as well as the windows names: -# Declare the source and destination images as well as the windows names:
@code{.cpp} @code{.cpp}
Mat src, dst; Mat src, dst;
char* source_window = "Source image"; char* source_window = "Source image";
char* equalized_window = "Equalized Image"; char* equalized_window = "Equalized Image";
@endcode @endcode
2. Load the source image: -# Load the source image:
@code{.cpp} @code{.cpp}
src = imread( argv[1], 1 ); src = imread( argv[1], 1 );
...@@ -84,18 +84,18 @@ Explanation ...@@ -84,18 +84,18 @@ Explanation
{ cout<<"Usage: ./Histogram_Demo <path_to_image>"<<endl; { cout<<"Usage: ./Histogram_Demo <path_to_image>"<<endl;
return -1;} return -1;}
@endcode @endcode
3. Convert it to grayscale: -# Convert it to grayscale:
@code{.cpp} @code{.cpp}
cvtColor( src, src, COLOR_BGR2GRAY ); cvtColor( src, src, COLOR_BGR2GRAY );
@endcode @endcode
4. Apply histogram equalization with the function @ref cv::equalizeHist : -# Apply histogram equalization with the function @ref cv::equalizeHist :
@code{.cpp} @code{.cpp}
equalizeHist( src, dst ); equalizeHist( src, dst );
@endcode @endcode
As it can be easily seen, the only arguments are the original image and the output (equalized) As it can be easily seen, the only arguments are the original image and the output (equalized)
image. image.
5. Display both images (original and equalized) : -# Display both images (original and equalized) :
@code{.cpp} @code{.cpp}
namedWindow( source_window, WINDOW_AUTOSIZE ); namedWindow( source_window, WINDOW_AUTOSIZE );
namedWindow( equalized_window, WINDOW_AUTOSIZE ); namedWindow( equalized_window, WINDOW_AUTOSIZE );
...@@ -103,7 +103,7 @@ Explanation ...@@ -103,7 +103,7 @@ Explanation
imshow( source_window, src ); imshow( source_window, src );
imshow( equalized_window, dst ); imshow( equalized_window, dst );
@endcode @endcode
6. Wait until user exists the program -# Wait until user exists the program
@code{.cpp} @code{.cpp}
waitKey(0); waitKey(0);
return 0; return 0;
...@@ -112,24 +112,24 @@ Explanation ...@@ -112,24 +112,24 @@ Explanation
Results Results
------- -------
1. To appreciate better the results of equalization, let's introduce an image with not much -# To appreciate better the results of equalization, let's introduce an image with not much
contrast, such as: contrast, such as:
![image](images/Histogram_Equalization_Original_Image.jpg) ![](images/Histogram_Equalization_Original_Image.jpg)
which, by the way, has this histogram: which, by the way, has this histogram:
![image](images/Histogram_Equalization_Original_Histogram.jpg) ![](images/Histogram_Equalization_Original_Histogram.jpg)
notice that the pixels are clustered around the center of the histogram. notice that the pixels are clustered around the center of the histogram.
2. After applying the equalization with our program, we get this result: -# After applying the equalization with our program, we get this result:
![image](images/Histogram_Equalization_Equalized_Image.jpg) ![](images/Histogram_Equalization_Equalized_Image.jpg)
this image has certainly more contrast. Check out its new histogram like this: this image has certainly more contrast. Check out its new histogram like this:
![image](images/Histogram_Equalization_Equalized_Histogram.jpg) ![](images/Histogram_Equalization_Equalized_Histogram.jpg)
Notice how the number of pixels is more distributed through the intensity range. Notice how the number of pixels is more distributed through the intensity range.
......
...@@ -20,7 +20,7 @@ The *Canny Edge detector* was developed by John F. Canny in 1986. Also known to ...@@ -20,7 +20,7 @@ The *Canny Edge detector* was developed by John F. Canny in 1986. Also known to
### Steps ### 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: kernel of \f$size = 5\f$ that might be used is shown below:
\f[K = \dfrac{1}{159}\begin{bmatrix} \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 ...@@ -31,8 +31,8 @@ The *Canny Edge detector* was developed by John F. Canny in 1986. Also known to
2 & 4 & 5 & 4 & 2 2 & 4 & 5 & 4 & 2
\end{bmatrix}\f] \end{bmatrix}\f]
2. Find the intensity gradient of the image. For this, we follow a procedure analogous to Sobel: -# 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: -# Apply a pair of convolution masks (in \f$x\f$ and \f$y\f$ directions:
\f[G_{x} = \begin{bmatrix} \f[G_{x} = \begin{bmatrix}
-1 & 0 & +1 \\ -1 & 0 & +1 \\
-2 & 0 & +2 \\ -2 & 0 & +2 \\
...@@ -43,44 +43,44 @@ The *Canny Edge detector* was developed by John F. Canny in 1986. Also known to ...@@ -43,44 +43,44 @@ The *Canny Edge detector* was developed by John F. Canny in 1986. Also known to
+1 & +2 & +1 +1 & +2 & +1
\end{bmatrix}\f] \end{bmatrix}\f]
2. Find the gradient strength and direction with: -# Find the gradient strength and direction with:
\f[\begin{array}{l} \f[\begin{array}{l}
G = \sqrt{ G_{x}^{2} + G_{y}^{2} } \\ G = \sqrt{ G_{x}^{2} + G_{y}^{2} } \\
\theta = \arctan(\dfrac{ G_{y} }{ G_{x} }) \theta = \arctan(\dfrac{ G_{y} }{ G_{x} })
\end{array}\f] \end{array}\f]
The direction is rounded to one of four possible angles (namely 0, 45, 90 or 135) 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. 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 -# 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. -# 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 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. connected to a pixel that is above the *upper* threshold.
Canny recommended a *upper*:*lower* ratio between 2:1 and 3:1. 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 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 - Asks the user to enter a numerical value to set the lower threshold for our *Canny Edge
Detector* (by means of a Trackbar) Detector* (by means of a Trackbar)
- Applies the *Canny Detector* and generates a **mask** (bright lines representing the edges - Applies the *Canny Detector* and generates a **mask** (bright lines representing the edges
on a black background). on a black background).
- Applies the mask obtained on the original image and display it in a window. - 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) [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 @includelineno samples/cpp/tutorial_code/ImgTrans/CannyDetector_Demo.cpp
Explanation Explanation
----------- -----------
1. Create some needed variables: -# Create some needed variables:
@code{.cpp} @code{.cpp}
Mat src, src_gray; Mat src, src_gray;
Mat dst, detected_edges; Mat dst, detected_edges;
...@@ -94,12 +94,12 @@ Explanation ...@@ -94,12 +94,12 @@ Explanation
@endcode @endcode
Note the following: Note the following:
1. We establish a ratio of lower:upper threshold of 3:1 (with the variable *ratio*) -# 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 set the kernel size of \f$3\f$ (for the Sobel operations to be performed internally by the
Canny function) 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} @code{.cpp}
/// Load an image /// Load an image
src = imread( argv[1] ); src = imread( argv[1] );
...@@ -107,35 +107,35 @@ Explanation ...@@ -107,35 +107,35 @@ Explanation
if( !src.data ) if( !src.data )
{ return -1; } { return -1; }
@endcode @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} @code{.cpp}
dst.create( src.size(), src.type() ); dst.create( src.size(), src.type() );
@endcode @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} @code{.cpp}
cvtColor( src, src_gray, COLOR_BGR2GRAY ); cvtColor( src, src_gray, COLOR_BGR2GRAY );
@endcode @endcode
5. Create a window to display the results -# Create a window to display the results
@code{.cpp} @code{.cpp}
namedWindow( window_name, WINDOW_AUTOSIZE ); namedWindow( window_name, WINDOW_AUTOSIZE );
@endcode @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} @code{.cpp}
createTrackbar( "Min Threshold:", window_name, &lowThreshold, max_lowThreshold, CannyThreshold ); createTrackbar( "Min Threshold:", window_name, &lowThreshold, max_lowThreshold, CannyThreshold );
@endcode @endcode
Observe the following: 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) *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. invoked.
7. Let's check the *CannyThreshold* function, step by step: -# Let's check the *CannyThreshold* function, step by step:
1. First, we blur the image with a filter of kernel size 3: -# First, we blur the image with a filter of kernel size 3:
@code{.cpp} @code{.cpp}
blur( src_gray, detected_edges, Size(3,3) ); blur( src_gray, detected_edges, Size(3,3) );
@endcode @endcode
2. Second, we apply the OpenCV function @ref cv::Canny : -# Second, we apply the OpenCV function @ref cv::Canny :
@code{.cpp} @code{.cpp}
Canny( detected_edges, detected_edges, lowThreshold, lowThreshold*ratio, kernel_size ); Canny( detected_edges, detected_edges, lowThreshold, lowThreshold*ratio, kernel_size );
@endcode @endcode
...@@ -149,11 +149,11 @@ Explanation ...@@ -149,11 +149,11 @@ Explanation
- *kernel_size*: We defined it to be 3 (the size of the Sobel kernel to be used - *kernel_size*: We defined it to be 3 (the size of the Sobel kernel to be used
internally) 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} @code{.cpp}
dst = Scalar::all(0); dst = Scalar::all(0);
@endcode @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). identified as edges (on a black background).
@code{.cpp} @code{.cpp}
src.copyTo( dst, detected_edges); src.copyTo( dst, detected_edges);
...@@ -163,20 +163,21 @@ Explanation ...@@ -163,20 +163,21 @@ Explanation
contours on a black background, the resulting *dst* will be black in all the area but the contours on a black background, the resulting *dst* will be black in all the area but the
detected edges. detected edges.
10. We display our result: -# We display our result:
@code{.cpp} @code{.cpp}
imshow( window_name, dst ); imshow( window_name, dst );
@endcode @endcode
Result Result
------ ------
- 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 the following image: 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: - 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. - Notice how the image is superposed to the black background on the edge regions.
...@@ -14,14 +14,14 @@ Theory ...@@ -14,14 +14,14 @@ Theory
@note The explanation below belongs to the book **Learning OpenCV** by Bradski and Kaehler. @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 naturally arises is how to handle the boundaries. How can we convolve them if the evaluated
points are at the edge of the image? 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 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 below). This way, the convolution can be performed over the needed pixels without problems (the
extra padding is cut after the operation is done). 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: image:
-# **BORDER_CONSTANT**: Pad the image with a constant value (i.e. black or \f$0\f$ -# **BORDER_CONSTANT**: Pad the image with a constant value (i.e. black or \f$0\f$
...@@ -33,91 +33,26 @@ Theory ...@@ -33,91 +33,26 @@ Theory
Code Code
---- ----
1. **What does this program do?** -# **What does this program do?**
- Load an image - Load an image
- Let the user choose what kind of padding use in the input image. There are two options: - 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. 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 original image.
The user chooses either option by pressing 'c' (constant) or 'r' (replicate) The user chooses either option by pressing 'c' (constant) or 'r' (replicate)
- The program finishes when the user presses 'ESC' - 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) [here](https://github.com/Itseez/opencv/tree/master/samples/cpp/tutorial_code/ImgTrans/copyMakeBorder_demo.cpp)
@code{.cpp} @includelineno samples/cpp/tutorial_code/ImgTrans/copyMakeBorder_demo.cpp
#include "opencv2/imgproc.hpp"
#include "opencv2/highgui.hpp"
#include <stdlib.h>
#include <stdio.h>
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 Explanation
----------- -----------
1. First we declare the variables we are going to use: -# First we declare the variables we are going to use:
@code{.cpp} @code{.cpp}
Mat src, dst; Mat src, dst;
int top, bottom, left, right; int top, bottom, left, right;
...@@ -129,7 +64,7 @@ Explanation ...@@ -129,7 +64,7 @@ Explanation
Especial attention deserves the variable *rng* which is a random number generator. We use it to 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. 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} @code{.cpp}
src = imread( argv[1] ); src = imread( argv[1] );
...@@ -138,17 +73,17 @@ Explanation ...@@ -138,17 +73,17 @@ Explanation
printf(" No data entered, please enter the path to an image file \n"); printf(" No data entered, please enter the path to an image file \n");
} }
@endcode @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} @code{.cpp}
namedWindow( window_name, WINDOW_AUTOSIZE ); namedWindow( window_name, WINDOW_AUTOSIZE );
@endcode @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*. *right*). We give them a value of 5% the size of *src*.
@code{.cpp} @code{.cpp}
top = (int) (0.05*src.rows); bottom = (int) (0.05*src.rows); top = (int) (0.05*src.rows); bottom = (int) (0.05*src.rows);
left = (int) (0.05*src.cols); right = (int) (0.05*src.cols); left = (int) (0.05*src.cols); right = (int) (0.05*src.cols);
@endcode @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: takes the value of *BORDER_CONSTANT* or *BORDER_REPLICATE* respectively:
@code{.cpp} @code{.cpp}
while( true ) while( true )
...@@ -162,14 +97,14 @@ Explanation ...@@ -162,14 +97,14 @@ Explanation
else if( (char)c == 'r' ) else if( (char)c == 'r' )
{ borderType = BORDER_REPLICATE; } { borderType = BORDER_REPLICATE; }
@endcode @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} @code{.cpp}
value = Scalar( rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255) ); value = Scalar( rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255) );
@endcode @endcode
with a random value generated by the **RNG** variable *rng*. This value is a number picked with a random value generated by the **RNG** variable *rng*. This value is a number picked
randomly in the range \f$[0,255]\f$ 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} @code{.cpp}
copyMakeBorder( src, dst, top, bottom, left, right, borderType, value ); copyMakeBorder( src, dst, top, bottom, left, right, borderType, value );
@endcode @endcode
...@@ -184,14 +119,15 @@ Explanation ...@@ -184,14 +119,15 @@ Explanation
-# *value*: If *borderType* is *BORDER_CONSTANT*, this is the value used to fill the border -# *value*: If *borderType* is *BORDER_CONSTANT*, this is the value used to fill the border
pixels. pixels.
8. We display our output image in the image created previously -# We display our output image in the image created previously
@code{.cpp} @code{.cpp}
imshow( window_name, dst ); imshow( window_name, dst );
@endcode @endcode
Results 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: result should be:
- By default, it begins with the border set to BORDER_CONSTANT. Hence, a succession of random - By default, it begins with the border set to BORDER_CONSTANT. Hence, a succession of random
...@@ -203,4 +139,4 @@ Results ...@@ -203,4 +139,4 @@ Results
Below some screenshot showing how the border changes color and how the *BORDER_REPLICATE* Below some screenshot showing how the border changes color and how the *BORDER_REPLICATE*
option looks: option looks:
![image](images/CopyMakeBorder_Tutorial_Results.jpg) ![](images/CopyMakeBorder_Tutorial_Results.jpg)
...@@ -23,18 +23,18 @@ In a very general sense, convolution is an operation between every part of an im ...@@ -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 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. 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? ### 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 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: 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. corresponding local pixels in the image.
2. Multiply the kernel coefficients by the corresponding image pixel values and sum the result. -# 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. -# 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. -# 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: Expressing the procedure above in the form of an equation we would have:
...@@ -46,7 +46,7 @@ these operations. ...@@ -46,7 +46,7 @@ these operations.
Code Code
---- ----
1. **What does this program do?** -# **What does this program do?**
- Loads an image - Loads an image
- Performs a *normalized box filter*. For instance, for a kernel of size \f$size = 3\f$, the - Performs a *normalized box filter*. For instance, for a kernel of size \f$size = 3\f$, the
kernel would be: kernel would be:
...@@ -61,7 +61,7 @@ Code ...@@ -61,7 +61,7 @@ Code
- The filter output (with each kernel) will be shown during 500 milliseconds - 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) [here](https://github.com/Itseez/opencv/tree/master/samples/cpp/tutorial_code/ImgTrans/filter2D_demo.cpp)
@code{.cpp} @code{.cpp}
#include "opencv2/imgproc.hpp" #include "opencv2/imgproc.hpp"
...@@ -125,26 +125,26 @@ int main ( int argc, char** argv ) ...@@ -125,26 +125,26 @@ int main ( int argc, char** argv )
Explanation Explanation
----------- -----------
1. Load an image -# Load an image
@code{.cpp} @code{.cpp}
src = imread( argv[1] ); src = imread( argv[1] );
if( !src.data ) if( !src.data )
{ return -1; } { return -1; }
@endcode @endcode
2. Create a window to display the result -# Create a window to display the result
@code{.cpp} @code{.cpp}
namedWindow( window_name, WINDOW_AUTOSIZE ); namedWindow( window_name, WINDOW_AUTOSIZE );
@endcode @endcode
3. Initialize the arguments for the linear filter -# Initialize the arguments for the linear filter
@code{.cpp} @code{.cpp}
anchor = Point( -1, -1 ); anchor = Point( -1, -1 );
delta = 0; delta = 0;
ddepth = -1; ddepth = -1;
@endcode @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: 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} @code{.cpp}
kernel_size = 3 + 2*( ind%5 ); kernel_size = 3 + 2*( ind%5 );
kernel = Mat::ones( kernel_size, kernel_size, CV_32F )/ (float)(kernel_size*kernel_size); kernel = Mat::ones( kernel_size, kernel_size, CV_32F )/ (float)(kernel_size*kernel_size);
...@@ -153,7 +153,7 @@ Explanation ...@@ -153,7 +153,7 @@ Explanation
line actually builds the kernel by setting its value to a matrix filled with \f$1's\f$ and 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. 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} @code{.cpp}
filter2D(src, dst, ddepth , kernel, anchor, delta, BORDER_DEFAULT ); filter2D(src, dst, ddepth , kernel, anchor, delta, BORDER_DEFAULT );
@endcode @endcode
...@@ -169,14 +169,14 @@ Explanation ...@@ -169,14 +169,14 @@ Explanation
-# *delta*: A value to be added to each pixel during the convolution. By default it is \f$0\f$ -# *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) -# *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. updated in the range indicated.
Results 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 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: 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)
...@@ -23,7 +23,7 @@ Theory ...@@ -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, 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: 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 - 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 Hough Transform: *The Hough gradient method*, which is made up of two main stages. The first
...@@ -34,82 +34,35 @@ Theory ...@@ -34,82 +34,35 @@ Theory
Code Code
---- ----
1. **What does this program do?** -# **What does this program do?**
- Loads an image and blur it to reduce the noise - Loads an image and blur it to reduce the noise
- Applies the *Hough Circle Transform* to the blurred image . - Applies the *Hough Circle Transform* to the blurred image .
- Display the detected circle in a window. - Display the detected circle in a window.
2. The sample code that we will explain can be downloaded from -# The sample code that we will explain can be downloaded from [here](https://github.com/Itseez/opencv/tree/master/samples/cpp/houghcircles.cpp).
|TutorialHoughCirclesSimpleDownload|_. A slightly fancier version (which shows trackbars for A slightly fancier version (which shows trackbars for
changing the threshold values) can be found |TutorialHoughCirclesFancyDownload|_. changing the threshold values) can be found [here](https://github.com/Itseez/opencv/tree/master/samples/cpp/tutorial_code/ImgTrans/HoughCircle_Demo.cpp).
@code{.cpp} @includelineno samples/cpp/houghcircles.cpp
#include "opencv2/highgui.hpp"
#include "opencv2/imgproc.hpp"
#include <iostream>
#include <stdio.h>
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 Explanation
----------- -----------
1. Load an image -# Load an image
@code{.cpp} @code{.cpp}
src = imread( argv[1], 1 ); src = imread( argv[1], 1 );
if( !src.data ) if( !src.data )
{ return -1; } { return -1; }
@endcode @endcode
2. Convert it to grayscale: -# Convert it to grayscale:
@code{.cpp} @code{.cpp}
cvtColor( src, src_gray, COLOR_BGR2GRAY ); cvtColor( src, src_gray, COLOR_BGR2GRAY );
@endcode @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} @code{.cpp}
GaussianBlur( src_gray, src_gray, Size(9, 9), 2, 2 ); GaussianBlur( src_gray, src_gray, Size(9, 9), 2, 2 );
@endcode @endcode
4. Proceed to apply Hough Circle Transform: -# Proceed to apply Hough Circle Transform:
@code{.cpp} @code{.cpp}
vector<Vec3f> circles; vector<Vec3f> circles;
...@@ -129,7 +82,7 @@ Explanation ...@@ -129,7 +82,7 @@ Explanation
- *min_radius = 0*: Minimum radio to be detected. If unknown, put zero as default. - *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. - *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} @code{.cpp}
for( size_t i = 0; i < circles.size(); i++ ) for( size_t i = 0; i < circles.size(); i++ )
{ {
...@@ -143,19 +96,19 @@ Explanation ...@@ -143,19 +96,19 @@ Explanation
@endcode @endcode
You can see that we will draw the circle(s) on red and the center(s) with a small green dot 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} @code{.cpp}
namedWindow( "Hough Circle Transform Demo", WINDOW_AUTOSIZE ); namedWindow( "Hough Circle Transform Demo", WINDOW_AUTOSIZE );
imshow( "Hough Circle Transform Demo", src ); imshow( "Hough Circle Transform Demo", src );
@endcode @endcode
7. Wait for the user to exit the program -# Wait for the user to exit the program
@code{.cpp} @code{.cpp}
waitKey(0); waitKey(0);
@endcode @endcode
Result Result
------ ------
The result of running the code above with a test image is shown below: 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)
...@@ -12,18 +12,22 @@ In this tutorial you will learn how to: ...@@ -12,18 +12,22 @@ In this tutorial you will learn how to:
Theory Theory
------ ------
@note The explanation below belongs to the book **Learning OpenCV** by Bradski and Kaehler. Hough @note The explanation below belongs to the book **Learning OpenCV** by Bradski and Kaehler.
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. 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? ### 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 **Cartesian coordinate system:** Parameters: \f$(m,b)\f$.
-# In the **Polar coordinate system:** Parameters: \f$(r,\theta)\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 For Hough Transforms, we will express lines in the *Polar system*. Hence, a line equation can be
written as: written as:
...@@ -32,7 +36,7 @@ straight lines. \#. To apply the Transform, first an edge detection pre-processi ...@@ -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$ 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: that point as:
\f[r_{\theta} = x_{0} \cdot \cos \theta + y_{0} \cdot \sin \theta\f] \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$ ...@@ -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 Meaning that each pair \f$(r_{\theta},\theta)\f$ represents each line that passes by
\f$(x_{0}, y_{0})\f$. \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 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$): \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$. 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 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 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: 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 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 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. \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 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* 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. 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 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. 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** ...@@ -86,83 +90,20 @@ b. **The Probabilistic Hough Line Transform**
Code Code
---- ----
1. **What does this program do?** -# **What does this program do?**
- Loads an image - Loads an image
- Applies either a *Standard Hough Line Transform* or a *Probabilistic Line Transform*. - Applies either a *Standard Hough Line Transform* or a *Probabilistic Line Transform*.
- Display the original image and the detected line in two windows. - 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 (which shows both Hough standard and probabilistic with trackbars for changing the threshold
values) can be found here_. values) can be found [here](https://github.com/Itseez/opencv/tree/master/samples/cpp/tutorial_code/ImgTrans/HoughLines_Demo.cpp).
@code{.cpp} @includelineno samples/cpp/houghlines.cpp
#include "opencv2/highgui.hpp"
#include "opencv2/imgproc.hpp"
#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 Explanation
----------- -----------
1. Load an image -# Load an image
@code{.cpp} @code{.cpp}
Mat src = imread(filename, 0); Mat src = imread(filename, 0);
if(src.empty()) if(src.empty())
...@@ -172,14 +113,14 @@ Explanation ...@@ -172,14 +113,14 @@ Explanation
return -1; return -1;
} }
@endcode @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} @code{.cpp}
Canny(src, dst, 50, 200, 3); Canny(src, dst, 50, 200, 3);
@endcode @endcode
Now we will apply the Hough Line Transform. We will explain how to use both OpenCV functions Now we will apply the Hough Line Transform. We will explain how to use both OpenCV functions
available for this purpose: available for this purpose:
3. **Standard Hough Line Transform** -# **Standard Hough Line Transform**
-# First, you apply the Transform: -# First, you apply the Transform:
@code{.cpp} @code{.cpp}
vector<Vec2f> lines; vector<Vec2f> lines;
...@@ -211,7 +152,7 @@ Explanation ...@@ -211,7 +152,7 @@ Explanation
line( cdst, pt1, pt2, Scalar(0,0,255), 3, LINE_AA); line( cdst, pt1, pt2, Scalar(0,0,255), 3, LINE_AA);
} }
@endcode @endcode
4. **Probabilistic Hough Line Transform** -# **Probabilistic Hough Line Transform**
-# First you apply the transform: -# First you apply the transform:
@code{.cpp} @code{.cpp}
vector<Vec4i> lines; vector<Vec4i> lines;
...@@ -239,15 +180,16 @@ Explanation ...@@ -239,15 +180,16 @@ Explanation
line( cdst, Point(l[0], l[1]), Point(l[2], l[3]), Scalar(0,0,255), 3, LINE_AA); line( cdst, Point(l[0], l[1]), Point(l[2], l[3]), Scalar(0,0,255), 3, LINE_AA);
} }
@endcode @endcode
5. Display the original image and the detected lines: -# Display the original image and the detected lines:
@code{.cpp} @code{.cpp}
imshow("source", src); imshow("source", src);
imshow("detected lines", cdst); imshow("detected lines", cdst);
@endcode @endcode
6. Wait until the user exits the program -# Wait until the user exits the program
@code{.cpp} @code{.cpp}
waitKey(); waitKey();
@endcode @endcode
Result Result
------ ------
...@@ -258,11 +200,11 @@ Result ...@@ -258,11 +200,11 @@ Result
Using an input image such as: 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: 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 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 explanation is sort of evident: If you establish a higher threshold, fewer lines will be detected
......
...@@ -12,16 +12,16 @@ In this tutorial you will learn how to: ...@@ -12,16 +12,16 @@ In this tutorial you will learn how to:
Theory 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. 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 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: 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 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 attempt to detect edges in an image. However, note that zeros will not only appear in edges
...@@ -30,81 +30,34 @@ Theory ...@@ -30,81 +30,34 @@ Theory
### Laplacian Operator ### 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 Since images are "*2D*", we would need to take the derivative in both dimensions. Here, the
Laplacian operator comes handy. 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] \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 since the Laplacian uses the gradient of images, it calls internally the *Sobel* operator to
perform its computation. perform its computation.
Code Code
---- ----
1. **What does this program do?** -# **What does this program do?**
- Loads an image - Loads an image
- Remove noise by applying a Gaussian blur and then convert the original image to grayscale - 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 - Applies a Laplacian operator to the grayscale image and stores the output image
- Display the result in a window - 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) [here](https://github.com/Itseez/opencv/tree/master/samples/cpp/tutorial_code/ImgTrans/Laplace_Demo.cpp)
@code{.cpp} @includelineno samples/cpp/tutorial_code/ImgTrans/Laplace_Demo.cpp
#include "opencv2/imgproc.hpp"
#include "opencv2/highgui.hpp"
#include <stdlib.h>
#include <stdio.h>
using namespace cv;
/* @function main */
int main( int argc, char** argv )
{
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 Explanation
----------- -----------
1. Create some needed variables: -# Create some needed variables:
@code{.cpp} @code{.cpp}
Mat src, src_gray, dst; Mat src, src_gray, dst;
int kernel_size = 3; int kernel_size = 3;
...@@ -113,22 +66,22 @@ Explanation ...@@ -113,22 +66,22 @@ Explanation
int ddepth = CV_16S; int ddepth = CV_16S;
char* window_name = "Laplace Demo"; char* window_name = "Laplace Demo";
@endcode @endcode
2. Loads the source image: -# Loads the source image:
@code{.cpp} @code{.cpp}
src = imread( argv[1] ); src = imread( argv[1] );
if( !src.data ) if( !src.data )
{ return -1; } { return -1; }
@endcode @endcode
3. Apply a Gaussian blur to reduce noise: -# Apply a Gaussian blur to reduce noise:
@code{.cpp} @code{.cpp}
GaussianBlur( src, src, Size(3,3), 0, 0, BORDER_DEFAULT ); GaussianBlur( src, src, Size(3,3), 0, 0, BORDER_DEFAULT );
@endcode @endcode
4. Convert the image to grayscale using @ref cv::cvtColor -# Convert the image to grayscale using @ref cv::cvtColor
@code{.cpp} @code{.cpp}
cvtColor( src, src_gray, COLOR_RGB2GRAY ); cvtColor( src, src_gray, COLOR_RGB2GRAY );
@endcode @endcode
5. Apply the Laplacian operator to the grayscale image: -# Apply the Laplacian operator to the grayscale image:
@code{.cpp} @code{.cpp}
Laplacian( src_gray, dst, ddepth, kernel_size, scale, delta, BORDER_DEFAULT ); Laplacian( src_gray, dst, ddepth, kernel_size, scale, delta, BORDER_DEFAULT );
@endcode @endcode
...@@ -142,27 +95,26 @@ Explanation ...@@ -142,27 +95,26 @@ Explanation
this example. this example.
- *scale*, *delta* and *BORDER_DEFAULT*: We leave them as default values. - *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} @code{.cpp}
convertScaleAbs( dst, abs_dst ); convertScaleAbs( dst, abs_dst );
@endcode @endcode
7. Display the result in a window: -# Display the result in a window:
@code{.cpp} @code{.cpp}
imshow( window_name, abs_dst ); imshow( window_name, abs_dst );
@endcode @endcode
Results 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: 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 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 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. 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)
...@@ -276,6 +276,6 @@ Results ...@@ -276,6 +276,6 @@ Results
* And here are two snapshots of the display window. The first picture shows the output after using the operator **Opening** with a cross kernel. The second picture (right side, shows the result of using a **Blackhat** operator with an ellipse kernel. * And here are two snapshots of the display window. The first picture shows the output after using the operator **Opening** with a cross kernel. The second picture (right side, shows the result of using a **Blackhat** operator with an ellipse kernel.
.. image:: images/Morphology_2_Tutorial_Cover.jpg .. image:: images/Morphology_2_Tutorial_Result.jpg
:alt: Morphology 2: Result sample :alt: Morphology 2: Result sample
:align: center :align: center
...@@ -16,8 +16,8 @@ Theory ...@@ -16,8 +16,8 @@ Theory
- Usually we need to convert an image to a size different than its original. For this, there are - Usually we need to convert an image to a size different than its original. For this, there are
two possible options: two possible options:
1. *Upsize* the image (zoom in) or -# *Upsize* the image (zoom in) or
2. *Downsize* it (zoom out). -# *Downsize* it (zoom out).
- Although there is a *geometric transformation* function in OpenCV that -literally- resize an - Although there is a *geometric transformation* function in OpenCV that -literally- resize an
image (@ref cv::resize , which we will show in a future tutorial), in this section we analyze image (@ref cv::resize , which we will show in a future tutorial), in this section we analyze
first the use of **Image Pyramids**, which are widely applied in a huge range of vision first the use of **Image Pyramids**, which are widely applied in a huge range of vision
...@@ -37,7 +37,7 @@ Theory ...@@ -37,7 +37,7 @@ Theory
- Imagine the pyramid as a set of layers in which the higher the layer, the smaller the size. - Imagine the pyramid as a set of layers in which the higher the layer, the smaller the size.
![image](images/Pyramids_Tutorial_Pyramid_Theory.png) ![](images/Pyramids_Tutorial_Pyramid_Theory.png)
- Every layer is numbered from bottom to top, so layer \f$(i+1)\f$ (denoted as \f$G_{i+1}\f$ is smaller - Every layer is numbered from bottom to top, so layer \f$(i+1)\f$ (denoted as \f$G_{i+1}\f$ is smaller
than layer \f$i\f$ (\f$G_{i}\f$). than layer \f$i\f$ (\f$G_{i}\f$).
...@@ -162,14 +162,14 @@ Results ...@@ -162,14 +162,14 @@ Results
that comes in the *tutorial_code/image* folder. Notice that this image is \f$512 \times 512\f$, that comes in the *tutorial_code/image* folder. Notice that this image is \f$512 \times 512\f$,
hence a downsample won't generate any error (\f$512 = 2^{9}\f$). The original image is shown below: hence a downsample won't generate any error (\f$512 = 2^{9}\f$). The original image is shown below:
![image](images/Pyramids_Tutorial_Original_Image.jpg) ![](images/Pyramids_Tutorial_Original_Image.jpg)
- First we apply two successive @ref cv::pyrDown operations by pressing 'd'. Our output is: - First we apply two successive @ref cv::pyrDown operations by pressing 'd'. Our output is:
![image](images/Pyramids_Tutorial_PyrDown_Result.jpg) ![](images/Pyramids_Tutorial_PyrDown_Result.jpg)
- Note that we should have lost some resolution due to the fact that we are diminishing the size - Note that we should have lost some resolution due to the fact that we are diminishing the size
of the image. This is evident after we apply @ref cv::pyrUp twice (by pressing 'u'). Our output of the image. This is evident after we apply @ref cv::pyrUp twice (by pressing 'u'). Our output
is now: is now:
![image](images/Pyramids_Tutorial_PyrUp_Result.jpg) ![](images/Pyramids_Tutorial_PyrUp_Result.jpg)
...@@ -49,10 +49,11 @@ In Linux it can be achieved with the following command in Terminal: ...@@ -49,10 +49,11 @@ In Linux it can be achieved with the following command in Terminal:
cd ~/<my_working _directory> cd ~/<my_working _directory>
git clone https://github.com/Itseez/opencv.git git clone https://github.com/Itseez/opencv.git
@endcode @endcode
Building OpenCV Building OpenCV
--------------- ---------------
1. Create a build directory, make it current and run the following command: -# Create a build directory, make it current and run the following command:
@code{.bash} @code{.bash}
cmake [<some optional parameters>] -DCMAKE_TOOLCHAIN_FILE=<path to the OpenCV source directory>/platforms/linux/arm-gnueabi.toolchain.cmake <path to the OpenCV source directory> cmake [<some optional parameters>] -DCMAKE_TOOLCHAIN_FILE=<path to the OpenCV source directory>/platforms/linux/arm-gnueabi.toolchain.cmake <path to the OpenCV source directory>
@endcode @endcode
...@@ -69,13 +70,15 @@ Building OpenCV ...@@ -69,13 +70,15 @@ Building OpenCV
cmake -DCMAKE_TOOLCHAIN_FILE=../arm-gnueabi.toolchain.cmake ../../.. cmake -DCMAKE_TOOLCHAIN_FILE=../arm-gnueabi.toolchain.cmake ../../..
@endcode @endcode
2. Run make in build (\<cmake_binary_dir\>) directory:
-# Run make in build (\<cmake_binary_dir\>) directory:
@code{.bash} @code{.bash}
make make
@endcode @endcode
@note @note
Optionally you can strip symbols info from the created library via install/strip make target. Optionally you can strip symbols info from the created library via install/strip make target.
This option produces smaller binary (\~ twice smaller) but makes further debugging harder. This option produces smaller binary (\~ twice smaller) but makes further debugging harder.
### Enable hardware optimizations ### Enable hardware optimizations
...@@ -86,5 +89,4 @@ extensions. ...@@ -86,5 +89,4 @@ extensions.
TBB is supported on multi core ARM SoCs also. Add -DWITH_TBB=ON and -DBUILD_TBB=ON to enable it. TBB is supported on multi core ARM SoCs also. Add -DWITH_TBB=ON and -DBUILD_TBB=ON to enable it.
Cmake scripts download TBB sources from official project site Cmake scripts download TBB sources from official project site
[](http://threadingbuildingblocks.org/) and build it. <http://threadingbuildingblocks.org/> and build it.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment