OpenCV_用直方图统计像素

*计算图像直方图

直方图是一个简单的表格,表示一个图像中具有某个值的像素的数量。因此灰度图像有256个项目,也叫箱子。0号箱子提供值为0的像素的数量,1号箱子提供值为1的像素的数量,等等。

效果:



代码:

#include <iostream>  
#include "opencv.hpp"  
#include "opencv2/core/core.hpp"  
#include "opencv2/highgui/highgui.hpp"       

//计算图像直方图
class Histogram1D
{
private:
	int histSize[1];
	float hranges[2];
	const float* ranges[1];
	int channels[1];
public:
	Histogram1D()
	{
		histSize[0] = 256;
		hranges[0] = 0.0;
		hranges[1] = 256.0;
		ranges[0] = hranges;
		channels[0] = 0;
	}
	cv::Mat getHistogram(const cv::Mat& image)
	{
		cv::Mat hist;
		cv::calcHist(&image,
			1,//仅为一个图像的直方图
			channels,//使用的通道
			cv::Mat(),//不使用掩码
			hist,//作为结果的直方图
			1,//这时一维的直方图
			histSize,//箱子数量
			ranges//像素值的范围
		);
		return hist;
	}
};

int main()
{
	cv::Mat image = cv::imread("puppy.jpg");
	cv::imshow("图片1", image);
	Histogram1D ch;
	cv::Mat histo = ch.getHistogram(image);
	for (int i = 0; i < 256; i++)
	{
		std::cout << "Value " << i << " = " << histo.at<float>(i) << std::endl;
	}
	cv::waitKey();
}


用直方图很难得到任何直观的意义,比较实用的做法是以函数的方式显示直方图,例如使用柱状图

效果:

代码:

#include <iostream>  
#include "opencv.hpp"  
#include "opencv2/core/core.hpp"  
#include "opencv2/highgui/highgui.hpp"       

//计算图像直方图
class Histogram1D
{
private:
	int histSize[1];
	float hranges[2];
	const float* ranges[1];
	int channels[1];
public:
	Histogram1D()
	{
		histSize[0] = 256;
		hranges[0] = 0.0;
		hranges[1] = 256.0;
		ranges[0] = hranges;
		channels[0] = 0;
	}
	cv::Mat getHistogram(const cv::Mat& image)
	{
		cv::Mat hist;
		cv::calcHist(&image,
			1,//仅为一个图像的直方图
			channels,//使用的通道
			cv::Mat(),//不使用掩码
			hist,//作为结果的直方图
			1,//这时一维的直方图
			histSize,//箱子数量
			ranges//像素值的范围
		);
		return hist;
	}
	cv::Mat getHistogramImage(const cv::Mat& image, int zoom = 1)
	{
		cv::Mat hist = getHistogram(image);
		return getImageofHistogram(hist, zoom);

	}
	static cv::Mat getImageofHistogram(const cv::Mat &hist, int zoom)
	{
		double maxVal = 0;
		double minVal = 0;
		cv::minMaxLoc(hist, &minVal, &maxVal, 0, 0);

		int histSize = hist.rows;
		cv::Mat histImg(histSize*zoom, histSize*zoom, CV_8U, cv::Scalar(255));
		//设置最高点为90%箱子的个数
		int hpt = static_cast<int>(0.9*histSize);
		for (int h = 0; h < histSize; h++)
		{
			float binVal = hist.at<float>(h);
			if (binVal > 0)
			{
				int intensity = static_cast<int>(binVal*hpt / maxVal);
				cv::line(histImg, cv::Point(h*zoom, histSize*zoom),
					cv::Point(h*zoom, (histSize - intensity)*zoom),
					cv::Scalar(0), zoom);
			}
		}
		return histImg;
	}
};

int main()
{
	cv::Mat image = cv::imread("puppy.jpg");
	cv::imshow("图片1", image);
	Histogram1D ch;
	cv::Mat histo = ch.getHistogram(image);
	for (int i = 0; i < 256; i++)
	{
		std::cout << "Value " << i << " = " << histo.at<float>(i) << std::endl;
	}
	cv::Mat hist = ch.getHistogramImage(image);
	cv::imshow("直方图", hist);
	cv::waitKey();
}

*利用查找表修改图像外观

查找表是个一对一的函数,定义了如何把像素值转换成新的值。

OpenCV中的cv::LUT函数在图像上应用查找表,可生成一个新的图像。

负片效果:


代码:

static cv::Mat applyLookUp(const cv::Mat& image, const cv::Mat& lookup)
	{
		cv::Mat result;
		cv::LUT(image, lookup, result);
		return result;
	}
//创建一个图像反转的查找表
	int dim(256);
	cv::Mat lut(1, &dim, CV_8U);
	for (int i = 0; i < 256; i++)
	{
		lut.at<uchar>(i) = 255 - i;
	}
	Histogram1D ch;
	cv::Mat result = ch.applyLookUp(image, lut);
	cv::imshow("反转后图形", result);

