Opencv学习笔记(8)——直方图统计

Opencv学习笔记(8)——直方图统计

本章我将为大家介绍直方图的相关知识,相关内容分别有:直方图均衡化直方图计算直方图比较直方图反向投射

一.直方图均衡化

1.相关理论

1.什么是直方图(Histogram)?
Opencv学习笔记(8)——直方图统计_第1张图片
图像直方图,是指对整个图像像在灰度范围内的像素值(0~255)统计出现频率次数,据此生成的直方图,称为图像直方图-直方图。直方图反映了图像灰度的分布情况。是图像的统计学特征。
Opencv学习笔记(8)——直方图统计_第2张图片
2.直方图均衡化
是一种提高图像对比度的方法,拉伸图像灰度值范围。
Opencv学习笔记(8)——直方图统计_第3张图片
如何实现,通过上一课中的remap我们知道可以将图像灰度分布从一个分布映射到另外一个分布,然后在得到映射后的像素值即可。
Opencv学习笔记(8)——直方图统计_第4张图片

2.相关API应用

cv::equalizeHist

equalizeHist(
InputArray src,//输入图像,必须是8-bit的单通道图像
OutputArray dst// 输出结果
)

3.程序示例

#include
#include
using namespace cv;
using namespace std;
Mat src, dst, map_x, map_y;
const char* OUTPUT_TITLE = "equalizeHist demo";
int main(int argc, char** argv) {
	src = imread("E:/photoss/dog1.jpeg");
	if (!src.data) {
		printf("Could not load image...\n");
		return 1;
	}
	char input_win[] = "input image";
	namedWindow(input_win, CV_WINDOW_AUTOSIZE);
	namedWindow(OUTPUT_TITLE, CV_WINDOW_AUTOSIZE);
	cvtColor(src, src, CV_BGR2GRAY);
	imshow(input_win, src);
	equalizeHist(src, dst);//直方图均衡化
	imshow(OUTPUT_TITLE, dst);
	waitKey(0);
	return 0;
}

运行效果:
Opencv学习笔记(8)——直方图统计_第5张图片

二.直方图计算

1.相关原理

1.直方图概念
Opencv学习笔记(8)——直方图统计_第6张图片
Opencv学习笔记(8)——直方图统计_第7张图片
上述直方图概念是基于图像像素值,其实对图像梯度、每个像素的角度、等一切图像的属性值,我们都可以建立直方图。这个才是直方图的概念真正意义,不过是基于图像像素灰度直方图是最常见的。
直方图最常见的几个属性:

  • dims 表示维度,对灰度图像来说只有一个通道值dims=1
  • bins 表示在维度中子区域大小划分,bins=256,划分为256个级别
  • range 表示值得范围,灰度值范围为[0~255]之间

2.相关API说明

1.cv::split

split(// 把多通道图像分为多个单通道图像
const Mat &src, //输入图像
Mat* mvbegin)// 输出的通道图像数组

2.cv::calcHist

calcHist(
 const Mat* images,//输入图像指针
int images,// 图像数目
const int* channels,// 通道数
InputArray mask,// 输入mask,可选,不用则输入Mat()
OutputArray hist,//输出的直方图数据
int dims,// 维数
const int* histsize,// 直方图级数
const float* ranges,// 值域范围
bool uniform,// true by default,是否归一化
bool accumulate// false by defaut,是否累计各通道值
)

3. 程序示例

#include
#include
using namespace cv;
using namespace std;

