Doxygen tutorials: basic structure
This commit is contained in:
@@ -0,0 +1,259 @@
|
||||
Back Projection {#tutorial_back_projection}
|
||||
===============
|
||||
|
||||
Goal
|
||||
----
|
||||
|
||||
In this tutorial you will learn:
|
||||
|
||||
- What is Back Projection and why it is useful
|
||||
- How to use the OpenCV function @ref cv::calcBackProject to calculate Back Projection
|
||||
- How to mix different channels of an image by using the OpenCV function @ref cv::mixChannels
|
||||
|
||||
Theory
|
||||
------
|
||||
|
||||
### What is Back Projection?
|
||||
|
||||
- Back Projection is a way of recording how well the pixels of a given image fit the distribution
|
||||
of pixels in a histogram model.
|
||||
- To make it simpler: For Back Projection, you calculate the histogram model of a feature and then
|
||||
use it to find this feature in an image.
|
||||
- Application example: If you have a histogram of flesh color (say, a Hue-Saturation histogram ),
|
||||
then you can use it to find flesh color areas in an image:
|
||||
|
||||
### How does it work?
|
||||
|
||||
- We explain this by using the skin example:
|
||||
- Let's say you have gotten a skin histogram (Hue-Saturation) based on the image below. The
|
||||
histogram besides is going to be our *model histogram* (which we know represents a sample of
|
||||
skin tonality). You applied some mask to capture only the histogram of the skin area:
|
||||
|
||||
------ ------
|
||||
|T0| |T1|
|
||||
------ ------
|
||||
|
||||
- Now, let's imagine that you get another hand image (Test Image) like the one below: (with its
|
||||
respective histogram):
|
||||
|
||||
------ ------
|
||||
|T2| |T3|
|
||||
------ ------
|
||||
|
||||
- What we want to do is to use our *model histogram* (that we know represents a skin tonality) to
|
||||
detect skin areas in our Test Image. Here are the steps
|
||||
a. In each pixel of our Test Image (i.e. \f$p(i,j)\f$ ), collect the data and find the
|
||||
correspondent bin location for that pixel (i.e. \f$( h_{i,j}, s_{i,j} )\f$ ).
|
||||
b. Lookup the *model histogram* in the correspondent bin - \f$( h_{i,j}, s_{i,j} )\f$ - and read
|
||||
the bin value.
|
||||
c. Store this bin value in a new image (*BackProjection*). Also, you may consider to normalize
|
||||
the *model histogram* first, so the output for the Test Image can be visible for you.
|
||||
d. Applying the steps above, we get the following BackProjection image for our Test Image:
|
||||
|
||||

|
||||
|
||||
e. In terms of statistics, the values stored in *BackProjection* represent the *probability*
|
||||
that a pixel in *Test Image* belongs to a skin area, based on the *model histogram* that we
|
||||
use. For instance in our Test image, the brighter areas are more probable to be skin area
|
||||
(as they actually are), whereas the darker areas have less probability (notice that these
|
||||
"dark" areas belong to surfaces that have some shadow on it, which in turns affects the
|
||||
detection).
|
||||
|
||||
Code
|
||||
----
|
||||
|
||||
- **What does this program do?**
|
||||
- Loads an image
|
||||
- Convert the original to HSV format and separate only *Hue* channel to be used for the
|
||||
Histogram (using the OpenCV function @ref cv::mixChannels )
|
||||
- Let the user to enter the number of bins to be used in the calculation of the histogram.
|
||||
- Calculate the histogram (and update it if the bins change) and the backprojection of the
|
||||
same image.
|
||||
- Display the backprojection and the histogram in windows.
|
||||
- **Downloadable code**:
|
||||
|
||||
a. Click
|
||||
[here](https://github.com/Itseez/opencv/tree/master/samples/cpp/tutorial_code/Histograms_Matching/calcBackProject_Demo1.cpp)
|
||||
for the basic version (explained in this tutorial).
|
||||
b. For stuff slightly fancier (using H-S histograms and floodFill to define a mask for the
|
||||
skin area) you can check the [improved
|
||||
demo](https://github.com/Itseez/opencv/tree/master/samples/cpp/tutorial_code/Histograms_Matching/calcBackProject_Demo2.cpp)
|
||||
c. ...or you can always check out the classical
|
||||
[camshiftdemo](https://github.com/Itseez/opencv/tree/master/samples/cpp/camshiftdemo.cpp)
|
||||
in samples.
|
||||
|
||||
- **Code at glance:**
|
||||
@code{.cpp}
|
||||
#include "opencv2/imgproc.hpp"
|
||||
#include "opencv2/highgui.hpp"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
using namespace cv;
|
||||
using namespace std;
|
||||
|
||||
/// Global Variables
|
||||
Mat src; Mat hsv; Mat hue;
|
||||
int bins = 25;
|
||||
|
||||
/// Function Headers
|
||||
void Hist_and_Backproj(int, void* );
|
||||
|
||||
/* @function main */
|
||||
int main( int argc, char** argv )
|
||||
{
|
||||
/// Read the image
|
||||
src = imread( argv[1], 1 );
|
||||
/// Transform it to HSV
|
||||
cvtColor( src, hsv, COLOR_BGR2HSV );
|
||||
|
||||
/// Use only the Hue value
|
||||
hue.create( hsv.size(), hsv.depth() );
|
||||
int ch[] = { 0, 0 };
|
||||
mixChannels( &hsv, 1, &hue, 1, ch, 1 );
|
||||
|
||||
/// Create Trackbar to enter the number of bins
|
||||
char* window_image = "Source image";
|
||||
namedWindow( window_image, WINDOW_AUTOSIZE );
|
||||
createTrackbar("* Hue bins: ", window_image, &bins, 180, Hist_and_Backproj );
|
||||
Hist_and_Backproj(0, 0);
|
||||
|
||||
/// Show the image
|
||||
imshow( window_image, src );
|
||||
|
||||
/// Wait until user exits the program
|
||||
waitKey(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* @function Hist_and_Backproj
|
||||
* @brief Callback to Trackbar
|
||||
*/
|
||||
void Hist_and_Backproj(int, void* )
|
||||
{
|
||||
MatND hist;
|
||||
int histSize = MAX( bins, 2 );
|
||||
float hue_range[] = { 0, 180 };
|
||||
const float* ranges = { hue_range };
|
||||
|
||||
/// Get the Histogram and normalize it
|
||||
calcHist( &hue, 1, 0, Mat(), hist, 1, &histSize, &ranges, true, false );
|
||||
normalize( hist, hist, 0, 255, NORM_MINMAX, -1, Mat() );
|
||||
|
||||
/// Get Backprojection
|
||||
MatND backproj;
|
||||
calcBackProject( &hue, 1, 0, hist, backproj, &ranges, 1, true );
|
||||
|
||||
/// Draw the backproj
|
||||
imshow( "BackProj", backproj );
|
||||
|
||||
/// Draw the histogram
|
||||
int w = 400; int h = 400;
|
||||
int bin_w = cvRound( (double) w / histSize );
|
||||
Mat histImg = Mat::zeros( w, h, CV_8UC3 );
|
||||
|
||||
for( int i = 0; i < bins; i ++ )
|
||||
{ rectangle( histImg, Point( i*bin_w, h ), Point( (i+1)*bin_w, h - cvRound( hist.at<float>(i)*h/255.0 ) ), Scalar( 0, 0, 255 ), -1 ); }
|
||||
|
||||
imshow( "Histogram", histImg );
|
||||
}
|
||||
@endcode
|
||||
Explanation
|
||||
-----------
|
||||
|
||||
1. Declare the matrices to store our images and initialize the number of bins to be used by our
|
||||
histogram:
|
||||
@code{.cpp}
|
||||
Mat src; Mat hsv; Mat hue;
|
||||
int bins = 25;
|
||||
@endcode
|
||||
2. Read the input image and transform it to HSV format:
|
||||
@code{.cpp}
|
||||
src = imread( argv[1], 1 );
|
||||
cvtColor( src, hsv, COLOR_BGR2HSV );
|
||||
@endcode
|
||||
3. For this tutorial, we will use only the Hue value for our 1-D histogram (check out the fancier
|
||||
code in the links above if you want to use the more standard H-S histogram, which yields better
|
||||
results):
|
||||
@code{.cpp}
|
||||
hue.create( hsv.size(), hsv.depth() );
|
||||
int ch[] = { 0, 0 };
|
||||
mixChannels( &hsv, 1, &hue, 1, ch, 1 );
|
||||
@endcode
|
||||
as you see, we use the function :mix_channels:mixChannels to get only the channel 0 (Hue) from
|
||||
the hsv image. It gets the following parameters:
|
||||
|
||||
- **&hsv:** The source array from which the channels will be copied
|
||||
- **1:** The number of source arrays
|
||||
- **&hue:** The destination array of the copied channels
|
||||
- **1:** The number of destination arrays
|
||||
- **ch[] = {0,0}:** The array of index pairs indicating how the channels are copied. In this
|
||||
case, the Hue(0) channel of &hsv is being copied to the 0 channel of &hue (1-channel)
|
||||
- **1:** Number of index pairs
|
||||
|
||||
4. Create a Trackbar for the user to enter the bin values. Any change on the Trackbar means a call
|
||||
to the **Hist_and_Backproj** callback function.
|
||||
@code{.cpp}
|
||||
char* window_image = "Source image";
|
||||
namedWindow( window_image, WINDOW_AUTOSIZE );
|
||||
createTrackbar("* Hue bins: ", window_image, &bins, 180, Hist_and_Backproj );
|
||||
Hist_and_Backproj(0, 0);
|
||||
@endcode
|
||||
5. Show the image and wait for the user to exit the program:
|
||||
@code{.cpp}
|
||||
imshow( window_image, src );
|
||||
|
||||
waitKey(0);
|
||||
return 0;
|
||||
@endcode
|
||||
6. **Hist_and_Backproj function:** Initialize the arguments needed for @ref cv::calcHist . The
|
||||
number of bins comes from the Trackbar:
|
||||
@code{.cpp}
|
||||
void Hist_and_Backproj(int, void* )
|
||||
{
|
||||
MatND hist;
|
||||
int histSize = MAX( bins, 2 );
|
||||
float hue_range[] = { 0, 180 };
|
||||
const float* ranges = { hue_range };
|
||||
@endcode
|
||||
7. Calculate the Histogram and normalize it to the range \f$[0,255]\f$
|
||||
@code{.cpp}
|
||||
calcHist( &hue, 1, 0, Mat(), hist, 1, &histSize, &ranges, true, false );
|
||||
normalize( hist, hist, 0, 255, NORM_MINMAX, -1, Mat() );
|
||||
@endcode
|
||||
8. Get the Backprojection of the same image by calling the function @ref cv::calcBackProject
|
||||
@code{.cpp}
|
||||
MatND backproj;
|
||||
calcBackProject( &hue, 1, 0, hist, backproj, &ranges, 1, true );
|
||||
@endcode
|
||||
all the arguments are known (the same as used to calculate the histogram), only we add the
|
||||
backproj matrix, which will store the backprojection of the source image (&hue)
|
||||
|
||||
9. Display backproj:
|
||||
@code{.cpp}
|
||||
imshow( "BackProj", backproj );
|
||||
@endcode
|
||||
10. Draw the 1-D Hue histogram of the image:
|
||||
@code{.cpp}
|
||||
int w = 400; int h = 400;
|
||||
int bin_w = cvRound( (double) w / histSize );
|
||||
Mat histImg = Mat::zeros( w, h, CV_8UC3 );
|
||||
|
||||
for( int i = 0; i < bins; i ++ )
|
||||
{ rectangle( histImg, Point( i*bin_w, h ), Point( (i+1)*bin_w, h - cvRound( hist.at<float>(i)*h/255.0 ) ), Scalar( 0, 0, 255 ), -1 ); }
|
||||
|
||||
imshow( "Histogram", histImg );
|
||||
@endcode
|
||||
Results
|
||||
-------
|
||||
|
||||
1. Here are the output by using a sample image ( guess what? Another hand ). You can play with the
|
||||
bin values and you will observe how it affects the results:
|
||||
|
||||
------ ------ ------
|
||||
|R0| |R1| |R2|
|
||||
------ ------ ------
|
||||
|
||||
|
@@ -0,0 +1,289 @@
|
||||
Histogram Calculation {#tutorial_histogram_calculation}
|
||||
=====================
|
||||
|
||||
Goal
|
||||
----
|
||||
|
||||
In this tutorial you will learn how to:
|
||||
|
||||
- Use the OpenCV function @ref cv::split to divide an image into its correspondent planes.
|
||||
- To calculate histograms of arrays of images by using the OpenCV function @ref cv::calcHist
|
||||
- To normalize an array by using the function @ref cv::normalize
|
||||
|
||||
@note In the last tutorial (@ref histogram_equalization) we talked about a particular kind of
|
||||
histogram called *Image histogram*. Now we will considerate it in its more general concept. Read on!
|
||||
|
||||
### What are histograms?
|
||||
|
||||
- Histograms are collected *counts* of data organized into a set of predefined *bins*
|
||||
- When we say *data* we are not restricting it to be intensity values (as we saw in the previous
|
||||
Tutorial). The data collected can be whatever feature you find useful to describe your image.
|
||||
- Let's see an example. Imagine that a Matrix contains information of an image (i.e. intensity in
|
||||
the range \f$0-255\f$):
|
||||
|
||||

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

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

|
||||
|
||||
2. Produces the following histogram:
|
||||
|
||||

|
||||
|
||||
|
@@ -0,0 +1,168 @@
|
||||
Histogram Comparison {#tutorial_histogram_comparison}
|
||||
====================
|
||||
|
||||
Goal
|
||||
----
|
||||
|
||||
In this tutorial you will learn how to:
|
||||
|
||||
- Use the function @ref cv::compareHist to get a numerical parameter that express how well two
|
||||
histograms match with each other.
|
||||
- Use different metrics to compare histograms
|
||||
|
||||
Theory
|
||||
------
|
||||
|
||||
- To compare two histograms ( \f$H_{1}\f$ and \f$H_{2}\f$ ), first we have to choose a *metric*
|
||||
(\f$d(H_{1}, H_{2})\f$) to express how well both histograms match.
|
||||
- OpenCV implements the function @ref cv::compareHist to perform a comparison. It also offers 4
|
||||
different metrics to compute the matching:
|
||||
a. **Correlation ( CV_COMP_CORREL )**
|
||||
|
||||
\f[d(H_1,H_2) = \frac{\sum_I (H_1(I) - \bar{H_1}) (H_2(I) - \bar{H_2})}{\sqrt{\sum_I(H_1(I) - \bar{H_1})^2 \sum_I(H_2(I) - \bar{H_2})^2}}\f]
|
||||
|
||||
where
|
||||
|
||||
\f[\bar{H_k} = \frac{1}{N} \sum _J H_k(J)\f]
|
||||
|
||||
and \f$N\f$ is the total number of histogram bins.
|
||||
|
||||
b. **Chi-Square ( CV_COMP_CHISQR )**
|
||||
|
||||
\f[d(H_1,H_2) = \sum _I \frac{\left(H_1(I)-H_2(I)\right)^2}{H_1(I)}\f]
|
||||
|
||||
c. **Intersection ( method=CV_COMP_INTERSECT )**
|
||||
|
||||
\f[d(H_1,H_2) = \sum _I \min (H_1(I), H_2(I))\f]
|
||||
|
||||
d. **Bhattacharyya distance ( CV_COMP_BHATTACHARYYA )**
|
||||
|
||||
\f[d(H_1,H_2) = \sqrt{1 - \frac{1}{\sqrt{\bar{H_1} \bar{H_2} N^2}} \sum_I \sqrt{H_1(I) \cdot H_2(I)}}\f]
|
||||
|
||||
Code
|
||||
----
|
||||
|
||||
- **What does this program do?**
|
||||
- Loads a *base image* and 2 *test images* to be compared with it.
|
||||
- Generate 1 image that is the lower half of the *base image*
|
||||
- Convert the images to HSV format
|
||||
- Calculate the H-S histogram for all the images and normalize them in order to compare them.
|
||||
- Compare the histogram of the *base image* with respect to the 2 test histograms, the
|
||||
histogram of the lower half base image and with the same base image histogram.
|
||||
- Display the numerical matching parameters obtained.
|
||||
- **Downloadable code**: Click
|
||||
[here](https://github.com/Itseez/opencv/tree/master/samples/cpp/tutorial_code/Histograms_Matching/compareHist_Demo.cpp)
|
||||
- **Code at glance:**
|
||||
|
||||
@includelineno cpp/tutorial_code/Histograms_Matching/compareHist_Demo.cpp
|
||||
|
||||
Explanation
|
||||
-----------
|
||||
|
||||
1. Declare variables such as the matrices to store the base image and the two other images to
|
||||
compare ( RGB and HSV )
|
||||
@code{.cpp}
|
||||
Mat src_base, hsv_base;
|
||||
Mat src_test1, hsv_test1;
|
||||
Mat src_test2, hsv_test2;
|
||||
Mat hsv_half_down;
|
||||
@endcode
|
||||
2. Load the base image (src_base) and the other two test images:
|
||||
@code{.cpp}
|
||||
if( argc < 4 )
|
||||
{ printf("** Error. Usage: ./compareHist_Demo <image_settings0> <image_setting1> <image_settings2>\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
src_base = imread( argv[1], 1 );
|
||||
src_test1 = imread( argv[2], 1 );
|
||||
src_test2 = imread( argv[3], 1 );
|
||||
@endcode
|
||||
3. Convert them to HSV format:
|
||||
@code{.cpp}
|
||||
cvtColor( src_base, hsv_base, COLOR_BGR2HSV );
|
||||
cvtColor( src_test1, hsv_test1, COLOR_BGR2HSV );
|
||||
cvtColor( src_test2, hsv_test2, COLOR_BGR2HSV );
|
||||
@endcode
|
||||
4. Also, create an image of half the base image (in HSV format):
|
||||
@code{.cpp}
|
||||
hsv_half_down = hsv_base( Range( hsv_base.rows/2, hsv_base.rows - 1 ), Range( 0, hsv_base.cols - 1 ) );
|
||||
@endcode
|
||||
5. Initialize the arguments to calculate the histograms (bins, ranges and channels H and S ).
|
||||
@code{.cpp}
|
||||
int h_bins = 50; int s_bins = 60;
|
||||
int histSize[] = { h_bins, s_bins };
|
||||
|
||||
float h_ranges[] = { 0, 180 };
|
||||
float s_ranges[] = { 0, 256 };
|
||||
|
||||
const float* ranges[] = { h_ranges, s_ranges };
|
||||
|
||||
int channels[] = { 0, 1 };
|
||||
@endcode
|
||||
6. Create the MatND objects to store the histograms:
|
||||
@code{.cpp}
|
||||
MatND hist_base;
|
||||
MatND hist_half_down;
|
||||
MatND hist_test1;
|
||||
MatND hist_test2;
|
||||
@endcode
|
||||
7. Calculate the Histograms for the base image, the 2 test images and the half-down base image:
|
||||
@code{.cpp}
|
||||
calcHist( &hsv_base, 1, channels, Mat(), hist_base, 2, histSize, ranges, true, false );
|
||||
normalize( hist_base, hist_base, 0, 1, NORM_MINMAX, -1, Mat() );
|
||||
|
||||
calcHist( &hsv_half_down, 1, channels, Mat(), hist_half_down, 2, histSize, ranges, true, false );
|
||||
normalize( hist_half_down, hist_half_down, 0, 1, NORM_MINMAX, -1, Mat() );
|
||||
|
||||
calcHist( &hsv_test1, 1, channels, Mat(), hist_test1, 2, histSize, ranges, true, false );
|
||||
normalize( hist_test1, hist_test1, 0, 1, NORM_MINMAX, -1, Mat() );
|
||||
|
||||
calcHist( &hsv_test2, 1, channels, Mat(), hist_test2, 2, histSize, ranges, true, false );
|
||||
normalize( hist_test2, hist_test2, 0, 1, NORM_MINMAX, -1, Mat() );
|
||||
@endcode
|
||||
8. Apply sequentially the 4 comparison methods between the histogram of the base image (hist_base)
|
||||
and the other histograms:
|
||||
@code{.cpp}
|
||||
for( int i = 0; i < 4; i++ )
|
||||
{ int compare_method = i;
|
||||
double base_base = compareHist( hist_base, hist_base, compare_method );
|
||||
double base_half = compareHist( hist_base, hist_half_down, compare_method );
|
||||
double base_test1 = compareHist( hist_base, hist_test1, compare_method );
|
||||
double base_test2 = compareHist( hist_base, hist_test2, compare_method );
|
||||
|
||||
printf( " Method [%d] Perfect, Base-Half, Base-Test(1), Base-Test(2) : %f, %f, %f, %f \n", i, base_base, base_half , base_test1, base_test2 );
|
||||
}
|
||||
@endcode
|
||||
Results
|
||||
-------
|
||||
|
||||
1. We use as input the following images:
|
||||
|
||||
----------- ----------- -----------
|
||||
|Base_0| |Test_1| |Test_2|
|
||||
----------- ----------- -----------
|
||||
|
||||
where the first one is the base (to be compared to the others), the other 2 are the test images.
|
||||
We will also compare the first image with respect to itself and with respect of half the base
|
||||
image.
|
||||
|
||||
2. We should expect a perfect match when we compare the base image histogram with itself. Also,
|
||||
compared with the histogram of half the base image, it should present a high match since both
|
||||
are from the same source. For the other two test images, we can observe that they have very
|
||||
different lighting conditions, so the matching should not be very good:
|
||||
3. Here the numeric results:
|
||||
|
||||
*Method* Base - Base Base - Half Base - Test 1 Base - Test 2
|
||||
----------------- ------------- ------------- --------------- ---------------
|
||||
*Correlation* 1.000000 0.930766 0.182073 0.120447
|
||||
*Chi-square* 0.000000 4.940466 21.184536 49.273437
|
||||
*Intersection* 24.391548 14.959809 3.889029 5.775088
|
||||
*Bhattacharyya* 0.000000 0.222609 0.646576 0.801869
|
||||
|
||||
For the *Correlation* and *Intersection* methods, the higher the metric, the more accurate the
|
||||
match. As we can see, the match *base-base* is the highest of all as expected. Also we can observe
|
||||
that the match *base-half* is the second best match (as we predicted). For the other two metrics,
|
||||
the less the result, the better the match. We can observe that the matches between the test 1 and
|
||||
test 2 with respect to the base are worse, which again, was expected.
|
||||
|
@@ -0,0 +1,180 @@
|
||||
Histogram Equalization {#tutorial_histogram_equalization}
|
||||
======================
|
||||
|
||||
Goal
|
||||
----
|
||||
|
||||
In this tutorial you will learn:
|
||||
|
||||
- What an image histogram is and why it is useful
|
||||
- To equalize histograms of images by using the OpenCV <function@ref> cv::equalizeHist
|
||||
|
||||
Theory
|
||||
------
|
||||
|
||||
### What is an Image Histogram?
|
||||
|
||||
- It is a graphical representation of the intensity distribution of an image.
|
||||
- It quantifies the number of pixels for each intensity value considered.
|
||||
|
||||

|
||||
|
||||
### What is Histogram Equalization?
|
||||
|
||||
- It is a method that improves the contrast in an image, in order to stretch out the intensity
|
||||
range.
|
||||
- To make it clearer, from the image above, you can see that the pixels seem clustered around the
|
||||
middle of the available range of intensities. What Histogram Equalization does is to *stretch
|
||||
out* this range. Take a look at the figure below: The green circles indicate the
|
||||
*underpopulated* intensities. After applying the equalization, we get an histogram like the
|
||||
figure in the center. The resulting image is shown in the picture at right.
|
||||
|
||||

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

|
||||
|
||||
- Finally, we use a simple remapping procedure to obtain the intensity values of the equalized
|
||||
image:
|
||||
|
||||
\f[equalized( x, y ) = H^{'}( src(x,y) )\f]
|
||||
|
||||
Code
|
||||
----
|
||||
|
||||
- **What does this program do?**
|
||||
- Loads an image
|
||||
- Convert the original image to grayscale
|
||||
- Equalize the Histogram by using the OpenCV function @ref cv::EqualizeHist
|
||||
- Display the source and equalized images in a window.
|
||||
- **Downloadable code**: Click
|
||||
[here](https://github.com/Itseez/opencv/tree/master/samples/cpp/tutorial_code/Histograms_Matching/EqualizeHist_Demo.cpp)
|
||||
- **Code at glance:**
|
||||
@code{.cpp}
|
||||
#include "opencv2/highgui.hpp"
|
||||
#include "opencv2/imgproc.hpp"
|
||||
#include <iostream>
|
||||
#include <stdio.h>
|
||||
|
||||
using namespace cv;
|
||||
using namespace std;
|
||||
|
||||
/* @function main */
|
||||
int main( int argc, char** argv )
|
||||
{
|
||||
Mat src, dst;
|
||||
|
||||
char* source_window = "Source image";
|
||||
char* equalized_window = "Equalized Image";
|
||||
|
||||
/// Load image
|
||||
src = imread( argv[1], 1 );
|
||||
|
||||
if( !src.data )
|
||||
{ cout<<"Usage: ./Histogram_Demo <path_to_image>"<<endl;
|
||||
return -1;}
|
||||
|
||||
/// Convert to grayscale
|
||||
cvtColor( src, src, COLOR_BGR2GRAY );
|
||||
|
||||
/// Apply Histogram Equalization
|
||||
equalizeHist( src, dst );
|
||||
|
||||
/// Display results
|
||||
namedWindow( source_window, WINDOW_AUTOSIZE );
|
||||
namedWindow( equalized_window, WINDOW_AUTOSIZE );
|
||||
|
||||
imshow( source_window, src );
|
||||
imshow( equalized_window, dst );
|
||||
|
||||
/// Wait until user exits the program
|
||||
waitKey(0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@endcode
|
||||
Explanation
|
||||
-----------
|
||||
|
||||
1. Declare the source and destination images as well as the windows names:
|
||||
@code{.cpp}
|
||||
Mat src, dst;
|
||||
|
||||
char* source_window = "Source image";
|
||||
char* equalized_window = "Equalized Image";
|
||||
@endcode
|
||||
2. Load the source image:
|
||||
@code{.cpp}
|
||||
src = imread( argv[1], 1 );
|
||||
|
||||
if( !src.data )
|
||||
{ cout<<"Usage: ./Histogram_Demo <path_to_image>"<<endl;
|
||||
return -1;}
|
||||
@endcode
|
||||
3. Convert it to grayscale:
|
||||
@code{.cpp}
|
||||
cvtColor( src, src, COLOR_BGR2GRAY );
|
||||
@endcode
|
||||
4. Apply histogram equalization with the function @ref cv::equalizeHist :
|
||||
@code{.cpp}
|
||||
equalizeHist( src, dst );
|
||||
@endcode
|
||||
As it can be easily seen, the only arguments are the original image and the output (equalized)
|
||||
image.
|
||||
|
||||
5. Display both images (original and equalized) :
|
||||
@code{.cpp}
|
||||
namedWindow( source_window, WINDOW_AUTOSIZE );
|
||||
namedWindow( equalized_window, WINDOW_AUTOSIZE );
|
||||
|
||||
imshow( source_window, src );
|
||||
imshow( equalized_window, dst );
|
||||
@endcode
|
||||
6. Wait until user exists the program
|
||||
@code{.cpp}
|
||||
waitKey(0);
|
||||
return 0;
|
||||
@endcode
|
||||
Results
|
||||
-------
|
||||
|
||||
1. To appreciate better the results of equalization, let's introduce an image with not much
|
||||
contrast, such as:
|
||||
|
||||

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

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

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

|
||||
|
||||
Notice how the number of pixels is more distributed through the intensity range.
|
||||
|
||||
**note**
|
||||
|
||||
Are you wondering how did we draw the Histogram figures shown above? Check out the following
|
||||
tutorial!
|
||||
|
@@ -0,0 +1,304 @@
|
||||
Template Matching {#tutorial_template_matching}
|
||||
=================
|
||||
|
||||
Goal
|
||||
----
|
||||
|
||||
In this tutorial you will learn how to:
|
||||
|
||||
- Use the OpenCV function @ref cv::matchTemplate to search for matches between an image patch and
|
||||
an input image
|
||||
- Use the OpenCV function @ref cv::minMaxLoc to find the maximum and minimum values (as well as
|
||||
their positions) in a given array.
|
||||
|
||||
Theory
|
||||
------
|
||||
|
||||
### What is template matching?
|
||||
|
||||
Template matching is a technique for finding areas of an image that match (are similar) to a
|
||||
template image (patch).
|
||||
|
||||
### How does it work?
|
||||
|
||||
- We need two primary components:
|
||||
|
||||
a. **Source image (I):** The image in which we expect to find a match to the template image
|
||||
b. **Template image (T):** The patch image which will be compared to the template image
|
||||
|
||||
our goal is to detect the highest matching area:
|
||||
|
||||

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

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

|
||||
|
||||
the image above is the result **R** of sliding the patch with a metric **TM_CCORR_NORMED**.
|
||||
The brightest locations indicate the highest matches. As you can see, the location marked by the
|
||||
red circle is probably the one with the highest value, so that location (the rectangle formed by
|
||||
that point as a corner and width and height equal to the patch image) is considered the match.
|
||||
|
||||
- In practice, we use the function @ref cv::minMaxLoc to locate the highest value (or lower,
|
||||
depending of the type of matching method) in the *R* matrix.
|
||||
|
||||
### Which are the matching methods available in OpenCV?
|
||||
|
||||
Good question. OpenCV implements Template matching in the function @ref cv::matchTemplate . The
|
||||
available methods are 6:
|
||||
|
||||
a. **method=CV_TM_SQDIFF**
|
||||
|
||||
\f[R(x,y)= \sum _{x',y'} (T(x',y')-I(x+x',y+y'))^2\f]
|
||||
|
||||
b. **method=CV_TM_SQDIFF_NORMED**
|
||||
|
||||
\f[R(x,y)= \frac{\sum_{x',y'} (T(x',y')-I(x+x',y+y'))^2}{\sqrt{\sum_{x',y'}T(x',y')^2 \cdot \sum_{x',y'} I(x+x',y+y')^2}}\f]
|
||||
|
||||
c. **method=CV_TM_CCORR**
|
||||
|
||||
\f[R(x,y)= \sum _{x',y'} (T(x',y') \cdot I(x+x',y+y'))\f]
|
||||
|
||||
d. **method=CV_TM_CCORR_NORMED**
|
||||
|
||||
\f[R(x,y)= \frac{\sum_{x',y'} (T(x',y') \cdot I(x+x',y+y'))}{\sqrt{\sum_{x',y'}T(x',y')^2 \cdot \sum_{x',y'} I(x+x',y+y')^2}}\f]
|
||||
|
||||
e. **method=CV_TM_CCOEFF**
|
||||
|
||||
\f[R(x,y)= \sum _{x',y'} (T'(x',y') \cdot I(x+x',y+y'))\f]
|
||||
|
||||
where
|
||||
|
||||
\f[\begin{array}{l} T'(x',y')=T(x',y') - 1/(w \cdot h) \cdot \sum _{x'',y''} T(x'',y'') \\ I'(x+x',y+y')=I(x+x',y+y') - 1/(w \cdot h) \cdot \sum _{x'',y''} I(x+x'',y+y'') \end{array}\f]
|
||||
|
||||
f. **method=CV_TM_CCOEFF_NORMED**
|
||||
|
||||
\f[R(x,y)= \frac{ \sum_{x',y'} (T'(x',y') \cdot I'(x+x',y+y')) }{ \sqrt{\sum_{x',y'}T'(x',y')^2 \cdot \sum_{x',y'} I'(x+x',y+y')^2} }\f]
|
||||
|
||||
Code
|
||||
----
|
||||
|
||||
- **What does this program do?**
|
||||
- Loads an input image and a image patch (*template*)
|
||||
- Perform a template matching procedure by using the OpenCV function @ref cv::matchTemplate
|
||||
with any of the 6 matching methods described before. The user can choose the method by
|
||||
entering its selection in the Trackbar.
|
||||
- Normalize the output of the matching procedure
|
||||
- Localize the location with higher matching probability
|
||||
- Draw a rectangle around the area corresponding to the highest match
|
||||
- **Downloadable code**: Click
|
||||
[here](https://github.com/Itseez/opencv/tree/master/samples/cpp/tutorial_code/Histograms_Matching/MatchTemplate_Demo.cpp)
|
||||
- **Code at glance:**
|
||||
@code{.cpp}
|
||||
#include "opencv2/highgui.hpp"
|
||||
#include "opencv2/imgproc.hpp"
|
||||
#include <iostream>
|
||||
#include <stdio.h>
|
||||
|
||||
using namespace std;
|
||||
using namespace cv;
|
||||
|
||||
/// Global Variables
|
||||
Mat img; Mat templ; Mat result;
|
||||
char* image_window = "Source Image";
|
||||
char* result_window = "Result window";
|
||||
|
||||
int match_method;
|
||||
int max_Trackbar = 5;
|
||||
|
||||
/// Function Headers
|
||||
void MatchingMethod( int, void* );
|
||||
|
||||
/* @function main */
|
||||
int main( int argc, char** argv )
|
||||
{
|
||||
/// Load image and template
|
||||
img = imread( argv[1], 1 );
|
||||
templ = imread( argv[2], 1 );
|
||||
|
||||
/// Create windows
|
||||
namedWindow( image_window, WINDOW_AUTOSIZE );
|
||||
namedWindow( result_window, WINDOW_AUTOSIZE );
|
||||
|
||||
/// Create Trackbar
|
||||
char* trackbar_label = "Method: \n 0: SQDIFF \n 1: SQDIFF NORMED \n 2: TM CCORR \n 3: TM CCORR NORMED \n 4: TM COEFF \n 5: TM COEFF NORMED";
|
||||
createTrackbar( trackbar_label, image_window, &match_method, max_Trackbar, MatchingMethod );
|
||||
|
||||
MatchingMethod( 0, 0 );
|
||||
|
||||
waitKey(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* @function MatchingMethod
|
||||
* @brief Trackbar callback
|
||||
*/
|
||||
void MatchingMethod( int, void* )
|
||||
{
|
||||
/// Source image to display
|
||||
Mat img_display;
|
||||
img.copyTo( img_display );
|
||||
|
||||
/// Create the result matrix
|
||||
int result_cols = img.cols - templ.cols + 1;
|
||||
int result_rows = img.rows - templ.rows + 1;
|
||||
|
||||
result.create( result_cols, result_rows, CV_32FC1 );
|
||||
|
||||
/// Do the Matching and Normalize
|
||||
matchTemplate( img, templ, result, match_method );
|
||||
normalize( result, result, 0, 1, NORM_MINMAX, -1, Mat() );
|
||||
|
||||
/// Localizing the best match with minMaxLoc
|
||||
double minVal; double maxVal; Point minLoc; Point maxLoc;
|
||||
Point matchLoc;
|
||||
|
||||
minMaxLoc( result, &minVal, &maxVal, &minLoc, &maxLoc, Mat() );
|
||||
|
||||
/// For SQDIFF and SQDIFF_NORMED, the best matches are lower values. For all the other methods, the higher the better
|
||||
if( match_method == CV_TM_SQDIFF || match_method == CV_TM_SQDIFF_NORMED )
|
||||
{ matchLoc = minLoc; }
|
||||
else
|
||||
{ matchLoc = maxLoc; }
|
||||
|
||||
/// Show me what you got
|
||||
rectangle( img_display, matchLoc, Point( matchLoc.x + templ.cols , matchLoc.y + templ.rows ), Scalar::all(0), 2, 8, 0 );
|
||||
rectangle( result, matchLoc, Point( matchLoc.x + templ.cols , matchLoc.y + templ.rows ), Scalar::all(0), 2, 8, 0 );
|
||||
|
||||
imshow( image_window, img_display );
|
||||
imshow( result_window, result );
|
||||
|
||||
return;
|
||||
}
|
||||
@endcode
|
||||
Explanation
|
||||
-----------
|
||||
|
||||
1. Declare some global variables, such as the image, template and result matrices, as well as the
|
||||
match method and the window names:
|
||||
@code{.cpp}
|
||||
Mat img; Mat templ; Mat result;
|
||||
char* image_window = "Source Image";
|
||||
char* result_window = "Result window";
|
||||
|
||||
int match_method;
|
||||
int max_Trackbar = 5;
|
||||
@endcode
|
||||
2. Load the source image and template:
|
||||
@code{.cpp}
|
||||
img = imread( argv[1], 1 );
|
||||
templ = imread( argv[2], 1 );
|
||||
@endcode
|
||||
3. Create the windows to show the results:
|
||||
@code{.cpp}
|
||||
namedWindow( image_window, WINDOW_AUTOSIZE );
|
||||
namedWindow( result_window, WINDOW_AUTOSIZE );
|
||||
@endcode
|
||||
4. Create the Trackbar to enter the kind of matching method to be used. When a change is detected
|
||||
the callback function **MatchingMethod** is called.
|
||||
@code{.cpp}
|
||||
char* trackbar_label = "Method: \n 0: SQDIFF \n 1: SQDIFF NORMED \n 2: TM CCORR \n 3: TM CCORR NORMED \n 4: TM COEFF \n 5: TM COEFF NORMED";
|
||||
createTrackbar( trackbar_label, image_window, &match_method, max_Trackbar, MatchingMethod );
|
||||
@endcode
|
||||
5. Wait until user exits the program.
|
||||
@code{.cpp}
|
||||
waitKey(0);
|
||||
return 0;
|
||||
@endcode
|
||||
6. Let's check out the callback function. First, it makes a copy of the source image:
|
||||
@code{.cpp}
|
||||
Mat img_display;
|
||||
img.copyTo( img_display );
|
||||
@endcode
|
||||
7. Next, it creates the result matrix that will store the matching results for each template
|
||||
location. Observe in detail the size of the result matrix (which matches all possible locations
|
||||
for it)
|
||||
@code{.cpp}
|
||||
int result_cols = img.cols - templ.cols + 1;
|
||||
int result_rows = img.rows - templ.rows + 1;
|
||||
|
||||
result.create( result_cols, result_rows, CV_32FC1 );
|
||||
@endcode
|
||||
8. Perform the template matching operation:
|
||||
@code{.cpp}
|
||||
matchTemplate( img, templ, result, match_method );
|
||||
@endcode
|
||||
the arguments are naturally the input image **I**, the template **T**, the result **R** and the
|
||||
match_method (given by the Trackbar)
|
||||
|
||||
9. We normalize the results:
|
||||
@code{.cpp}
|
||||
normalize( result, result, 0, 1, NORM_MINMAX, -1, Mat() );
|
||||
@endcode
|
||||
10. We localize the minimum and maximum values in the result matrix **R** by using @ref
|
||||
cv::minMaxLoc .
|
||||
@code{.cpp}
|
||||
double minVal; double maxVal; Point minLoc; Point maxLoc;
|
||||
Point matchLoc;
|
||||
|
||||
minMaxLoc( result, &minVal, &maxVal, &minLoc, &maxLoc, Mat() );
|
||||
@endcode
|
||||
the function calls as arguments:
|
||||
|
||||
- **result:** The source array
|
||||
- **&minVal** and **&maxVal:** Variables to save the minimum and maximum values in **result**
|
||||
- **&minLoc** and **&maxLoc:** The Point locations of the minimum and maximum values in the
|
||||
array.
|
||||
- **Mat():** Optional mask
|
||||
|
||||
11. For the first two methods ( TM_SQDIFF and MT_SQDIFF_NORMED ) the best match are the lowest
|
||||
values. For all the others, higher values represent better matches. So, we save the
|
||||
corresponding value in the **matchLoc** variable:
|
||||
@code{.cpp}
|
||||
if( match_method == TM_SQDIFF || match_method == TM_SQDIFF_NORMED )
|
||||
{ matchLoc = minLoc; }
|
||||
else
|
||||
{ matchLoc = maxLoc; }
|
||||
@endcode
|
||||
12. Display the source image and the result matrix. Draw a rectangle around the highest possible
|
||||
matching area:
|
||||
@code{.cpp}
|
||||
rectangle( img_display, matchLoc, Point( matchLoc.x + templ.cols , matchLoc.y + templ.rows ), Scalar::all(0), 2, 8, 0 );
|
||||
rectangle( result, matchLoc, Point( matchLoc.x + templ.cols , matchLoc.y + templ.rows ), Scalar::all(0), 2, 8, 0 );
|
||||
|
||||
imshow( image_window, img_display );
|
||||
imshow( result_window, result );
|
||||
@endcode
|
||||
Results
|
||||
-------
|
||||
|
||||
1. Testing our program with an input image such as:
|
||||
|
||||

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

|
||||
|
||||
2. Generate the following result matrices (first row are the standard methods SQDIFF, CCORR and
|
||||
CCOEFF, second row are the same methods in its normalized version). In the first column, the
|
||||
darkest is the better match, for the other two columns, the brighter a location, the higher the
|
||||
match.
|
||||
|
||||
|Result_0| |Result_2| |Result_4|
|
||||
------------- ------------- -------------
|
||||
|Result_1| |Result_3| |Result_5|
|
||||
|
||||
3. The right match is shown below (black rectangle around the face of the guy at the right). Notice
|
||||
that CCORR and CCDEFF gave erroneous best matches, however their normalized version did it
|
||||
right, this may be due to the fact that we are only considering the "highest match" and not the
|
||||
other possible high matches.
|
||||
|
||||

|
||||
|
||||
|
Reference in New Issue
Block a user