修正直方图增强对比度

1. 直方图(histogram)

设一副数字图像的像素总数为 N N N,灰度级为 L L L,第 k k k个灰度级的灰度等级为 r k r_k rk,共有像素 n k n_k nk个,则第 k k k个灰度级或者说 r k r_k rk出现的频率: h k = n k / N h_k=n_k/N hk=nk/N,其中 k = 0 , 1........ ( L − 1 ) k=0,1........(L-1) k=0,1........(L1)
k k k为横坐标(或 r k r_k rk为横坐标), h k h_k hk为纵坐标画出的柱状图,就成为了直方图。比如下图的直方图:
在这里插入图片描述
在这里插入图片描述

要画出直方图可以先通过opencv提供的calcHist函数来计算图像直方图。
calcHist的定义如下:

CV_EXPORTS void calcHist( const Mat* images, int nimages,
                          const int* channels, InputArray mask,
                          OutputArray hist, int dims, const int* histSize,
                          const float** ranges, bool uniform = true, bool accumulate = false );

其中参数:
images:输入数组,即需要计算直方图的图像;
nimages:输入数组的个数,即第一个参数中存放的图像个数
channels:需要统计通道的索引,对于灰度图像,只有一个通道,索引就是0;
mask:可选的操作掩码,无掩膜操作填Mat();
hist:输出的目标直方图;
dims:需要计算的直方图的维数,必须是正数;
histSize:存放每个维度直方图的尺寸的数组,即直方图有多少根柱子(bins);
ranges:表示每一维数值的取值范围;
uniform:直方图是否均匀的标识符,默认为true;
accumulate:累计标识符,默认为false。

计算出图像直方图后,得到hist并不是一副图像,只是一个存放直方图数据的数组,这时候需要根据得到的hist画出其直方图即可。

其实现代码如下:

#include<opencv2/opencv.hpp>
#include<iostream>
using namespace cv;
using namespace std;
Mat getHistogram1DImage(Mat& dstHist,int scale=1);
int main(int argc, char* argv[])
{
	Mat src= imread("1.jpg",0);
	if (!src.data)
	{
		cout << "图片加载失败" << endl;
		return -1;
	}
	namedWindow("原图", NORM_MINMAX);
	imshow("原图",src);
	Mat dstHist;
	int dims = 1;
	float hranges[] = { 0,256 };
	const float* ranges[] = { hranges };
	int bins ={256};
	int channels = {0};
	calcHist(&src,1,&channels,Mat(),dstHist,dims,&bins,ranges);
	//这里得到的dstHist是一个bins*1的数组
	
	
	Mat dstImg= getHistogram1DImage(dstHist);

	imshow("Histogram", dstImg);
	
	  cv::waitKey(0);
	return 0;


}
Mat getHistogram1DImage( Mat& dstHist,int scale ) {
	
	int bins = dstHist.rows;
	Mat dstImg(bins, bins * scale, CV_8UC3, Scalar(0));//Mat::Mat(int rows, int cols, int type, const Scalar& s)
	//注意构造时时先行后列

	double minValue = 0;
	double maxValue = 0;
	minMaxLoc(dstHist, &minValue, &maxValue, 0, 0);
	double hpt = saturate_cast<double>(0.9 * bins);

	for (int i = 0; i < bins; i++) {
		float binValue = dstHist.at<float>(i);
		int realValue = saturate_cast<int>((double)binValue * hpt / maxValue);
		rectangle(dstImg, Point(i * scale, bins - 1), Point(i * scale, bins - realValue), Scalar(0, 255, 0), 1);
		/*
		rectangle用线段代替也可以
		line(dstImg, Point(i * scale, bins - 1), Point(i * scale, bins - realValue), Scalar(0, 255, 0), 1, 8);
		 */
	}
	return dstImg;
}

2.直方图均化