int main(int argc, char** argv) {
	Mat src, dst, map_x, map_y;
	src = imread("E:/photoss/dog1.jpeg");
	if (!src.data) {
		printf("Could not load image...\n");
		return 1;
	}
	char input_win[] = "input image";
	const char* OUTPUT_TITLE = "histogram demo";
	namedWindow(input_win, CV_WINDOW_AUTOSIZE);
	namedWindow(OUTPUT_TITLE, CV_WINDOW_AUTOSIZE);
	imshow(input_win, src);

	//分通道显示
	vector<Mat> bgr_planes;
	split(src, bgr_planes);//对src图像进行通道分离至bgr_planes图像数组中
	//imshow("single channel demo",bgr_planes[0]);

	//计算直方图
	int histSize = 256;
	float range[] = { 0,256 };
	const float *histRanges = { range };
	Mat b_hist, g_hist, r_hist;
	calcHist(&bgr_planes[0], 1, 0, Mat(), b_hist, 1, &histSize, &histRanges, true, false);
	//输入图像为bgr_planes[0],输入图像个数为1,对图像的第一通道进行直方图统计,掩膜设置为Mat()即不设置掩膜,输出图像为b_hist,需要统计直方图个数为1维数为1,划分的区间数即bins为255,统计像素区间为0-255,进行归一化处理,不累加计算像素值得个数
	calcHist(&bgr_planes[1], 1, 0, Mat(), g_hist, 1, &histSize, &histRanges, true, false);
	calcHist(&bgr_planes[2], 1, 0, Mat(), r_hist, 1, &histSize, &histRanges, true, false);

	//归一化
	int hist_h = 400;
	int hist_w = 512;
	int bin_w = hist_w / histSize;//直方图每一区间宽度为总宽度除以bins总个数
	Mat histImage(hist_h, hist_w, CV_8UC3, Scalar(0, 0, 0));//定义图像高为hist_h,宽为hist_w
	normalize(b_hist, b_hist, 0, hist_h, NORM_MINMAX, -1, Mat());//归一化
	normalize(g_hist, g_hist, 0, hist_h, NORM_MINMAX, -1, Mat());
	normalize(r_hist, r_hist, 0, hist_h, NORM_MINMAX, -1, Mat());

	//做出各通道直方图,折线图
	for (int i = 1; i < histSize; i++) {
		line(histImage, Point((i - 1)*bin_w, hist_h - cvRound(b_hist.at<float>(i - 1))),
			Point((i)*bin_w, hist_h - cvRound(b_hist.at<float>(i))), Scalar(255, 0, 0), 2, LINE_AA);//b通道
		line(histImage, Point((i - 1)*bin_w, hist_h - cvRound(g_hist.at<float>(i - 1))),
			Point((i)*bin_w, hist_h - cvRound(g_hist.at<float>(i))), Scalar(0, 255, 0), 2, LINE_AA);//g通道
		line(histImage, Point((i - 1)*bin_w, hist_h - cvRound(r_hist.at<float>(i - 1))),
			Point((i)*bin_w, hist_h - cvRound(r_hist.at<float>(i))), Scalar(0, 0, 255), 2, LINE_AA);//r通道
	}
	imshow(OUTPUT_TITLE, histImage);
	waitKey(0);
	return 0;
}

运行效果:
Opencv学习笔记(8)——直方图统计_第8张图片

三.直方图比较

1.相关原理

