直方图规定化
直方图的应用很多,这里我提供一个直方图规定化的应用程序,是将原图匹配到目标图的直方图,然后看看原图,目标图,匹配后的结果图 三者对应的直方图的曲线是否符合要求
蓝线:原图直方图;绿线:目标图直方图;红线:匹配后的结果直方图
看代码:
#include "opencv2/core/core.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"
#include <iostream>
#include <string>
using namespace cv;
bool histMatch_Value(Mat matSrc, Mat matDst, Mat &matRet);
int histogram_Matching();
Mat MyHistMatch(Mat &srcImage, Mat &TargetImage);
void drawHist(Mat a, Mat b, Mat c);
int main()
{
histogram_Matching();
return 0;
}
int histogram_Matching()
{
Mat matSrc = imread("road.jpg"); // 源图像
Mat matDst = imread("cj.jpg"); // 目标图像
Mat srcBGR[3];
Mat dstBGR[3];
Mat retBGR[3];
split(matSrc, srcBGR);
split(matDst, dstBGR);
/* /////////////////////////////////////////////////////////////
//方法一效果差点
retBGR[0] = MyHistMatch(srcBGR[0], dstBGR[0]);
retBGR[1] = MyHistMatch(srcBGR[1], dstBGR[1]);
retBGR[2] = MyHistMatch(srcBGR[2], dstBGR[2]);
/////////////////////////////////////////////////////////////*/
//方法二
histMatch_Value(srcBGR[0], dstBGR[0], retBGR[0]);
histMatch_Value(srcBGR[1], dstBGR[1], retBGR[1]);
histMatch_Value(srcBGR[2], dstBGR[2], retBGR[2]);
/////////////////////////////////////////////////////////////
Mat matResult;
merge(retBGR, 3, matResult);
imshow("src", matSrc);
imshow("dst", matDst);
imshow("Ret", matResult);
drawHist( matSrc, matDst, matResult);
cvWaitKey();
return 0;
}
bool histMatch_Value(Mat matSrc, Mat matDst, Mat &matRet)
{
if (matSrc.empty() || matDst.empty() || 1 != matSrc.channels() || 1 != matDst.channels())
return false;
int nHeight = matDst.rows;
int nWidth = matDst.cols;
int nDstPixNum = nHeight * nWidth;
int nSrcPixNum = 0;
int arraySrcNum[256] = { 0 }; // 源图像各灰度统计个数
int arrayDstNum[256] = { 0 }; // 目标图像个灰度统计个数
double arraySrcProbability[256] = { 0.0 }; // 源图像各个灰度概率
double arrayDstProbability[256] = { 0.0 }; // 目标图像各个灰度概率
// 统计源图像
for (int j = 0; j < nHeight; j++)
{
for (int i = 0; i < nWidth; i++)
{
arrayDstNum[matDst.at<uchar>(j, i)]++;
}
}
// 统计目标图像
nHeight = matSrc.rows;
nWidth = matSrc.cols;
nSrcPixNum = nHeight * nWidth;
for (int j = 0; j < nHeight; j++)
{
for (int i = 0; i < nWidth; i++)
{
arraySrcNum[matSrc.at<uchar>(j, i)]++;
}
}
// 计算概率
for (int i = 0; i < 256; i++)
{
arraySrcProbability[i] = (double)(1.0 * arraySrcNum[i] / nSrcPixNum);
arrayDstProbability[i] = (double)(1.0 * arrayDstNum[i] / nDstPixNum);
}
// 构建直方图均衡映射
int L = 256;
int arraySrcMap[256] = { 0 };
int arrayDstMap[256] = { 0 };
for (int i = 0; i < L; i++)
{
double dSrcTemp = 0.0;
double dDstTemp = 0.0;
for (int j = 0; j <= i; j++)
{
dSrcTemp += arraySrcProbability[j];
dDstTemp += arrayDstProbability[j];
}
arraySrcMap[i] = (int)((L - 1) * dSrcTemp + 0.5);// 减去1,然后四舍五入
arrayDstMap[i] = (int)((L - 1) * dDstTemp + 0.5);// 减去1,然后四舍五入
}
// 构建直方图匹配灰度映射
int grayMatchMap[256] = { 0 };
for (int i = 0; i < L; i++) // i表示源图像灰度值
{
int nValue = 0; // 记录映射后的灰度值
int nValue_1 = 0; // 记录如果没有找到相应的灰度值时,最接近的灰度值
int k = 0;
int nTemp = arraySrcMap[i];
for (int j = 0; j < L; j++) // j表示目标图像灰度值
{
// 因为在离散情况下,之前图均衡化函数已经不是严格单调的了,
// 所以反函数可能出现一对多的情况,所以这里做个平均。
if (nTemp == arrayDstMap[j])
{
nValue += j;
k++;
}
if (nTemp < arrayDstMap[j])
{
nValue_1 = j;
break;
}
}
if (k == 0)// 离散情况下,反函数可能有些值找不到相对应的,这里去最接近的一个值
{
nValue = nValue_1;
k = 1;
}
grayMatchMap[i] = nValue / k;
}
// 构建新图像
matRet = Mat::zeros(nHeight, nWidth, CV_8UC1);
for (int j = 0; j < nHeight; j++)
{
for (int i = 0; i < nWidth; i++)
{
matRet.at<uchar>(j, i) = grayMatchMap[matSrc.at<uchar>(j, i)];
}
}
return true;
}
Mat MyHistMatch(Mat &srcImage, Mat &TargetImage)
{
int nRows = srcImage.rows;
int nCols = srcImage.cols;
int nSrcSumPix[256];
int nTargetSumPix[256];
double nSrcProDis[256];
double nTargetProDis[256];
//int srcGrayLevel[256];
int HistMatchMap[256];
double nSrcSumProDis[256];
double nTargetSumProDis[256];
int EqualizeSrcSumPix[256];
int EqualizeTargetSumPix[256];
int matchFlag = 0;
for (int i = 0; i < 256; i++)
{
nSrcSumPix[i] = 0;
nTargetSumPix[i] = 0;
nSrcProDis[i] = 0.0;
nTargetProDis[i] = 0.0;
//srcGrayLevel[i] = 0;
HistMatchMap[i] = 0;
nSrcSumProDis[i] = 0.0;
nTargetSumProDis[i] = 0.0;
EqualizeSrcSumPix[i] = 0;
EqualizeTargetSumPix[i] = 0;
}
for (int i = 0; i < nRows; i++)
{
for (int j = 0; j < nCols; j++)
{
nSrcSumPix[(int)srcImage.at<uchar>(i, j)]++;
nTargetSumPix[(int)TargetImage.at<uchar>(i, j)]++;
}
}
for (int i = 0; i < 256; i++)
{
nSrcProDis[i] = (double)nSrcSumPix[i] / (nRows * nCols);
nTargetProDis[i] = (double)nTargetSumPix[i] / (nRows * nCols);
}
nSrcSumProDis[0] = nSrcProDis[0];
nTargetSumProDis[0] = nTargetProDis[0];
for (int i = 1; i < 256; i++)
{
nSrcSumProDis[i] = nSrcSumProDis[i - 1] + nSrcProDis[i];
nTargetSumProDis[i] = nTargetSumProDis[i - 1] + nTargetProDis[i];
}
for (int i = 0; i < 256; i++)
{
EqualizeSrcSumPix[i] = cvRound((double)nSrcSumProDis[i] * 255);
EqualizeTargetSumPix[i] = cvRound((double)nTargetSumProDis[i] * 255);
}
for (int i = 0; i < 256; i++)
{
int minMatchPara = 20;
for (int j = 0; j < 256; j++)
{
if (minMatchPara > abs(EqualizeSrcSumPix[i] - EqualizeTargetSumPix[j]))
{
minMatchPara = abs(EqualizeSrcSumPix[i] - EqualizeTargetSumPix[j]);
matchFlag = j;
}
}
HistMatchMap[i] = matchFlag;
}
Mat resultImage(nRows, nCols, srcImage.type());
for (int i = 0; i < nRows; i++)
{
for (int j = 0; j < nCols; j++)
{
resultImage.at<uchar>(i, j) = HistMatchMap[(int)srcImage.at<uchar>(i, j)];
}
}
return resultImage;
}
void drawHist(Mat a, Mat b, Mat c)
{
Mat matSrc;
Mat matDst;
Mat matResult;
cvtColor(a, matSrc,COLOR_BGR2GRAY);
cvtColor(b, matDst, COLOR_BGR2GRAY);
cvtColor(c, matResult, COLOR_BGR2GRAY);
imshow("Src1",matSrc);
//imshow("Dst1", matDst);
imshow("ret1", matResult);
int Channels[] = { 0 };
int nHistSize[] = { 256 };//256 points
float range[] = { 0, 255 };
const float* fHistRanges[] = { range };
Mat histR, histG, histB;
// 计算直方图
calcHist(&matSrc, 1, Channels, Mat(), histB, 1, nHistSize, fHistRanges, true, false);
calcHist(&matDst, 1, Channels , Mat(), histG, 1, nHistSize, fHistRanges, true, false);
calcHist(&matResult, 1, Channels , Mat(), histR, 1, nHistSize, fHistRanges, true, false);
// 创建直方图画布
int nHistWidth = 640;
int nHistHeight = 480;
int nBinWidth = cvRound((double)nHistWidth / nHistSize[0]);//400/256=2
Mat matHistImage(nHistHeight, nHistWidth, CV_8UC3, Scalar(255, 255, 255));
// 直方图归一化
normalize(histB, histB, 0.0, matHistImage.rows, NORM_MINMAX, -1, Mat());
normalize(histG, histG, 0.0, matHistImage.rows, NORM_MINMAX, -1, Mat());
normalize(histR, histR, 0.0, matHistImage.rows, NORM_MINMAX, -1, Mat());
// 在直方图中画出直方图
for (int i = 1; i < nHistSize[0]; i++)
{
line(matHistImage,
Point(nBinWidth * (i - 1), nHistHeight - cvRound(histB.at<float>(i - 1))),
Point(nBinWidth * (i), nHistHeight - cvRound(histB.at<float>(i))),
Scalar(255, 0, 0),
2,
8,
0);
line(matHistImage,
Point(nBinWidth * (i - 1), nHistHeight - cvRound(histG.at<float>(i - 1))),
Point(nBinWidth * (i), nHistHeight - cvRound(histG.at<float>(i))),
Scalar(0, 255, 0),
2,
8,
0);
line(matHistImage,
Point(nBinWidth * (i - 1), nHistHeight - cvRound(histR.at<float>(i - 1))),
Point(nBinWidth * (i), nHistHeight - cvRound(histR.at<float>(i))),
Scalar(0, 0, 255),
2,
8,
0);
}
// 显示直方图
imshow("histogram", matHistImage);
}