Advertisement

【图像处理 直方图 OpenCV实现】

阅读量:

直方图与直方图匹配

最后一个代码比较实用,对于小角度的模板匹配,还是可以用此函数的。

文章目录

  • 前言

  • 一、直方图

  • 二、使用步骤

    • 1.引入库
    • 2.绘制直方图
  • 总结


前言

直方图统计的原理、直方图代表的图像特征、直方图的应用。

直方图作为图像处理的基础,是分析图像的基础工具。不可不了解

直方图表示用于统计像素数据的一种技术手段。它能够展示数据分布情况,并能表现图像中各个亮度等级对应的像素数量。

直方图的作用如下:

  1. 统计像素数据以掌握图像像素分布情况,并进一步分析其明暗对比程度。
  2. 利用直方图完成图像二值化处理。

直方图的操作流程包括以下几个关键步骤:首先对图像进行归一化处理以提高分析效果;其次通过统计完成后的直方图来观察图像的灰度分布特征;如果发现像素值在特定区间内分布不均,则需采用归一化方法进行重新计算以更好地反映灰度分布情况;在此过程中提供了五种不同的归一化方法供选择:1)无穷范数处理 2)L1范数(绝对值之和) 3)L2范数及模长归一化 4)L2范数平方处理 5)偏移归一化操作

2:直方图比较
3:直方图均衡
4:直方图匹配
5:直方图反向投影
6:图像的模板匹配

在Opencv中,提供了图像直方图的统计函数calcHist()


一、直方图

二、使用步骤

1.引入库

绘制直方图(示例):

复制代码
    #include<opencv2\opencv.hpp>
    #include<iostream>
    
    using namespace std;
    using namespace cv;

2.绘制直方图

绘制直方图(示例):

复制代码
    int main1() 
    {
    	Mat img;
    	img = imread("apple.jpg");
    
    	if (img.empty())
    	{
    		cout << "图像数据有问题" << endl;
    		return -1;
    	}
    
    	Mat gray;
    	cvtColor(img,gray,COLOR_BGR2GRAY);
    
    //以下为提取直方图的必要变量
    	Mat hist;
    	const int channels[1] = { 0 };//通道索引
    	float inRanges[2] = { 0,255 };//每个通道灰度值的取值范围
    	const float*ranges[1] = { inRanges };//像素的灰度值范围
    	const int bins[1] = { 256 };//每个直方图数组的尺寸
    	
    	calcHist(&gray,1,channels,Mat(),hist,1,bins,ranges);  //统计函数
    	//准备绘制直方图
    	int hist_w = 512;//对应直方图的列,因绘制的宽度是2,即需要256*2
    	int hist_h = 400;//对应行,若不进行归一化,则需要大于255
    	int width = 2;//绘制的间隙
    
    	Mat histImage = Mat::zeros(hist_h,hist_w,CV_8UC3);
    	for (int i = 1; i <= hist.rows; i++)
    	{
    	//简介:从图像的左下角开始绘制,即第一个点的坐标是0,hist_h-1。后面每个增width。  取整然后除以15是为了缩小范围,类似归一。
    		rectangle(histImage,Point(width*(i-1),hist_h-1),Point(width*i-1,hist_h-cvRound(hist.at<float>(i-1)/15)),Scalar(255,255,255),-1);
    		cout << hist.at<float>(i - 1) << endl;
    	}
    	namedWindow("直方图",WINDOW_NORMAL);
    	imshow("直方图",histImage);
    
    	waitKey(0);
    	return 0;
    }