1.直方图比较方法-概述
对输入的两张图像计算得到直方图H1与H2,归一化到相同的尺度空间
然后可以通过计算H1与H2的之间的距离得到两个直方图的相似程度进
而比较图像本身的相似程度。Opencv提供的比较方法有四种:

  • Correlation 相关性比较
  • Chi-Square 卡方比较
  • Intersection 十字交叉性(无法直观显示差距,不建议使用
  • Bhattacharyya distance 巴氏距离

2.相关性计算(CV_COMP_CORREL)
Opencv学习笔记(8)——直方图统计_第9张图片
3.卡方计算(CV_COMP_CHISQR)
Opencv学习笔记(8)——直方图统计_第10张图片
4.十字计算(CV_COMP_INTERSECT)
Opencv学习笔记(8)——直方图统计_第11张图片
5.巴氏距离计算(CV_COMP_BHATTACHARYYA )
Opencv学习笔记(8)——直方图统计_第12张图片

2.相关API

1.直方图比较相关步骤

  • 首先把图像从RGB色彩空间转换到HSV色彩空间cvtColor
  • 计算图像的直方图,然后归一化到[0~1]之间calcHist和normalize;
  • 使用上述四种比较方法之一进行比较compareHist

2.cv::compareHist

compareHist(
InputArray h1, // 直方图数据,下同
InputArray H2,
int method// 比较方法,上述四种方法之一
)

3.程序示例

#include 
#include 
#include 

using namespace std;
using namespace cv;

string convertToString(double d);
int main(int argc, char** argv) {
	Mat base, test1, test2;
	Mat hsvbase, hsvtest1, hsvtest2;
	base = imread("D:/photos/2.jpg");
	if (!base.data) {
		printf("could not load image...\n");
		return -1;
	}
	test1 = imread("D:/photos/333.png");
	test2 = imread("D:/photos/1112.jpg");
	//直方图比较步骤,需要将图像转变为HSV通道
	cvtColor(base, hsvbase, CV_BGR2HSV);
	cvtColor(test1, hsvtest1, CV_BGR2HSV);
	cvtColor(test2, hsvtest2, CV_BGR2HSV);

	int h_bins = 50; int s_bins = 60;//定义色调和饱和度区间数
	int histSize[] = { h_bins, s_bins };
	// hue varies from 0 to 179, saturation from 0 to 255     
	float h_ranges[] = { 0, 180 };//色调取值范围为0-180度
	float s_ranges[] = { 0, 256 };//饱和度取值范围为0-256
	const float* ranges[] = { h_ranges, s_ranges };//定义取值范围区间
	// Use the o-th and 1-st channels     
	int channels[] = { 0, 1 };//两通道
	MatND hist_base;//MatND是多维矩阵(>=3维)。Mat特指二维矩阵
	MatND hist_test1;
	MatND hist_test2;

	calcHist(&hsvbase, 1, channels, Mat(), hist_base, 2, histSize, ranges, true, false);//计算直方图
	normalize(hist_base, hist_base, 0, 1, NORM_MINMAX, -1, Mat());

	calcHist(&hsvtest1, 1, channels, Mat(), hist_test1, 2, histSize, ranges, true, false);
	normalize(hist_test1, hist_test1, 0, 1, NORM_MINMAX, -1, Mat());

	calcHist(&hsvtest2, 1, channels, Mat(), hist_test2, 2, histSize, ranges, true, false);
	normalize(hist_test2, hist_test2, 0, 1, NORM_MINMAX, -1, Mat());

	double basebase = compareHist(hist_base, hist_base, CV_COMP_INTERSECT);//进行直方图比较
	double basetest1 = compareHist(hist_base, hist_test1, CV_COMP_INTERSECT);
	double basetest2 = compareHist(hist_base, hist_test2, CV_COMP_INTERSECT);
	double tes1test2 = compareHist(hist_test1, hist_test2, CV_COMP_INTERSECT);
	printf("test1 compare with test2 correlation value :%f", tes1test2);

	Mat test12;
	test2.copyTo(test12);
	putText(base, convertToString(basebase), Point(50, 50), CV_FONT_HERSHEY_COMPLEX, 1, Scalar(0, 0, 255), 2, LINE_AA);//将比较结果以文本格式反应与图像上
	putText(test1, convertToString(basetest1), Point(50, 50), CV_FONT_HERSHEY_COMPLEX, 1, Scalar(0, 0, 255), 2, LINE_AA);
	putText(test2, convertToString(basetest2), Point(50, 50), CV_FONT_HERSHEY_COMPLEX, 1, Scalar(0, 0, 255), 2, LINE_AA);
	putText(test12, convertToString(tes1test2), Point(50, 50), CV_FONT_HERSHEY_COMPLEX, 1, Scalar(0, 0, 255), 2, LINE_AA);

	namedWindow("base", CV_WINDOW_AUTOSIZE);
	namedWindow("test1", CV_WINDOW_AUTOSIZE);
	namedWindow("test2", CV_WINDOW_AUTOSIZE);

	imshow("base", base);
	imshow("test1", test1);
	imshow("test2", test2);
	imshow("test12", test12);

	waitKey(0);
	return 0;
}

string convertToString(double d) {//数值转换为文本格式
	ostringstream os;
	if (os << d)
		return os.str();
	return "invalid conversion";
}

运行效果:

四.直方图反向投影

1.相关原理

1.反向投影
反向投影是一种记录给定图像中的像素点如何适应直方图模型像素分布的方式,简单来讲,反向投影就是首先计算某一特征的直方图模型,然后使用模型去寻找图像中存在的特征。反向投影在某一位置的值就是原图对应位置像素值在原图像中的总数目。
具体形象理解见博客:opencv学习(三十九)之反向投影calcBackProject()

  • 反向投影是反映直方图模型在目标图像中的分布情况。
  • 简单点说就是用直方图模型去目标图像中寻找是否有相似的对象。通常用HSV色彩空间的HS两个通道直方图模型。
    Opencv学习笔记(8)——直方图统计_第13张图片

2.实施步骤

  • 1.建立直方图模型
  • 2.计算待测图像直方图并映射到模型中
  • 3.从模型反向计算生成图像

2.相关API

1.处理步骤

  • 加载图片imread。
  • 将图像从RGB色彩空间转换到HSV色彩空间cvtColor。
  • 计算直方图和归一化calcHist与normalize。
    注:Mat与MatND其中Mat表示二维数组,MatND表示三维或者多维数据,此处均可以用Mat表示。
  • 计算反向投影图像 - calcBackProject。

2.cv::normalize

//该函数归一化输入数组使它的范数或者数值范围在一定的范围内。
   normalize(
   InputArray src,//输入数组
   OutputArray dst, // 输出数组,支持原地运算
   double alpha, // range normalization模式的最小值
   doublebeta, //range normalization模式的最大值,不用于norm normalization(范数归一化)模式。
   int norm_type, // 归一化的类型
   int dtype=-1, // dtype为负数时,输出数组的type与输入数组的type相同
   InputArray mask=noArray()// 操作掩膜,用于指示函数是否仅仅对指定的元素进行操作。
    )
    
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
    操作掩膜,用于指示函数是否仅仅对指定的元素进行操作。

3.cv::calcBackProjection()
calcBackProjection()函数共有三种形式,根据传入参数的不同选择不同的调用,为重载函数。

void cv::calcBackProject    (   const Mat *     images,
        int           nimages,
        const int*    channels,
        InputArray    hist,
        OutputArray   backProject,
        const float** ranges,
        double        scale = 1,
        bool          uniform = true 
    )
参数解释: 
const Mat* images:输入图像,图像深度必须位CV_8U,CV_16U或CV_32F中的一种,尺寸相同,每一幅图像都可以有任意的通道数 
int nimages:输入图像的数量 
const int* channels:用于计算反向投影的通道列表,通道数必须与直方图维度相匹配,第一个数组的通道是从0到image[0].channels()-1,第二个数组通道从图像image[0].channels()到image[0].channels()+image[1].channels()-1计数 
InputArray hist:输入的直方图,直方图的bin可以是密集(dense)或稀疏(sparse) 
OutputArray backProject:目标反向投影输出图像,是一个单通道图像,与原图像有相同的尺寸和深度 
const float ranges**:直方图中每个维度bin的取值范围 
double scale=1:可选输出反向投影的比例因子 
bool uniform=true:直方图是否均匀分布(uniform)的标识符,有默认值true   

3.程序示例

#include 
#include 
#include 

using namespace std;
using namespace cv;

Mat src; Mat hsv; Mat hue;
int bins = 12;
void Hist_And_Backprojection(int, void*);
int main(int argc, char** argv) {
	src = imread("D:/photos/2.jpg");
	if (src.empty()) {
		printf("could not load image...\n");
		return -1;
	}
	const char*  window_image = "input image";
	namedWindow(window_image, CV_WINDOW_NORMAL);
	namedWindow("BackProj", CV_WINDOW_NORMAL);
	namedWindow("Histogram", CV_WINDOW_NORMAL);

	cvtColor(src, hsv, CV_BGR2HSV);//将源图像转换为hsv图像通道
	hue.create(hsv.size(), hsv.depth());
	cout << "hsv的depth为" << hsv.depth();
	int nchannels[] = { 0, 0 };
	mixChannels(&hsv, 1, &hue, 1, nchannels, 1);//将hsv中第一个通道的矩阵复制给hue第一个通道

	createTrackbar("Histogram Bins:", window_image, &bins, 180, Hist_And_Backprojection);//动态调整bin值
	Hist_And_Backprojection(0, 0);

	imshow(window_image, src);
	waitKey(0);
	return 0;
}

void Hist_And_Backprojection(int, void*) {
	float range[] = { 0, 180 };
	const float *histRanges = { range };
	Mat h_hist;
	calcHist(&hue, 1, 0, Mat(), h_hist, 1, &bins, &histRanges, true, false);//&bin为每个维度bin的取值范围
	normalize(h_hist, h_hist, 0, 255, NORM_MINMAX, -1, Mat());

	Mat backPrjImage;
	calcBackProject(&hue, 1, 0, h_hist, backPrjImage, &histRanges, 1, true);
	imshow("BackProj", backPrjImage);

	int hist_h = 400;
	int hist_w = 400;
	Mat histImage(hist_w, hist_h, CV_8UC3, Scalar(0, 0, 0));
	int bin_w = (hist_w / bins);
	for (int i = 1; i < bins; i++) {
		rectangle(histImage,
			Point((i - 1)*bin_w, (hist_h - cvRound(h_hist.at<float>(i - 1) * (400 / 255)))),
			//Point(i*bin_w, (hist_h - cvRound(h_hist.at(i) * (400 / 255)))),
			Point(i*bin_w, hist_h),
			Scalar(0, 0, 255), -1);//绘制矩形来绘制直方分布图
	}
	imshow("Histogram", histImage);

	return;
}

运行效果:
Opencv学习笔记(8)——直方图统计_第14张图片
至此,Opencv中关于直方图介绍到此结束。

你可能感兴趣的:(视觉,opencv,计算机视觉)