Doxygen tutorials: basic structure
This commit is contained in:
111
doc/tutorials/core/adding_images/adding_images.markdown
Normal file
111
doc/tutorials/core/adding_images/adding_images.markdown
Normal file
@@ -0,0 +1,111 @@
|
||||
Adding (blending) two images using OpenCV {#tutorial_adding_images}
|
||||
=========================================
|
||||
|
||||
Goal
|
||||
----
|
||||
|
||||
In this tutorial you will learn:
|
||||
|
||||
- what is *linear blending* and why it is useful;
|
||||
- how to add two images using @ref cv::addWeighted
|
||||
|
||||
Theory
|
||||
------
|
||||
|
||||
@note
|
||||
The explanation below belongs to the book [Computer Vision: Algorithms and
|
||||
Applications](http://szeliski.org/Book/) by Richard Szeliski
|
||||
|
||||
From our previous tutorial, we know already a bit of *Pixel operators*. An interesting dyadic
|
||||
(two-input) operator is the *linear blend operator*:
|
||||
|
||||
\f[g(x) = (1 - \alpha)f_{0}(x) + \alpha f_{1}(x)\f]
|
||||
|
||||
By varying \f$\alpha\f$ from \f$0 \rightarrow 1\f$ this operator can be used to perform a temporal
|
||||
*cross-disolve* between two images or videos, as seen in slide shows and film productions (cool,
|
||||
eh?)
|
||||
|
||||
Code
|
||||
----
|
||||
|
||||
As usual, after the not-so-lengthy explanation, let's go to the code:
|
||||
@code{.cpp}
|
||||
#include <opencv2/opencv.hpp>
|
||||
#include <iostream>
|
||||
|
||||
using namespace cv;
|
||||
|
||||
int main( int argc, char** argv )
|
||||
{
|
||||
double alpha = 0.5; double beta; double input;
|
||||
|
||||
Mat src1, src2, dst;
|
||||
|
||||
/// Ask the user enter alpha
|
||||
std::cout<<" Simple Linear Blender "<<std::endl;
|
||||
std::cout<<"-----------------------"<<std::endl;
|
||||
std::cout<<"* Enter alpha [0-1]: ";
|
||||
std::cin>>input;
|
||||
|
||||
/// We use the alpha provided by the user if it is between 0 and 1
|
||||
if( input >= 0.0 && input <= 1.0 )
|
||||
{ alpha = input; }
|
||||
|
||||
/// Read image ( same size, same type )
|
||||
src1 = imread("../../images/LinuxLogo.jpg");
|
||||
src2 = imread("../../images/WindowsLogo.jpg");
|
||||
|
||||
if( !src1.data ) { printf("Error loading src1 \n"); return -1; }
|
||||
if( !src2.data ) { printf("Error loading src2 \n"); return -1; }
|
||||
|
||||
/// Create Windows
|
||||
namedWindow("Linear Blend", 1);
|
||||
|
||||
beta = ( 1.0 - alpha );
|
||||
addWeighted( src1, alpha, src2, beta, 0.0, dst);
|
||||
|
||||
imshow( "Linear Blend", dst );
|
||||
|
||||
waitKey(0);
|
||||
return 0;
|
||||
}
|
||||
@endcode
|
||||
Explanation
|
||||
-----------
|
||||
|
||||
1. Since we are going to perform:
|
||||
|
||||
\f[g(x) = (1 - \alpha)f_{0}(x) + \alpha f_{1}(x)\f]
|
||||
|
||||
We need two source images (\f$f_{0}(x)\f$ and \f$f_{1}(x)\f$). So, we load them in the usual way:
|
||||
@code{.cpp}
|
||||
src1 = imread("../../images/LinuxLogo.jpg");
|
||||
src2 = imread("../../images/WindowsLogo.jpg");
|
||||
@endcode
|
||||
**warning**
|
||||
|
||||
Since we are *adding* *src1* and *src2*, they both have to be of the same size (width and
|
||||
height) and type.
|
||||
|
||||
2. Now we need to generate the @ref cv::g(x)\` image. For this, the function
|
||||
add_weighted:addWeighted comes quite handy:
|
||||
|
||||
.. code-block:: cpp
|
||||
|
||||
beta = ( 1.0 - alpha );
|
||||
addWeighted( src1, alpha, src2, beta, 0.0, dst);
|
||||
|
||||
since @ref cv::addWeighted produces:
|
||||
|
||||
.. math::
|
||||
|
||||
dst = \\alpha \\cdot src1 + \\beta \\cdot src2 + \\gamma
|
||||
|
||||
In this case, :math: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.
|
||||
|
||||
Result
|
||||
------
|
||||
|
||||

|
||||
|
@@ -0,0 +1,244 @@
|
||||
Basic Drawing {#tutorial_basic_geometric_drawing}
|
||||
=============
|
||||
|
||||
Goals
|
||||
-----
|
||||
|
||||
In this tutorial you will learn how to:
|
||||
|
||||
- Use @ref cv::Point to define 2D points in an image.
|
||||
- Use @ref cv::Scalar and why it is useful
|
||||
- Draw a **line** by using the OpenCV function @ref cv::line
|
||||
- Draw an **ellipse** by using the OpenCV function @ref cv::ellipse
|
||||
- Draw a **rectangle** by using the OpenCV function @ref cv::rectangle
|
||||
- Draw a **circle** by using the OpenCV function @ref cv::circle
|
||||
- Draw a **filled polygon** by using the OpenCV function @ref cv::fillPoly
|
||||
|
||||
OpenCV Theory
|
||||
-------------
|
||||
|
||||
For this tutorial, we will heavily use two structures: @ref cv::Point and @ref cv::Scalar :
|
||||
|
||||
### Point
|
||||
|
||||
It represents a 2D point, specified by its image coordinates \f$x\f$ and \f$y\f$. We can define it as:
|
||||
@code{.cpp}
|
||||
Point pt;
|
||||
pt.x = 10;
|
||||
pt.y = 8;
|
||||
@endcode
|
||||
or
|
||||
@code{.cpp}
|
||||
Point pt = Point(10, 8);
|
||||
@endcode
|
||||
### Scalar
|
||||
|
||||
- Represents a 4-element vector. The type Scalar is widely used in OpenCV for passing pixel
|
||||
values.
|
||||
- In this tutorial, we will use it extensively to represent RGB color values (3 parameters). It is
|
||||
not necessary to define the last argument if it is not going to be used.
|
||||
- Let's see an example, if we are asked for a color argument and we give:
|
||||
@code{.cpp}
|
||||
Scalar( a, b, c )
|
||||
@endcode
|
||||
We would be defining a RGB color such as: *Red = c*, *Green = b* and *Blue = a*
|
||||
|
||||
Code
|
||||
----
|
||||
|
||||
- This code is in your OpenCV sample folder. Otherwise you can grab it from
|
||||
[here](https://github.com/Itseez/opencv/tree/master/samples/cpp/tutorial_code/core/Matrix/Drawing_1.cpp)
|
||||
|
||||
Explanation
|
||||
-----------
|
||||
|
||||
1. Since we plan to draw two examples (an atom and a rook), we have to create 02 images and two
|
||||
windows to display them.
|
||||
@code{.cpp}
|
||||
/// Windows names
|
||||
char atom_window[] = "Drawing 1: Atom";
|
||||
char rook_window[] = "Drawing 2: Rook";
|
||||
|
||||
/// Create black empty images
|
||||
Mat atom_image = Mat::zeros( w, w, CV_8UC3 );
|
||||
Mat rook_image = Mat::zeros( w, w, CV_8UC3 );
|
||||
@endcode
|
||||
2. We created functions to draw different geometric shapes. For instance, to draw the atom we used
|
||||
*MyEllipse* and *MyFilledCircle*:
|
||||
@code{.cpp}
|
||||
/// 1. Draw a simple atom:
|
||||
|
||||
/// 1.a. Creating ellipses
|
||||
MyEllipse( atom_image, 90 );
|
||||
MyEllipse( atom_image, 0 );
|
||||
MyEllipse( atom_image, 45 );
|
||||
MyEllipse( atom_image, -45 );
|
||||
|
||||
/// 1.b. Creating circles
|
||||
MyFilledCircle( atom_image, Point( w/2.0, w/2.0) );
|
||||
@endcode
|
||||
3. And to draw the rook we employed *MyLine*, *rectangle* and a *MyPolygon*:
|
||||
@code{.cpp}
|
||||
/// 2. Draw a rook
|
||||
|
||||
/// 2.a. Create a convex polygon
|
||||
MyPolygon( rook_image );
|
||||
|
||||
/// 2.b. Creating rectangles
|
||||
rectangle( rook_image,
|
||||
Point( 0, 7*w/8.0 ),
|
||||
Point( w, w),
|
||||
Scalar( 0, 255, 255 ),
|
||||
-1,
|
||||
8 );
|
||||
|
||||
/// 2.c. Create a few lines
|
||||
MyLine( rook_image, Point( 0, 15*w/16 ), Point( w, 15*w/16 ) );
|
||||
MyLine( rook_image, Point( w/4, 7*w/8 ), Point( w/4, 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 ) );
|
||||
@endcode
|
||||
4. Let's check what is inside each of these functions:
|
||||
- *MyLine*
|
||||
@code{.cpp}
|
||||
void MyLine( Mat img, Point start, Point end )
|
||||
{
|
||||
int thickness = 2;
|
||||
int lineType = 8;
|
||||
line( img, start, end,
|
||||
Scalar( 0, 0, 0 ),
|
||||
thickness,
|
||||
lineType );
|
||||
}
|
||||
@endcode
|
||||
As we can see, *MyLine* just call the function @ref cv::line , which does the following:
|
||||
|
||||
- Draw a line from Point **start** to Point **end**
|
||||
- The line is displayed in the image **img**
|
||||
- The line color is defined by **Scalar( 0, 0, 0)** which is the RGB value correspondent
|
||||
to **Black**
|
||||
- The line thickness is set to **thickness** (in this case 2)
|
||||
- The line is a 8-connected one (**lineType** = 8)
|
||||
- *MyEllipse*
|
||||
@code{.cpp}
|
||||
void MyEllipse( Mat img, double angle )
|
||||
{
|
||||
int thickness = 2;
|
||||
int lineType = 8;
|
||||
|
||||
ellipse( img,
|
||||
Point( w/2.0, w/2.0 ),
|
||||
Size( w/4.0, w/16.0 ),
|
||||
angle,
|
||||
0,
|
||||
360,
|
||||
Scalar( 255, 0, 0 ),
|
||||
thickness,
|
||||
lineType );
|
||||
}
|
||||
@endcode
|
||||
From the code above, we can observe that the function @ref cv::ellipse draws an ellipse such
|
||||
that:
|
||||
|
||||
- The ellipse is displayed in the image **img**
|
||||
- The ellipse center is located in the point **(w/2.0, w/2.0)** and is enclosed in a box
|
||||
of size **(w/4.0, w/16.0)**
|
||||
- The ellipse is rotated **angle** degrees
|
||||
- The ellipse extends an arc between **0** and **360** degrees
|
||||
- The color of the figure will be **Scalar( 255, 255, 0)** which means blue in RGB value.
|
||||
- The ellipse's **thickness** is 2.
|
||||
- *MyFilledCircle*
|
||||
@code{.cpp}
|
||||
void MyFilledCircle( Mat img, Point center )
|
||||
{
|
||||
int thickness = -1;
|
||||
int lineType = 8;
|
||||
|
||||
circle( img,
|
||||
center,
|
||||
w/32.0,
|
||||
Scalar( 0, 0, 255 ),
|
||||
thickness,
|
||||
lineType );
|
||||
}
|
||||
@endcode
|
||||
Similar to the ellipse function, we can observe that *circle* receives as arguments:
|
||||
|
||||
- The image where the circle will be displayed (**img**)
|
||||
- The center of the circle denoted as the Point **center**
|
||||
- The radius of the circle: **w/32.0**
|
||||
- The color of the circle: **Scalar(0, 0, 255)** which means *Red* in BGR
|
||||
- Since **thickness** = -1, the circle will be drawn filled.
|
||||
- *MyPolygon*
|
||||
@code{.cpp}
|
||||
void MyPolygon( Mat img )
|
||||
{
|
||||
int lineType = 8;
|
||||
|
||||
/* Create some points */
|
||||
Point rook_points[1][20];
|
||||
rook_points[0][0] = Point( w/4.0, 7*w/8.0 );
|
||||
rook_points[0][1] = Point( 3*w/4.0, 7*w/8.0 );
|
||||
rook_points[0][2] = Point( 3*w/4.0, 13*w/16.0 );
|
||||
rook_points[0][3] = Point( 11*w/16.0, 13*w/16.0 );
|
||||
rook_points[0][4] = Point( 19*w/32.0, 3*w/8.0 );
|
||||
rook_points[0][5] = Point( 3*w/4.0, 3*w/8.0 );
|
||||
rook_points[0][6] = Point( 3*w/4.0, w/8.0 );
|
||||
rook_points[0][7] = Point( 26*w/40.0, w/8.0 );
|
||||
rook_points[0][8] = Point( 26*w/40.0, w/4.0 );
|
||||
rook_points[0][9] = Point( 22*w/40.0, w/4.0 );
|
||||
rook_points[0][10] = Point( 22*w/40.0, w/8.0 );
|
||||
rook_points[0][11] = Point( 18*w/40.0, w/8.0 );
|
||||
rook_points[0][12] = Point( 18*w/40.0, w/4.0 );
|
||||
rook_points[0][13] = Point( 14*w/40.0, w/4.0 );
|
||||
rook_points[0][14] = Point( 14*w/40.0, w/8.0 );
|
||||
rook_points[0][15] = Point( w/4.0, w/8.0 );
|
||||
rook_points[0][16] = Point( w/4.0, 3*w/8.0 );
|
||||
rook_points[0][17] = Point( 13*w/32.0, 3*w/8.0 );
|
||||
rook_points[0][18] = Point( 5*w/16.0, 13*w/16.0 );
|
||||
rook_points[0][19] = Point( w/4.0, 13*w/16.0) ;
|
||||
|
||||
const Point* ppt[1] = { rook_points[0] };
|
||||
int npt[] = { 20 };
|
||||
|
||||
fillPoly( img,
|
||||
ppt,
|
||||
npt,
|
||||
1,
|
||||
Scalar( 255, 255, 255 ),
|
||||
lineType );
|
||||
}
|
||||
@endcode
|
||||
To draw a filled polygon we use the function @ref cv::fillPoly . We note that:
|
||||
|
||||
- The polygon will be drawn on **img**
|
||||
- The vertices of the polygon are the set of points in **ppt**
|
||||
- The total number of vertices to be drawn are **npt**
|
||||
- The number of polygons to be drawn is only **1**
|
||||
- The color of the polygon is defined by **Scalar( 255, 255, 255)**, which is the BGR
|
||||
value for *white*
|
||||
- *rectangle*
|
||||
@code{.cpp}
|
||||
rectangle( rook_image,
|
||||
Point( 0, 7*w/8.0 ),
|
||||
Point( w, w),
|
||||
Scalar( 0, 255, 255 ),
|
||||
-1, 8 );
|
||||
@endcode
|
||||
Finally we have the @ref cv::rectangle function (we did not create a special function for
|
||||
this guy). We note that:
|
||||
|
||||
- The rectangle will be drawn on **rook_image**
|
||||
- Two opposite vertices of the rectangle are defined by *\* Point( 0, 7*w/8.0 )*\*
|
||||
andPoint( w, w)*\*
|
||||
- The color of the rectangle is given by **Scalar(0, 255, 255)** which is the BGR value
|
||||
for *yellow*
|
||||
- Since the thickness value is given by **-1**, the rectangle will be filled.
|
||||
|
||||
Result
|
||||
------
|
||||
|
||||
Compiling and running your program should give you a result like this:
|
||||
|
||||

|
||||
|
@@ -0,0 +1,178 @@
|
||||
Changing the contrast and brightness of an image! {#tutorial_basic_linear_transform}
|
||||
=================================================
|
||||
|
||||
Goal
|
||||
----
|
||||
|
||||
In this tutorial you will learn how to:
|
||||
|
||||
- Access pixel values
|
||||
- Initialize a matrix with zeros
|
||||
- Learn what @ref cv::saturate_cast does and why it is useful
|
||||
- Get some cool info about pixel transformations
|
||||
|
||||
Theory
|
||||
------
|
||||
|
||||
@note
|
||||
The explanation below belongs to the book [Computer Vision: Algorithms and
|
||||
Applications](http://szeliski.org/Book/) by Richard Szeliski
|
||||
|
||||
### Image Processing
|
||||
|
||||
- A general image processing operator is a function that takes one or more input images and
|
||||
produces an output image.
|
||||
- Image transforms can be seen as:
|
||||
- Point operators (pixel transforms)
|
||||
- Neighborhood (area-based) operators
|
||||
|
||||
### Pixel Transforms
|
||||
|
||||
- In this kind of image processing transform, each output pixel's value depends on only the
|
||||
corresponding input pixel value (plus, potentially, some globally collected information or
|
||||
parameters).
|
||||
- Examples of such operators include *brightness and contrast adjustments* as well as color
|
||||
correction and transformations.
|
||||
|
||||
### Brightness and contrast adjustments
|
||||
|
||||
- Two commonly used point processes are *multiplication* and *addition* with a constant:
|
||||
|
||||
\f[g(x) = \alpha f(x) + \beta\f]
|
||||
|
||||
- The parameters \f$\alpha > 0\f$ and \f$\beta\f$ are often called the *gain* and *bias* parameters;
|
||||
sometimes these parameters are said to control *contrast* and *brightness* respectively.
|
||||
- You can think of \f$f(x)\f$ as the source image pixels and \f$g(x)\f$ as the output image pixels. Then,
|
||||
more conveniently we can write the expression as:
|
||||
|
||||
\f[g(i,j) = \alpha \cdot f(i,j) + \beta\f]
|
||||
|
||||
where \f$i\f$ and \f$j\f$ indicates that the pixel is located in the *i-th* row and *j-th* column.
|
||||
|
||||
Code
|
||||
----
|
||||
|
||||
- The following code performs the operation \f$g(i,j) = \alpha \cdot f(i,j) + \beta\f$ :
|
||||
@code{.cpp}
|
||||
#include <opencv2/opencv.hpp>
|
||||
#include <iostream>
|
||||
|
||||
using namespace cv;
|
||||
|
||||
double alpha; /*< Simple contrast control */
|
||||
int beta; /*< Simple brightness control */
|
||||
|
||||
int main( int argc, char** argv )
|
||||
{
|
||||
/// Read image given by user
|
||||
Mat image = imread( argv[1] );
|
||||
Mat new_image = Mat::zeros( image.size(), image.type() );
|
||||
|
||||
/// Initialize values
|
||||
std::cout<<" Basic Linear Transforms "<<std::endl;
|
||||
std::cout<<"-------------------------"<<std::endl;
|
||||
std::cout<<"* Enter the alpha value [1.0-3.0]: ";std::cin>>alpha;
|
||||
std::cout<<"* Enter the beta value [0-100]: "; std::cin>>beta;
|
||||
|
||||
/// Do the operation new_image(i,j) = alpha*image(i,j) + beta
|
||||
for( int y = 0; y < image.rows; y++ ) {
|
||||
for( int x = 0; x < image.cols; x++ ) {
|
||||
for( int c = 0; c < 3; c++ ) {
|
||||
new_image.at<Vec3b>(y,x)[c] =
|
||||
saturate_cast<uchar>( alpha*( image.at<Vec3b>(y,x)[c] ) + beta );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Create Windows
|
||||
namedWindow("Original Image", 1);
|
||||
namedWindow("New Image", 1);
|
||||
|
||||
/// Show stuff
|
||||
imshow("Original Image", image);
|
||||
imshow("New Image", new_image);
|
||||
|
||||
/// Wait until user press some key
|
||||
waitKey();
|
||||
return 0;
|
||||
}
|
||||
@endcode
|
||||
Explanation
|
||||
-----------
|
||||
|
||||
1. We begin by creating parameters to save \f$\alpha\f$ and \f$\beta\f$ to be entered by the user:
|
||||
@code{.cpp}
|
||||
double alpha;
|
||||
int beta;
|
||||
@endcode
|
||||
2. We load an image using @ref cv::imread and save it in a Mat object:
|
||||
@code{.cpp}
|
||||
Mat image = imread( argv[1] );
|
||||
@endcode
|
||||
3. 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:
|
||||
|
||||
- Initial pixel values equal to zero
|
||||
- Same size and type as the original image
|
||||
@code{.cpp}
|
||||
Mat new_image = Mat::zeros( image.size(), image.type() );
|
||||
@endcode
|
||||
We observe that @ref cv::Mat::zeros returns a Matlab-style zero initializer based on
|
||||
*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
|
||||
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:
|
||||
@code{.cpp}
|
||||
for( int y = 0; y < image.rows; y++ ) {
|
||||
for( int x = 0; x < image.cols; x++ ) {
|
||||
for( int c = 0; c < 3; c++ ) {
|
||||
new_image.at<Vec3b>(y,x)[c] =
|
||||
saturate_cast<uchar>( alpha*( image.at<Vec3b>(y,x)[c] ) + beta );
|
||||
}
|
||||
}
|
||||
}
|
||||
@endcode
|
||||
Notice the following:
|
||||
|
||||
- To access each pixel in the images we are using this syntax: *image.at\<Vec3b\>(y,x)[c]*
|
||||
where *y* is the row, *x* is the column and *c* is R, G or B (0, 1 or 2).
|
||||
- Since the operation @ref cv::alpha cdot p(i,j) + beta\` can give values out of range or not
|
||||
integers (if \f$\alpha\f$ is float), we use :saturate_cast:\`saturate_cast to make sure the
|
||||
values are valid.
|
||||
|
||||
5. Finally, we create windows and show the images, the usual way.
|
||||
@code{.cpp}
|
||||
namedWindow("Original Image", 1);
|
||||
namedWindow("New Image", 1);
|
||||
|
||||
imshow("Original Image", image);
|
||||
imshow("New Image", new_image);
|
||||
|
||||
waitKey(0);
|
||||
@endcode
|
||||
@note
|
||||
Instead of using the **for** loops to access each pixel, we could have simply used this command:
|
||||
@code{.cpp}
|
||||
image.convertTo(new_image, -1, alpha, beta);
|
||||
@endcode
|
||||
where @ref cv::convertTo would effectively perform *new_image = a*image + beta\*. However, we
|
||||
wanted to show you how to access each pixel. In any case, both methods give the same result but
|
||||
convertTo is more optimized and works a lot faster.
|
||||
|
||||
Result
|
||||
------
|
||||
|
||||
- Running our code and using \f$\alpha = 2.2\f$ and \f$\beta = 50\f$
|
||||
@code{.bash}
|
||||
\f$ ./BasicLinearTransforms lena.jpg
|
||||
Basic Linear Transforms
|
||||
-------------------------
|
||||
* Enter the alpha value [1.0-3.0]: 2.2
|
||||
* Enter the beta value [0-100]: 50
|
||||
@endcode
|
||||
- We get this:
|
||||
|
||||

|
||||
|
||||
|
@@ -0,0 +1,159 @@
|
||||
Discrete Fourier Transform {#tutorial_discrete_fourier_transform}
|
||||
==========================
|
||||
|
||||
Goal
|
||||
----
|
||||
|
||||
We'll seek answers for the following questions:
|
||||
|
||||
- What is a Fourier transform and why use it?
|
||||
- How to do it in OpenCV?
|
||||
- Usage of functions such as: @ref cv::copyMakeBorder() , @ref cv::merge() , @ref cv::dft() , @ref
|
||||
cv::getOptimalDFTSize() , @ref cv::log() and @ref cv::normalize() .
|
||||
|
||||
Source code
|
||||
-----------
|
||||
|
||||
You can [download this from here
|
||||
](samples/cpp/tutorial_code/core/discrete_fourier_transform/discrete_fourier_transform.cpp) or
|
||||
find it in the
|
||||
`samples/cpp/tutorial_code/core/discrete_fourier_transform/discrete_fourier_transform.cpp` of the
|
||||
OpenCV source code library.
|
||||
|
||||
Here's a sample usage of @ref cv::dft() :
|
||||
|
||||
@includelineno cpp/tutorial_code/core/discrete_fourier_transform/discrete_fourier_transform.cpp
|
||||
|
||||
lines
|
||||
1-4, 6, 20-21, 24-79
|
||||
|
||||
Explanation
|
||||
-----------
|
||||
|
||||
The Fourier Transform will decompose an image into its sinus and cosines components. In other words,
|
||||
it will transform an image from its spatial domain to its frequency domain. The idea is that any
|
||||
function may be approximated exactly with the sum of infinite sinus and cosines functions. The
|
||||
Fourier Transform is a way how to do this. Mathematically a two dimensional images Fourier transform
|
||||
is:
|
||||
|
||||
\f[F(k,l) = \displaystyle\sum\limits_{i=0}^{N-1}\sum\limits_{j=0}^{N-1} f(i,j)e^{-i2\pi(\frac{ki}{N}+\frac{lj}{N})}\f]\f[e^{ix} = \cos{x} + i\sin {x}\f]
|
||||
|
||||
Here f is the image value in its spatial domain and F in its frequency domain. The result of the
|
||||
transformation is complex numbers. Displaying this is possible either via a *real* image and a
|
||||
*complex* image or via a *magnitude* and a *phase* image. However, throughout the image processing
|
||||
algorithms only the *magnitude* image is interesting as this contains all the information we need
|
||||
about the images geometric structure. Nevertheless, if you intend to make some modifications of the
|
||||
image in these forms and then you need to retransform it you'll need to preserve both of these.
|
||||
|
||||
In this sample I'll show how to calculate and show the *magnitude* image of a Fourier Transform. In
|
||||
case of digital images are discrete. This means they may take up a value from a given domain value.
|
||||
For example in a basic gray scale image values usually are between zero and 255. Therefore the
|
||||
Fourier Transform too needs to be of a discrete type resulting in a Discrete Fourier Transform
|
||||
(*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*):
|
||||
|
||||
1. **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
|
||||
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
|
||||
optimal size and we can use the @ref cv::copyMakeBorder() function to expand the borders of an
|
||||
image:
|
||||
@code{.cpp}
|
||||
Mat padded; //expand input image to optimal size
|
||||
int m = getOptimalDFTSize( I.rows );
|
||||
int n = getOptimalDFTSize( I.cols ); // on the border add zero pixels
|
||||
copyMakeBorder(I, padded, 0, m - I.rows, 0, n - I.cols, BORDER_CONSTANT, Scalar::all(0));
|
||||
@endcode
|
||||
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
|
||||
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.
|
||||
Therefore, we store these usually at least in a *float* format. Therefore we'll convert our
|
||||
input image to this type and expand it with another channel to hold the complex values:
|
||||
@code{.cpp}
|
||||
Mat planes[] = {Mat_<float>(padded), Mat::zeros(padded.size(), CV_32F)};
|
||||
Mat complexI;
|
||||
merge(planes, 2, complexI); // Add to the expanded another plane with zeros
|
||||
@endcode
|
||||
3. **Make the Discrete Fourier Transform**. It's possible an in-place calculation (same input as
|
||||
output):
|
||||
@code{.cpp}
|
||||
dft(complexI, complexI); // this way the result may fit in the source matrix
|
||||
@endcode
|
||||
4. **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
|
||||
DFT is:
|
||||
|
||||
\f[M = \sqrt[2]{ {Re(DFT(I))}^2 + {Im(DFT(I))}^2}\f]
|
||||
|
||||
Translated to OpenCV code:
|
||||
@code{.cpp}
|
||||
split(complexI, planes); // planes[0] = Re(DFT(I), planes[1] = Im(DFT(I))
|
||||
magnitude(planes[0], planes[1], planes[0]);// planes[0] = magnitude
|
||||
Mat magI = planes[0];
|
||||
@endcode
|
||||
5. **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
|
||||
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
|
||||
we can transform our linear scale to a logarithmic one:
|
||||
|
||||
\f[M_1 = \log{(1 + M)}\f]
|
||||
|
||||
Translated to OpenCV code:
|
||||
@code{.cpp}
|
||||
magI += Scalar::all(1); // switch to logarithmic scale
|
||||
log(magI, magI);
|
||||
@endcode
|
||||
6. **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
|
||||
quadrants of the result, so that the origin (zero, zero) corresponds with the image center.
|
||||
@code{.cpp}
|
||||
magI = magI(Rect(0, 0, magI.cols & -2, magI.rows & -2));
|
||||
int cx = magI.cols/2;
|
||||
int cy = magI.rows/2;
|
||||
|
||||
Mat q0(magI, Rect(0, 0, cx, cy)); // Top-Left - Create a ROI per quadrant
|
||||
Mat q1(magI, Rect(cx, 0, cx, cy)); // Top-Right
|
||||
Mat q2(magI, Rect(0, cy, cx, cy)); // Bottom-Left
|
||||
Mat q3(magI, Rect(cx, cy, cx, cy)); // Bottom-Right
|
||||
|
||||
Mat tmp; // swap quadrants (Top-Left with Bottom-Right)
|
||||
q0.copyTo(tmp);
|
||||
q3.copyTo(q0);
|
||||
tmp.copyTo(q3);
|
||||
|
||||
q1.copyTo(tmp); // swap quadrant (Top-Right with Bottom-Left)
|
||||
q2.copyTo(q1);
|
||||
tmp.copyTo(q2);
|
||||
@endcode
|
||||
7. **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
|
||||
this range using the @ref cv::normalize() function.
|
||||
@code{.cpp}
|
||||
normalize(magI, magI, 0, 1, NORM_MINMAX); // Transform the matrix with float values into a
|
||||
// viewable image form (float between values 0 and 1).
|
||||
@endcode
|
||||
Result
|
||||
------
|
||||
|
||||
An application idea would be to determine the geometrical orientation present in the image. For
|
||||
example, let us find out if a text is horizontal or not? Looking at some text you'll notice that the
|
||||
text lines sort of form also horizontal lines and the letters form sort of vertical lines. These two
|
||||
main components of a text snippet may be also seen in case of the Fourier transform. Let us use
|
||||
[this horizontal ](samples/data/imageTextN.png) and [this rotated](samples/data/imageTextR.png)
|
||||
image about a text.
|
||||
|
||||
In case of the horizontal text:
|
||||
|
||||

|
||||
|
||||
In case of a rotated text:
|
||||
|
||||

|
||||
|
||||
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
|
||||
the offset and perform an image rotation to correct eventual miss alignments.
|
||||
|
@@ -0,0 +1,273 @@
|
||||
File Input and Output using XML and YAML files {#tutorial_file_input_output_with_xml_yml}
|
||||
==============================================
|
||||
|
||||
Goal
|
||||
----
|
||||
|
||||
You'll find answers for the following questions:
|
||||
|
||||
- How to print and read text entries to a file and OpenCV using YAML or XML files?
|
||||
- How to do the same for OpenCV data structures?
|
||||
- How to do this for your data structures?
|
||||
- Usage of OpenCV data structures such as @ref cv::FileStorage , @ref cv::FileNode or @ref
|
||||
cv::FileNodeIterator .
|
||||
|
||||
Source code
|
||||
-----------
|
||||
|
||||
You can [download this from here
|
||||
](samples/cpp/tutorial_code/core/file_input_output/file_input_output.cpp) or find it in the
|
||||
`samples/cpp/tutorial_code/core/file_input_output/file_input_output.cpp` of the OpenCV source code
|
||||
library.
|
||||
|
||||
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
|
||||
|
||||
lines
|
||||
1-7, 21-154
|
||||
|
||||
Explanation
|
||||
-----------
|
||||
|
||||
Here we talk only about XML and YAML file inputs. Your output (and its respective input) file may
|
||||
have only one of these extensions and the structure coming from this. They are two kinds of data
|
||||
structures you may serialize: *mappings* (like the STL map) and *element sequence* (like the STL
|
||||
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.
|
||||
|
||||
1. **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
|
||||
specify that this structure to which file binds on your hard drive you can use either its
|
||||
constructor or the *open()* function of this:
|
||||
@code{.cpp}
|
||||
string filename = "I.xml";
|
||||
FileStorage fs(filename, FileStorage::WRITE);
|
||||
//...
|
||||
fs.open(filename, FileStorage::READ);
|
||||
@endcode
|
||||
Either one of this you use the second argument is a constant specifying the type of operations
|
||||
you'll be able to on them: WRITE, READ or APPEND. The extension specified in the file name also
|
||||
determinates the output format that will be used. The output may be even compressed if you
|
||||
specify an extension such as *.xml.gz*.
|
||||
|
||||
The file automatically closes when the @ref cv::FileStorage objects is destroyed. However, you
|
||||
may explicitly call for this by using the *release* function:
|
||||
@code{.cpp}
|
||||
fs.release(); // explicit close
|
||||
@endcode
|
||||
2. **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
|
||||
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 :
|
||||
@code{.cpp}
|
||||
fs << "iterationNr" << 100;
|
||||
@endcode
|
||||
Reading in is a simple addressing (via the [] operator) and casting operation or a read via
|
||||
the \>\> operator :
|
||||
@code{.cpp}
|
||||
int itNr;
|
||||
fs["iterationNr"] >> itNr;
|
||||
itNr = (int) fs["iterationNr"];
|
||||
@endcode
|
||||
3. **Input/Output of OpenCV Data structures.** Well these behave exactly just as the basic C++
|
||||
types:
|
||||
@code{.cpp}
|
||||
Mat R = Mat_<uchar >::eye (3, 3),
|
||||
T = Mat_<double>::zeros(3, 1);
|
||||
|
||||
fs << "R" << R; // Write cv::Mat
|
||||
fs << "T" << T;
|
||||
|
||||
fs["R"] >> R; // Read cv::Mat
|
||||
fs["T"] >> T;
|
||||
@endcode
|
||||
4. **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
|
||||
then we have to specify if our output is either a sequence or map.
|
||||
|
||||
For sequence before the first element print the "[" character and after the last one the "]"
|
||||
character:
|
||||
@code{.cpp}
|
||||
fs << "strings" << "["; // text - string sequence
|
||||
fs << "image1.jpg" << "Awesomeness" << "baboon.jpg";
|
||||
fs << "]"; // close sequence
|
||||
@endcode
|
||||
For maps the drill is the same however now we use the "{" and "}" delimiter characters:
|
||||
@code{.cpp}
|
||||
fs << "Mapping"; // text - mapping
|
||||
fs << "{" << "One" << 1;
|
||||
fs << "Two" << 2 << "}";
|
||||
@endcode
|
||||
To read from these we use the @ref cv::FileNode and the @ref cv::FileNodeIterator data
|
||||
structures. The [] operator of the @ref cv::FileStorage class returns a @ref cv::FileNode data
|
||||
type. If the node is sequential we can use the @ref cv::FileNodeIterator to iterate through the
|
||||
items:
|
||||
@code{.cpp}
|
||||
FileNode n = fs["strings"]; // Read string sequence - Get node
|
||||
if (n.type() != FileNode::SEQ)
|
||||
{
|
||||
cerr << "strings is not a sequence! FAIL" << endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
FileNodeIterator it = n.begin(), it_end = n.end(); // Go through the node
|
||||
for (; it != it_end; ++it)
|
||||
cout << (string)*it << endl;
|
||||
@endcode
|
||||
For maps you can use the [] operator again to acces the given item (or the \>\> operator too):
|
||||
@code{.cpp}
|
||||
n = fs["Mapping"]; // Read mappings from a sequence
|
||||
cout << "Two " << (int)(n["Two"]) << "; ";
|
||||
cout << "One " << (int)(n["One"]) << endl << endl;
|
||||
@endcode
|
||||
5. **Read and write your own data structures.** Suppose you have a data structure such as:
|
||||
@code{.cpp}
|
||||
class MyData
|
||||
{
|
||||
public:
|
||||
MyData() : A(0), X(0), id() {}
|
||||
public: // Data Members
|
||||
int A;
|
||||
double X;
|
||||
string id;
|
||||
};
|
||||
@endcode
|
||||
It's possible to serialize this through the OpenCV I/O XML/YAML interface (just as in case of
|
||||
the OpenCV data structures) by adding a read and a write function inside and outside of your
|
||||
class. For the inside part:
|
||||
@code{.cpp}
|
||||
void write(FileStorage& fs) const //Write serialization for this class
|
||||
{
|
||||
fs << "{" << "A" << A << "X" << X << "id" << id << "}";
|
||||
}
|
||||
|
||||
void read(const FileNode& node) //Read serialization for this class
|
||||
{
|
||||
A = (int)node["A"];
|
||||
X = (double)node["X"];
|
||||
id = (string)node["id"];
|
||||
}
|
||||
@endcode
|
||||
Then you need to add the following functions definitions outside the class:
|
||||
@code{.cpp}
|
||||
void write(FileStorage& fs, const std::string&, const MyData& x)
|
||||
{
|
||||
x.write(fs);
|
||||
}
|
||||
|
||||
void read(const FileNode& node, MyData& x, const MyData& default_value = MyData())
|
||||
{
|
||||
if(node.empty())
|
||||
x = default_value;
|
||||
else
|
||||
x.read(node);
|
||||
}
|
||||
@endcode
|
||||
Here you can observe that in the read section we defined what happens if the user tries to read
|
||||
a non-existing node. In this case we just return the default initialization value, however a
|
||||
more verbose solution would be to return for instance a minus one value for an object ID.
|
||||
|
||||
Once you added these four functions use the \>\> operator for write and the \<\< operator for
|
||||
read:
|
||||
@code{.cpp}
|
||||
MyData m(1);
|
||||
fs << "MyData" << m; // your own data structures
|
||||
fs["MyData"] >> m; // Read your own structure_
|
||||
@endcode
|
||||
Or to try out reading a non-existing read:
|
||||
@code{.cpp}
|
||||
fs["NonExisting"] >> m; // Do not add a fs << "NonExisting" << m command for this to work
|
||||
cout << endl << "NonExisting = " << endl << m << endl;
|
||||
@endcode
|
||||
Result
|
||||
------
|
||||
|
||||
Well mostly we just print out the defined numbers. On the screen of your console you could see:
|
||||
@code{.bash}
|
||||
Write Done.
|
||||
|
||||
Reading:
|
||||
100image1.jpg
|
||||
Awesomeness
|
||||
baboon.jpg
|
||||
Two 2; One 1
|
||||
|
||||
|
||||
R = [1, 0, 0;
|
||||
0, 1, 0;
|
||||
0, 0, 1]
|
||||
T = [0; 0; 0]
|
||||
|
||||
MyData =
|
||||
{ id = mydata1234, X = 3.14159, A = 97}
|
||||
|
||||
Attempt to read NonExisting (should initialize the data structure with its default).
|
||||
NonExisting =
|
||||
{ id = , X = 0, A = 0}
|
||||
|
||||
Tip: Open up output.xml with a text editor to see the serialized data.
|
||||
@endcode
|
||||
Nevertheless, it's much more interesting what you may see in the output xml file:
|
||||
@code{.xml}
|
||||
<?xml version="1.0"?>
|
||||
<opencv_storage>
|
||||
<iterationNr>100</iterationNr>
|
||||
<strings>
|
||||
image1.jpg Awesomeness baboon.jpg</strings>
|
||||
<Mapping>
|
||||
<One>1</One>
|
||||
<Two>2</Two></Mapping>
|
||||
<R type_id="opencv-matrix">
|
||||
<rows>3</rows>
|
||||
<cols>3</cols>
|
||||
<dt>u</dt>
|
||||
<data>
|
||||
1 0 0 0 1 0 0 0 1</data></R>
|
||||
<T type_id="opencv-matrix">
|
||||
<rows>3</rows>
|
||||
<cols>1</cols>
|
||||
<dt>d</dt>
|
||||
<data>
|
||||
0. 0. 0.</data></T>
|
||||
<MyData>
|
||||
<A>97</A>
|
||||
<X>3.1415926535897931e+000</X>
|
||||
<id>mydata1234</id></MyData>
|
||||
</opencv_storage>
|
||||
@endcode
|
||||
Or the YAML file:
|
||||
@code{.yaml}
|
||||
%YAML:1.0
|
||||
iterationNr: 100
|
||||
strings:
|
||||
- "image1.jpg"
|
||||
- Awesomeness
|
||||
- "baboon.jpg"
|
||||
Mapping:
|
||||
One: 1
|
||||
Two: 2
|
||||
R: !!opencv-matrix
|
||||
rows: 3
|
||||
cols: 3
|
||||
dt: u
|
||||
data: [ 1, 0, 0, 0, 1, 0, 0, 0, 1 ]
|
||||
T: !!opencv-matrix
|
||||
rows: 3
|
||||
cols: 1
|
||||
dt: d
|
||||
data: [ 0., 0., 0. ]
|
||||
MyData:
|
||||
A: 97
|
||||
X: 3.1415926535897931e+000
|
||||
id: mydata1234
|
||||
@endcode
|
||||
You may observe a runtime instance of this on the [YouTube
|
||||
here](https://www.youtube.com/watch?v=A4yqVnByMMM) .
|
||||
|
||||
\htmlonly
|
||||
<div align="center">
|
||||
<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>
|
||||
\endhtmlonly
|
||||
|
@@ -0,0 +1,253 @@
|
||||
How to scan images, lookup tables and time measurement with OpenCV {#tutorial_how_to_scan_images}
|
||||
==================================================================
|
||||
|
||||
Goal
|
||||
----
|
||||
|
||||
We'll seek answers for the following questions:
|
||||
|
||||
- How to go through each and every pixel of an image?
|
||||
- How is OpenCV matrix values stored?
|
||||
- How to measure the performance of our algorithm?
|
||||
- What are lookup tables and why use them?
|
||||
|
||||
Our test case
|
||||
-------------
|
||||
|
||||
Let us consider a simple color reduction method. By using the unsigned char C and C++ type for
|
||||
matrix item storing, a channel of pixel may have up to 256 different values. For a three channel
|
||||
image this can allow the formation of way too many colors (16 million to be exact). Working with so
|
||||
many color shades may give a heavy blow to our algorithm performance. However, sometimes it is
|
||||
enough to work with a lot less of them to get the same final result.
|
||||
|
||||
In this cases it's common that we make a *color space reduction*. This means that we divide the
|
||||
color space current value with a new input value to end up with fewer colors. For instance every
|
||||
value between zero and nine takes the new value zero, every value between ten and nineteen the value
|
||||
ten and so on.
|
||||
|
||||
When you divide an *uchar* (unsigned char - aka values between zero and 255) value with an *int*
|
||||
value the result will be also *char*. These values may only be char values. Therefore, any fraction
|
||||
will be rounded down. Taking advantage of this fact the upper operation in the *uchar* domain may be
|
||||
expressed as:
|
||||
|
||||
\f[I_{new} = (\frac{I_{old}}{10}) * 10\f]
|
||||
|
||||
A simple color space reduction algorithm would consist of just passing through every pixel of an
|
||||
image matrix and applying this formula. It's worth noting that we do a divide and a multiplication
|
||||
operation. These operations are bloody expensive for a system. If possible it's worth avoiding them
|
||||
by using cheaper operations such as a few subtractions, addition or in best case a simple
|
||||
assignment. Furthermore, note that we only have a limited number of input values for the upper
|
||||
operation. In case of the *uchar* system this is 256 to be exact.
|
||||
|
||||
Therefore, for larger images it would be wise to calculate all possible values beforehand and during
|
||||
the assignment just make the assignment, by using a lookup table. Lookup tables are simple arrays
|
||||
(having one or more dimensions) that for a given input value variation holds the final output value.
|
||||
Its strength lies that we do not need to make the calculation, we just need to read the result.
|
||||
|
||||
Our test case program (and the sample presented here) will do the following: read in a console line
|
||||
argument image (that may be either color or gray scale - console line argument too) and apply the
|
||||
reduction with the given console line argument integer value. In OpenCV, at the moment they are
|
||||
three major ways of going through an image pixel by pixel. To make things a little more interesting
|
||||
will make the scanning for each image using all of these methods, and print out how long it took.
|
||||
|
||||
You can download the full source code [here
|
||||
](samples/cpp/tutorial_code/core/how_to_scan_images/how_to_scan_images.cpp) or look it up in
|
||||
the samples directory of OpenCV at the cpp tutorial code for the core section. Its basic usage is:
|
||||
@code{.bash}
|
||||
how_to_scan_images imageName.jpg intValueToReduce [G]
|
||||
@endcode
|
||||
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.
|
||||
|
||||
@includelineno cpp/tutorial_code/core/how_to_scan_images/how_to_scan_images.cpp
|
||||
|
||||
lines
|
||||
49-61
|
||||
|
||||
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.
|
||||
No OpenCV specific stuff here.
|
||||
|
||||
Another issue is how do we measure time? Well OpenCV offers two simple functions to achieve this
|
||||
@ref cv::getTickCount() and @ref cv::getTickFrequency() . The first returns the number of ticks of
|
||||
your systems CPU from a certain event (like since you booted your system). The second returns how
|
||||
many times your CPU emits a tick during a second. So to measure in seconds the number of time
|
||||
elapsed between two operations is easy as:
|
||||
@code{.cpp}
|
||||
double t = (double)getTickCount();
|
||||
// do something ...
|
||||
t = ((double)getTickCount() - t)/getTickFrequency();
|
||||
cout << "Times passed in seconds: " << t << endl;
|
||||
@endcode
|
||||
How the image matrix is stored in the memory?
|
||||
---------------------------------------------
|
||||
|
||||
As you could already read in my @ref matTheBasicImageContainer tutorial the size of the matrix
|
||||
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:
|
||||
|
||||
\f[\newcommand{\tabItG}[1] { \textcolor{black}{#1} \cellcolor[gray]{0.8}}
|
||||
\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
|
||||
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}}
|
||||
\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
|
||||
is large enough to store the rows in a successive fashion the rows may follow one after another,
|
||||
creating a single long row. Because everything is in a single place following one after another this
|
||||
may help to speed up the scanning process. We can use the @ref cv::isContinuous() function to *ask*
|
||||
the matrix if this is the case. Continue on to the next section to find an example.
|
||||
|
||||
The efficient way
|
||||
-----------------
|
||||
|
||||
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:
|
||||
|
||||
@includelineno cpp/tutorial_code/core/how_to_scan_images/how_to_scan_images.cpp
|
||||
|
||||
lines
|
||||
126-153
|
||||
|
||||
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
|
||||
pointer a single time and go all the way to the end. We need to look out for color images: we have
|
||||
three channels so we need to pass through three times more items in each row.
|
||||
|
||||
There's another way of this. The *data* data member of a *Mat* object returns the pointer to the
|
||||
first row, first column. If this pointer is null you have no valid input in that object. Checking
|
||||
this is the simplest method to check if your image loading was a success. In case the storage is
|
||||
continues we can use this to go through the whole data pointer. In case of a gray scale image this
|
||||
would look like:
|
||||
@code{.cpp}
|
||||
uchar* p = I.data;
|
||||
|
||||
for( unsigned int i =0; i < ncol*nrows; ++i)
|
||||
*p++ = table[*p];
|
||||
@endcode
|
||||
You would get the same result. However, this code is a lot harder to read later on. It gets even
|
||||
harder if you have some more advanced technique there. Moreover, in practice I've observed you'll
|
||||
get the same performance result (as most of the modern compilers will probably make this small
|
||||
optimization trick automatically for you).
|
||||
|
||||
The iterator (safe) method
|
||||
--------------------------
|
||||
|
||||
In case of the efficient way making sure that you pass through the right amount of *uchar* fields
|
||||
and to skip the gaps that may occur between the rows was your responsibility. The iterator method is
|
||||
considered a safer way as it takes over these tasks from the user. All you need to do is ask 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).
|
||||
|
||||
@includelineno cpp/tutorial_code/core/how_to_scan_images/how_to_scan_images.cpp
|
||||
|
||||
lines
|
||||
155-183
|
||||
|
||||
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
|
||||
column we use simple operator[] access. It's important to remember that OpenCV iterators go through
|
||||
the columns and automatically skip to the next row. Therefore in case of color images if you use a
|
||||
simple *uchar* iterator you'll be able to access only the blue channel values.
|
||||
|
||||
On-the-fly address calculation with reference returning
|
||||
-------------------------------------------------------
|
||||
|
||||
The final method isn't recommended for scanning. It was made to acquire or modify somehow random
|
||||
elements in the image. Its basic usage is to specify the row and column number of the item you want
|
||||
to access. During our earlier scanning methods you could already observe that is important through
|
||||
what type we are looking at the image. It's no different here as you need manually to specify what
|
||||
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):
|
||||
|
||||
@includelineno cpp/tutorial_code/core/how_to_scan_images/how_to_scan_images.cpp
|
||||
|
||||
lines
|
||||
185-217
|
||||
|
||||
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
|
||||
non-constant when you *set* the value. As a safety step in **debug mode only**\* there is performed
|
||||
a check that your input coordinates are valid and does exist. If this isn't the case you'll get a
|
||||
nice output message of this on the standard error output stream. Compared to the efficient way in
|
||||
release mode the only difference in using this is that for every element of the image you'll get a
|
||||
new row pointer for what we use the C operator[] to acquire the column element.
|
||||
|
||||
If you need to multiple lookups using this method for an image it may be troublesome and time
|
||||
consuming to enter the type and the at keyword for each of the accesses. To solve this problem
|
||||
OpenCV has a @ref cv::Mat_ data type. It's the same as Mat with the extra need that at definition
|
||||
you need to specify the data type through what to look at the data matrix, however in return you can
|
||||
use the operator() for fast access of items. To make things even better this is easily convertible
|
||||
from and to the usual @ref cv::Mat data type. A sample usage of this you can see in case of the
|
||||
color images of the upper function. Nevertheless, it's important to note that the same operation
|
||||
(with the same runtime speed) could have been done with the @ref cv::at() function. It's just a less
|
||||
to write for the lazy programmer trick.
|
||||
|
||||
The Core Function
|
||||
-----------------
|
||||
|
||||
This is a bonus method of achieving lookup table modification in an image. Because in image
|
||||
processing it's quite common that you want to replace all of a given image value to some other value
|
||||
OpenCV has a function that makes the modification without the need from you to write the scanning of
|
||||
the image. We use the @ref cv::LUT() function of the core module. First we build a Mat type of the
|
||||
lookup table:
|
||||
|
||||
@includelineno cpp/tutorial_code/core/how_to_scan_images/how_to_scan_images.cpp
|
||||
|
||||
lines
|
||||
108-111
|
||||
|
||||
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
|
||||
|
||||
lines
|
||||
116
|
||||
|
||||
Performance Difference
|
||||
----------------------
|
||||
|
||||
For the best result compile the program and run it on your own speed. For showing off better the
|
||||
differences I've used a quite large (2560 X 1600) image. The performance presented here are for
|
||||
color images. For a more accurate value I've averaged the value I got from the call of the function
|
||||
for hundred times.
|
||||
|
||||
--------------- ----------------------
|
||||
Efficient Way 79.4717 milliseconds
|
||||
Iterator 83.7201 milliseconds
|
||||
On-The-Fly RA 93.7878 milliseconds
|
||||
LUT function 32.5759 milliseconds
|
||||
--------------- ----------------------
|
||||
|
||||
We can conclude a couple of things. If possible, use the already made functions of OpenCV (instead
|
||||
reinventing these). The fastest method turns out to be the LUT function. This is because the OpenCV
|
||||
library is multi-thread enabled via Intel Threaded Building Blocks. However, if you need to write a
|
||||
simple image scan prefer the pointer method. The iterator is a safer bet, however quite slower.
|
||||
Using the on-the-fly reference access method for full image scan is the most costly in debug mode.
|
||||
In the release mode it may beat the iterator approach or not, however it surely sacrifices for this
|
||||
the safety trait of iterators.
|
||||
|
||||
Finally, you may watch a sample run of the program on the [video
|
||||
posted](https://www.youtube.com/watch?v=fB3AN5fjgwc) on our YouTube channel.
|
||||
|
||||
\htmlonly
|
||||
<div align="center">
|
||||
<iframe title="How to scan images in OpenCV?" width="560" height="349" src="http://www.youtube.com/embed/fB3AN5fjgwc?rel=0&loop=1" frameborder="0" allowfullscreen align="middle"></iframe>
|
||||
</div>
|
||||
\endhtmlonly
|
||||
|
@@ -0,0 +1,144 @@
|
||||
Intel® IPP Asynchronous C/C++ library in OpenCV {#tutorial_how_to_use_ippa_conversion}
|
||||
===============================================
|
||||
|
||||
Goal
|
||||
----
|
||||
|
||||
The tutorial demonstrates the [Intel® IPP Asynchronous
|
||||
C/C++](http://software.intel.com/en-us/intel-ipp-preview) library usage with OpenCV. The code
|
||||
example below illustrates implementation of the Sobel operation, accelerated with Intel® IPP
|
||||
Asynchronous C/C++ functions. In this code example, @ref cv::hpp::getMat and @ref cv::hpp::getHpp
|
||||
functions are used for data conversion between
|
||||
[hppiMatrix](http://software.intel.com/en-us/node/501660) and Mat matrices.
|
||||
|
||||
Code
|
||||
----
|
||||
|
||||
You may also find the source code in the
|
||||
`samples/cpp/tutorial_code/core/ippasync/ippasync_sample.cpp` file of the OpenCV source library or
|
||||
download it from here
|
||||
\<../../../../samples/cpp/tutorial_code/core/ippasync/ippasync_sample.cpp\>.
|
||||
|
||||
@includelineno cpp/tutorial_code/core/ippasync/ippasync_sample.cpp
|
||||
|
||||
Explanation
|
||||
-----------
|
||||
|
||||
1. Create parameters for OpenCV:
|
||||
@code{.cpp}
|
||||
VideoCapture cap;
|
||||
Mat image, gray, result;
|
||||
@endcode
|
||||
and IPP Async:
|
||||
@code{.cpp}
|
||||
hppiMatrix* src,* dst;
|
||||
hppAccel accel = 0;
|
||||
hppAccelType accelType;
|
||||
hppStatus sts;
|
||||
hppiVirtualMatrix * virtMatrix;
|
||||
@endcode
|
||||
2. Load input image or video. How to open and read video stream you can see in the @ref
|
||||
videoInputPSNRMSSIM tutorial.
|
||||
@code{.cpp}
|
||||
if( useCamera )
|
||||
{
|
||||
printf("used camera\n");
|
||||
cap.open(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("used image %s\n", file.c_str());
|
||||
cap.open(file.c_str());
|
||||
}
|
||||
|
||||
if( !cap.isOpened() )
|
||||
{
|
||||
printf("can not open camera or video file\n");
|
||||
return -1;
|
||||
}
|
||||
@endcode
|
||||
3. Create accelerator instance using
|
||||
[hppCreateInstance](http://software.intel.com/en-us/node/501686):
|
||||
@code{.cpp}
|
||||
accelType = sAccel == "cpu" ? HPP_ACCEL_TYPE_CPU:
|
||||
sAccel == "gpu" ? HPP_ACCEL_TYPE_GPU:
|
||||
HPP_ACCEL_TYPE_ANY;
|
||||
|
||||
//Create accelerator instance
|
||||
sts = hppCreateInstance(accelType, 0, &accel);
|
||||
CHECK_STATUS(sts, "hppCreateInstance");
|
||||
@endcode
|
||||
4. Create an array of virtual matrices using
|
||||
[hppiCreateVirtualMatrices](http://software.intel.com/en-us/node/501700) function.
|
||||
@code{.cpp}
|
||||
virtMatrix = hppiCreateVirtualMatrices(accel, 1);
|
||||
@endcode
|
||||
5. Prepare a matrix for input and output data:
|
||||
@code{.cpp}
|
||||
cap >> image;
|
||||
if(image.empty())
|
||||
break;
|
||||
|
||||
cvtColor( image, gray, COLOR_BGR2GRAY );
|
||||
|
||||
result.create( image.rows, image.cols, CV_8U);
|
||||
@endcode
|
||||
6. Convert Mat to [hppiMatrix](http://software.intel.com/en-us/node/501660) using @ref cv::getHpp
|
||||
and call [hppiSobel](http://software.intel.com/en-us/node/474701) function.
|
||||
@code{.cpp}
|
||||
//convert Mat to hppiMatrix
|
||||
src = getHpp(gray, accel);
|
||||
dst = getHpp(result, accel);
|
||||
|
||||
sts = hppiSobel(accel,src, HPP_MASK_SIZE_3X3,HPP_NORM_L1,virtMatrix[0]);
|
||||
CHECK_STATUS(sts,"hppiSobel");
|
||||
|
||||
sts = hppiConvert(accel, virtMatrix[0], 0, HPP_RND_MODE_NEAR, dst, HPP_DATA_TYPE_8U);
|
||||
CHECK_STATUS(sts,"hppiConvert");
|
||||
|
||||
// Wait for tasks to complete
|
||||
sts = hppWait(accel, HPP_TIME_OUT_INFINITE);
|
||||
CHECK_STATUS(sts, "hppWait");
|
||||
@endcode
|
||||
We use [hppiConvert](http://software.intel.com/en-us/node/501746) because
|
||||
[hppiSobel](http://software.intel.com/en-us/node/474701) returns destination matrix with
|
||||
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.
|
||||
|
||||
7. Create windows and show the images, the usual way.
|
||||
@code{.cpp}
|
||||
imshow("image", image);
|
||||
imshow("rez", result);
|
||||
|
||||
waitKey(15);
|
||||
@endcode
|
||||
8. Delete hpp matrices.
|
||||
@code{.cpp}
|
||||
sts = hppiFreeMatrix(src);
|
||||
CHECK_DEL_STATUS(sts,"hppiFreeMatrix");
|
||||
|
||||
sts = hppiFreeMatrix(dst);
|
||||
CHECK_DEL_STATUS(sts,"hppiFreeMatrix");
|
||||
@endcode
|
||||
9. Delete virtual matrices and accelerator instance.
|
||||
@code{.cpp}
|
||||
if (virtMatrix)
|
||||
{
|
||||
sts = hppiDeleteVirtualMatrices(accel, virtMatrix);
|
||||
CHECK_DEL_STATUS(sts,"hppiDeleteVirtualMatrices");
|
||||
}
|
||||
|
||||
if (accel)
|
||||
{
|
||||
sts = hppDeleteInstance(accel);
|
||||
CHECK_DEL_STATUS(sts, "hppDeleteInstance");
|
||||
}
|
||||
@endcode
|
||||
Result
|
||||
------
|
||||
|
||||
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.
|
||||
|
||||

|
||||
|
@@ -0,0 +1,164 @@
|
||||
Interoperability with OpenCV 1 {#tutorial_interoperability_with_OpenCV_1}
|
||||
==============================
|
||||
|
||||
Goal
|
||||
----
|
||||
|
||||
For the OpenCV developer team it's important to constantly improve the library. We are constantly
|
||||
thinking about methods that will ease your work process, while still maintain the libraries
|
||||
flexibility. The new C++ interface is a development of us that serves this goal. Nevertheless,
|
||||
backward compatibility remains important. We do not want to break your code written for earlier
|
||||
version of the OpenCV library. Therefore, we made sure that we add some functions that deal with
|
||||
this. In the following you'll learn:
|
||||
|
||||
- What changed with the version 2 of OpenCV in the way you use the library compared to its first
|
||||
version
|
||||
- How to add some Gaussian noise to an image
|
||||
- What are lookup tables and why use them?
|
||||
|
||||
General
|
||||
-------
|
||||
|
||||
When making the switch you first need to learn some about the new data structure for images: @ref
|
||||
matTheBasicImageContainer, this replaces the old *CvMat* and *IplImage* ones. Switching to the new
|
||||
functions is easier. You just need to remember a couple of new things.
|
||||
|
||||
OpenCV 2 received reorganization. No longer are all the functions crammed into a single library. We
|
||||
have many modules, each of them containing data structures and functions relevant to certain tasks.
|
||||
This way you do not need to ship a large library if you use just a subset of OpenCV. This means that
|
||||
you should also include only those headers you will use. For example:
|
||||
@code{.cpp}
|
||||
#include <opencv2/core.hpp>
|
||||
#include <opencv2/imgproc.hpp>
|
||||
#include <opencv2/highgui.hpp>
|
||||
@endcode
|
||||
All the OpenCV related stuff is put into the *cv* namespace to avoid name conflicts with other
|
||||
libraries data structures and functions. Therefore, either you need to prepend the *cv::* keyword
|
||||
before everything that comes from OpenCV or after the includes, you just add a directive to use
|
||||
this:
|
||||
@code{.cpp}
|
||||
using namespace cv; // The new C++ interface API is inside this namespace. Import it.
|
||||
@endcode
|
||||
Because the functions are already in a namespace there is no need for them to contain the *cv*
|
||||
prefix in their name. As such all the new C++ compatible functions don't have this and they follow
|
||||
the camel case naming rule. This means the first letter is small (unless it's a name, like Canny)
|
||||
and the subsequent words start with a capital letter (like *copyMakeBorder*).
|
||||
|
||||
Now, remember that you need to link to your application all the modules you use, and in case you are
|
||||
on Windows using the *DLL* system you will need to add, again, to the path all the binaries. For
|
||||
more in-depth information if you're on Windows read @ref Windows_Visual_Studio_How_To and for
|
||||
Linux an example usage is explained in @ref Linux_Eclipse_Usage.
|
||||
|
||||
Now for converting the *Mat* object you can use either the *IplImage* or the *CvMat* operators.
|
||||
While in the C interface you used to work with pointers here it's no longer the case. In the C++
|
||||
interface we have mostly *Mat* objects. These objects may be freely converted to both *IplImage* and
|
||||
*CvMat* with simple assignment. For example:
|
||||
@code{.cpp}
|
||||
Mat I;
|
||||
IplImage pI = I;
|
||||
CvMat mI = I;
|
||||
@endcode
|
||||
Now if you want pointers the conversion gets just a little more complicated. The compilers can no
|
||||
longer automatically determinate what you want and as you need to explicitly specify your goal. This
|
||||
is to call the *IplImage* and *CvMat* operators and then get their pointers. For getting the pointer
|
||||
we use the & sign:
|
||||
@code{.cpp}
|
||||
Mat I;
|
||||
IplImage* pI = &I.operator IplImage();
|
||||
CvMat* mI = &I.operator CvMat();
|
||||
@endcode
|
||||
One of the biggest complaints of the C interface is that it leaves all the memory management to you.
|
||||
You need to figure out when it is safe to release your unused objects and make sure you do so before
|
||||
the program finishes or you could have troublesome memory leeks. To work around this issue in OpenCV
|
||||
there is introduced a sort of smart pointer. This will automatically release the object when it's no
|
||||
longer in use. To use this declare the pointers as a specialization of the *Ptr* :
|
||||
@code{.cpp}
|
||||
Ptr<IplImage> piI = &I.operator IplImage();
|
||||
@endcode
|
||||
Converting from the C data structures to the *Mat* is done by passing these inside its constructor.
|
||||
For example:
|
||||
@code{.cpp}
|
||||
Mat K(piL), L;
|
||||
L = Mat(pI);
|
||||
@endcode
|
||||
A case study
|
||||
------------
|
||||
|
||||
Now that you have the basics done [here's
|
||||
](samples/cpp/tutorial_code/core/interoperability_with_OpenCV_1/interoperability_with_OpenCV_1.cpp)
|
||||
an example that mixes the usage of the C interface with the C++ one. You will also find it in the
|
||||
sample directory of the OpenCV source code library at the
|
||||
`samples/cpp/tutorial_code/core/interoperability_with_OpenCV_1/interoperability_with_OpenCV_1.cpp` .
|
||||
To further help on seeing the difference the programs supports two modes: one mixed C and C++ and
|
||||
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.
|
||||
|
||||
@includelineno
|
||||
cpp/tutorial_code/core/interoperability_with_OpenCV_1/interoperability_with_OpenCV_1.cpp
|
||||
|
||||
lines
|
||||
1-10, 23-26, 29-46
|
||||
|
||||
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.
|
||||
|
||||
@includelineno
|
||||
cpp/tutorial_code/core/interoperability_with_OpenCV_1/interoperability_with_OpenCV_1.cpp
|
||||
|
||||
lines
|
||||
48-53
|
||||
|
||||
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:
|
||||
in the first example it processes each plane using one of the three major image scanning algorithms
|
||||
in OpenCV (C [] operator, iterator, individual element access). In a second variant we add to the
|
||||
image some Gaussian noise and then mix together the channels according to some formula.
|
||||
|
||||
The scanning version looks like:
|
||||
|
||||
@includelineno
|
||||
cpp/tutorial_code/core/interoperability_with_OpenCV_1/interoperability_with_OpenCV_1.cpp
|
||||
|
||||
lines
|
||||
57-77
|
||||
|
||||
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
|
||||
description of these in the @ref howToScanImagesOpenCV tutorial. Converting from the old function
|
||||
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:
|
||||
|
||||
@includelineno
|
||||
cpp/tutorial_code/core/interoperability_with_OpenCV_1/interoperability_with_OpenCV_1.cpp
|
||||
|
||||
lines
|
||||
81-113
|
||||
|
||||
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.
|
||||
|
||||
@includelineno
|
||||
cpp/tutorial_code/core/interoperability_with_OpenCV_1/interoperability_with_OpenCV_1.cpp
|
||||
|
||||
lines
|
||||
117-129
|
||||
|
||||
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
|
||||
output:
|
||||
|
||||

|
||||
|
||||
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
|
||||
](samples/cpp/tutorial_code/core/interoperability_with_OpenCV_1/interoperability_with_OpenCV_1.cpp)
|
||||
or find it in the
|
||||
`samples/cpp/tutorial_code/core/interoperability_with_OpenCV_1/interoperability_with_OpenCV_1.cpp`
|
||||
of the OpenCV source code library.
|
||||
|
||||
\htmlonly
|
||||
<div align="center">
|
||||
<iframe title="Interoperability with OpenCV 1" width="560" height="349" src="http://www.youtube.com/embed/qckm-zvo31w?rel=0&loop=1" frameborder="0" allowfullscreen align="middle"></iframe>
|
||||
</div>
|
||||
\endhtmlonly
|
||||
|
@@ -0,0 +1,146 @@
|
||||
Mask operations on matrices {#tutorial_mat_mask_operations}
|
||||
===========================
|
||||
|
||||
Mask operations on matrices are quite simple. The idea is that we recalculate each pixels value in
|
||||
an image according to a mask matrix (also known as kernel). This mask holds values that will adjust
|
||||
how much influence neighboring pixels (and the current pixel) have on the new pixel value. From a
|
||||
mathematical point of view we make a weighted average, with our specified values.
|
||||
|
||||
Our test case
|
||||
-------------
|
||||
|
||||
Let us consider the issue of an image contrast enhancement method. Basically we want to apply for
|
||||
every pixel of the image the following formula:
|
||||
|
||||
\f[I(i,j) = 5*I(i,j) - [ I(i-1,j) + I(i+1,j) + I(i,j-1) + I(i,j+1)]\f]\f[\iff I(i,j)*M, \text{where }
|
||||
M = \bordermatrix{ _i\backslash ^j & -1 & 0 & +1 \cr
|
||||
-1 & 0 & -1 & 0 \cr
|
||||
0 & -1 & 5 & -1 \cr
|
||||
+1 & 0 & -1 & 0 \cr
|
||||
}\f]
|
||||
|
||||
The first notation is by using a formula, while the second is a compacted version of the first by
|
||||
using a mask. You use the mask by putting the center of the mask matrix (in the upper case noted by
|
||||
the zero-zero index) on the pixel you want to calculate and sum up the pixel values multiplied with
|
||||
the overlapped matrix values. It's the same thing, however in case of large matrices the latter
|
||||
notation is a lot easier to look over.
|
||||
|
||||
Now let us see how we can make this happen by using the basic pixel access method or by using the
|
||||
@ref cv::filter2D function.
|
||||
|
||||
The Basic Method
|
||||
----------------
|
||||
|
||||
Here's a function that will do this:
|
||||
@code{.cpp}
|
||||
void Sharpen(const Mat& myImage, Mat& Result)
|
||||
{
|
||||
CV_Assert(myImage.depth() == CV_8U); // accept only uchar images
|
||||
|
||||
Result.create(myImage.size(), myImage.type());
|
||||
const int nChannels = myImage.channels();
|
||||
|
||||
for(int j = 1; j < myImage.rows - 1; ++j)
|
||||
{
|
||||
const uchar* previous = myImage.ptr<uchar>(j - 1);
|
||||
const uchar* current = myImage.ptr<uchar>(j );
|
||||
const uchar* next = myImage.ptr<uchar>(j + 1);
|
||||
|
||||
uchar* output = Result.ptr<uchar>(j);
|
||||
|
||||
for(int i = nChannels; i < nChannels * (myImage.cols - 1); ++i)
|
||||
{
|
||||
*output++ = saturate_cast<uchar>(5 * current[i]
|
||||
-current[i - nChannels] - current[i + nChannels] - previous[i] - next[i]);
|
||||
}
|
||||
}
|
||||
|
||||
Result.row(0).setTo(Scalar(0));
|
||||
Result.row(Result.rows - 1).setTo(Scalar(0));
|
||||
Result.col(0).setTo(Scalar(0));
|
||||
Result.col(Result.cols - 1).setTo(Scalar(0));
|
||||
}
|
||||
@endcode
|
||||
At first we make sure that the input images data is in unsigned char format. For this we use the
|
||||
@ref cv::CV_Assert function that throws an error when the expression inside it is false.
|
||||
@code{.cpp}
|
||||
CV_Assert(myImage.depth() == CV_8U); // accept only uchar images
|
||||
@endcode
|
||||
We create an output image with the same size and the same type as our input. As you can see in the
|
||||
@ref How_Image_Stored_Memory section, depending on the number of channels we may have one or more
|
||||
subcolumns. We will iterate through them via pointers so the total number of elements depends from
|
||||
this number.
|
||||
@code{.cpp}
|
||||
Result.create(myImage.size(), myImage.type());
|
||||
const int nChannels = myImage.channels();
|
||||
@endcode
|
||||
We'll use the plain C [] operator to access pixels. Because we need to access multiple rows at the
|
||||
same time we'll acquire the pointers for each of them (a previous, a current and a next line). We
|
||||
need another pointer to where we're going to save the calculation. Then simply access the right
|
||||
items with the [] operator. For moving the output pointer ahead we simply increase this (with one
|
||||
byte) after each operation:
|
||||
@code{.cpp}
|
||||
for(int j = 1; j < myImage.rows - 1; ++j)
|
||||
{
|
||||
const uchar* previous = myImage.ptr<uchar>(j - 1);
|
||||
const uchar* current = myImage.ptr<uchar>(j );
|
||||
const uchar* next = myImage.ptr<uchar>(j + 1);
|
||||
|
||||
uchar* output = Result.ptr<uchar>(j);
|
||||
|
||||
for(int i = nChannels; i < nChannels * (myImage.cols - 1); ++i)
|
||||
{
|
||||
*output++ = saturate_cast<uchar>(5 * current[i]
|
||||
-current[i - nChannels] - current[i + nChannels] - previous[i] - next[i]);
|
||||
}
|
||||
}
|
||||
@endcode
|
||||
On the borders of the image the upper notation results inexistent pixel locations (like minus one -
|
||||
minus one). In these points our formula is undefined. A simple solution is to not apply the kernel
|
||||
in these points and, for example, set the pixels on the borders to zeros:
|
||||
@code{.cpp}
|
||||
Result.row(0).setTo(Scalar(0)); // The top row
|
||||
Result.row(Result.rows - 1).setTo(Scalar(0)); // The bottom row
|
||||
Result.col(0).setTo(Scalar(0)); // The left column
|
||||
Result.col(Result.cols - 1).setTo(Scalar(0)); // The right column
|
||||
@endcode
|
||||
The filter2D function
|
||||
---------------------
|
||||
|
||||
Applying such filters are so common in image processing that in OpenCV there exist a function that
|
||||
will take care of applying the mask (also called a kernel in some places). For this you first need
|
||||
to define a *Mat* object that holds the mask:
|
||||
@code{.cpp}
|
||||
Mat kern = (Mat_<char>(3,3) << 0, -1, 0,
|
||||
-1, 5, -1,
|
||||
0, -1, 0);
|
||||
@endcode
|
||||
Then call the @ref cv::filter2D function specifying the input, the output image and the kernell to
|
||||
use:
|
||||
@code{.cpp}
|
||||
filter2D(I, K, I.depth(), kern);
|
||||
@endcode
|
||||
The function even has a fifth optional argument to specify the center of the kernel, and a sixth one
|
||||
for determining what to do in the regions where the operation is undefined (borders). Using this
|
||||
function has the advantage that it's shorter, less verbose and because there are some optimization
|
||||
techniques implemented it is usually faster than the *hand-coded method*. For example in my test
|
||||
while the second one took only 13 milliseconds the first took around 31 milliseconds. Quite some
|
||||
difference.
|
||||
|
||||
For example:
|
||||
|
||||

|
||||
|
||||
You can download this source code from [here
|
||||
](samples/cpp/tutorial_code/core/mat_mask_operations/mat_mask_operations.cpp) or look in the
|
||||
OpenCV source code libraries sample directory at
|
||||
`samples/cpp/tutorial_code/core/mat_mask_operations/mat_mask_operations.cpp`.
|
||||
|
||||
Check out an instance of running the program on our [YouTube
|
||||
channel](http://www.youtube.com/watch?v=7PF1tAU9se4) .
|
||||
|
||||
\htmlonly
|
||||
<div align="center">
|
||||
<iframe width="560" height="349" src="https://www.youtube.com/embed/7PF1tAU9se4?hd=1" frameborder="0" allowfullscreen></iframe>
|
||||
</div>
|
||||
\endhtmlonly
|
@@ -0,0 +1,358 @@
|
||||
Mat - The Basic Image Container {#tutorial_mat_the_basic_image_container}
|
||||
===============================
|
||||
|
||||
Goal
|
||||
----
|
||||
|
||||
We have multiple ways to acquire digital images from the real world: digital cameras, scanners,
|
||||
computed tomography, and magnetic resonance imaging to name a few. In every case what we (humans)
|
||||
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.
|
||||
|
||||

|
||||
|
||||
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
|
||||
vary according to our needs, but in the end all images inside a computer world may be reduced to
|
||||
numerical matrices and other information describing the matrix itself. *OpenCV* is a computer vision
|
||||
library whose main focus is to process and manipulate this information. Therefore, the first thing
|
||||
you need to be familiar with is how OpenCV stores and handles images.
|
||||
|
||||
*Mat*
|
||||
-----
|
||||
|
||||
OpenCV has been around since 2001. In those days the library was built around a *C* interface and to
|
||||
store the image in the memory they used a C structure called *IplImage*. This is the one you'll see
|
||||
in most of the older tutorials and educational materials. The problem with this is that it brings to
|
||||
the table all the minuses of the C language. The biggest issue is the manual memory management. It
|
||||
builds on the assumption that the user is responsible for taking care of memory allocation and
|
||||
deallocation. While this is not a problem with smaller programs, once your code base grows it will
|
||||
be more of a struggle to handle all this rather than focusing on solving your development goal.
|
||||
|
||||
Luckily C++ came around and introduced the concept of classes making easier for the user through
|
||||
automatic memory management (more or less). The good news is that C++ is fully compatible with C so
|
||||
no compatibility issues can arise from making the change. Therefore, OpenCV 2.0 introduced a new C++
|
||||
interface which offered a new way of doing things which means you do not need to fiddle with memory
|
||||
management, making your code concise (less to write, to achieve more). The main downside of the C++
|
||||
interface is that many embedded development systems at the moment support only C. Therefore, unless
|
||||
you are targeting embedded platforms, there's no point to using the *old* methods (unless you're a
|
||||
masochist programmer and you're asking for trouble).
|
||||
|
||||
The first thing you need to know about *Mat* is that you no longer need to manually allocate its
|
||||
memory and release it as soon as you do not need it. While doing this is still a possibility, most
|
||||
of the OpenCV functions will allocate its output data automatically. As a nice bonus if you pass on
|
||||
an already existing *Mat* object, which has already allocated the required space for the matrix,
|
||||
this will be reused. In other words we use at all times only as much memory as we need to perform
|
||||
the task.
|
||||
|
||||
*Mat* is basically a class with two data parts: the matrix header (containing information such as
|
||||
the size of the matrix, the method used for storing, at which address is the matrix stored, and so
|
||||
on) and a pointer to the matrix containing the pixel values (taking any dimensionality depending on
|
||||
the method chosen for storing) . The matrix header size is constant, however the size of the matrix
|
||||
itself may vary from image to image and usually is larger by orders of magnitude.
|
||||
|
||||
OpenCV is an image processing library. It contains a large collection of image processing functions.
|
||||
To solve a computational challenge, most of the time you will end up using multiple functions of the
|
||||
library. Because of this, passing images to functions is a common practice. We should not forget
|
||||
that we are talking about image processing algorithms, which tend to be quite computational heavy.
|
||||
The last thing we want to do is further decrease the speed of your program by making unnecessary
|
||||
copies of potentially *large* images.
|
||||
|
||||
To tackle this issue OpenCV uses a reference counting system. The idea is that each *Mat* object has
|
||||
its own header, however the matrix may be shared between two instance of them by having their matrix
|
||||
pointers point to the same address. Moreover, the copy operators **will only copy the headers** and
|
||||
the pointer to the large matrix, not the data itself.
|
||||
@code{.cpp}
|
||||
Mat A, C; // creates just the header parts
|
||||
A = imread(argv[1], IMREAD_COLOR); // here we'll know the method used (allocate matrix)
|
||||
|
||||
Mat B(A); // Use the copy constructor
|
||||
|
||||
C = A; // Assignment operator
|
||||
@endcode
|
||||
All the above objects, in the end, point to the same single data matrix. Their headers are
|
||||
different, however, and making a modification using any of them will affect all the other ones as
|
||||
well. In practice the different objects just provide different access method to the same underlying
|
||||
data. Nevertheless, their header parts are different. The real interesting part is that you can
|
||||
create headers which refer to only a subsection of the full data. For example, to create a region of
|
||||
interest (*ROI*) in an image you just create a new header with the new boundaries:
|
||||
@code{.cpp}
|
||||
Mat D (A, Rect(10, 10, 100, 100) ); // using a rectangle
|
||||
Mat E = A(Range::all(), Range(1,3)); // using row and column boundaries
|
||||
@endcode
|
||||
Now you may ask if the matrix itself may belong to multiple *Mat* objects who takes responsibility
|
||||
for cleaning it up when it's no longer needed. The short answer is: the last object that used it.
|
||||
This is handled by using a reference counting mechanism. Whenever somebody copies a header of a
|
||||
*Mat* object, a counter is increased for the matrix. Whenever a header is cleaned this counter is
|
||||
decreased. When the counter reaches zero the matrix too is freed. Sometimes you will want to copy
|
||||
the matrix itself too, so OpenCV provides the @ref cv::clone() and @ref cv::copyTo() functions.
|
||||
@code{.cpp}
|
||||
Mat F = A.clone();
|
||||
Mat G;
|
||||
A.copyTo(G);
|
||||
@endcode
|
||||
Now modifying *F* or *G* will not affect the matrix pointed by the *Mat* header. What you need to
|
||||
remember from all this is that:
|
||||
|
||||
- Output image allocation for OpenCV functions is automatic (unless specified otherwise).
|
||||
- You do not need to think about memory management with OpenCVs C++ interface.
|
||||
- The assignment operator and the copy constructor only copies the header.
|
||||
- The underlying matrix of an image may be copied using the @ref cv::clone() and @ref cv::copyTo()
|
||||
functions.
|
||||
|
||||
*Storing* methods
|
||||
-----------------
|
||||
|
||||
This is about how you store the pixel values. You can select the color space and the data type used.
|
||||
The color space refers to how we combine color components in order to code a given color. The
|
||||
simplest one is the gray scale where the colors at our disposal are black and white. The combination
|
||||
of these allows us to create many shades of gray.
|
||||
|
||||
For *colorful* ways we have a lot more methods to choose from. Each of them breaks it down to three
|
||||
or four basic components and we can use the combination of these to create the others. The most
|
||||
popular one is RGB, mainly because this is also how our eye builds up colors. Its base colors are
|
||||
red, green and blue. To code the transparency of a color sometimes a fourth element: alpha (A) is
|
||||
added.
|
||||
|
||||
There are, however, many other color systems each with their own advantages:
|
||||
|
||||
- RGB is the most common as our eyes use something similar, our display systems also compose
|
||||
colors using these.
|
||||
- The HSV and HLS decompose colors into their hue, saturation and value/luminance components,
|
||||
which is a more natural way for us to describe colors. You might, for example, dismiss the last
|
||||
component, making your algorithm less sensible to the light conditions of the input image.
|
||||
- YCrCb is used by the popular JPEG image format.
|
||||
- CIE L\*a\*b\* is a perceptually uniform color space, which comes handy if you need to measure
|
||||
the *distance* of a given color to another color.
|
||||
|
||||
Each of the building components has their own valid domains. This leads to the data type used. How
|
||||
we store a component defines the control we have over its domain. The smallest data type possible is
|
||||
*char*, which means one byte or 8 bits. This may be unsigned (so can store values from 0 to 255) or
|
||||
signed (values from -127 to +127). Although in case of three components this already gives 16
|
||||
million possible colors to represent (like in case of RGB) we may acquire an even finer control by
|
||||
using the float (4 byte = 32 bit) or double (8 byte = 64 bit) data types for each component.
|
||||
Nevertheless, remember that increasing the size of a component also increases the size of the whole
|
||||
picture in the memory.
|
||||
|
||||
Creating a *Mat* object explicitly
|
||||
----------------------------------
|
||||
|
||||
In the @ref Load_Save_Image tutorial you have already learned how to write a matrix to an image
|
||||
file by using the @ref cv::imwrite() function. However, for debugging purposes it's much more
|
||||
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.
|
||||
|
||||
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
|
||||
object in multiple ways:
|
||||
|
||||
- @ref cv::Mat() Constructor
|
||||
|
||||
@includelineno
|
||||
cpp/tutorial_code/core/mat_the_basic_image_container/mat_the_basic_image_container.cpp
|
||||
|
||||
lines
|
||||
27-28
|
||||
|
||||

|
||||
|
||||
For two dimensional and multichannel images we first define their size: row and column count wise.
|
||||
|
||||
Then we need to specify the data type to use for storing the elements and the number of channels
|
||||
per matrix point. To do this we have multiple definitions constructed according to the following
|
||||
convention:
|
||||
@code{.cpp}
|
||||
CV_[The number of bits per item][Signed or Unsigned][Type Prefix]C[The channel number]
|
||||
@endcode
|
||||
For instance, *CV_8UC3* means we use unsigned char types that are 8 bit long and each pixel has
|
||||
three of these to form the three channels. This are predefined for up to four channel numbers. The
|
||||
@ref cv::Scalar is four element short vector. Specify this and you can initialize all matrix
|
||||
points with a custom value. If you need more you can create the type with the upper macro, setting
|
||||
the channel number in parenthesis as you can see below.
|
||||
|
||||
- Use C/C++ arrays and initialize via constructor
|
||||
|
||||
@includelineno
|
||||
cpp/tutorial_code/core/mat_the_basic_image_container/mat_the_basic_image_container.cpp
|
||||
|
||||
lines
|
||||
35-36
|
||||
|
||||
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
|
||||
same.
|
||||
|
||||
- Create a header for an already existing IplImage pointer:
|
||||
@code{.cpp}
|
||||
IplImage* img = cvLoadImage("greatwave.png", 1);
|
||||
Mat mtx(img); // convert IplImage* -> Mat
|
||||
@endcode
|
||||
- @ref cv::Create() function:
|
||||
|
||||
@includelineno
|
||||
cpp/tutorial_code/core/mat_the_basic_image_container/mat_the_basic_image_container.cpp
|
||||
|
||||
lines
|
||||
31-32
|
||||
|
||||

|
||||
|
||||
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.
|
||||
|
||||
- MATLAB style initializer: @ref cv::zeros() , @ref cv::ones() , @ref cv::eye() . Specify size and
|
||||
data type to use:
|
||||
|
||||
@includelineno
|
||||
cpp/tutorial_code/core/mat_the_basic_image_container/mat_the_basic_image_container.cpp
|
||||
|
||||
lines
|
||||
40-47
|
||||
|
||||

|
||||
|
||||
- For small matrices you may use comma separated initializers:
|
||||
|
||||
@includelineno
|
||||
cpp/tutorial_code/core/mat_the_basic_image_container/mat_the_basic_image_container.cpp
|
||||
|
||||
lines
|
||||
50-51
|
||||
|
||||

|
||||
|
||||
- Create a new header for an existing *Mat* object and @ref cv::clone() or @ref cv::copyTo() it.
|
||||
|
||||
@includelineno
|
||||
cpp/tutorial_code/core/mat_the_basic_image_container/mat_the_basic_image_container.cpp
|
||||
|
||||
lines
|
||||
53-54
|
||||
|
||||

|
||||
|
||||
@note
|
||||
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:
|
||||
|
||||
@includelineno
|
||||
cpp/tutorial_code/core/mat_the_basic_image_container/mat_the_basic_image_container.cpp
|
||||
|
||||
lines
|
||||
57-58
|
||||
|
||||
Output formatting
|
||||
-----------------
|
||||
|
||||
In the above examples you could see the default formatting option. OpenCV, however, allows you to
|
||||
format your matrix output:
|
||||
|
||||
- Default
|
||||
|
||||
@includelineno
|
||||
cpp/tutorial_code/core/mat_the_basic_image_container/mat_the_basic_image_container.cpp
|
||||
|
||||
lines
|
||||
61
|
||||
|
||||

|
||||
|
||||
- Python
|
||||
|
||||
@includelineno
|
||||
cpp/tutorial_code/core/mat_the_basic_image_container/mat_the_basic_image_container.cpp
|
||||
|
||||
lines
|
||||
62
|
||||
|
||||

|
||||
|
||||
- Comma separated values (CSV)
|
||||
|
||||
@includelineno
|
||||
cpp/tutorial_code/core/mat_the_basic_image_container/mat_the_basic_image_container.cpp
|
||||
|
||||
lines
|
||||
64
|
||||
|
||||

|
||||
|
||||
- Numpy
|
||||
|
||||
@includelineno
|
||||
cpp/tutorial_code/core/mat_the_basic_image_container/mat_the_basic_image_container.cpp
|
||||
|
||||
lines
|
||||
63
|
||||
|
||||

|
||||
|
||||
- C
|
||||
|
||||
@includelineno
|
||||
cpp/tutorial_code/core/mat_the_basic_image_container/mat_the_basic_image_container.cpp
|
||||
|
||||
lines
|
||||
65
|
||||
|
||||

|
||||
|
||||
Output of other common items
|
||||
----------------------------
|
||||
|
||||
OpenCV offers support for output of other common OpenCV data structures too via the \<\< operator:
|
||||
|
||||
- 2D Point
|
||||
|
||||
@includelineno
|
||||
cpp/tutorial_code/core/mat_the_basic_image_container/mat_the_basic_image_container.cpp
|
||||
|
||||
lines
|
||||
67-68
|
||||
|
||||

|
||||
|
||||
- 3D Point
|
||||
|
||||
@includelineno
|
||||
cpp/tutorial_code/core/mat_the_basic_image_container/mat_the_basic_image_container.cpp
|
||||
|
||||
lines
|
||||
70-71
|
||||
|
||||

|
||||
|
||||
- std::vector via cv::Mat
|
||||
|
||||
@includelineno
|
||||
cpp/tutorial_code/core/mat_the_basic_image_container/mat_the_basic_image_container.cpp
|
||||
|
||||
lines
|
||||
74-77
|
||||
|
||||

|
||||
|
||||
- std::vector of points
|
||||
|
||||
@includelineno
|
||||
cpp/tutorial_code/core/mat_the_basic_image_container/mat_the_basic_image_container.cpp
|
||||
|
||||
lines
|
||||
79-83
|
||||
|
||||

|
||||
|
||||
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)
|
||||
or in the core section of the cpp samples.
|
||||
|
||||
You can also find a quick video demonstration of this on
|
||||
[YouTube](https://www.youtube.com/watch?v=1tibU7vGWpk).
|
||||
|
||||
\htmlonly
|
||||
<div align="center">
|
||||
<iframe title="Install OpenCV by using its source files - Part 1" width="560" height="349" src="http://www.youtube.com/embed/1tibU7vGWpk?rel=0&loop=1" frameborder="0" allowfullscreen align="middle"></iframe>
|
||||
</div>
|
||||
\endhtmlonly
|
||||
|
@@ -0,0 +1,252 @@
|
||||
Random generator and text with OpenCV {#tutorial_random_generator_and_text}
|
||||
=====================================
|
||||
|
||||
Goals
|
||||
-----
|
||||
|
||||
In this tutorial you will learn how to:
|
||||
|
||||
- Use the *Random Number generator class* (@ref cv::RNG ) and how to get a random number from a
|
||||
uniform distribution.
|
||||
- Display text on an OpenCV window by using the function @ref cv::putText
|
||||
|
||||
Code
|
||||
----
|
||||
|
||||
- In the previous tutorial (@ref Drawing_1) we drew diverse geometric figures, giving as input
|
||||
parameters such as coordinates (in the form of @ref cv::Points ), color, thickness, etc. You
|
||||
might have noticed that we gave specific values for these arguments.
|
||||
- In this tutorial, we intend to use *random* values for the drawing parameters. Also, we intend
|
||||
to populate our image with a big number of geometric figures. Since we will be initializing them
|
||||
in a random fashion, this process will be automatic and made by using *loops* .
|
||||
- This code is in your OpenCV sample folder. Otherwise you can grab it from
|
||||
[here](http://code.opencv.org/projects/opencv/repository/revisions/master/raw/samples/cpp/tutorial_code/core/Matrix/Drawing_2.cpp)
|
||||
.
|
||||
|
||||
Explanation
|
||||
-----------
|
||||
|
||||
1. Let's start by checking out the *main* function. We observe that first thing we do is creating a
|
||||
*Random Number Generator* object (RNG):
|
||||
@code{.cpp}
|
||||
RNG rng( 0xFFFFFFFF );
|
||||
@endcode
|
||||
RNG implements a random number generator. In this example, *rng* is a RNG element initialized
|
||||
with the value *0xFFFFFFFF*
|
||||
|
||||
2. Then we create a matrix initialized to *zeros* (which means that it will appear as black),
|
||||
specifying its height, width and its type:
|
||||
@code{.cpp}
|
||||
/// Initialize a matrix filled with zeros
|
||||
Mat image = Mat::zeros( window_height, window_width, CV_8UC3 );
|
||||
|
||||
/// Show it in a window during DELAY ms
|
||||
imshow( window_name, image );
|
||||
@endcode
|
||||
3. 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:
|
||||
@code{.cpp}
|
||||
/// Now, let's draw some lines
|
||||
c = Drawing_Random_Lines(image, window_name, rng);
|
||||
if( c != 0 ) return 0;
|
||||
|
||||
/// Go on drawing, this time nice rectangles
|
||||
c = Drawing_Random_Rectangles(image, window_name, rng);
|
||||
if( c != 0 ) return 0;
|
||||
|
||||
/// Draw some ellipses
|
||||
c = Drawing_Random_Ellipses( image, window_name, rng );
|
||||
if( c != 0 ) return 0;
|
||||
|
||||
/// Now some polylines
|
||||
c = Drawing_Random_Polylines( image, window_name, rng );
|
||||
if( c != 0 ) return 0;
|
||||
|
||||
/// Draw filled polygons
|
||||
c = Drawing_Random_Filled_Polygons( image, window_name, rng );
|
||||
if( c != 0 ) return 0;
|
||||
|
||||
/// Draw circles
|
||||
c = Drawing_Random_Circles( image, window_name, rng );
|
||||
if( c != 0 ) return 0;
|
||||
|
||||
/// Display text in random positions
|
||||
c = Displaying_Random_Text( image, window_name, rng );
|
||||
if( c != 0 ) return 0;
|
||||
|
||||
/// Displaying the big end!
|
||||
c = Displaying_Big_End( image, window_name, rng );
|
||||
@endcode
|
||||
All of these functions follow the same pattern, so we will analyze only a couple of them, since
|
||||
the same explanation applies for all.
|
||||
|
||||
4. Checking out the function **Drawing_Random_Lines**:
|
||||
@code{.cpp}
|
||||
int Drawing_Random_Lines( Mat image, char* window_name, RNG rng )
|
||||
{
|
||||
int lineType = 8;
|
||||
Point pt1, pt2;
|
||||
|
||||
for( int i = 0; i < NUMBER; i++ )
|
||||
{
|
||||
pt1.x = rng.uniform( x_1, x_2 );
|
||||
pt1.y = rng.uniform( y_1, y_2 );
|
||||
pt2.x = rng.uniform( x_1, x_2 );
|
||||
pt2.y = rng.uniform( y_1, y_2 );
|
||||
|
||||
line( image, pt1, pt2, randomColor(rng), rng.uniform(1, 10), 8 );
|
||||
imshow( window_name, image );
|
||||
if( waitKey( DELAY ) >= 0 )
|
||||
{ return -1; }
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@endcode
|
||||
We can observe the following:
|
||||
|
||||
- The *for* loop will repeat **NUMBER** times. Since the function @ref cv::line is inside this
|
||||
loop, that means that **NUMBER** lines will be generated.
|
||||
- The line extremes are given by *pt1* and *pt2*. For *pt1* we can see that:
|
||||
@code{.cpp}
|
||||
pt1.x = rng.uniform( x_1, x_2 );
|
||||
pt1.y = rng.uniform( y_1, y_2 );
|
||||
@endcode
|
||||
- We know that **rng** is a *Random number generator* object. In the code above we are
|
||||
calling **rng.uniform(a,b)**. This generates a radombly uniformed distribution between
|
||||
the values **a** and **b** (inclusive in **a**, exclusive in **b**).
|
||||
- From the explanation above, we deduce that the extremes *pt1* and *pt2* will be random
|
||||
values, so the lines positions will be quite impredictable, giving a nice visual effect
|
||||
(check out the Result section below).
|
||||
- As another observation, we notice that in the @ref cv::line arguments, for the *color*
|
||||
input we enter:
|
||||
@code{.cpp}
|
||||
randomColor(rng)
|
||||
@endcode
|
||||
Let's check the function implementation:
|
||||
@code{.cpp}
|
||||
static Scalar randomColor( RNG& rng )
|
||||
{
|
||||
int icolor = (unsigned) rng;
|
||||
return Scalar( icolor&255, (icolor>>8)&255, (icolor>>16)&255 );
|
||||
}
|
||||
@endcode
|
||||
As we can see, the return value is an *Scalar* with 3 randomly initialized values, which
|
||||
are used as the *R*, *G* and *B* parameters for the line color. Hence, the color of the
|
||||
lines will be random too!
|
||||
|
||||
5. The explanation above applies for the other functions generating circles, ellipses, polygones,
|
||||
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
|
||||
*Displaying_Big_End*, since they both have a few interesting features:
|
||||
7. **Display_Random_Text:**
|
||||
@code{.cpp}
|
||||
int Displaying_Random_Text( Mat image, char* window_name, RNG rng )
|
||||
{
|
||||
int lineType = 8;
|
||||
|
||||
for ( int i = 1; i < NUMBER; i++ )
|
||||
{
|
||||
Point org;
|
||||
org.x = rng.uniform(x_1, x_2);
|
||||
org.y = rng.uniform(y_1, y_2);
|
||||
|
||||
putText( image, "Testing text rendering", org, rng.uniform(0,8),
|
||||
rng.uniform(0,100)*0.05+0.1, randomColor(rng), rng.uniform(1, 10), lineType);
|
||||
|
||||
imshow( window_name, image );
|
||||
if( waitKey(DELAY) >= 0 )
|
||||
{ return -1; }
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@endcode
|
||||
Everything looks familiar but the expression:
|
||||
@code{.cpp}
|
||||
putText( image, "Testing text rendering", org, rng.uniform(0,8),
|
||||
rng.uniform(0,100)*0.05+0.1, randomColor(rng), rng.uniform(1, 10), lineType);
|
||||
@endcode
|
||||
So, what does the function @ref cv::putText do? In our example:
|
||||
|
||||
- Draws the text **"Testing text rendering"** in **image**
|
||||
- The bottom-left corner of the text will be located in the Point **org**
|
||||
- The font type is a random integer value in the range: \f$[0, 8>\f$.
|
||||
- The scale of the font is denoted by the expression **rng.uniform(0, 100)x0.05 + 0.1**
|
||||
(meaning its range is: \f$[0.1, 5.1>\f$)
|
||||
- The text color is random (denoted by **randomColor(rng)**)
|
||||
- The text thickness ranges between 1 and 10, as specified by **rng.uniform(1,10)**
|
||||
|
||||
As a result, we will get (analagously to the other drawing functions) **NUMBER** texts over our
|
||||
image, in random locations.
|
||||
|
||||
8. **Displaying_Big_End**
|
||||
@code{.cpp}
|
||||
int Displaying_Big_End( Mat image, char* window_name, RNG rng )
|
||||
{
|
||||
Size textsize = getTextSize("OpenCV forever!", FONT_HERSHEY_COMPLEX, 3, 5, 0);
|
||||
Point org((window_width - textsize.width)/2, (window_height - textsize.height)/2);
|
||||
int lineType = 8;
|
||||
|
||||
Mat image2;
|
||||
|
||||
for( int i = 0; i < 255; i += 2 )
|
||||
{
|
||||
image2 = image - Scalar::all(i);
|
||||
putText( image2, "OpenCV forever!", org, FONT_HERSHEY_COMPLEX, 3,
|
||||
Scalar(i, i, 255), 5, lineType );
|
||||
|
||||
imshow( window_name, image2 );
|
||||
if( waitKey(DELAY) >= 0 )
|
||||
{ return -1; }
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@endcode
|
||||
Besides the function **getTextSize** (which gets the size of the argument text), the new
|
||||
operation we can observe is inside the *foor* loop:
|
||||
@code{.cpp}
|
||||
image2 = image - Scalar::all(i)
|
||||
@endcode
|
||||
So, **image2** is the substraction of **image** and **Scalar::all(i)**. In fact, what happens
|
||||
here is that every pixel of **image2** will be the result of substracting every pixel of
|
||||
**image** minus the value of **i** (remember that for each pixel we are considering three values
|
||||
such as R, G and B, so each of them will be affected)
|
||||
|
||||
Also remember that the substraction operation *always* performs internally a **saturate**
|
||||
operation, which means that the result obtained will always be inside the allowed range (no
|
||||
negative and between 0 and 255 for our example).
|
||||
|
||||
Result
|
||||
------
|
||||
|
||||
As you just saw in the Code section, the program will sequentially execute diverse drawing
|
||||
functions, which will produce:
|
||||
|
||||
1. First a random set of *NUMBER* lines will appear on screen such as it can be seen in this
|
||||
screenshot:
|
||||
|
||||

|
||||
|
||||
2. 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
|
||||
length:
|
||||
|
||||

|
||||
|
||||
4. Now, *polylines* with 03 segments will appear on screen, again in random configurations.
|
||||
|
||||

|
||||
|
||||
5. Filled polygons (in this example triangles) will follow.
|
||||
6. The last geometric figure to appear: circles!
|
||||
|
||||

|
||||
|
||||
7. Near the end, the text *"Testing Text Rendering"* will appear in a variety of fonts, sizes,
|
||||
colors and positions.
|
||||
8. And the big end (which by the way expresses a big truth too):
|
||||
|
||||

|
||||
|
||||
|
@@ -0,0 +1,103 @@
|
||||
The Core Functionality (core module) {#tutorial_table_of_content_core}
|
||||
=====================================
|
||||
|
||||
Here you will learn the about the basic building blocks of the library. A must read and know for
|
||||
understanding how to manipulate the images on a pixel level.
|
||||
|
||||
- @subpage tutorial_mat_the_basic_image_container
|
||||
|
||||
*Compatibility:* \> OpenCV 2.0
|
||||
|
||||
*Author:* Bernát Gábor
|
||||
|
||||
You will learn how to store images in the memory and how to print out their content to the
|
||||
console.
|
||||
|
||||
- @subpage tutorial_how_to_scan_images
|
||||
|
||||
*Compatibility:* \> OpenCV 2.0
|
||||
|
||||
*Author:* Bernát Gábor
|
||||
|
||||
You'll find out how to scan images (go through each of the image pixels) with OpenCV.
|
||||
Bonus: time measurement with OpenCV.
|
||||
|
||||
|
||||
- @subpage tutorial_mat_mask_operations
|
||||
|
||||
*Compatibility:* \> OpenCV 2.0
|
||||
|
||||
*Author:* Bernát Gábor
|
||||
|
||||
You'll find out how to scan images with neighbor access and use the @ref cv::filter2D
|
||||
function to apply kernel filters on images.
|
||||
|
||||
|
||||
- @subpage tutorial_adding_images
|
||||
|
||||
*Compatibility:* \> OpenCV 2.0
|
||||
|
||||
*Author:* Ana Huamán
|
||||
|
||||
We will learn how to blend two images!
|
||||
|
||||
- @subpage tutorial_basic_linear_transform
|
||||
|
||||
*Compatibility:* \> OpenCV 2.0
|
||||
|
||||
*Author:* Ana Huamán
|
||||
|
||||
We will learn how to change our image appearance!
|
||||
|
||||
- @subpage tutorial_basic_geometric_drawing
|
||||
|
||||
*Compatibility:* \> OpenCV 2.0
|
||||
|
||||
*Author:* Ana Huamán
|
||||
|
||||
We will learn how to draw simple geometry with OpenCV!
|
||||
|
||||
- @subpage tutorial_random_generator_and_text
|
||||
|
||||
*Compatibility:* \> OpenCV 2.0
|
||||
|
||||
*Author:* Ana Huamán
|
||||
|
||||
We will draw some *fancy-looking* stuff using OpenCV!
|
||||
|
||||
- @subpage tutorial_discrete_fourier_transform
|
||||
|
||||
*Compatibility:* \> OpenCV 2.0
|
||||
|
||||
*Author:* Bernát Gábor
|
||||
|
||||
You will see how and why use the Discrete Fourier transformation with OpenCV.
|
||||
|
||||
|
||||
- @subpage tutorial_file_input_output_with_xml_yml
|
||||
|
||||
*Compatibility:* \> OpenCV 2.0
|
||||
|
||||
*Author:* Bernát Gábor
|
||||
|
||||
You will see how to use the @ref cv::FileStorage data structure of OpenCV to write and read
|
||||
data to XML or YAML file format.
|
||||
|
||||
- @subpage tutorial_interoperability_with_OpenCV_1
|
||||
|
||||
*Compatibility:* \> OpenCV 2.0
|
||||
|
||||
*Author:* Bernát Gábor
|
||||
|
||||
Did you used OpenCV before its 2.0 version? Do you wanna know what happened with your library
|
||||
with 2.0? Don't you know how to convert your old OpenCV programs to the new C++ interface?
|
||||
Look here to shed light on all this questions.
|
||||
|
||||
|
||||
- @subpage tutorial_how_to_use_ippa_conversion
|
||||
|
||||
*Compatibility:* \> OpenCV 2.0
|
||||
|
||||
*Author:* Elena Gvozdeva
|
||||
|
||||
You will see how to use the IPP Async with OpenCV.
|
Reference in New Issue
Block a user