复制代码
    //归一化
    int main2() 
    {
    	system("color F0");
    
    	vector<double> positiveData = { 2.0,8.0,10.0 };//为了只管观察归一化,定义了一个数组
    	vector<double> normalized_l1, normalized_L2, normalized_Inf, normalized_L2SQR;
    
    	
    
    	//测试不同的归一方法
    	normalize(positiveData,normalized_l1,1.0,0.0,NORM_L1);
    	cout << "normalized_L1" << normalized_l1[0] << ":" << normalized_l1[1] << ":" << normalized_l1[2] << ";;;;"<<endl;
    	normalize(positiveData, normalized_L2, 1.0, 0.0, NORM_L2);
    	cout << "normalized_L2" << normalized_L2[0] << "+" << normalized_L2[1] << "+" << normalized_L2[2] << endl;
    	normalize(positiveData,normalized_Inf,1.0,0.0,NORM_INF);
    	cout << "normalized_inf" << normalized_Inf[0] << "+" << normalized_Inf[1] << "+"  << normalized_Inf[2] << endl;
    	normalize(positiveData, normalized_L2SQR, 1.0, 0.0, NORM_MINMAX);
    	cout << "MINMAX" << normalized_L2SQR[0] << normalized_L2SQR[1] << normalized_L2SQR[2] << endl;
    
    	//将图像直方图归一化
    	//Mat img = imread("apple.jpg");
    
    	Mat img(512, 512, CV_32FC3, Scalar(100, 100, 100));
    	if (img.empty())
    	{
    		cout << "图像数据有问题" << endl;
    		return -1;
    	}
    	imshow("apple",img);
    
    	Mat gray, hist;
    	cvtColor(img, gray, COLOR_BGR2GRAY);
    	const int channels[1] = { 0 };
    	float inRanges[2] = {0,255};
    	const float*ranges[1] = { inRanges };
    	const int bins[1] = { 256 };
    
    	calcHist(&gray,1,channels,Mat(),hist,1,bins,ranges);
    
    	int hist_w = 512;
    	int hist_h = 400;
    	int width = 2;
    
    
    	Mat hisImage_L1 = Mat::zeros(hist_h,hist_w,CV_8UC3);
    	Mat hisImage_Inf = Mat::zeros(hist_h, hist_w, CV_8UC3);
    
    	Mat hist_L1, hist_Inf;
    
    	normalize(hist,hist_L1,1,0,NORM_L1,-1,Mat());
    	//hist_L1是一个行为256,列为1的矩阵。所以遍历了rows。每个矩阵的没一个点,代表了这个像素归一化的值。
    	for (int i = 1; i < hist_L1.rows; i++)
    	{
    		rectangle(hisImage_L1,Point(width*(i-1),hist_h-1),Point(width*i-1,hist_h-cvRound(30*hist_h*hist_L1.at<float>(i-1))-1),Scalar(255,255,255),-1);
    
    	}
    
    	normalize(hist,hist_Inf,1,0,NORM_INF,-1,Mat());
    
    	for (int i = 1; i < hist_Inf.rows; i++)
    	{
    		rectangle(hisImage_Inf,Point(width*(i-1),hist_h-1),Point(width*i-1,hist_h-cvRound(hist_h*hist_Inf.at<float>(i-1))-1),Scalar(255,255,255),-1);
    		cout << hist_Inf.at<float>(i - 1) << endl;
    	}
    	
    	namedWindow("L1归一化",WINDOW_NORMAL);
    	imshow("L1归一化",hisImage_L1);
    
    	namedWindow("INF归一化",WINDOW_NORMAL);
    	imshow("INF归一化",hisImage_Inf);
    
    	waitKey(0);
    	return 0;
    
    
    }
复制代码
    //直方图的反向投影
    void drawHist3(Mat &hist,int type,string name) 
    {
    	int hist_w = 512;
    	int hist_h = 400;
    	int width = 2;
    	Mat histImage = Mat::zeros(hist_h,hist_w,CV_8UC3);
    	normalize(hist,hist,255,0,type,-1,Mat());
    	namedWindow(name,WINDOW_NORMAL);
    	imshow(name,hist);
    }
    
    int main66() 
    {
    	Mat img0 = imread("apple.jpg");
    	Mat img1 = imread("sub.jpg");
    
    	Mat img0_HSV, img1_HSV, hist0, hist1;
    	if (img0.empty()||img1.empty())
    	{
    		cout << "图像数据有问题" << endl;
    		waitKey(3000);
    		return - 1;
    	}
    
    	cvtColor(img1,img1_HSV,COLOR_BGR2HSV);
    	cvtColor(img0, img0_HSV, COLOR_BGR2HSV);
    	int h_bins = 32; int s_bins = 32;
    	int histSize[] = { h_bins,s_bins };
    
    	//H通道为0-179
    	float h_ranges[] = { 0,180 };
    	//S通道
    	float s_ranges[] = { 0,256 };
    	//每个通道的范围
    	const float*ranges[] = { h_ranges,s_ranges };
    	//统计通道的索引
    	int channels[] = { 0,1 };
    	//绘制H-S 二维直方图
    	//calcHist(&img0_HSV,1,channels,Mat(),hist0,2,histSize,ranges,true,false);
    	calcHist(&img1_HSV, 1, channels, Mat(), hist0, 2, histSize, ranges, true, false);
    
    	drawHist3(img0_HSV,NORM_INF,"hist00");
    	//drawHist3(img1_HSV,NORM_INF,"hist1");
    
    	double dur;
    	clock_t start, end;
    	start = clock();
    
    
    	imwrite("111.png",img0);
    	end = clock();
    	dur = (double)(end - start);
    
    	printf("Use Time:%f\n", (dur / CLOCKS_PER_SEC));
    
    
    	Mat backproj;
    
    
    
    	calcBackProject(&img0_HSV,1,channels,hist0,backproj,ranges,1.0);
    
    	imshow("反向投影的结果",backproj);
    	waitKey(0);
    	return 0;
    
    
    
    }
