1.直方图变换的累积方法
利用像素累积的方法进行直方图变换的大致过程如下:
(1).将源图像转换为灰度图,并计算图像的灰度直方图。
(2).建立映射表,对直方图进行像素累计。
(3).根据映射表进行元素映射得到最终的直方图变换。
相应的程序如下:
//利用统计像素进行直方图变换
#include <iostream>
#include <opencv2\highgui\highgui.hpp>
#include <opencv2\imgproc\imgproc.hpp>
using namespace cv;
using namespace std;
int main()
{
Mat srcImage = imread("2345.jpg");
if (!srcImage.data)
{
cout << "读入图片失败" << endl;
return -1;
}
//先将图像转换为灰度图像
Mat grayImage;
cvtColor(srcImage, grayImage, CV_BGR2GRAY);
//计算图像的直方图
const int channels[1] = { 0 };
const int histSize[1] = { 256 };
float hranges[2] = { 0, 255 };
const float *ranges[1] = { hranges };
MatND hist;
calcHist(&grayImage, 1, channels, Mat(), hist,
1, histSize, ranges);
float table[256];
int nPix = grayImage.rows*grayImage.cols; //像素总数
//建立映射表
for (int i = 0; i < 256; i++)
{
float temp[256];
//进行像素变换
temp[i] = hist.at<float>(i) / nPix * 255;
if (i != 0)
{
//进行像素累计
table[i] = table[i - 1] + temp[i];
}
else
{
table[i] = temp[i];
}
}
//通过映射进行查找表变换
Mat lookUpTable(Size(1, 256), CV_8U);
for (int i = 0; i < 256; i++)
{
lookUpTable.at<uchar>(i) = static_cast<uchar>(table[i]);
}
Mat histTransResult;
LUT(grayImage, lookUpTable, histTransResult);
imshow("灰度图像", grayImage);
imshow("直方图变换结果", histTransResult);
waitKey();
return 0;
}
执行程序后输出的结果为:
2.直方图匹配
关于直方图匹配的原理已经在直方图的上篇给出,这里只简单介绍一下:
在实际的场景之中,常常需要用来增强某一特定区间的图像信息,因此可以按照给定的直方图形状来调整原先的图像的直方图信息。
直方图匹配总和了直方图变换和均衡化的原理和思想,通过建立映射变换关系,使得期望图像的直方图达到一种特定的形态。对于原图像直方图均衡化y=f(x),目标图像直方图均衡化z=g(k),使得y=z,也就满足了k=g^-1(z)=g^-1(f(x))
进行直方图匹配的大致步骤如下
(1).分别计算原图像和目标图像的累积概率分布
(2).分别对源图像与目标图像进行直方图均衡化
(3).利用组映射关系使源图像直方图按照规定进行变换
使用程序进行相关的实现如下:
//进行直方图匹配操作
#include <iostream>
#include <opencv2\core\core.hpp>
#include <opencv2\highgui\highgui.hpp>
#include <opencv2\imgproc\imgproc.hpp>
using namespace cv;
using namespace std;
int main()
{
//加载原图像
Mat srcImage = imread("1234.jpg");
//加载用来匹配的图像
Mat dstImage = imread("2345.jpg");
if (!srcImage.data || !srcImage.data)
{
cout << "读入图片有误!" << endl;
return -1;
}
//resize操作使得两张图的尺寸相同
//resize(srcImage, dstImage, Size(srcImage.cols, srcImage.rows), 0, 0, CV_INTER_LINEAR);
imshow("原图像", srcImage);
imshow("待匹配的图像", dstImage);
//初始化累计分布参数
float srcCdfArr[256];
float dstCdfArr[256];
int srcAddTemp[256];
int dstAddTemp[256];
int histMatchMap[256];
for (int i = 0; i < 256; i++)
{
srcAddTemp[i] = 0;
dstAddTemp[i] = 0;
srcCdfArr[i] = 0;
dstCdfArr[i] = 0;
histMatchMap[i] = 0;
}
float sumSrcTemp = 0;
float sumDstTemp = 0;
int nSrcPix = srcImage.rows*srcImage.cols;
int nDstPix = dstImage.rows*dstImage.cols;
int matchFlag = 0;
//求解原图像与目标图像的累积直方图
for (size_t nrow = 0; nrow < srcImage.rows; nrow++)
{
for (size_t ncol = 0; ncol < srcImage.cols; ncol++)
{
srcAddTemp[(int)srcImage.at<uchar>(nrow, ncol)]++;
dstAddTemp[(int)dstImage.at<uchar>(nrow, ncol)]++;
}
}
//求解原图像与目标图像的累计概率分布
for (int i =0 ; i < 256; i++)
{
sumSrcTemp += srcAddTemp[i];
srcCdfArr[i] = sumSrcTemp / nSrcPix;
sumDstTemp += dstAddTemp[i];
dstCdfArr[i] = sumDstTemp / nDstPix;
}
//进行直方图匹配的实现
for (int i = 0; i < 256; i++)
{
float minMatchPara = 20;
for (int j = 0; j < 256; j++)
{
//判断当前直方图累计差异
if (minMatchPara>abs(srcCdfArr[i] - dstCdfArr[j]))
{
minMatchPara = abs(srcCdfArr[i] - dstCdfArr[j]);
matchFlag = j;
}
}
histMatchMap[i] = matchFlag;
}
//初始化匹配图像
Mat histMatchImage = Mat::zeros(srcImage.rows, srcImage.cols, CV_8UC3);
cvtColor(srcImage, histMatchImage, CV_BGR2GRAY);
//通过map映射为匹配图像
for (int i = 0; i < histMatchImage.rows; i++)
{
for (int j = 0; j < histMatchImage.cols; j++)
{
histMatchImage.at<uchar>(i, j) =
histMatchMap[(int)histMatchImage.at<uchar>(i, j)];
}
}
imshow("直方图匹配后的图像", histMatchImage);
waitKey();
return 0;
}
程序执行后的结果如下:
直方图的反向投影是利用直方图模型计算给定的像素点的特征。反向投影在某一位置的值是源图像在对应位置的像素值的累计。
反向投影操作可以实现检测输入的源图像给定图像块的最匹配的区域,一般可以用于基于图像内容的检索或查找特定的内容,对于原始灰度图像f(x,y),计算相应的灰度直方图时,可以利用直方图变换bin来计算相应的反向投影,bin越大,反向投影显示的结果也就越大。
例如,对于下面这个矩阵
在求这个灰度直方图时,bins用来指定区间:[0,2],[3,5],[6,7],[8,10)。求解可知,直方图hist = 4 4 6 2
根据直方图反向投影原理可以知道
为了方便地计算直方图的反向投影,OpenCV中提供了一个用来简单计算hue通道的直方图反向投影的函数calcBackProject,下面对这个函数进行说明:
函数的原型如下:
void calcBackProject( const Mat* images, int nimages,const int* channels, InputArray hist,OutputArray backProject,
const float** ranges,double scale=1, bool uniform=true );
这个函数的主要功能是实现直方图反向投影的计算。
第一个参数 images表示输入图像源指针,需要注意的是,图像的源必须具有同样的深度信息,也就是说,可以是CV_8U或CV_32U,图像可以有任意的通道数。
第二个参数nimages表示的是待计算图像源中图像的个数,通常单幅图像计算直方图时这个参数的值为1.
第三个参数channels指的是需要同级的图像的通道维数数组索引,第一个数组的通道由0到arrays[0].channels()-1,第二个数组的通道从array[0].channels()到arrays[0].channels() + array[1].channels()-1。并以此类推。
第四个参数hist表示输入源图像的直方图
第五个参数backProject表示的是目标图像的反向投影图,这个图可以是单通道,与Image[0]具有同样的尺度和深度
第六个参数ranges表示用于指出直方图每一维的每个bin上下界范围的数组,对于均匀直方图这个参数是一个包含两个元素的数组。
第七个参数scale表示可选的输出反向投影的尺寸的参数
第八个参数uniform是直方图统一显示的标志。
在给出具体程序之前,先简单介绍一下会用到的一个OpenCV中的函数,函数声明如下:
void mixChannels(const Mat* src, size_t nsrcs, Mat* dst, size_t ndsts, const int* fromTo, size_t npairs);
CV_EXPORTS void mixChannels(const vector<Mat>& src, vector<Mat>& dst, const int* fromTo, size_t npairs);
void mixChannels(InputArrayOfArrays src, InputArrayOfArrays dst,const vector<int>& fromTo);
函数实现的功能是通道复制到特定的通道
参数src表示的是输入图像源组,被复制通道的输入图像数据
参数nsrc指的是待输入图像源中图像的图像的个数
参数dst表示的是输出目标图像数组,存储复制后的通道,所有的数组必须要事先进行分配,而且要和输入数组的大小和深度相同。
参数ndsts指的是目标数组中图像的总数
参数fromTo指的是通道索引对的数组,表示的是将输入图像数组复制到目标数组通道,如果是偶数表示输入矩阵索引,如果是奇数表示的是输出矩阵索引,如果是偶数而且其下标为负,则相应输出矩阵为0
参数npairs表示fromTo中的索引对
下面使用OpenCV进行图像的反向投影的实现
//实现直方图的反向投影
#include <iostream>
#include <opencv2\core\core.hpp>
#include <opencv2\highgui\highgui.hpp>
#include <opencv2\imgproc\imgproc.hpp>
using namespace cv;
using namespace std;
int main()
{
Mat srcImage = imread("2345.jpg");
if (!srcImage.data)
{
cout << "图像打开失败!" << endl;
return -1;
}
// 将图像转换到HSV颜色空间
Mat hsvImage;
cvtColor(srcImage, hsvImage, CV_BGR2HSV);
//进行 hue 通道分离
Mat hueImage;
hueImage.create(hsvImage.size(), hsvImage.depth());
int ch[] = { 0, 0 };
mixChannels(&hsvImage, 1, &hueImage, 1, ch, 1);
//初始化直方图计算参数
int bins = 25;
MatND hist;
int histSize = MAX(bins, 2);
float hue_range[] = { 0, 100 };
const float*ranges = { hue_range };
//计算直方图并进行归一化操作
calcHist(&hueImage, 1, 0, Mat(), hist, 1,
&histSize, &ranges, true, false);
normalize(hist, hist, 0, 255, NORM_MINMAX, -1, Mat());
//计算反向投影
MatND backproj;
calcBackProject(&hueImage, 1, 0, hist, backproj, &ranges, 1, true);
//定义输出图像
int w = 320, h = 360;
int bin_w = cvRound((double)w / histSize);
Mat histImage = Mat::zeros(w, h, CV_8UC3);
for (int i = 0; i < bins; i++)
{
//绘制直方图
rectangle(histImage, Point(i*bin_w, h),
Point((i + 1)*bin_w, h - cvRound(hist.at<float>(i)*h / 255.0)),
Scalar(0, 0, 255), -1);
}
//显示原图像和反向投影图像
imshow("反向投影图", backproj);
imshow("原图像", srcImage);
imshow("直方图", histImage);
//进行直方图均衡化
equalizeHist(backproj, backproj);
imshow("直方图均衡化后的直方图", backproj);
waitKey();
return 0;
}
执行程序后,结果如下
End
by 大幕