1、直方图
一幅图像由不同灰度值的像素组成,图像中灰度的分布情况是该图像的一个重要特征。图像的灰度直方图就描述了图像中灰度分布情况,能够很直观的展示出图像中各个灰度级所占的多少。
图像的灰度直方图是灰度级的函数,描述的是图像中具有该灰度级的像素的个数:其中,横坐标是灰度级,纵坐标是该灰度级出现的频率。
不过通常会将纵坐标归一化到[0,1][0,1]区间内,也就是将灰度级出现的频率(像素个数)除以图像中像素的总数。灰度直方图的计算公式如下:
直方图的计算是很简单的,无非是遍历图像的像素,统计每个灰度级的个数。OpenCV提供了一个可靠的直方图函数calcHist,其声明如下
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,输入图像的数组,这些图像要有相同大大小,相同的深度(CV_8U CV_16U CV_32F).
nimages ,输入图像的个数
channels,要计算直方图的通道个数。
mask,可选的掩码,不使用时可设为空。要和输入图像具有相同的大小,在进行直方图计算的时候,只会统计该掩码不为0的对应像素
hist,输出的直方图
dims,直方图的维度
histSize,直方图每个维度的大小
ranges,直方图每个维度要统计的灰度级的范围
uniform,是否进行归一化,默认为true
accumulate,累积标志,默认值为false。
示例:
下面是一个使用caclHist的一个示例,这里对其进行了一个封装,只绘制灰度直方图
#include "stdafx.h" #include#include #include #include #include using namespace cv; using namespace std; class Histogram1D { private: int histSize[1]; // 项的数量 float hranges[2]; // 统计像素的最大值和最小值 const float* ranges[1]; int channels[1]; // 仅计算一个通道 public: Histogram1D() { // 准备1D直方图的参数 histSize[0] = 256; hranges[0] = 0.0f; hranges[1] = 255.0f; ranges[0] = hranges; channels[0] = 0; } MatND getHistogram(const Mat &image) { MatND hist; // 计算直方图 calcHist(&image,// 要计算图像的 1, // 只计算一幅图像的直方图 channels, // 通道数量 Mat(), // 不使用掩码 hist, // 存放直方图 1, // 1D直方图 histSize, // 统计的灰度的个数 ranges); // 灰度值的范围 return hist; } Mat getHistogramImage(const Mat &image) { MatND hist = getHistogram(image); // 最大值,最小值 double maxVal = 0.0f; double minVal = 0.0f; minMaxLoc(hist, &minVal, &maxVal); //显示直方图的图像 Mat histImg(histSize[0], histSize[0], CV_8U, Scalar(255)); // 设置最高点为nbins的90% int hpt = static_cast (0.9 * histSize[0]); //每个条目绘制一条垂直线 for (int h = 0; h < histSize[0]; h++) { float binVal = hist.at (h); int intensity = static_cast (binVal * hpt / maxVal); // 两点之间绘制一条直线 line(histImg, Point(h, histSize[0]), Point(h, histSize[0] - intensity), Scalar::all(0)); } return histImg; } }; int main() { //图像的获取 Mat srcImage = imread("111.jpg"); if (!srcImage.data) { cout << "读入图片失败!" << endl; return -1; } Histogram1D hist; Mat histImg; histImg = hist.getHistogramImage(srcImage); imshow("Image", srcImage); imshow("Histogram", histImg); waitKey(); return 0; }
运行结果如下:
假如图像的灰度分布不均匀,其灰度分布集中在较窄的范围内,使图像的细节不够清晰,对比度较低。通常采用直方图均衡化及直方图规定化两种变换,使图像的灰度范围拉开或使灰度均匀分布,从而增大反差,使图像细节清晰,以达到增强的目的。
2、直方图均衡
直方图均衡化就是对图像进行非线性拉伸,重新分配图像的灰度值,使一定范围内图像的灰度值大致相等,它是以累计分布函数变化法为基础的直方图修正法。它将当前的灰度分布通过一个变换函数,变换为范围更宽、灰度分布更均匀的图像。也就是将原图像的直方图修改为在整个灰度区间内大致均匀分布,因此扩大了图像的动态范围,增强图像的对比度。
方图均衡化算法的步骤如下所示:
- 计算原图像的灰度直方图
-
- 计算原始图像的累积直方图
- 实现图像直方图均衡化:
其代码实现如下:
- 在上面中封装了求灰度直方图的类,这里直接应用该方法得到图像的灰度直方图;
- 将灰度直方图进行归一化,计算灰度的累积概率;
- 创建灰度变化的查找表
- 应用查找表,将原图像变换为灰度均衡的图像
代码实现如下所示:
void equalization_self(const Mat &src, Mat &dst) { Histogram1D hist1D; MatND hist = hist1D.getHistogram(src); hist /= (src.rows * src.cols); // 对得到的灰度直方图进行归一化 float cdf[256] = { 0 }; // 灰度的累积概率 Mat lut(1, 256, CV_8U); // 灰度变换的查找表 for (int i = 0; i < 256; i++) { // 计算灰度级的累积概率 if (i == 0) cdf[i] = hist.at(i); else cdf[i] = cdf[i - 1] + hist.at (i); lut.at (i) = static_cast (255 * cdf[i]); // 创建灰度的查找表 } LUT(src, lut, dst); // 应用查找表,进行灰度变化,得到均衡化后的图像 }
实际在OpenCV中也提供了灰度均衡化的函数equalizeHist
,该函数的使用很简单,只有两个参数:输入图像,输出图像。
示例:
下面是一个直方图均衡化的示例
#include "stdafx.h" #include#include #include #include #include using namespace cv; using namespace std; class Histogram1D { private: int histSize[1]; // 项的数量 float hranges[2]; // 统计像素的最大值和最小值 const float* ranges[1]; int channels[1]; // 仅计算一个通道 public: Histogram1D() { // 准备1D直方图的参数 histSize[0] = 256; hranges[0] = 0.0f; hranges[1] = 255.0f; ranges[0] = hranges; channels[0] = 0; } MatND getHistogram(const Mat &image) { MatND hist; // 计算直方图 calcHist(&image,// 要计算图像的 1, // 只计算一幅图像的直方图 channels, // 通道数量 Mat(), // 不使用掩码 hist, // 存放直方图 1, // 1D直方图 histSize, // 统计的灰度的个数 ranges); // 灰度值的范围 return hist; } Mat getHistogramImage(const Mat &image) { MatND hist = getHistogram(image); // 最大值,最小值 double maxVal = 0.0f; double minVal = 0.0f; minMaxLoc(hist, &minVal, &maxVal); //显示直方图的图像 Mat histImg(histSize[0], histSize[0], CV_8U, Scalar(255)); // 设置最高点为nbins的90% int hpt = static_cast (0.9 * histSize[0]); //每个条目绘制一条垂直线 for (int h = 0; h < histSize[0]; h++) { float binVal = hist.at (h); int intensity = static_cast (binVal * hpt / maxVal); // 两点之间绘制一条直线 line(histImg, Point(h, histSize[0]), Point(h, histSize[0] - intensity), Scalar::all(0)); } return histImg; } }; void equalization_self(const Mat &src, Mat &dst) { Histogram1D hist1D; MatND hist = hist1D.getHistogram(src); hist /= (src.rows * src.cols); // 对得到的灰度直方图进行归一化 float cdf[256] = { 0 }; // 灰度的累积概率 Mat lut(1, 256, CV_8U); // 灰度变换的查找表 for (int i = 0; i < 256; i++) { // 计算灰度级的累积概率 if (i == 0) cdf[i] = hist.at (i); else cdf[i] = cdf[i - 1] + hist.at (i); lut.at (i) = static_cast (255 * cdf[i]); // 创建灰度的查找表 } LUT(src, lut, dst); // 应用查找表,进行灰度变化,得到均衡化后的图像 } int main() { //图像的获取 Mat srcImage = imread("111.jpg"); Mat srcgray; cvtColor(srcImage, srcgray, CV_BGR2GRAY); if (!srcImage.data) { cout << "读入图片失败!" << endl; return -1; } Histogram1D hist; Mat histImg; histImg = hist.getHistogramImage(srcImage); //直方图均衡化 Mat equalize, equalizeImg, equalize1, equalizeImg1, srcgrayhist; //函数均衡 equalization_self(srcImage, equalize); equalizeImg= hist.getHistogramImage(equalize); //OpenCV自带函数均衡 srcgrayhist = hist.getHistogramImage(srcgray); equalizeHist(srcgray, equalize1); equalizeImg1 = hist.getHistogramImage(equalize1); imshow("Image", srcImage); imshow("Histogram", histImg); imshow("equalizeImg", equalizeImg); imshow("equalize", equalize); imshow("srcgray", srcgray); imshow("srcgrayhist", srcgrayhist); imshow("equalizeImg1", equalizeImg1); imshow("equalize1", equalize1); waitKey(); return 0; }
程序运行结果如下,左边两张图片是原图及我们写的函数的效果,右边两张图是灰度图及OpenCV自带函数的效果。
需要注意的是,OpenCV自带的函数只能对单通道进行直方图均衡化,如果对原图进行均衡化,由于原图是三通道的,此时会报错。但是这并不意味着无法用equalizeHist对彩色图像进行处理,详情请看参考资料OpenCV直方图(直方图、直方图均衡,直方图匹配,原理、实现)
3、直方图匹配(直方图规定化)
直方图均衡化可以自动的确定变换函数,该函数寻求产生又均匀直方图的输出图像,需要自动增强时,这是一种好方法,因为这种方法得到的结果可以预知,并且这种方法的实现也很简单,但是在有些应用中这种自动的增强并不是最好的方法。有时候,需要图像具有
某一特定的直方图形状(也就是灰度分布),而不是均匀分布的直方图,这时候可以使用直方图规定化。
直方图规定化,也叫做直方图匹配,用于将图像变换为某一特定的灰度分布,也就是其目的的灰度直方图是已知的。这其实和均衡化很类似,均衡化后的灰度直方图也是已知的,是一个均匀分布的直方图;而规定化后的直方图可以随意的指定,也就是在执行规定化操
作时,首先要知道变换后的灰度直方图,这样才能确定变换函数。规定化操作能够有目的的增强某个灰度区间,相比于均衡化操作,规定化多了一个输入,但是其变换后的结果也更灵活。
直方图的规定化也较为简单。可以利用均衡化后的直方图作为一个中间过程,然后求取规定化的变换函数。具体步骤如下:
- 将原始图像的灰度直方图进行均衡化,得到一个变换函数
其中s是均衡化后的像素,r是原始像素 - 对规定的直方图进行均衡化,得到一个变换函数 其中v是均衡化后的像素,z是规定化的像素
-
上式的逆变换为可通过均衡化后的灰度级v求出目标函数的灰度级z。由于对目标图像和原始图像都进行了均衡化处理,因此具有相同的分布密度,即
直方图规定化在原理上很简单,在实践中,常见的困难是寻找T(r)和G-1的有意义表达式。幸运的是,在处理离散量时,问题可以大大得到简化。
其中MN是图像的像素总数,nj是具有灰度值rj的像素数,L是图像中可能的灰度级数。
类似的,给出个规定的Sk值
对一个q值,有
则我们用反变换找到期望的值
即对每一个S值给出一个Z值,这样就实现了从S到Z的一个映射,即在实践中,我们不需要计算G的反变换。
即直方图的规定化过程如下:
直方图规定化的实现
直方图规定化的实现可以分为一下几步:
- 计算原图像的累积直方图
- 计算规定直方图的累积直方图
- 计算两累积直方图的差值的绝对值
- 根据累积直方图差值建立灰度级的映射
void hist_specify(const Mat &src, const Mat &dst,Mat &result) { Histogram1D hist1D; MatND src_hist = hist1D.getHistogram(src); MatND dst_hist = hist1D.getHistogram(dst); float src_cdf[256] = { 0 }; float dst_cdf[256] = { 0 }; // 源图像和目标图像的大小不一样,要将得到的直方图进行归一化处理 src_hist /= (src.rows * src.cols); dst_hist /= (dst.rows * dst.cols); // 计算原始直方图和规定直方图的累积概率 for (int i = 0; i < 256; i++) { if (i == 0) { src_cdf[i] = src_hist.at(i); dst_cdf[i] = dst_hist.at (i); } else { src_cdf[i] = src_cdf[i - 1] + src_hist.at (i); dst_cdf[i] = dst_cdf[i - 1] + dst_hist.at (i); } } // 累积概率的差值 float diff_cdf[256][256]; for (int i = 0; i < 256; i++) for (int j = 0; j < 256; j++) diff_cdf[i][j] = fabs(src_cdf[i] - dst_cdf[j]); // 构建灰度级映射表 Mat lut(1, 256, CV_8U); for (int i = 0; i < 256; i++) { // 查找源灰度级为i的映射灰度 // 和i的累积概率差值最小的规定化灰度 float min = diff_cdf[i][0]; int index = 0; for (int j = 1; j < 256; j++) { if (min > diff_cdf[i][j]) { min = diff_cdf[i][j]; index = j; } } lut.at (i) = static_cast (index); } // 应用查找表,做直方图规定化 LUT(src, lut, result); }
示例:
#include "stdafx.h" #include#include #include #include #include using namespace cv; using namespace std; class Histogram1D { private: int histSize[1]; // 项的数量 float hranges[2]; // 统计像素的最大值和最小值 const float* ranges[1]; int channels[1]; // 仅计算一个通道 public: Histogram1D() { // 准备1D直方图的参数 histSize[0] = 256; hranges[0] = 0.0f; hranges[1] = 255.0f; ranges[0] = hranges; channels[0] = 0; } MatND getHistogram(const Mat &image) { MatND hist; // 计算直方图 calcHist(&image,// 要计算图像的 1, // 只计算一幅图像的直方图 channels, // 通道数量 Mat(), // 不使用掩码 hist, // 存放直方图 1, // 1D直方图 histSize, // 统计的灰度的个数 ranges); // 灰度值的范围 return hist; } Mat getHistogramImage(const Mat &image) { MatND hist = getHistogram(image); // 最大值,最小值 double maxVal = 0.0f; double minVal = 0.0f; minMaxLoc(hist, &minVal, &maxVal); //显示直方图的图像 Mat histImg(histSize[0], histSize[0], CV_8U, Scalar(255)); // 设置最高点为nbins的90% int hpt = static_cast (0.9 * histSize[0]); //每个条目绘制一条垂直线 for (int h = 0; h < histSize[0]; h++) { float binVal = hist.at (h); int intensity = static_cast (binVal * hpt / maxVal); // 两点之间绘制一条直线 line(histImg, Point(h, histSize[0]), Point(h, histSize[0] - intensity), Scalar::all(0)); } return histImg; } }; void equalization_self(const Mat &src, Mat &dst) { Histogram1D hist1D; MatND hist = hist1D.getHistogram(src); hist /= (src.rows * src.cols); // 对得到的灰度直方图进行归一化 float cdf[256] = { 0 }; // 灰度的累积概率 Mat lut(1, 256, CV_8U); // 灰度变换的查找表 for (int i = 0; i < 256; i++) { // 计算灰度级的累积概率 if (i == 0) cdf[i] = hist.at (i); else cdf[i] = cdf[i - 1] + hist.at (i); lut.at (i) = static_cast (255 * cdf[i]); // 创建灰度的查找表 } LUT(src, lut, dst); // 应用查找表,进行灰度变化,得到均衡化后的图像 } void hist_specify(const Mat &src, const Mat &dst, Mat &result) { Histogram1D hist1D; MatND src_hist = hist1D.getHistogram(src); MatND dst_hist = hist1D.getHistogram(dst); float src_cdf[256] = { 0 }; float dst_cdf[256] = { 0 }; // 源图像和目标图像的大小不一样,要将得到的直方图进行归一化处理 src_hist /= (src.rows * src.cols); dst_hist /= (dst.rows * dst.cols); // 计算原始直方图和规定直方图的累积概率 for (int i = 0; i < 256; i++) { if (i == 0) { src_cdf[i] = src_hist.at (i); dst_cdf[i] = dst_hist.at (i); } else { src_cdf[i] = src_cdf[i - 1] + src_hist.at (i); dst_cdf[i] = dst_cdf[i - 1] + dst_hist.at (i); } } // 累积概率的差值 float diff_cdf[256][256]; for (int i = 0; i < 256; i++) for (int j = 0; j < 256; j++) diff_cdf[i][j] = fabs(src_cdf[i] - dst_cdf[j]); // 构建灰度级映射表 Mat lut(1, 256, CV_8U); for (int i = 0; i < 256; i++) { // 查找源灰度级为i的映射灰度 // 和i的累积概率差值最小的规定化灰度 float min = diff_cdf[i][0]; int index = 0; for (int j = 1; j < 256; j++) { if (min > diff_cdf[i][j]) { min = diff_cdf[i][j]; index = j; } } lut.at (i) = static_cast (index); } // 应用查找表,做直方图规定化 LUT(src, lut, result); } int main() { //图像的获取 Mat srcImage = imread("111.jpg"); Mat srcImage1 = imread("222.jpg"); Mat srcgray, srcgray1; cvtColor(srcImage, srcgray, CV_BGR2GRAY); cvtColor(srcImage1, srcgray1, CV_BGR2GRAY); Histogram1D hist; Mat histImg, histImg1, histImg_result; histImg = hist.getHistogramImage(srcImage); histImg1 = hist.getHistogramImage(srcImage1); //直方图规定化 Mat result, equalizeImg, equalize1, equalizeImg1; hist_specify(srcgray, srcgray1, result); histImg_result = hist.getHistogramImage(result); imshow("Image", srcImage); imshow("Histogram", histImg); imshow("Histogram1", histImg1); imshow("histImg_result", histImg_result); imshow("srcgray", srcgray); imshow("srcgray1", srcgray1); imshow("result", result); waitKey(); return 0; }
4、H-S直方图绘制
图像的表示方法除了我们通常使用的RGB方法外,还可以通过HSV空间来进行表示,HSL和HSV都是一种将RGB色彩模型中的点在圆柱坐标系中的表示法。这两种表示法试图做到比RGB基于笛卡尔坐标系的几何结构更加直观。HSV即色相、饱和度、明度(英语:Hue, Saturation, Value),又称HSB,其中B即英语:Brightness。因此在分析图像的H-S直方图时,需要先将源RGB的图像转换到HSV颜色空间内,然后再将对应的H和S通道进行单元划分,再其二维空间上计算对应的直方图,最后再次通过计算直方图空间上的最大值,归一化绘制相应的直方图信息。
H-S直方图通常是应用在目标检测、特征分析以及目标特征跟踪等场景中。其主要关注点为图像中的位置信息。
示例:
//实现直方图的反向投影 #include "stdafx.h" #include#include #include #include using namespace cv; using namespace std; int main() { Mat srcImage = imread("111.jpg"); if (!srcImage.data) { cout << "读入图片失败!" << endl; return -1; } Mat hsvImage; //将图像转换到HSV空间 cvtColor(srcImage, hsvImage, CV_BGR2HSV); //初始化灰度阶参数 int hbins = 30, sbins = 32; int histSize[] = { hbins, sbins }; //灰度变化范围设置 float hranges[] = { 0, 180 }; //饱和度变化范围 float sranges[] = { 0, 256 }; const float *ranges[] = { hranges, sranges }; MatND hist; //选取计算直方图通道 int channels[] = { 0, 1 }; //计算当前通道直方图 calcHist(&hsvImage, 1, channels, Mat(), hist, 2, histSize, ranges, true, false); double maxValue; //找到直方图的最大值 minMaxLoc(hist, 0, &maxValue, 0, 0); int scale = 10; Mat histImage = Mat::zeros(sbins*scale, hbins * 10, CV_8UC3); //遍历H和S通道 for (int h = 0; h < hbins; h++) { for (int s = 0; s < sbins; s++) { float binVal = hist.at (h, s); //根据最大值计算变换范围 int intensity = cvRound(binVal * 255 / maxValue); //进行绘图 rectangle(histImage, Point(h*scale, s*scale), Point((h + 1)*scale - 1, (s + 1)*scale - 1), Scalar::all(intensity), CV_FILLED); } } imshow("原图像", srcImage); imshow("H-S直方图", histImage); waitKey(); return 0; }
5、直方图的反向投影
如果一幅图像的区域中显示的是一种纹理结构或者一个独特的物体,那么这个区域的直方图可以看作是一个概率函数,其表现形式是某个像素属于该纹理或物体的概率。直方图的反向投影是利用直方图模型计算给定的像素点的特征。反向投影在某一位置的值是源图像在对应位置的像素值的累计。简单的讲就是首先计算某一特征的直方图模型,然后使用模型去寻找图像中存在的该特征的方法。主要用于在输入图像中查找与特定图像最匹配的点或者区域,即定位模板图像出现在图像中的位置。
为了方便地计算直方图的反向投影,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& src, vector & dst, const int* fromTo, size_t npairs); void mixChannels(InputArrayOfArrays src, InputArrayOfArrays dst,const vector & fromTo);
此函数为重排图像通道提供了比较先进的机制,这里函数实现的功能是通道复制到特定的通道
参数src表示的是输入图像源组,被复制通道的输入图像数据
参数nsrc指的是待输入图像源中图像的图像的个数
参数dst表示的是输出目标图像数组,存储复制后的通道,所有的数组必须要事先进行分配,而且要和输入数组的大小和深度相同。
参数ndsts指的是目标数组中图像的总数
参数fromTo指的是通道索引对的数组,表示的是将输入图像数组复制到目标数组通道,如果是偶数表示输入矩阵索引,如果是奇数表示的是输出矩阵索引,如果是偶数而且其下标为负,则相应输出矩阵为0
参数npairs表示fromTo中的索引对
示例:
//实现直方图的反向投影 #include "stdafx.h" #include#include #include #include using namespace cv; using namespace std; int main() { Mat srcImage = imread("111.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 (i)*h / 255.0)), Scalar(0, 0, 255), -1); } //显示原图像和反向投影图像 imshow("反向投影图", backproj); imshow("原图像", srcImage); imshow("直方图", histImage); //进行直方图均衡化 equalizeHist(backproj, backproj); imshow("直方图均衡化后的直方图", backproj); waitKey(); return 0; }
程序运行结果如下所示: