0、预备知识
归一化就是要把需要处理的数据经过处理后(通过某种算法)限制在你需要的一定范围内。
函数原型:
<span style="font-size:18px;">void normalize(InputArray src,OutputArray dst, double alpha=1,doublebeta=0, int norm_type=NORM_L2, int dtype=-1, InputArray mask=noArray() )
</span>
<span style="font-size:18px;">该函数归一化输入数组使它的范数或者数值范围在一定的范围内。
Parameters:
src 输入数组
dst 输出数组,支持原地运算
alpha
range normalization模式的最小值
beta
range normalization模式的最大值,不用于norm normalization(范数归一化)模式。
normType
归一化的类型,可以有以下的取值:
NORM_MINMAX:数组的数值被平移或缩放到一个指定的范围,线性归一化,一般较常用。
NORM_INF: 此类型的定义没有查到,根据OpenCV 1的对应项,可能是归一化数组的C-范数(绝对值的最大值)
NORM_L1 : 归一化数组的L1-范数(绝对值的和)
NORM_L2: 归一化数组的(欧几里德)L2-范数
dtype
dtype为负数时,输出数组的type与输入数组的type相同;
否则,输出数组与输入数组只是通道数相同,而tpye=CV_MAT_DEPTH(dtype).
mask
操作掩膜,用于指示函数是否仅仅对指定的元素进行操作</span>
归一化公式:
1、线性函数转换,表达式如下:(对应NORM_MINMAX)
ifmask(i,j)!=0
dst(i,j)=(src(i,j)-min(src))*(b‘-a‘)/(max(src)-min(src))+ a‘
else
dst(i,j)=src(i,j)
其中b‘=MAX(a,b), a‘=MIN(a,b);
2. 当norm_type!=CV_MINMAX:
ifmask(i,j)!=0
dst(i,j)=src(i,j)*a/norm (src,norm_type,mask)
else
dst(i,j)=src(i,j)
其中,函数norm的功能是计算norm(范数)的绝对值
Thefunctions normcalculate an absolute norm of src1 (when there is no src2 ):
、将该数值存储在新的图像中(BackProjection),也可以先归一化hue直方图数值到0-255范围,这样可以直接显示BackProjection图像(单通道图像)。
mixChannels: 从输入中拷贝某通道到输出中特定的通道。
C++: voidmixChannels(const Mat*src, size_t nsrcs, Mat* dst, size_t ndsts, const int*fromTo, size_t npairs)
double compareHist(InputArray H1, //直方图1 InputArray H2, //直方图2 intmethod//对比方法 );
method有CV_COMP_CORREL, CV_COMP_CHISQR,CV_COMP_INTERSECT,CV_COMP_BHATTACHARYYA四种方法,对应公式如下:
来个实例
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
using namespace cv;
int main( )
{
Mat srcImage_base, hsvImage_base;
Mat srcImage_test1, hsvImage_test1;
Mat srcImage_test2, hsvImage_test2;
Mat hsvImage_halfDown;
srcImage_base = imread( "1.jpg",1 );
srcImage_test1 = imread( "2.jpg", 1 );
srcImage_test2 = imread( "3.jpg", 1 );
imshow("基准图像",srcImage_base);
imshow("测试图像1",srcImage_test1);
imshow("测试图像2",srcImage_test2);
cvtColor( srcImage_base, hsvImage_base, COLOR_BGR2HSV );
cvtColor( srcImage_test1, hsvImage_test1, COLOR_BGR2HSV );
cvtColor( srcImage_test2, hsvImage_test2, COLOR_BGR2HSV );
hsvImage_halfDown = hsvImage_base( Range( hsvImage_base.rows/2, hsvImage_base.rows - 1 ), Range( 0, hsvImage_base.cols - 1 ) );
int h_bins = 50; int s_bins = 60;
int histSize[] = { h_bins, s_bins };
float h_ranges[] = { 0, 256 };
float s_ranges[] = { 0, 180 };
const float* ranges[] = { h_ranges, s_ranges };
int channels[] = { 0, 1 };
MatND baseHist;
MatND halfDownHist;
MatND testHist1;
MatND testHist2;
calcHist( &hsvImage_base, 1, channels, Mat(), baseHist, 2, histSize, ranges, true, false );
normalize( baseHist, baseHist, 0, 1, NORM_MINMAX, -1, Mat() );
calcHist( &hsvImage_halfDown, 1, channels, Mat(), halfDownHist, 2, histSize, ranges, true, false );
normalize( halfDownHist, halfDownHist, 0, 1, NORM_MINMAX, -1, Mat() );
calcHist( &hsvImage_test1, 1, channels, Mat(), testHist1, 2, histSize, ranges, true, false );
normalize( testHist1, testHist1, 0, 1, NORM_MINMAX, -1, Mat() );
calcHist( &hsvImage_test2, 1, channels, Mat(), testHist2, 2, histSize, ranges, true, false );
normalize( testHist2, testHist2, 0, 1, NORM_MINMAX, -1, Mat() );
for( int i = 0; i < 4; i++ )
{
int compare_method = i;
double base_base = compareHist( baseHist, baseHist, compare_method );
double base_half = compareHist( baseHist, halfDownHist, compare_method );
double base_test1 = compareHist( baseHist, testHist1, compare_method );
double base_test2 = compareHist( baseHist, testHist2, compare_method );
printf( " 方法 [%d] 的匹配结果如下:\n\n 【基准图 - 基准图】:%f, 【基准图 - 半身图】:%f,【基准图 - 测试图1】: %f, 【基准图 - 测试图2】:%f \n-----------------------------------------------------------------\n", i, base_base, base_half , base_test1, base_test2 );
}
printf( "检测结束。" );
waitKey(0);
return 0;
}
二、模版匹配
模板匹配是一项在一幅图像中寻找与另一幅模板图像最匹配(相似)部分的技术. 模板匹配是一种用于在源图像S中寻找定位给定目标图像T(即模板图像)的技术。其原理很简单,就是通过一些相似度准则来衡量两个图像块之间的相似度Similarity(S,T)。
通过 模版滑动, 我们的意思是图像块一次移动一个像素 (从左往右,从上往下). 在每一个位置, 都进行一次度量计算来表明它是 “好” 或 “坏” 地与那个位置匹配 (或者说块图像和原图像的特定区域有多么相似).
对于 T模版覆盖在 I原图 上的每个位置,你把度量值 保存 到 结果图像矩阵 (R) 中. 在 R 中的每个位置 (x,y) 都包含匹配度量值: 最白的位置代表最高的匹配. 正如您所见, 红色椭圆框住的位置很可能是结果图像矩阵中的最大数值, 所以这个区域 (以这个点为顶点,长宽和模板图像一样大小的矩阵) 被认为是匹配的. 我们使用函数 minMaxLoc 来定位在矩阵 R 中的最大值点 (或者最小值, 根据函数输入的匹配参数) .
目标匹配函数:
Void MatchTemplate( InputArray image, InputArray temp1, OutputArray result,int method );
Image 待搜索图像
Templ 模板图像
Result 匹配结果 用来存放通过以下方法计算出滑动窗口与模板的相似值
Method 计算匹配程度的方法
关于匹配方法,使用不同的方法产生的结果的意义可能不太一样,有些返回的值越大表示匹配程度越好,而有些方法返回的值越小表示匹配程度越好
关于参数 method:
TM_SQDIFF平方差匹配法:该方法采用平方差来进行匹配;最好的匹配值为0;匹配越差,匹配值越大。
TM_CCORR相关匹配法:该方法采用乘法操作;数值越大表明匹配程度越好。
TM_CCOEFF相关系数匹配法:1表示完美的匹配;-1表示最差的匹配。
TM_SQDIFF_NORMED归一化平方差匹配法
CV_TM_CCORR_NORMED归一化相关匹配法
CV_TM_CCOEFF_NORMED归一化相关系数匹配法
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include <iostream>
#include <stdio.h>
using namespace std;
using namespace cv;
Mat img; Mat templ; Mat result;
char* image_window = "Source Image";
char* result_window = "Result window";
int match_method;
int max_Trackbar = 5;
void MatchingMethod( int, void* );
int main( int argc, char** argv )
{
img = imread("1.jpg" , 1 );
templ = imread( "2.jpg", 1 );
namedWindow( image_window, CV_WINDOW_AUTOSIZE );
namedWindow( result_window, CV_WINDOW_AUTOSIZE );
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;
}
void MatchingMethod( int, void* )
{
Mat img_display;
img.copyTo( img_display );
int result_cols = img.cols - templ.cols + 1;
int result_rows = img.rows - templ.rows + 1;
result.create( result_cols, result_rows, CV_32FC1 );
matchTemplate( img, templ, result, match_method );
normalize( result, result, 0, 1, NORM_MINMAX, -1, Mat() );
double minVal; double maxVal; Point minLoc; Point maxLoc;
Point matchLoc;
minMaxLoc( result, &minVal, &maxVal, &minLoc, &maxLoc, Mat() );
if( match_method == CV_TM_SQDIFF || match_method == CV_TM_SQDIFF_NORMED )
{ matchLoc = minLoc; }
else
{ matchLoc = maxLoc; }
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;
}
三、反向投影
什么是反向投影直方图呢?简单的说在灰度图像的每个点(x,y),用它对应的直方图的bin的值(就是有多少像素落在bin内)来代替它。所以·如果这个bin的值比较大,那么反向投影显示的结果会比较亮,否则就比较暗。
从统计学的角度,反输出图像象素点的值是观测数组在某个分布(直方图)下的的概率。
所以加入我们已经得到了一个物体的直方图,我们可以计算它在另一幅图像中的反向投影,来判断这幅图像中是否有该物体。
1.反向投影的作用是什么?
反向投影用于在输入图像(通常较大)中查找特定图像(通常较小或者仅1个像素,以下将其称为模板图像)最匹配的点或者区域,也就是定位模板图像出现在输入图像的位置。
2.反向投影如何查找(工作)?
查找的方式就是不断的在输入图像中切割跟模板图像大小一致的图像块,并用直方图对比的方式与模板图像进行比较。
3.反向投影的结果是什么?
反向投影的结果包含了:以每个输入图像像素点为起点的直方图对比结果。可以把它看成是一个二维的浮点型数组,二维矩阵,或者单通道的浮点型图像。backproject是直接取直方图中的值,即以灰度为例,某种灰度值在整幅图像中所占面积越大,其在直方图中的值越大,backproject时,其对应的像素的新值越大(越亮),反过来,某灰度值所占面积越小,其新值就越小。
假设我们有一张100x100的输入图像,有一张10x10的模板图像,查找的过程是这样的:
(1)从输入图像的左上角(0,0)开始,切割一块(0,0)至(10,10)的临时图像;
(2)生成临时图像的直方图;
(3)用临时图像的直方图和模板图像的直方图对比,对比结果记为c;
(4)直方图对比结果c,就是结果图像(0,0)处的像素值;
(5)切割输入图像从(0,1)至(10,11)的临时图像,对比直方图,并记录到结果图像;
(6)重复(1)~(5)步直到输入图像的右下角。
原图中以某点为基准,抠出来作对比的部分也转换为直方图,两个直方图作匹配,匹配的结果作为此点的值。结果会是一张概率图,概率越大的地方,代表此区域与模板的相似度越高。
利用Hue直方图解释反向投影原理:
1、获取测试图像中每个像素的hue数据 hi,j,并找到 hi,j 在hue直方图中的bin的位置。
2、查询hue直方图中对应bin的数值。
3、将该数值存储在新的图像中(BackProjection),也可以先归一化hue直方图数值到0-255范围,这样可以直接显示BackProjection图像(单通道图像)。
4、通过对测试图像每个像素采取以上步骤,可以得到最终的BackProjection图像。
void cvCalcBackProject( IplImage** image, CvArr* back_project, const CvHistogram* hist );
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"
using namespace cv;
#define WINDOW_NAME1 "【原始图】"
Mat g_srcImage; Mat g_hsvImage; Mat g_hueImage;
int g_bins = 30;//直方图组距
void on_BinChange(int, void* );
int main( )
{
g_srcImage = imread( "1.jpg", 1 );
if(!g_srcImage.data ) { printf("读取图片错误,请确定目录下是否有imread函数指定图片存在~! \n"); return false; }
cvtColor( g_srcImage, g_hsvImage, COLOR_BGR2HSV );
g_hueImage.create( g_hsvImage.size(), g_hsvImage.depth() );
int ch[ ] = { 0, 0 };
mixChannels( &g_hsvImage, 1, &g_hueImage, 1, ch, 1 );
namedWindow( WINDOW_NAME1 , WINDOW_AUTOSIZE );
createTrackbar("色调组距 ", WINDOW_NAME1 , &g_bins, 180, on_BinChange );
on_BinChange(0, 0);//进行一次初始化
imshow( WINDOW_NAME1 , g_srcImage );
waitKey(0);
return 0;
}
void on_BinChange(int, void* )
{
MatND hist;
int histSize = MAX( g_bins, 2 );
float hue_range[] = { 0, 180 };
const float* ranges = { hue_range };
calcHist( &g_hueImage, 1, 0, Mat(), hist, 1, &histSize, &ranges, true, false );
normalize( hist, hist, 0, 255, NORM_MINMAX, -1, Mat() );
MatND backproj;
calcBackProject( &g_hueImage, 1, 0, hist, backproj, &ranges, 1, true );
imshow( "反向投影图", backproj );
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 < g_bins; i ++ )
{ rectangle( histImg, Point( i*bin_w, h ), Point( (i+1)*bin_w, h - cvRound( hist.at<float>(i)*h/255.0 ) ),
<span style="font-family: Arial, Helvetica, sans-serif;"> Scalar( 100, 123, 255 ), -1 ); }
</span><pre name="code" class="cpp" style="color: rgb(63, 63, 63);"> imshow( "直方图", histImg );
}