复制代码
    //比较直方图
    void drawingHist(Mat &hist,int type ,string name) 
    {
    	int width = 2;
    	int hist_h = 400;
    	int hist_w = 512;
    	Mat histImage = Mat::zeros(hist_h,hist_w,CV_8UC3);
    	normalize(hist,hist,1,0,type,-1,Mat());
    
    	for (int i = 1; i < hist.rows; i++)
    	{
    		rectangle(histImage,Point(width*(i-1),hist_h-1),Point(width*(i-1),hist_h-cvRound(hist_h*hist.at<float>(i-1))-1),Scalar(255,255,255),-1);
    	}
    	imshow(name, histImage);
    
    }
    
    
    int main55() 
    {
    
    	Mat img = imread("apple.jpg");
    	if (img.empty())
    	{
    		cout << "图像数据有问题" << endl;
    		return -1;
    	}
    	Mat gray, gray2, gray3, hist, hist2, hist3;
    	cvtColor(img, gray,COLOR_BGR2GRAY);
    	resize(gray,gray2,Size(),0.5,0.5);
    	gray3 = imread("lena.png",IMREAD_GRAYSCALE);
    
    	const int channels[1] = { 0 };
    	float inRanges[2] = { 0,255 };
    	const float*ranges[1] = { inRanges };
    	const int bins[1] = { 256 };
    	calcHist(&gray, 1, channels, Mat(), hist, 1, bins, ranges);
    	calcHist(&gray2,1, channels, Mat(), hist2,1,bins,ranges);
    	calcHist(&gray3,1, channels, Mat(), hist3,1,bins,ranges);
    
    	drawingHist(hist,NORM_INF,"hist");
    	drawingHist(hist2,NORM_INF,"hist2");
    	drawingHist(hist3,NORM_INF,"hist3");
    
    	//将原来的直方图和现在的直方图进行比较
    	double histNumer = compareHist(hist,hist2,HISTCMP_CORREL);
    	cout << histNumer << endl;
    
    	double histN2 = compareHist(hist,hist3,HISTCMP_CORREL);
    	cout << histN2 << endl;
    
    	waitKey(0);
    	return 0;
    }
复制代码
    //直方图均衡
    void  drawHist(Mat &hist,int type,string name)  //归一化并绘制直方图函数 
    {
    	int hist_w = 600;
    	int hist_h = 400;
    	int width = 2;
    	Mat histImage = Mat::zeros(hist_h,hist_w,CV_8UC3);
    	normalize(hist,hist,1,0,type,-1,Mat());
    
    	for (int i = 1; i < hist.rows; i++)
    	{
    		rectangle(histImage,Point(width*(i-1),hist_h-1),Point(width*i-1,hist_h-cvRound(hist_h*hist.at<float>(i-1))-1),Scalar(255,255,255),-1);
    	}
    	imshow(name,histImage);
    }
    
    int main4() 
    {
    	Mat img = imread("gearwHeel.jpg");
    	if (img.empty())
    	{
    		cout << "数据有问题" << endl;
    		return -1;
    	}
    	Mat gray, hist, hist2;
    	cvtColor(img,gray,COLOR_BGR2GRAY);
    	Mat equalImg;
    	equalizeHist(gray,equalImg);//将直方图均值化
    
    	const int channels[1] = { 0 };
    	float inRanges[2] = { 0,255 };
    	const float*ranges[1] = {inRanges};
    	const int bins[1] = { 256 };
    
    
    	calcHist(&gray,1,channels,Mat(),hist,1,bins,ranges);
    	calcHist(&equalImg,1,channels,Mat(),hist2,1,bins,ranges);
    
    	drawHist(hist,NORM_INF,"hist");
    	drawHist(hist2,NORM_INF,"hist2");
    
    	namedWindow("原图",WINDOW_NORMAL);
    	imshow("原图",gray);
    	imshow("均值化图",equalImg);
    
    	waitKey(0);
    	return 0;
    
    }
复制代码
    //图像的模板匹配
    int main() 
    {
    	Mat img = imread("4.Bmp");
    	Mat temp = imread("6.Bmp");//待匹配的图,可以是从原图截取出来的一张图
    
    	if (img.empty() || temp.empty())
    	{
    		cout << "图像数据有问题" << endl;
    
    		waitKey(3000);
    		return -1;
    
    	}
    
    	Mat result;
    	matchTemplate(img,temp,result,TM_CCOEFF_NORMED);
    
    	double maxVal, minVal;
    	Point minLoc, maxLoc;
    	//寻找匹配结果最大的值
    	minMaxLoc(result,&minVal,&maxVal,&minLoc,&maxLoc);
    	//绘制最佳的矩形
    	rectangle(img,cv::Rect(maxLoc.x,maxLoc.y,temp.cols,temp.rows),Scalar(0,0,0),8);
    	namedWindow("原图",WINDOW_NORMAL);
    	imshow("原图",img);
    	namedWindow("匹配图",WINDOW_NORMAL);
    	imshow("匹配图",temp);
    	namedWindow("result",WINDOW_AUTOSIZE);
    	imshow("result",result);
    
    	waitKey(0);
    	return 0;
    }

该处使用的url网络请求的数据。


总结

代码参考 《OpenCV 4快速入门》 冯振 郭延宁 吕跃勇

本门意在总结在本书学到的知识、复习。

亲测对于简单的,角度差异小的模板匹配,灰度匹配的效果还是可以的。

后期会抽时间进一步讲解此文的代码。以上代码欢迎交流

全部评论 (0)

还没有任何评论哟~