对于数字图像,,设原图像像素总数为 N N N,有 L L L个灰度级,第 k k k个灰度级灰度 r k r_k rk出现的频数为 n k n_k nk。若原图像素点在点 ( i , j ) (i,j) (i,j)的灰度为 r k r_k rk,则直方图均化处理后在点 ( i , j ) (i,j) (i,j)的灰度值的变换公式为:
在这里插入图片描述
其中 k = 0 , 1 , . . . . . . . . . . . . , L − 1 , h k k=0,1,............,L-1,h_k k=0,1,............,L1,hk称作第 k k k个灰度级出现的概率。
从直方图的算法可以看出,其核心思想在于使用灰度累计概率作为变换后的输出结果,替代原图像素的灰度。

2.1方法一

算法流程如下:
在这里插入图片描述
程序代码如下:

#include<opencv2/opencv.hpp>
#include<iostream>
using namespace cv;
using namespace std;
Mat avgHistogram(Mat input);
Mat CalcGetHistogram3DImage(Mat src, int scale = 1, Color color=Color());//计算且得到直方图像

int main(int argc, char* argv[])
{
Mat src = imread("1.jpg");
	if (!src.data)
	{
		cout << "图片加载失败" << endl;
		return -1;
	}
	namedWindow("原图", NORM_MINMAX);
	imshow("原图", src);
	Mat dstImg = CalcGetHistogram3DImage(src);
	imshow("原图的直方图", dstImg);
	Mat dst;
	dst = avgHistogram(src);
	namedWindow("均化后", NORM_MINMAX);
	imshow("均化后", dst);
	dstImg = CalcGetHistogram3DImage(dst);
	imshow("均化后的直方图", dstImg); 
	waitKey(0);
	return 0;


}

Mat avgHistogram(Mat input ) {
	double MaxGrey;//最大灰度值
	minMaxLoc(input, 0, &MaxGrey);
	int totalsize = input.rows * input.cols;//像素总数
	double *grey=new double[MaxGrey + 1];//原始图像灰度
	uchar* newgrey = new uchar[MaxGrey + 1];//变换后图像灰度
	int channels = input.channels();
	for (int k = 0; k < channels; k++) {
		for (int i = 0; i <= MaxGrey; i++) {
			grey[i] = 0;
			newgrey[i] = 0;
		}
		for (int i = k; i < input.cols*channels; i=i+channels) {
			for (int j = 0; j < input.rows; j++) {
				grey[input.at<uchar>(j, i)]++;//统计各灰度包含的像素总数
			}

		}
		for (int i = 0; i <= MaxGrey; i++) {
			grey[i] /= totalsize;//提前归一化,免得数据过大后面操作出现溢出
		}
		for (int i = 1/*注意初始,防止越界*/; i <= MaxGrey; i++) {
			grey[i] += grey[i - 1];//计算累积概率
		}
		for (int i = 0; i <= MaxGrey; i++) {
			newgrey[i] = (uchar)(grey[i] * MaxGrey);
		}
		for (int i = k; i < input.cols*channels; i=i+channels) {
			for (int j = 0; j < input.rows; j++) {
				input.at<uchar>(j, i) = newgrey[input.at<uchar>(j, i)];//灰度替换
			}

		}
	}
	return input;
}
Mat CalcGetHistogram3DImage(Mat src, int scale, Color color ) {
	Mat dstHist;
	int dims = 1;
	float hranges[] = { 0,256 };
	const float* ranges[] = { hranges };
	int bins = { 256 };
	int channels = src.channels();
	Mat dstImg(bins,bins*scale*channels, CV_8UC3, Scalar(0));
	for (int channelsIndex = 0; channelsIndex < channels; channelsIndex++) {
		calcHist(&src, 1, &channelsIndex, Mat(), dstHist, dims, &bins, ranges);
		//这里得到的dstHist是一个bins*1的数组
		double minValue = 0;
		double maxValue = 0;
		minMaxLoc(dstHist, &minValue, &maxValue, 0, 0);
		double hpt = saturate_cast<double>(0.9 * bins);

		for (int i = channelsIndex * bins; i < bins + channelsIndex * bins; i++) {
			float binValue = dstHist.at<float>(i-channelsIndex*bins);
			int realValue = saturate_cast<int>((double)binValue * hpt / maxValue);
			rectangle(dstImg, Point(i * scale, bins - 1), Point(i * scale, bins - realValue), color.geti(channelsIndex), 1);
		}

	}
	return dstImg;
}

效果如下:
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

2.2方法二

