Doxygen tutorials: cpp done
This commit is contained in:
@@ -28,17 +28,14 @@ Theory
|
||||
- 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
|
||||
@@ -50,7 +47,7 @@ Theory
|
||||
the *model histogram* first, so the output for the Test Image can be visible for you.
|
||||
-# Applying the steps above, we get the following BackProjection image for our Test Image:
|
||||
|
||||

|
||||

|
||||
|
||||
-# In terms of statistics, the values stored in *BackProjection* represent the *probability*
|
||||
that a pixel in *Test Image* belongs to a skin area, based on the *model histogram* that we
|
||||
@@ -83,98 +80,23 @@ Code
|
||||
in samples.
|
||||
|
||||
- **Code at glance:**
|
||||
@code{.cpp}
|
||||
#include "opencv2/imgproc.hpp"
|
||||
#include "opencv2/highgui.hpp"
|
||||
@includelineno samples/cpp/tutorial_code/Histograms_Matching/calcBackProject_Demo1.cpp
|
||||
|
||||
#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
|
||||
-# 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:
|
||||
-# 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
|
||||
-# 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}
|
||||
@@ -182,7 +104,7 @@ Explanation
|
||||
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
|
||||
as you see, we use the function @ref cv::mixChannels to get only the channel 0 (Hue) from
|
||||
the hsv image. It gets the following parameters:
|
||||
|
||||
- **&hsv:** The source array from which the channels will be copied
|
||||
@@ -193,7 +115,7 @@ Explanation
|
||||
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
|
||||
-# 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";
|
||||
@@ -201,14 +123,14 @@ Explanation
|
||||
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:
|
||||
-# 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
|
||||
-# **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* )
|
||||
@@ -218,12 +140,12 @@ Explanation
|
||||
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$
|
||||
-# 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
|
||||
-# 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 );
|
||||
@@ -231,11 +153,11 @@ Explanation
|
||||
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:
|
||||
-# Display backproj:
|
||||
@code{.cpp}
|
||||
imshow( "BackProj", backproj );
|
||||
@endcode
|
||||
10. Draw the 1-D Hue histogram of the image:
|
||||
-# Draw the 1-D Hue histogram of the image:
|
||||
@code{.cpp}
|
||||
int w = 400; int h = 400;
|
||||
int bin_w = cvRound( (double) w / histSize );
|
||||
@@ -246,12 +168,12 @@ Explanation
|
||||
|
||||
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|
|
||||
------ ------ ------
|
||||
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:
|
||||

|
||||

|
||||

|
||||
|
@@ -21,7 +21,7 @@ histogram called *Image histogram*. Now we will considerate it in its more gener
|
||||
- Let's see an example. Imagine that a Matrix contains information of an image (i.e. intensity in
|
||||
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
|
||||
@@ -36,7 +36,7 @@ histogram called *Image histogram*. Now we will considerate it in its more gener
|
||||
this to the example above we get the image below ( axis x represents the bins and axis y the
|
||||
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
|
||||
@@ -73,18 +73,18 @@ Code
|
||||
Explanation
|
||||
-----------
|
||||
|
||||
1. Create the necessary matrices:
|
||||
-# Create the necessary matrices:
|
||||
@code{.cpp}
|
||||
Mat src, dst;
|
||||
@endcode
|
||||
2. Load the source image
|
||||
-# 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
|
||||
-# 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;
|
||||
@@ -93,7 +93,7 @@ Explanation
|
||||
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
|
||||
-# Now we are ready to start configuring the **histograms** for each plane. Since we are working
|
||||
with the B, G and R planes, we know that our values will range in the interval \f$[0,255]\f$
|
||||
-# Establish number of bins (5, 10...):
|
||||
@code{.cpp}
|
||||
@@ -137,7 +137,7 @@ Explanation
|
||||
- **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:
|
||||
-# 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;
|
||||
@@ -145,7 +145,7 @@ Explanation
|
||||
|
||||
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
|
||||
-# 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 ]
|
||||
@@ -164,7 +164,7 @@ Explanation
|
||||
- **-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):
|
||||
-# 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++ )
|
||||
@@ -189,7 +189,7 @@ Explanation
|
||||
b_hist.at<float>( i, j )
|
||||
@endcode
|
||||
|
||||
8. Finally we display our histograms and wait for the user to exit:
|
||||
-# Finally we display our histograms and wait for the user to exit:
|
||||
@code{.cpp}
|
||||
namedWindow("calcHist Demo", WINDOW_AUTOSIZE );
|
||||
imshow("calcHist Demo", histImage );
|
||||
@@ -202,10 +202,10 @@ Explanation
|
||||
Result
|
||||
------
|
||||
|
||||
1. Using as input argument an image like the shown below:
|
||||
-# Using as input argument an image like the shown below:
|
||||
|
||||

|
||||

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

|
||||

|
||||
|
@@ -18,25 +18,18 @@ Theory
|
||||
- OpenCV implements the function @ref cv::compareHist to perform a comparison. It also offers 4
|
||||
different metrics to compute the matching:
|
||||
-# **Correlation ( CV_COMP_CORREL )**
|
||||
|
||||
\f[d(H_1,H_2) = \frac{\sum_I (H_1(I) - \bar{H_1}) (H_2(I) - \bar{H_2})}{\sqrt{\sum_I(H_1(I) - \bar{H_1})^2 \sum_I(H_2(I) - \bar{H_2})^2}}\f]
|
||||
|
||||
where
|
||||
|
||||
\f[\bar{H_k} = \frac{1}{N} \sum _J H_k(J)\f]
|
||||
|
||||
and \f$N\f$ is the total number of histogram bins.
|
||||
|
||||
-# **Chi-Square ( CV_COMP_CHISQR )**
|
||||
|
||||
\f[d(H_1,H_2) = \sum _I \frac{\left(H_1(I)-H_2(I)\right)^2}{H_1(I)}\f]
|
||||
|
||||
-# **Intersection ( method=CV_COMP_INTERSECT )**
|
||||
|
||||
\f[d(H_1,H_2) = \sum _I \min (H_1(I), H_2(I))\f]
|
||||
|
||||
-# **Bhattacharyya distance ( CV_COMP_BHATTACHARYYA )**
|
||||
|
||||
\f[d(H_1,H_2) = \sqrt{1 - \frac{1}{\sqrt{\bar{H_1} \bar{H_2} N^2}} \sum_I \sqrt{H_1(I) \cdot H_2(I)}}\f]
|
||||
|
||||
Code
|
||||
@@ -59,7 +52,7 @@ Code
|
||||
Explanation
|
||||
-----------
|
||||
|
||||
1. Declare variables such as the matrices to store the base image and the two other images to
|
||||
-# Declare variables such as the matrices to store the base image and the two other images to
|
||||
compare ( RGB and HSV )
|
||||
@code{.cpp}
|
||||
Mat src_base, hsv_base;
|
||||
@@ -67,7 +60,7 @@ Explanation
|
||||
Mat src_test2, hsv_test2;
|
||||
Mat hsv_half_down;
|
||||
@endcode
|
||||
2. Load the base image (src_base) and the other two test images:
|
||||
-# Load the base image (src_base) and the other two test images:
|
||||
@code{.cpp}
|
||||
if( argc < 4 )
|
||||
{ printf("** Error. Usage: ./compareHist_Demo <image_settings0> <image_setting1> <image_settings2>\n");
|
||||
@@ -78,17 +71,17 @@ Explanation
|
||||
src_test1 = imread( argv[2], 1 );
|
||||
src_test2 = imread( argv[3], 1 );
|
||||
@endcode
|
||||
3. Convert them to HSV format:
|
||||
-# 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):
|
||||
-# 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 ).
|
||||
-# 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 };
|
||||
@@ -100,14 +93,14 @@ Explanation
|
||||
|
||||
int channels[] = { 0, 1 };
|
||||
@endcode
|
||||
6. Create the MatND objects to store the histograms:
|
||||
-# 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:
|
||||
-# 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() );
|
||||
@@ -121,7 +114,7 @@ Explanation
|
||||
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)
|
||||
-# 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++ )
|
||||
@@ -134,34 +127,32 @@ Explanation
|
||||
printf( " Method [%d] Perfect, Base-Half, Base-Test(1), Base-Test(2) : %f, %f, %f, %f \n", i, base_base, base_half , base_test1, base_test2 );
|
||||
}
|
||||
@endcode
|
||||
|
||||
Results
|
||||
-------
|
||||
|
||||
1. We use as input the following images:
|
||||
|
||||
----------- ----------- -----------
|
||||
|Base_0| |Test_1| |Test_2|
|
||||
----------- ----------- -----------
|
||||
|
||||
-# We use as input the following images:
|
||||

|
||||

|
||||

