我的OpenCV学习笔记(13):计算直方图,利用查找表拉伸直方图,直方图均衡...

一些头文件:

#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <iostream>

using namespace std;
using namespace cv;


首先建立一个类:

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] = 255.0;
		 ranges[0] = hranges;
		 channels[0] = 0;

	}


在OpenCV中,使用calcHist计算直方图

	Mat getHistogram(const Mat &image)
	{
		Mat hist;
		//计算直方图函数
		//参数为:源图像(序列)地址,输入图像的个数,通道数,掩码,输出结果,直方图维数,每一维的大小,每一维的取值范围
		calcHist(&image,1,channels,Mat(),hist,1,histSize,ranges);

		return hist;
	}


但是,返回的结果并不是我们希望的“直方图”,hist只是记录了每个bin下的像素个数。我们需要自己利用line函数画图:

	Mat getHistogramImage(const Mat &image)
	{
		//首先计算直方图
		Mat hist = getHistogram(image);

		//获取最大值和最小值
		double maxVal = 0;
		double minVal = 0;
		//minMaxLoc用来获得最大值和最小值,后面两个参数为最小值和最大值的位置,0代表不需要获取
		minMaxLoc(hist,&minVal,&maxVal,0,0);
		//展示直方图的画板:底色为白色
		Mat histImg(histSize[0],histSize[0],CV_8U,Scalar(255));

		//将最高点设为bin总数的90%
		int hpt = static_cast<int>(0.9*histSize[0]);
		//为每一个bin画一条线
		for(int h = 0; h < histSize[0];h++)
		{
			float binVal = hist.at<float>(h);
			int intensity = static_cast<int>(binVal*hpt/maxVal);
			//int intensity = static_cast<int>(binVal);
			line(histImg,Point(h,histSize[0]),Point(h,histSize[0]-intensity),Scalar::all(0));
			
		}
		return histImg;
	}


函数中对亮度进行了“压缩”,主要目的是让直方图能够更好的显示在画板上。

查找表,就是一个一对一或者多对一函数,指定了一个亮度经过查找表以后变成另外一个像素。在OpenCV中,使用LUT函数来实现

	Mat applyLookUp(const Mat& image,const Mat& lookup)
	{
		Mat result;
		LUT(image,lookup,result);
		return result;
	}


而变化的具体内容,依赖于你自己定义的查找表,比如,如果你想让查找表的结果为当前图像的翻转,可以在main函数中定义查找表为:

	//用查找表翻转图像灰度
	int dim(256);
	Mat lut(1,&dim,CV_8U);
	
	for(int i = 0; i < 256; i++)
	{
		lut.at<uchar>(i) = 255-i;
	}
	imshow("灰度翻转后的图像",h.applyLookUp(image,lut));


当你想让图像经过查找表以后直方图拉伸,变得效果好一点,可以这样:

将个数低于指定数目(默认为0)的bin舍去,剩下的最小值变为0,最大值变为255,中间的部分线性拉伸:

	Mat strech(const Mat &image,int minValue = 0)
	{
		//首先计算直方图
		Mat hist = getHistogram(image);
		//左边入口
		int imin = 0;
		for(;imin< histSize[0];imin++)
		{
			cout<<hist.at<float>(imin)<<endl;
			if(hist.at<float>(imin) > minValue)
				break;

		}
		//右边入口
		int imax = histSize[0]-1;
		for(;imax >= 0; imax--)
		{
			if(hist.at<float>(imax) > minValue)
				break;
		}

		//创建查找表
		int dim(256);
		Mat lookup(1,&dim,CV_8U);
		
		for(int i = 0; i < 256; i++)
		{
			if(i < imin)
			{
				lookup.at<uchar>(i) = 0;
			}
			else if(i > imax)
			{
				lookup.at<uchar>(i) = 255;
			}
			else
			{
				lookup.at<uchar>(i) = static_cast<uchar>(255.0*(i-imin)/(imax-imin)+0.5);
			}
		}
		Mat result;
		result = applyLookUp(image,lookup);
		return result;

	}

当然,这样的效果还不是最好的,最好的均衡应该使得直方图非常平缓,每个bin中的数目差不多,在OpenCV中,使用equalizeHist函数可以实现直方图均衡功能:

	Mat equalize(const Mat &image)
	{
		Mat result;
		equalizeHist(image,result);
		return result;
	}


有了上面定义的类,我们main函数就变得简单多了:

#include "histogram.h"

int main()
{
	//以灰度方式读取图像
	Mat image = imread("D:/picture/images/group.jpg",0);
	imshow("源灰度图像",image);
	if (!image.data)
		return -1;
	Histogram1D h;
	imshow("直方图",h.getHistogramImage(image));

	//用查找表翻转图像灰度
	int dim(256);
	Mat lut(1,&dim,CV_8U);
	
	for(int i = 0; i < 256; i++)
	{
		lut.at<uchar>(i) = 255-i;
	}
	imshow("灰度翻转后的图像",h.applyLookUp(image,lut));

	//用查找表拉伸图像灰度值
	//忽略那些个数少于100个像素的bin
	Mat strech = h.strech(image,100);
	imshow("拉伸后的图像",strech);
	imshow("拉伸后的直方图",h.getHistogramImage(strech));

	//图像均衡
	Mat afterEqualize = h.equalize(image);
	imshow("均衡后的解果",afterEqualize);
	imshow("均衡后的直方图",h.getHistogramImage(afterEqualize));
	waitKey(0);
	return 0;
}


由于画的图比较多,这里就不贴结果了。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值