*伸展直方图以提高图像对比度

物品们可以通过伸展直方图来生成一个对比度更高的图像。我们必须在其强度值中找到一个最小值(imin)和最大值(imax),使得所要求的最小像素的数量高于阈值指定的百分比。然后重新映射强度值,使imin的值变成强度值0,imax值变成强度值255.两者之间的强度值i 可以简单地做线性映射,如下:

255.0*(i - imin ) /(imax - imin)

效果:



*直方图均衡化

通常直方图会极大地改进图像的外观,但是改进的结果会因图像可视内容不同而出现差异。

效果:




错误记录:

直方图均衡化函数cv::equalizedHist()似乎必须将GRB图像转化为GRAY图像

使用原图均衡化时报错,用函数cv::cvtColor函数参数CV_RGB2GRAY转换后可以使用equalizedHist函数

代码:

cv::Mat image = cv::imread("puppy.jpg");
	cv::Mat result;
	cv::cvtColor(image, image, CV_RGB2GRAY);
	equalizeHist(image, result);
	cv::imshow("原图", image);
	cv::imshow("均衡化图像", result);


*反向投影直方图检测特定图像内容

如果图像的某个区域含有特定的纹理或物体,这个区域的直方图就可以看作一个函数,该函数返回某个像素属于这个特殊纹理或物体的概率。

过程:

1、获得兴趣区

2、提取兴趣区直方图

3、归一化直方图得到一个函数

4、反向投影(从归一化直方图中读取概率值并把输入图像中的每个像素替换成与之对应的概率值)

效果:



代码:

int main()
{
	cv::Mat image = cv::imread("cloud.jpg");
	cv::cvtColor(image, image, CV_RGB2GRAY);
	cv::imshow("原图", image);
	cv::Mat imageROI;
	imageROI = image(cv::Rect(0, 0, 80, 120));//云彩区域
	cv::imshow("兴趣区", imageROI);
	//提取兴趣区直方图
	Histogram1D h;
	cv::Mat hist = h.getHistogram(imageROI);
	//通过归一化直方图得到一个函数
	cv::normalize(hist, hist, 1.0);
	cv::Mat result;
	int channels[1] = {0};
	const float* ranges[1];
	float hranges[2] = { 0.0, 255.0 };
	ranges[0]  = hranges;
	cv::calcBackProject(&image,1,channels, hist,result, ranges, 255.0);
	cv::imshow("概率分布图", result);
	cvWaitKey();
}

*反向映射颜色直方图

与灰度图像的反向映射直方图类似,此处是彩色图像的反向映射直方图,因此该处新封装了一个类,ColorHistogram,该类中的成员函数可获得彩色图像的直方图。采用兴趣区的均衡化直方图去寻找原图中与兴趣区对应的图像区域,此处寻找的是天空区域。

效果:



可以看出,用RGB色彩空间识别图像中的物体并不是最好的方法。

代码:

ColorHistogram类:

class ColorHistogram
{
private:
	int histSize[3];
	float hranges[2];
	const float* ranges[3];
	int channels[3];
public:
	ColorHistogram()
	{
		histSize[0] = histSize[1] = histSize[2] = 256;
		hranges[0] = 0.0;
		hranges[1] = 256.0;
		ranges[0] = ranges[1] = ranges[2] = hranges;
		channels[0] = 0;
		channels[1] = 1;
		channels[2] = 2;
	}
	cv::Mat getHistogram(const cv::Mat& image)
	{
		cv::Mat hist;
		hranges[0] = 0.0;
		hranges[1] = 256.0;
		channels[0] = 0;
		channels[1] = 1;
		channels[2] = 2;

		cv::calcHist(&image, 1, channels, cv::Mat(), hist, 3, histSize, ranges);
		return hist;
	}
	void setSize(int size)
	{
		histSize[0] = histSize[1] = histSize[2] = size;
	}
};


main函数:

int main()
{
	ColorHistogram hc;
	cv::Mat image = cv::imread("cloud.jpg");
	cv::imshow("原图", image);
	cv::Mat imageROI = image(cv::Rect(350, 50, 50, 250));
	cv::imshow("兴趣区域", imageROI);
	hc.setSize(8);
	cv::Mat shist = hc.getHistogram(imageROI);
	ContentFinder finder;
	finder.setHistogram(shist);
	finder.setThreshold(0.05f);
	cv::Mat result = finder.find(image);
	cv::imshow("检测图像", result);
	cvWaitKey();
}


*均值平移算法查找目标

