直方图规范化也叫规定化。通过直方图的规定,能够把两幅图像的色调拉成一样。
数学原理在网上很多可以找到。这里说一下实现过程步骤和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;
-
-
}
原图:
规定图:
转化结果: