【opencv】之直方图的应用

    这一篇我们来学习下直方图的应用,主要有直方图的拉伸、直方图均衡化以及利用直方图寻找相似图像。

1. 直方图拉伸

    图像对比度增强分为两类:直接对比度增强和间接对比度增强。直方图拉伸和直方图均衡化是两种最常用的间接对比度增强方法。直方图拉伸是通过对比度拉伸对直方图进行调整,从而扩大“前景”和背景灰度的差别,以达到增强对比度的目的。

    公式如下:

    设f(x,y)为输入图像,则

        最小灰度级A=min[f(x,y)]

        最大灰度级B=max[f(x,y)]

    将A和B分别线性映射到0和255,最终得到的图像为:

         g(x,y)=(255/(B-A))[f(x,y)-A]
   

    以下为代码实现:

/*------------------------------------【程序说明】-------------------------------------
描述:直方图拉伸
2015/10/16 by czp 
2015/10/18 【修改】由于现有的图片像素区间在【0,255】,无法进行拉伸,故进行反向运算,得到sky-lancer1.jpg
---------------------------------------------------------------------------------------*/


/*--------------------------------【程序头文件部分】----------------------------------
描述:包含程序所依赖的文件
--------------------------------------------------------------------------------------*/
#include <iostream>
#include "highgui.h"
#include <opencv2/opencv.hpp>

/*--------------------------------【命名空间部分】-------------------------------------
描述:包含程序所使用的命名空间
--------------------------------------------------------------------------------------*/
using namespace cv;
using namespace std;

/*--------------------------------【类Histogram1D】-------------------------------------
描述:计算一维直方图
--------------------------------------------------------------------------------------*/
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;
	}

	MatND getHistogram(const Mat &image){
		MatND hist;
		calcHist(&image,
			1,
			channels,
			Mat(),
			hist,
			1,
			histSize,
			ranges
			);
		return hist;
	}
	Mat getHistogramImage(const Mat &image){
		MatND hist = getHistogram(image);
		double maxVal = 0;
		double minVal = 0;

		minMaxLoc(hist, &minVal, &maxVal, 0, 0);

		Mat histImg(histSize[0], histSize[0], CV_8U, Scalar(255));

		int hpt = static_cast<int>(0.9*histSize[0]);

		for (int h = 0; h < histSize[0]; h++){
			float binVal = hist.at<float>(h);
			int intensity = static_cast<int>(binVal*hpt / maxVal);
			line(histImg, Point(h, histSize[0]), Point(h, histSize[0] - intensity), Scalar::all(0));
		}
		return histImg;
	}
};


/*--------------------------------【main()函数】---------------------------------------
描述:控制台应用程序入口
--------------------------------------------------------------------------------------*/
int main(){
	
	int PixMax_array[3] = { 0, 0, 0 }, PixMin_array[3] = { 255, 255, 255 };    //存储像素的最大值和最小值
	float DifferenceOfMaxMin[3];
	int i, j, nl, nc;

	Mat srcImg = imread("sky-lancer1.jpg", 1); //读入原图像,这里只研究彩色图像
	if (!srcImg.data){ printf("读取图片错误!!\n"); return false; }
	Mat dstImg = srcImg.clone();

	//遍历图像,寻找最大和最小值
	nl = dstImg.rows; //行数
	nc = dstImg.cols; //列数
	for (i = 0; i < nl; i++)
	{
		for (j = 0; j < nc; j++)
		{

			if (dstImg.at<Vec3b>(i, j)[0] < PixMin_array[0]){ PixMin_array[0] = dstImg.at<Vec3b>(i, j)[0]; }
			if (dstImg.at<Vec3b>(i, j)[1] < PixMin_array[1]){ PixMin_array[1] = dstImg.at<Vec3b>(i, j)[1]; }
			if (dstImg.at<Vec3b>(i, j)[2] < PixMin_array[2]){ PixMin_array[2] = dstImg.at<Vec3b>(i, j)[2]; }

			if (dstImg.at<Vec3b>(i, j)[0] > PixMax_array[0]){ PixMax_array[0] = dstImg.at<Vec3b>(i, j)[0]; }
			if (dstImg.at<Vec3b>(i, j)[1] > PixMax_array[1]){ PixMax_array[1] = dstImg.at<Vec3b>(i, j)[1]; }
			if (dstImg.at<Vec3b>(i, j)[2] > PixMax_array[2]){ PixMax_array[2] = dstImg.at<Vec3b>(i, j)[2]; }

		}
	}
		


	//遍历图像,进行直方图拉伸
		for (i = 0; i < 3; i++)
		{
			DifferenceOfMaxMin[i] = 255.0/(float)(PixMax_array[i] - PixMin_array[i]);
		}
		for (i = 0; i < nl; i++)
		{
			for (j = 0; j < nc; j++)
			{
				dstImg.at<Vec3b>(i, j)[0] = (int)(DifferenceOfMaxMin[0] * (dstImg.at<Vec3b>(i, j)[0] - PixMin_array[0]));
				dstImg.at<Vec3b>(i, j)[1] = (int)(DifferenceOfMaxMin[1] * (dstImg.at<Vec3b>(i, j)[1] - PixMin_array[1]));
				dstImg.at<Vec3b>(i, j)[2] = (int)(DifferenceOfMaxMin[2] * (dstImg.at<Vec3b>(i, j)[2] - PixMin_array[2]));
			}
		}
		

	    namedWindow("srcImg");
		namedWindow("dstImg");
		imshow("srcImg", srcImg);
		waitKey(5000);
		imshow("dstImg", dstImg);
		waitKey(5000);

		//查看各个通道的直方图是否有变化(以通道0为例子)
		vector<Mat> mv,nv;
		split(srcImg,mv);
		split(dstImg, nv);
		Histogram1D hm, hn;
		namedWindow("Histogram_m");
		imshow("Histogram_m", hm.getHistogramImage(mv.at(0)));
		waitKey(5000);
		namedWindow("Histogram_n");
		imshow("Histogram_n", hn.getHistogramImage(nv.at(0)));
		waitKey(5000);
}