直方图反向投影的结果是一个概率分布图,表示一个指定图像片段出现在特定位置的概率。假设我们已经知道了图像中每个物体的大概位置,就可以利用概率分布图找到物体的准确位置。最可能出现的位置就是窗口中概率最大的位置。因此我们可以从一个初始位置开始,在周围反复移动就可能找到物体所在的准确位置。这个实现方法称为均值平移算法


效果:



在使用颜色的色调分量时,要把它的饱和度考虑在内(饱和度是矩向量的第二个入口),如果颜色的饱和度很低,它的色调信息就会变得不稳定而且不可靠,这是因为低饱和度颜色的RGB分量几乎是相等的,这导致很难确定它所表示的准确颜色。

代码:

ColorHistogram中getHueHistogram方法:

cv::Mat getHueHistogram(const cv::Mat& image, int minSaturation = 0)
	{
		cv::Mat hist;
		cv::Mat hsv;
		cv::cvtColor(image, hsv, CV_BGR2HSV);
		cv::Mat mask;
		if (minSaturation > 0)
		{
			//把3个通道分割进3个图像
			std::vector<cv::Mat> v;
			cv::split(hsv, v);
			//屏蔽低饱和度的像素
			cv::threshold(v[1], mask, minSaturation, 255, cv::THRESH_BINARY);
		}
		//准备一维色调直方图的参数
		hranges[0] = 0.0;
		hranges[1] = 180.0;
		channels[0] = 0;
		cv::calcHist(&hsv, 1, channels, mask, hist, 1, histSize, ranges);
		return hist;
	}

main函数:

int main()
{
	cv::Mat image = cv::imread("baboon.jpg");
	cv::imshow("原图", image);
	cv::Mat imageROI = image(cv::Rect(121,131,141,143));
	cv::imshow("兴趣区", imageROI);
	int minSat = 65;
	ColorHistogram hc;
	cv::Mat colorhist = hc.getHueHistogram(imageROI, minSat);
	ContentFinder finder;
	finder.setHistogram(colorhist);
	image = cv::imread("baboon2.jpg");
	int ch[1] = { 0 };
	finder.setThreshold(-1.0f);
	cv::Mat result = finder.find(image, 0.0f, 180.0f, ch);
	cv::Rect rect(289, 484, 170, 204);
	cv::TermCriteria criteria(cv::TermCriteria::MAX_ITER,10,0.01);
	cv::meanShift(result, rect, criteria);
	cv:rectangle(image, rect.tl(), rect.br(), CV_RGB(0, 255, 0));
	cv::imshow("检测图像", image);
	cvWaitKey();
}

*比较直方图搜索相似图像

(比较直方图测量两图的相似程度)

效果:



ImageComparator类使用了交叉点法(带有CV_COMP_INTERSECT标志),该方法只是逐个比较每个直方图中的数值,并保存最小值,然后把这些最小值累加,最为相似度测量值。因此,两个没有相同颜色的直方图得到的交叉值为0,两个完全相同的直方图得到的值就等于像素总数。

其他可用的算法有:

卡尔测量法(CV_COMP_CHISQR标志),累加各箱子的归一化平方差;

关联性算法(CV_COMP_CORREL标志),基于信号处理中的归一化交叉关联操作符测量两个信号的相似度;

Bhattacharyya算法(CV_COMP_BHATTACHARYYA标志),用在统计学中,评估两个概率分布的相似度。

代码:

用于直方图比较的ImageComparator类:

class ImageComparator
{
private:
	cv::Mat refH;//基准直方图
	cv::Mat inputH;//输入图像直方图
	ColorHistogram hist;//生成直方图
	int nBins;//每个通道使用的箱子数量
public:
	ImageComparator() :nBins(8) {}
	void setNumberOfBins(int bins)
	{
		nBins = bins;
	}
	void setReferenceImage(const cv::Mat& image)
	{
		hist.setSize(nBins);
		refH = hist.getHistogram(image);
	}
	double compare(const cv::Mat& image)
	{
		inputH = hist.getHistogram(image);
		return cv::compareHist(refH, inputH, CV_COMP_INTERSECT);
	}
};

main函数:

int main()
{
	cv::Mat referenceImage = cv::imread("beach.jpg");
	cv::imshow("基准图像", referenceImage);
	ImageComparator c;
	c.setReferenceImage(referenceImage);
	cv::Mat image2 = cv::imread("beach2.jpg");
	cv::imshow("image2", image2);
	std::cout << "图像2的匹配度为" << c.compare(image2) << std::endl;
	cv::Mat image3 = cv::imread("beach3.jpg");
	cv::imshow("image3", image3);
	std::cout << "图像3的匹配度为" << c.compare(image3) << std::endl;
	cv::Mat image4 = cv::imread("beach4.jpg");
	cv::imshow("image4", image4);
	std::cout << "图像4的匹配度为" << c.compare(image4) << std::endl;
	cvWaitKey();
}


  • 19
    点赞
  • 51
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值