|
||||
where the first one is the base (to be compared to the others), the other 2 are the test images.
|
||||
We will also compare the first image with respect to itself and with respect of half the base
|
||||
image.
|
||||
|
||||
2. We should expect a perfect match when we compare the base image histogram with itself. Also,
|
||||
-# We should expect a perfect match when we compare the base image histogram with itself. Also,
|
||||
compared with the histogram of half the base image, it should present a high match since both
|
||||
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.
|
||||
-# 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.
|
||||
|
@@ -17,7 +17,7 @@ Theory
|
||||
- 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?
|
||||
|
||||
@@ -29,7 +29,7 @@ Theory
|
||||
*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?
|
||||
|
||||
@@ -46,7 +46,7 @@ Theory
|
||||
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:
|
||||
@@ -69,14 +69,14 @@ Code
|
||||
Explanation
|
||||
-----------
|
||||
|
||||
1. Declare the source and destination images as well as the windows names:
|
||||
-# Declare the source and destination images as well as the windows names:
|
||||
@code{.cpp}
|
||||
Mat src, dst;
|
||||
|
||||
char* source_window = "Source image";
|
||||
char* equalized_window = "Equalized Image";
|
||||
@endcode
|
||||
2. Load the source image:
|
||||
-# Load the source image:
|
||||
@code{.cpp}
|
||||
src = imread( argv[1], 1 );
|
||||
|
||||
@@ -84,18 +84,18 @@ Explanation
|
||||
{ cout<<"Usage: ./Histogram_Demo <path_to_image>"<<endl;
|
||||
return -1;}
|
||||
@endcode
|
||||
3. Convert it to grayscale:
|
||||
-# Convert it to grayscale:
|
||||
@code{.cpp}
|
||||
cvtColor( src, src, COLOR_BGR2GRAY );
|
||||
@endcode
|
||||
4. Apply histogram equalization with the function @ref cv::equalizeHist :
|
||||
-# 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) :
|
||||
-# Display both images (original and equalized) :
|
||||
@code{.cpp}
|
||||
namedWindow( source_window, WINDOW_AUTOSIZE );
|
||||
namedWindow( equalized_window, WINDOW_AUTOSIZE );
|
||||
@@ -103,7 +103,7 @@ Explanation
|
||||
imshow( source_window, src );
|
||||
imshow( equalized_window, dst );
|
||||
@endcode
|
||||
6. Wait until user exists the program
|
||||
-# Wait until user exists the program
|
||||
@code{.cpp}
|
||||
waitKey(0);
|
||||
return 0;
|
||||
@@ -112,24 +112,24 @@ Explanation
|
||||
Results
|
||||
-------
|
||||
|
||||
1. To appreciate better the results of equalization, let's introduce an image with not much
|
||||
-# To appreciate better the results of equalization, let's introduce an image with not much
|
||||
contrast, such as:
|
||||
|
||||

|
||||

|
||||
|
||||
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:
|
||||
-# 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.
|
||||
|
||||
|
@@ -28,12 +28,12 @@ template image (patch).
|
||||
|
||||
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
|
||||
@@ -41,7 +41,7 @@ template image (patch).
|
||||
- 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
|
||||
@@ -56,23 +56,23 @@ template image (patch).
|
||||
Good question. OpenCV implements Template matching in the function @ref cv::matchTemplate . The
|
||||
available methods are 6:
|
||||
|
||||
a. **method=CV_TM_SQDIFF**
|
||||
-# **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**
|
||||
-# **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**
|
||||
-# **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**
|
||||
-# **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**
|
||||
-# **method=CV_TM_CCOEFF**
|
||||
|
||||
\f[R(x,y)= \sum _{x',y'} (T'(x',y') \cdot I(x+x',y+y'))\f]
|
||||
|
||||
@@ -80,7 +80,7 @@ e. **method=CV_TM_CCOEFF**
|
||||
|
||||
\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**
|
||||
-# **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]
|
||||
|
||||
@@ -98,93 +98,12 @@ Code
|
||||
- **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>
|
||||
@includelineno samples/cpp/tutorial_code/Histograms_Matching/MatchTemplate_Demo.cpp
|
||||
|
||||
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
|
||||
-# 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;
|
||||
@@ -194,33 +113,33 @@ Explanation
|
||||
int match_method;
|
||||
int max_Trackbar = 5;
|
||||
@endcode
|
||||
2. Load the source image and template:
|
||||
-# 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:
|
||||
-# 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
|
||||
-# 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.
|
||||
-# 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:
|
||||
-# 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
|
||||
-# 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}
|
||||
@@ -229,18 +148,18 @@ Explanation
|
||||
|
||||
result.create( result_cols, result_rows, CV_32FC1 );
|
||||
@endcode
|
||||
8. Perform the template matching operation:
|
||||
-# 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:
|
||||
-# 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
|
||||
-# 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;
|
||||
@@ -256,7 +175,7 @@ Explanation
|
||||
array.
|
||||
- **Mat():** Optional mask
|
||||
|
||||
11. For the first two methods ( TM_SQDIFF and MT_SQDIFF_NORMED ) the best match are the lowest
|
||||
-# 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}
|
||||
@@ -265,7 +184,7 @@ Explanation
|
||||
else
|
||||
{ matchLoc = maxLoc; }
|
||||
@endcode
|
||||
12. Display the source image and the result matrix. Draw a rectangle around the highest possible
|
||||
-# 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 );
|
||||
@@ -274,29 +193,32 @@ Explanation
|
||||
imshow( image_window, img_display );
|
||||
imshow( result_window, result );
|
||||
@endcode
|
||||
|
||||
Results
|
||||
-------
|
||||
|
||||
1. Testing our program with an input image such as:
|
||||
-# 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
|
||||
-# 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
|
||||
-# 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