直方图规范化的opencv的实现

直方图规范化也叫规定化。通过直方图的规定,能够把两幅图像的色调拉成一样。

数学原理在网上很多可以找到。这里说一下实现过程步骤和opencv的C++实现。


上表一行行看下来,大部分应该没什么问题。SML映射看的是原始累计直方图的值最相近的规定累计直方图的位置,(比如0.14在下面找最接近的是0.19,是就是3).

然后下面就很简单了,灰度0映射到灰度3,灰度1映射到灰度4。。。

代码还是比较简单的,就是网上搜了一下也没有,OPENCV本身也没有封装。就自己写了一个,和大家共享。

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

using namespace std;
using namespace cv;


bool Cal_Hist(Mat Gray_img, MatND &hist){
	int bins = 256;
	int hist_size[] = { bins };
	float range[] = { 0, 256 };
	const float* ranges[] = { range };

	int channels[] = { 0 };
	//计算直方图  
	calcHist(&Gray_img, 1, channels, Mat(), // do not use mask    
		hist, 1, hist_size, ranges,
		true, // the histogram is uniform    
		false);
	if (hist.data == 0) return false;
	return true;
}
void DrawGrayHist(const char* pTitle, MatND& hist)
{
	int hist_height = 256;
	int bins = 256;
	double max_val;  //直方图的最大值  
	int scale = 2;   //直方图的宽度  
	minMaxLoc(hist, 0, &max_val, 0, 0); //计算直方图最大值  

	Mat hist_img = Mat::zeros(hist_height, bins*scale, CV_8UC3); //创建一个直方图图像并初始化为0   
	for (int i = 0; i<bins; i++)
	{
		float bin_val = hist.at<float>(i); // 第i灰度级上的数      
		int intensity = cvRound(bin_val*hist_height / max_val);  //要绘制的高度    
		//填充第i灰度级的数据  
		rectangle(hist_img, Point(i*scale, hist_height - 1),
			Point((i + 1)*scale - 1, hist_height - intensity),
			CV_RGB(255, 255, 255));
	}
	imshow(pTitle, hist_img);
}

void one_channel_hist_specify(Mat input_img, Mat dst_img, Mat &output_img)//单通道
{
	int i,j; 

	//计算输入,规定图像的直方图
	MatND input_hist, dst_hist;
	Cal_Hist(input_img, input_hist);
	Cal_Hist(dst_img, dst_hist);

	//计算概率直方图
	MatND input_p_hist, dst_p_hist;
	input_p_hist = MatND::zeros(input_hist.size[0], input_hist.size[1], CV_32FC1);//原始概率直方图
	dst_p_hist = MatND::zeros(dst_hist.size[0], dst_hist.size[1], CV_32FC1);//规定概率直方图
	float input_totalnum = 0;
	float dst_totalnum = 0;
	for (i = 0; i < input_hist.rows; i++)
		input_totalnum += input_hist.at<float>(i);
	for (i = 0; i < dst_hist.rows; i++)
		dst_totalnum += dst_hist.at<float>(i);
	for (i = 0; i < input_hist.rows; i++)
		input_p_hist.at<float>(i) = input_hist.at<float>(i) / input_totalnum;
	for (i = 0; i < dst_hist.rows; i++)
		dst_p_hist.at<float>(i) = dst_hist.at<float>(i) / dst_totalnum;

	//计算累计直方图
	MatND input_c_hist, dst_c_hist;
	input_c_hist = MatND::zeros(input_hist.size[0], input_hist.size[1], CV_32FC1);//原始累计直方图
	dst_c_hist = MatND::zeros(dst_hist.size[0], dst_hist.size[1], CV_32FC1);//规定累计直方图
	float input_accum_p = 0;
	float dst_accum_p = 0;
	for (i = 0; i < input_hist.rows; i++)
	{
		input_accum_p += input_p_hist.at<float>(i);
		input_c_hist.at<float>(i) = input_accum_p;
	}
	for (i = 0; i < dst_hist.rows; i++)
	{
		dst_accum_p += dst_p_hist.at<float>(i);
		dst_c_hist.at<float>(i) = dst_accum_p;
	}
	
	//计算单映射规则
	MatND SML = MatND::zeros(input_hist.size[0], input_hist.size[1], CV_32FC1);//SML单映射规则
	for (i = 0; i < input_c_hist.rows; i++)
	{
		int minind = 0;
		float minval = 1;
		for (j = 0; j < dst_c_hist.rows; j++)
		{
			float abssub = abs(input_c_hist.at<float>(i)-dst_c_hist.at<float>(j));
			if (abssub < minval)
			{
				minval = abssub;
				minind = j;
			}
		}
		SML.at<float>(i) = minind;
	}
	
	//计算输出图像
	Mat outimg = Mat::zeros(input_img.size[0], input_img.size[1], CV_8U);
	for (i = 0; i < input_img.rows; i++)
	{
		for (j = 0; j < input_img.cols; j++)
		{
			outimg.at<uchar>(i, j) = SML.at<float>(input_img.at<uchar>(i, j));
		}
	}
	outimg.copyTo(output_img);

	//计算输出图像直方图
	//MatND output_hist;
	//Cal_Hist(output_img, output_hist);

	//DrawGrayHist("input_hist", input_hist);
	//DrawGrayHist("dst_hist", dst_hist);
	//DrawGrayHist("output_hist", output_hist);
}

void three_channel_hist_specify(Mat input_img, Mat dst_img, Mat &output_img)//三通道
{
	//Mat src = imread("path", 1);  //读入目标图像  
	Mat out_img(input_img.rows, input_img.cols, CV_8UC3); //用来存储目的图片的矩阵  
	//Mat数组来存车分离后的三个通道,每个通道都初始化为0;  
	Mat input_planes[] = { Mat::zeros(input_img.size(), CV_8UC1), Mat::zeros(input_img.size(), CV_8UC1), Mat::zeros(input_img.size(), CV_8UC1) };
	Mat dst_planes[] = { Mat::zeros(dst_img.size(), CV_8UC1), Mat::zeros(dst_img.size(), CV_8UC1), Mat::zeros(dst_img.size(), CV_8UC1) };
	//多通道分成3个单通道,BGR
	split(input_img, input_planes);
	split(dst_img, dst_planes);
	Mat B_output_img, G_output_img, R_output_img;
	one_channel_hist_specify(input_planes[0], dst_planes[0], B_output_img);
	one_channel_hist_specify(input_planes[1], dst_planes[1], G_output_img);
	one_channel_hist_specify(input_planes[2], dst_planes[2], R_output_img);
	Mat output_planes[3];
	output_planes[0] = B_output_img;
	output_planes[1] = G_output_img;
	output_planes[2] = R_output_img;
	merge(output_planes,3, out_img);  //通道合并
	out_img.copyTo(output_img);
}

int main()
{

	Mat src, gray, src2, gray2;
	//src=imread("D://input//buti.jpg");    
	src = imread("F:\\大海.jpg");
	cvtColor(src, gray, CV_RGB2GRAY);  //转换成灰度图  

	src2 = imread("F:\\沙漠.jpg");
	cvtColor(src2, gray2, CV_RGB2GRAY);  //转换成灰度图  
	MatND hist2;

	imshow("Source", src);
	//imshow( "Gray Histogram", hist_img );    
	imshow("Source2", src2);

	Mat output_img;
	three_channel_hist_specify(src, src2, output_img);
	
	imshow("5", output_img);
	//DrawGrayHist("2", hist2);

	waitKey();
	return 0;

}

原图:

规定图:

转化结果:



  • 6
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值