直接通过calcHist计算出的直方图数据进行均化处理
代码如下:

#include<opencv2/opencv.hpp>
#include<iostream>
using namespace cv;
using namespace std;
class Color {
private:
	Scalar c1;//颜色分量1
	Scalar c2;//颜色分量2
	Scalar c3;//颜色分量3
	
public:	
	 Color(Scalar c1 = Scalar(255, 0, 0),
	 Scalar c2 = Scalar(0, 255, 0),
	 Scalar c3 = Scalar(0, 0, 255)) {
		 this->c1 = c1;
		 this->c2 = c2;
		 this->c3 = c3;
	 }
	 Scalar geti(int i) {
		 if (i == 0) return c1;
		 else if (i == 1)return c2;
		 else if (i == 2) return c3;
		 else {
			 cout << "Color::get(int i)参数错误" << endl;
		 }
	 }
};

Mat avgHistogram1(Mat input);
Mat CalcGetHistogram3DImage(Mat src, int scale = 1, Color color=Color());//计算且得到直方图像
int main(int argc, char* argv[])
{
	Mat src = imread("1.jpg");
	if (!src.data)
	{
		cout << "图片加载失败" << endl;
		return -1;
	}
	namedWindow("原图", NORM_MINMAX);
	imshow("原图", src);
	Mat dstImg = CalcGetHistogram3DImage(src);
	imshow("原图的直方图", dstImg);
	Mat dst;
	dst = avgHistogram1(src);
	namedWindow("均化后", NORM_MINMAX);
	imshow("均化后", dst);
	dstImg = CalcGetHistogram3DImage(dst);
	imshow("均化后的直方图", dstImg); 
	waitKey(0);
	return 0;


}
Mat avgHistogram1(Mat input) {//另一种直方图均化的方法
	Mat Hist;
	const int channels = input.channels();
	float hranges[] = { 0,256 };
	const float* ranges[] = { hranges };
	int bins = { 256 };
	int dims = 1;
	for (int channelsIndex = 0; channelsIndex < channels; channelsIndex++) {
		calcHist(&input, 1, &channelsIndex, Mat(), Hist, dims, &bins, ranges);
		int MaxGrey = 255;
		double* newgrey = new double[MaxGrey+1];
		int totalSize = input.cols * input.rows;
	     
		for (int i = 0; i <= MaxGrey; i++) {
			newgrey[i] = 0;
			for (int j = 0; j <= i; j++) {
				
				newgrey[i] += (double)Hist.at<float>(j)*MaxGrey/totalSize;
				
			}
		}
		
		for (int i = 0; i < input.rows; i++) {
			for (int j = channelsIndex; j < input.cols * channels; j = j + channels) {
				input.at<uchar>(i, j) = (uchar)newgrey[input.at<uchar>(i, j)];//灰度替换
			}
		}
		
	}
	return input;
	}

Mat CalcGetHistogram3DImage(Mat src, int scale, Color color ) {
	Mat dstHist;
	int dims = 1;
	float hranges[] = { 0,256 };
	const float* ranges[] = { hranges };
	int bins = { 256 };
	int channels = src.channels();
	Mat dstImg(bins,bins*scale*channels, CV_8UC3, Scalar(0));
	for (int channelsIndex = 0; channelsIndex < channels; channelsIndex++) {
		calcHist(&src, 1, &channelsIndex, Mat(), dstHist, dims, &bins, ranges);
		//这里得到的dstHist是一个bins*1的数组
		double minValue = 0;
		double maxValue = 0;
		minMaxLoc(dstHist, &minValue, &maxValue, 0, 0);
		double hpt = saturate_cast<double>(0.9 * bins);

		for (int i = channelsIndex * bins; i < bins + channelsIndex * bins; i++) {
			float binValue = dstHist.at<float>(i-channelsIndex*bins);
			int realValue = saturate_cast<int>((double)binValue * hpt / maxValue);
			rectangle(dstImg, Point(i * scale, bins - 1), Point(i * scale, bins - realValue), color.geti(channelsIndex), 1);
		}

	}
	return dstImg;
}

结果如下:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
从直方图看出以上两种方法均实现了一样的直方图修正算法

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值