效果图:

  

        

参考文章:http://blog.csdn.net/guoyk1990/article/details/8104130

2.直方图均衡化

    如果一幅图像的像素占有很多的灰度级而且分布均匀,那么这样的图像往往有高对比度和多变的灰度色调。直方图均衡化就是为了达到这种效果。它的基本思想是对图像中像素个数多的灰度级进行展宽,而对图像中像素个数少的灰度进行压缩,从而扩展像原取值的动态范围,提高了对比度和灰度色调的变化,使图像更加清晰。【百度百科】

    简单的说,均衡化就是“把一个分布(给定的直方图)映射到另一个分布(一个更宽更统一的强度值分布),所以强度值分布会在整个范围内展开”。直方图均衡化是通过累计分布函数来实现的。

  详细的原理参见: 直方图原理

    OpenCV提供了一个简单易用的函数来执行直方图均衡化。

          cv::equalizeHist(Mat &srcImage,Mat &dstImage);

         其中输入输出均为单通道图像。

        代码及其实现:

//------------------------------------【程序功能】-----------------------------------------------
//  描述:直方图均衡化
//-----------------------------------------------------------------------------------------------

//----------------------------------【头文件包含部分】-------------------------------------------
//  描述:包含程序所依赖的文件
//-----------------------------------------------------------------------------------------------
#include <iostream>
#include "highgui.h"
#include <opencv2/opencv.hpp>

//-----------------------------------【命名空间声明】--------------------------------------------
//  描述:包含程序所使用的命名空间
//-----------------------------------------------------------------------------------------------
using namespace cv;
using namespace std;

// 定义直方图的类
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;
	}

	MatND getHistogram(const Mat &image){
		MatND hist;
		calcHist(&image,
			1,
			channels,
			Mat(),
			hist,
			1,
			histSize,
			ranges
			);
		return hist;
	}
	Mat getHistogramImage(const Mat &image){
		MatND hist = getHistogram(image);
		double maxVal = 0;
		double minVal = 0;

		minMaxLoc(hist, &minVal, &maxVal, 0, 0);

		Mat histImg(histSize[0], histSize[0], CV_8U, Scalar(255));

		int hpt = static_cast<int>(0.9*histSize[0]);

		for (int h = 0; h < histSize[0]; h++){
			float binVal = hist.at<float>(h);
			int intensity = static_cast<int>(binVal*hpt / maxVal);
			line(histImg, Point(h, histSize[0]), Point(h, histSize[0] - intensity), Scalar::all(0));
		}
		return histImg;
	}

};

//-------------------------------------【main()函数】-----------------------------------------------
//描述:控制台应用程序的入口
//--------------------------------------------------------------------------------------------------
int main(){

	Mat srcImage = imread("sky-lancer.jpg", 0), dstImage;  //读入灰度图像
	if (!srcImage.data)  {printf("读取图像错误!"); return false;}
	
	equalizeHist(srcImage, dstImage);

	//处理前和处理后的图像对比
	namedWindow("srcImage");
	imshow("srcImage", srcImage);
	waitKey(5000);
	namedWindow("dstImage");
	imshow("dstImage", dstImage);
	waitKey(5000);

	//处理前和处理后的直方图对比
	Histogram1D h;
	namedWindow("srcHistogram");
	/*split(srcImage, nv);*/
	imshow("srcHistogram", h.getHistogramImage(srcImage));
	waitKey(5000);
	namedWindow("EqualizationOfHistogram");
	imshow("EqualizationOfHistogram", h.getHistogramImage(dstImage));
	waitKey(5000);
}


  

  




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值