图像直方图:是用以表示数字图像中亮度分布的直方图,标会了图像中每个亮度值的像素数。可以借助观察该直方图了解需要如何调整亮度分布。
直方图的横坐标的左侧为纯黑、较暗的区域;右侧为较亮、纯白的区域。
计算机视觉邻域常借助图像直方图来实现图像的二值化。
意义:是图像中像素强度分布的图像并表达方式;它统计了每一个强度值所具有的的像素个数。
( bin:为直条或组距----数据可为梯度、方向、色彩、或其他特征
dims:需要统计的特征的数目。
range:每个特征空间的取值范围。[0,255]
)
①计算直方图:calcHist()函数----用于计算一个或多个阵列的直方图。
void calcHist(
const Mat* images, //输入的数组集
int nimages, //输入数组的个数,即参数一存放了多少张图像,有几个原数组
const int* channels,//需要统计的通道(dim)索引
InputArray mask, //可选的操作掩码
uputArray hist, //输出的直方图目标
int dims, //需要计算的直方图维度,必须为正数
const int* histSize,//存放每个维度的直方图尺寸的数组
const float** ranges,//表示每一个维度数组的每一维的边界阵列(取值范围)
bool uniform=true, //指示直方图是否均匀的标识符
bool accumulate=false//累计标识符
);
②寻找最值:minMaxLoc()函数----在数组中找到全局最小值和最大值。
void minMaxLoc(
InputArray src, //输入的单通道阵列
double* minVal, //返回最小值的指针(无需返回用null)
double* maxVal=0,//返回最大值的指针(无需返回用null)
Point* minLoc=0,//返回最小位置的指针(无需返回用null)
Point* maxLoc=0,//返回最大位置的指针(无需返回用null)
nputArray mask=noArray()//用于选择子阵列的可选掩膜
);
代码示例:
//----------------------【绘制H-S直方图(色调-饱和度直方图)】---------------------
// 描述:计算彩色图像的色调、饱和度二维直方图
//---------------------------------------------------------------------------------
#include
#include
#include
using namespace cv;
//----------------------【main()函数】---------------------
// 描述:程序入口
//---------------------------------------------------------
int main() {
//【1】载入原图,转化为HSV颜色模型
Mat srcImage, hsvImage;
srcImage = imread("juan.jpg");
cvtColor(srcImage,hsvImage,COLOR_BGR2HSV);
//【2】参数准备
//将色调量化为30个等级,将饱和度量化为32个等级
int hueBinNum = 30;//色调直方图条数量
int saturationBinNum = 32;//饱和度的直方图直条数量
int histSize[] = { hueBinNum,saturationBinNum };
//定义色调的变化范围为0-179
float hueRanges[] = {0,180};
//定义饱和度的变化范围为0(黑、白、灰)到255(纯光谱颜色)
float saturationRanges[] = {0,256};
const float* ranges[] = {hueRanges,saturationRanges};
MatND dstHist;
//参数准备,calcHist函数中将计算第0通道和第1通道的直方图
int channels[] = {0,1};
//【3】正式调用calcHist,进行直方图计算
calcHist(
&hsvImage,//输入图像
1,//数组个数为1
channels,//通道索引
Mat(),//不使用掩模
dstHist,//输出目标直方图
2,//需要计算的直方图的维度2
histSize,//存放每个维度的直方图尺寸的数组
ranges,//每一维数值的取值范围数组
true,//指示直方图是否均匀的标识符,true表示均匀的直方图
false//累计标识符,false表示直方图在配置阶段会被清零
);
//【4】为绘制直方图准备参数
double maxValue = 0;
minMaxLoc(dstHist,0,&maxValue,0,0);//查找数组和子数组的全局最小值和最大值存入macValue中
int scale = 10;
Mat histImg = Mat::zeros(saturationBinNum*scale,hueBinNum*10,CV_8UC3);
//【5】双层循环,进行直方图绘制
for (int hue = 0; hue < hueBinNum; hue++)
for (int saturation=0; saturation < saturationBinNum;saturation++) {
float binValue = dstHist.at(hue,saturation);//直方图直条的值
int intensity = cvRound(binValue*255/maxValue);//强度
//正式进行绘制
rectangle(histImg,Point(hue*scale,saturation*scale),
Point((hue+1)*scale-1,(saturation+1)*scale-1),Scalar::all(intensity),FILLED);
}
//【6】显示效果图
imshow("原图",srcImage);
imshow("H-S直方图",histImg);
waitKey();
}
//----------------------【计算并绘制图像一维直方图】---------------------
// 描述:一维直方图绘制
//---------------------------------------------------------------------------------
#include
#include
#include
#include
using namespace cv;
//----------------------【main()函数】---------------------
// 描述:程序入口
//---------------------------------------------------------
int main() {
//【1】载入原图,转化为HSV颜色模型
Mat srcImage, hsvImage;
srcImage = imread("juan.jpg");
imshow("原图", srcImage);
//【2】定义变量
MatND dstHist;
int dims = 1;
float hranges[] = {0,255};
const float *ranges[] = {hranges};
int size = 256;
int channels = 0;
//【3】正式调用calcHist,进行直方图计算
calcHist(&srcImage,1,&channels,Mat(),dstHist,dims,&size,ranges);
int scale = 1;
Mat dstImage(size*scale,size,CV_8U,Scalar(0));
//【4】获得最大值和最小值
double maxValue = 0;
double minValue = 0;
minMaxLoc(dstHist, &minValue, &maxValue, 0, 0);//查找数组和子数组的全局最小值和最大值存入macValue中
//【5】双层循环,进行直方图绘制
int hpt = saturate_cast(0.9*size);
for (int i = 0; i < 256; i++)
{
float binValue = dstHist.at(i);//直方图直条的值
int realValue = saturate_cast(binValue * hpt / maxValue);//强度
//正式进行绘制
rectangle(dstImage, Point(i*scale, size-1),
Point((i + 1)*scale - 1, size - realValue), Scalar(255));
}
//【6】显示效果图
imshow("一维直方图", dstImage);
waitKey(0);
return 0;
}
//----------------------【绘制RGB三色直方图】---------------------
// 描述:RGB直方图绘制
//---------------------------------------------------------------------------------
#include
#include
#include
using namespace cv;
//----------------------【main()函数】---------------------
// 描述:程序入口
//---------------------------------------------------------
int main() {
//【1】载入原图,转化为HSV颜色模型
Mat srcImage;
srcImage = imread("juan.jpg");
imshow("原图", srcImage);
//【2】参数准备
int bins = 256;
int hist_size[] = {bins};
float range[] = {0,256};
const float* ranges[] = {range};
MatND redHist, greenHist, blueHist;
//【3】正式调用calcHist,进行直方图计算(红色部分)
int channels_r[] = {0};
calcHist(
&srcImage,
1,
channels_r,
Mat(),
redHist,//输出目标直方图
1,//需要计算的直方图的维度2
hist_size,//存放每个维度的直方图尺寸的数组
ranges,//每一维数值的取值范围数组
true,//指示直方图是否均匀的标识符,true表示均匀的直方图
false//累计标识符,false表示直方图在配置阶段会被清零
);
//【4】正式调用calcHist,进行直方图计算(绿色部分)
int channels_g[] = { 1 };
calcHist(
&srcImage,//输入图像
1,//数组个数为1
channels_g,//通道索引
Mat(),//不使用掩模
greenHist,//输出目标直方图
1,//需要计算的直方图的维度2
hist_size,//存放每个维度的直方图尺寸的数组
ranges,//每一维数值的取值范围数组
true,//指示直方图是否均匀的标识符,true表示均匀的直方图
false//累计标识符,false表示直方图在配置阶段会被清零
);
//【5】正式调用calcHist,进行直方图计算(绿色部分)
int channels_b[] = { 2 };
calcHist(
&srcImage,//输入图像
1,//数组个数为1
channels_b,//通道索引
Mat(),//不使用掩模
blueHist,//输出目标直方图
1,//需要计算的直方图的维度2
hist_size,//存放每个维度的直方图尺寸的数组
ranges,//每一维数值的取值范围数组
true,//指示直方图是否均匀的标识符,true表示均匀的直方图
false//累计标识符,false表示直方图在配置阶段会被清零
);
//【4】绘制出三色直方图
double maxValue_red , maxValue_green, maxValue_blue;
minMaxLoc(redHist, 0, &maxValue_red, 0, 0);//查找数组和子数组的全局最小值和最大值存入macValue中
minMaxLoc(greenHist, 0, &maxValue_green, 0, 0);
minMaxLoc(blueHist, 0, &maxValue_blue, 0, 0);
int scale = 1;
int histHeight = 256;
Mat histImage = Mat::zeros(histHeight,bins*3, CV_8UC3);
//【5】双层循环,进行直方图绘制
for (int i= 0; i < bins ;i++){
//参数准备
float binValue_red = redHist.at(i);//直方图直条的值
float binValue_green =greenHist.at(i);
float binValue_blue = blueHist.at(i);
int intensity_red = cvRound(binValue_red * histHeight / maxValue_red);//强度
int intensity_green = cvRound(binValue_green * histHeight / maxValue_green);
int intensity_blue = cvRound(binValue_blue * histHeight / maxValue_blue);
//正式进行绘制
rectangle(histImage, Point(i*scale, histHeight-1),
Point((i+ 1)*scale - 1,histHeight-intensity_red), Scalar(255,0,0));
rectangle(histImage, Point((i+bins)*scale, histHeight - 1),
Point((i + bins+1)*scale - 1, histHeight - intensity_green), Scalar(0,255, 0));
rectangle(histImage, Point((i+bins*2)*scale, histHeight - 1),
Point((i +bins*2+1)*scale - 1, histHeight - intensity_blue), Scalar(0, 0,255));
}
//【6】显示效果图
imshow("RGB直方图", histImage);
waitKey(0);
return 0;
}
对比直方图:compareHist()函数------用于两幅直方图进行比较
double compareHist(InputArray H1,InputArray H2,int method);
double compareHist(const SpareMar& H1,const SpareMar& H2,int method);
参数三是所选择的距离标准。(相关、卡方、直方图相交、Bhattacharyya距离)
直方图对比综合示例:
//----------------------【直方图对比】----------------------------
// 描述:直方图对比
//---------------------------------------------------------------
#include
#include
#include
#include
using namespace cv;
//----------------------【main()函数】---------------------
// 描述:程序入口
//---------------------------------------------------------
int main() {
//【1】声明储存基准图像和另外两张对比图像的矩阵(RGB和HSV)
Mat srcImage_base,hsvImage_base;
Mat srcImage_test1, hsvImage_test1;
Mat srcImage_test2, hsvImage_test2;
Mat hsvImage_halfDown;
//【2】载入、显示原图像
srcImage_base = imread("juan.jpg",1);
srcImage_test1= imread("lenna.jpg",1);
srcImage_test2 = imread("lenna.jpg", 1);
imshow("基准图像", srcImage_base);
imshow("测试图像1", srcImage_test1);
imshow("测试图像2", srcImage_test2);
//【3】将图像由BGR色彩转换到HSV色彩空间
cvtColor(srcImage_base, hsvImage_base,COLOR_BGR2HSV);
cvtColor(srcImage_test1, hsvImage_test1, COLOR_BGR2HSV);
cvtColor(srcImage_test2, hsvImage_test2, COLOR_BGR2HSV);
//【4】创建包含基准图像下半部的半身图像(HSV格式)
hsvImage_halfDown = hsvImage_base(Range(hsvImage_base.rows/2,hsvImage_base.rows-1),Range(0, hsvImage_base.cols- 1));
//【5】初始化计算计算直方图需要的实参
//对hue通道使用50个bin,对saturation通道使用60个bin
int h_bins = 50;
int s_bins = 60;
int histSize[] = {h_bins,s_bins};
//hue的取值范围从0-256,saturation取值范围从0-180
float h_ranges[] = {0,256};
float s_ranges[] = {0,180};
const float* ranges[] = {h_ranges,s_ranges};
//使用第0和第1通道
int channels[] = {0,1};
//【6】创建存储直方图的MatND类的实例
MatND baseHist;
MatND halfDownHist;
MatND testHist1;
MatND testHist2;
//【7】计算基准图像,两张测试图像,半身基准图像的HSV直方图
calcHist(
&hsvImage_base,//输入图像
1,//数组个数为1
channels,//通道索引
Mat(),//不使用掩模
baseHist,//输出目标直方图
2,//需要计算的直方图的维度2
histSize,//存放每个维度的直方图尺寸的数组
ranges,//每一维数值的取值范围数组
true,//指示直方图是否均匀的标识符,true表示均匀的直方图
false//累计标识符,false表示直方图在配置阶段会被清零
);
normalize(baseHist,baseHist,0,1,NORM_MINMAX,-1,Mat());
calcHist(
&hsvImage_halfDown,//输入图像
1,//数组个数为1
channels,//通道索引
Mat(),//不使用掩模
halfDownHist,//输出目标直方图
2,//需要计算的直方图的维度2
histSize,//存放每个维度的直方图尺寸的数组
ranges,//每一维数值的取值范围数组
true,//指示直方图是否均匀的标识符,true表示均匀的直方图
false//累计标识符,false表示直方图在配置阶段会被清零
);
normalize(halfDownHist, halfDownHist, 0, 1, NORM_MINMAX, -1, Mat());
calcHist(
&hsvImage_test1,//输入图像
1,//数组个数为1
channels,//通道索引
Mat(),//不使用掩模
testHist1,//输出目标直方图
2,//需要计算的直方图的维度2
histSize,//存放每个维度的直方图尺寸的数组
ranges,//每一维数值的取值范围数组
true,//指示直方图是否均匀的标识符,true表示均匀的直方图
false//累计标识符,false表示直方图在配置阶段会被清零
);
normalize(testHist1, testHist1, 0, 1, NORM_MINMAX, -1, Mat());
calcHist(
&hsvImage_test2,//输入图像
1,//数组个数为1
channels,//通道索引
Mat(),//不使用掩模
testHist2,//输出目标直方图
2,//需要计算的直方图的维度2
histSize,//存放每个维度的直方图尺寸的数组
ranges,//每一维数值的取值范围数组
true,//指示直方图是否均匀的标识符,true表示均匀的直方图
false//累计标识符,false表示直方图在配置阶段会被清零
);
normalize(testHist2, testHist2, 0, 1, NORM_MINMAX, -1, Mat());
//【8】按照顺序使用4种对比标准将基准图像的直方图与其余各直方图进行对比
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);
}
//【6】显示效果图
printf("检测结果");
waitKey(0);
return 0;
}
反射投影是一种记录给定图像中的像素点如何适应直方图模型像素分布方式的一种方法。
即首先计算某一特征的直方图模型,然后使用模型去寻找图像中存在的该特征方程的方法。
作用:用于在输入图像中查找与特定图像最匹配的点或区域,也就是定位模板图像出现在输入图像的位置。
计算反射投影:calcBackProject()函数----用于计算直方图的反向投影
void calcBackProject(
const Mat* iamge, //输入的数组集
int nimages, //输入数组的个数
const int* channels,//需要统计的通道索引
InputArray hist, //输入直方图
OutputArray backProject, //输出的目标反向投影阵列,其须为单通道
const float** ranges, //每一维数值的取值范围
double scale=1, //缩放因子(默认1)
bool uniform=true //直方图是否均匀的标识符
);
通道复制:mixChannels()函数-----由输入参数复制某通道到输出参数特定的通道中。
void mixChannels(
const Mat* src,//输入的数组,所有的矩阵必须有相同的尺寸和深度
size_t nsrcs, //第一个参数src输入的矩阵个数
Mat* dst,//输出的数组,所有矩阵必须被初始化,且大小和深度必须与src[0]相同
size_t ndsts,//第三个参数dst输入的矩阵数
const int* formTo,//对指定的通道进行复制的数组索引
size_t npairs//第五个参数formTO的索引数
);
反射投影综合示例:
//---------------------------------【反射投影】------------------------------
// 描述:反射投影
//---------------------------------------------------------------------------
#include
#include
#include
using namespace cv;
//---------------------------------【宏定义部分】------------------------------
// 描述:定义一些辅助宏
//---------------------------------------------------------------------------
#define WINDOW_NAME "原始图" //为窗口标题定义的宏
//---------------------------------【全局变量声明部分】------------------------------
// 描述:全局变量的声明
//---------------------------------------------------------------------------
Mat g_srcImage;
Mat g_hsvImage;
Mat g_hueImage;
int g_bins = 30;
//----------------------【全局函数声明部分】---------------------
// 描述:全局函数声明部分
//---------------------------------------------------------
void on_BinChange(int,void*);
//----------------------【main()函数】---------------------
// 描述:程序入口
//---------------------------------------------------------
int main() {
//【1】载入原图,转化为HSV颜色模型
g_srcImage= imread("juan.jpg");
cvtColor(g_srcImage, g_hsvImage, COLOR_BGR2HSV);
//【2】分离Hue色调通道
g_hueImage.create(g_hsvImage.size(),g_hueImage.depth());
int ch[] = {0,0};
mixChannels(&g_hsvImage,1,&g_hueImage,1,ch,1);
//【3】创建TrackBar来输入bin的数目
namedWindow(WINDOW_NAME,WINDOW_AUTOSIZE);
createTrackbar("色调组距",WINDOW_NAME,&g_bins,180,on_BinChange);
on_BinChange(0,0);//初始化
//【4】显示效果图
imshow(WINDOW_NAME, g_srcImage);
waitKey(0);
return 0;
}
//----------------------【on_BinChange()函数】---------------------
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(i)*h/255.0)),Scalar(100,123,255),-1);
}
//显示直方图窗口
imshow("直方图",histImg);
}
模板匹配:是一项在一幅图像中寻找与另外一副模板图像最匹配部分的技术。
不是基于直方图,而是通过在输入图像上滑动图像块,对实际的图像块和输入图像进行匹配的一种匹配方法。
实现模板匹配:matchTemplate()函数-------用于匹配出和模板重叠的图像区域。
void matchTemplate(
InputArray image,//待搜搜图像
InputArray temp1,//搜索模板
OuatputArray result,//比较结果的映射图像
int method//指定的匹配方法
);
匹配方法:
平方差匹配法------TM_SQDIFF
归一化平方差匹配法------TM_SQDIFF_NORMED
相关匹配法------TM_CCORR
归一化相关匹配法------TM_CCORR_NORMED
系数匹配法------TM_CCOFF
化相关系数匹配法------TM_CCOFF_NORMED
模板匹配综合示例:
//---------------------------------【模板匹配】------------------------------
// 描述:模板匹配
//---------------------------------------------------------------------------
#include
#include
#include
using namespace cv;
//---------------------------------【宏定义部分】------------------------------
// 描述:定义一些辅助宏
//---------------------------------------------------------------------------
#define WINDOW_NAME1 "原始图" //为窗口标题定义的宏
#define WINDOW_NAME2 "效果图" //为窗口标题定义的宏
//---------------------------------【全局变量声明部分】------------------------------
// 描述:全局变量的声明
//---------------------------------------------------------------------------
Mat g_srcImage,g_templateImage,g_resultImage;
int g_nMatchMethod;
int g_nMaxTrackbarNum = 5;
//----------------------【全局函数声明部分】---------------------
// 描述:全局函数声明部分
//---------------------------------------------------------
void on_Matching(int, void*);
//----------------------【main()函数】---------------------
// 描述:程序入口
//---------------------------------------------------------
int main() {
//【1】载入原图,转化为HSV颜色模型
g_srcImage = imread("juan.jpg");
g_templateImage = imread("temp.jpg");
//【2】创建窗口
namedWindow(WINDOW_NAME1,CV_WINDOW_AUTOSIZE);
namedWindow(WINDOW_NAME2,CV_WINDOW_AUTOSIZE);
//【3】创建TrackBar
createTrackbar("方法", WINDOW_NAME1, &g_nMatchMethod, g_nMaxTrackbarNum, on_Matching);
on_Matching(0, 0);//初始化
waitKey(0);
return 0;
}
//----------------------【on_Matching()函数】---------------------
void on_Matching(int, void*) {
//给局部变量初始化
Mat srcImage;
g_srcImage.copyTo(srcImage);
//初始化用于结果输出的矩阵
int resultImage_cols = g_srcImage.cols - g_templateImage.cols + 1;
int resultImage_rows = g_srcImage.rows - g_templateImage.rows + 1;
g_resultImage.create(resultImage_cols,resultImage_rows,CV_32FC1);
//进行匹配和标准化
matchTemplate(g_srcImage,g_templateImage,g_resultImage,g_nMatchMethod);
normalize(g_resultImage,g_resultImage,0,1,NORM_MINMAX,-1,Mat());
//通过函数minMaxLocal定位最匹配的位置
double minValue;
double maxValue;
Point minLocation;
Point maxLocation;
Point matchLocation;
minMaxLoc(g_resultImage,&minValue,&maxValue,&minLocation,&maxLocation,Mat());
//对于方法SQDIFF和SQDIFF_NORMED,越小的数值有着更高的匹配结果。其余的方法,数值越大匹配效果越好。
if (g_nMatchMethod==TM_SQDIFF||g_nMatchMethod==TM_SQDIFF_NORMED)
matchLocation = minLocation;
else
matchLocation = maxLocation;
//绘制出矩形,并显示最终结果
rectangle(srcImage,matchLocation,Point(matchLocation.x+
g_templateImage.cols,matchLocation.y+g_templateImage.rows),
Scalar(0,0,255),2,8,0);
rectangle(g_resultImage, matchLocation, Point(matchLocation.x +
g_templateImage.cols, matchLocation.y + g_templateImage.rows),
Scalar(0, 0, 255), 2, 8, 0);
//显示直方图窗口
imshow(WINDOW_NAME1,srcImage);
imshow(WINDOW_NAME2, g_resultImage);
}