1、直方图

  一幅图像由不同灰度值的像素组成,图像中灰度的分布情况是该图像的一个重要特征。图像的灰度直方图就描述了图像中灰度分布情况,能够很直观的展示出图像中各个灰度级所占的多少。
图像的灰度直方图是灰度级的函数,描述的是图像中具有该灰度级的像素的个数:其中,横坐标是灰度级,纵坐标是该灰度级出现的频率。

不过通常会将纵坐标归一化到[0,1][0,1]区间内,也就是将灰度级出现的频率(像素个数)除以图像中像素的总数。灰度直方图的计算公式如下:

其中

  • rk是像素的灰度级
  • nk是具有灰度rk的像素个数
  • MN是图像中总的像素个数

直方图的计算是很简单的,无非是遍历图像的像素,统计每个灰度级的个数。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 <vector>
#include <iostream>
#include <opencv2\core\core.hpp>
#include <opencv2\highgui\highgui.hpp>
#include <opencv2\imgproc\imgproc.hpp> 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<int>(0.9 * histSize[0]);
//每个条目绘制一条垂直线
for (int h = 0; h < histSize[0]; h++)
{
float binVal = hist.at<float>(h);
int intensity = static_cast<int>(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、直方图均衡

直方图均衡化就是对图像进行非线性拉伸,重新分配图像的灰度值,使一定范围内图像的灰度值大致相等,它是以累计分布函数变化法为基础的直方图修正法。它将当前的灰度分布通过一个变换函数,变换为范围更宽、灰度分布更均匀的图像。也就是将原图像的直方图修改为在整个灰度区间内大致均匀分布,因此扩大了图像的动态范围,增强图像的对比度。

方图均衡化算法的步骤如下所示:

  • 计算原图像的灰度直方图
  • 其中n为像素总数,nk为灰度级Sk的像素个数

  • 计算原始图像的累积直方图 
  • 实现图像直方图均衡化:

    其中Dj是目的图像的像素,CDF(Si)是源图像灰度为i的累积分布,L是图像中最大灰度级(灰度图为255)

其代码实现如下:

  • 在上面中封装了求灰度直方图的类,这里直接应用该方法得到图像的灰度直方图;
  • 将灰度直方图进行归一化,计算灰度的累积概率;
  • 创建灰度变化的查找表
  • 应用查找表,将原图像变换为灰度均衡的图像

代码实现如下所示:

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<float>(i);
else
cdf[i] = cdf[i - 1] + hist.at<float>(i); lut.at<uchar>(i) = static_cast<uchar>(255 * cdf[i]); // 创建灰度的查找表
} LUT(src, lut, dst); // 应用查找表,进行灰度变化,得到均衡化后的图像 }

  实际在OpenCV中也提供了灰度均衡化的函数equalizeHist,该函数的使用很简单,只有两个参数:输入图像,输出图像。

示例:

下面是一个直方图均衡化的示例

#include "stdafx.h"
#include <vector>
#include <iostream>
#include <opencv2\core\core.hpp>
#include <opencv2\highgui\highgui.hpp>
#include <opencv2\imgproc\imgproc.hpp> 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<int>(0.9 * histSize[0]);
//每个条目绘制一条垂直线
for (int h = 0; h < histSize[0]; h++)
{
float binVal = hist.at<float>(h);
int intensity = static_cast<int>(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<float>(i);
else
cdf[i] = cdf[i - 1] + hist.at<float>(i); lut.at<uchar>(i) = static_cast<uchar>(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。由于对目标图像和原始图像都进行了均衡化处理,因此具有相同的分布密度,即

    因而可以用原始图像均衡化以后的灰度级s代表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<float>(i);
dst_cdf[i] = dst_hist.at<float>(i);
}
else
{
src_cdf[i] = src_cdf[i - 1] + src_hist.at<float>(i);
dst_cdf[i] = dst_cdf[i - 1] + dst_hist.at<float>(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<uchar>(i) = static_cast<uchar>(index);
} // 应用查找表,做直方图规定化
LUT(src, lut, result);
}

示例:

#include "stdafx.h"
#include <vector>
#include <iostream>
#include <opencv2\core\core.hpp>
#include <opencv2\highgui\highgui.hpp>
#include <opencv2\imgproc\imgproc.hpp> 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<int>(0.9 * histSize[0]);
//每个条目绘制一条垂直线
for (int h = 0; h < histSize[0]; h++)
{
float binVal = hist.at<float>(h);
int intensity = static_cast<int>(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<float>(i);
else
cdf[i] = cdf[i - 1] + hist.at<float>(i); lut.at<uchar>(i) = static_cast<uchar>(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<float>(i);
dst_cdf[i] = dst_hist.at<float>(i);
}
else
{
src_cdf[i] = src_cdf[i - 1] + src_hist.at<float>(i);
dst_cdf[i] = dst_cdf[i - 1] + dst_hist.at<float>(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<uchar>(i) = static_cast<uchar>(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 <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("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<float>(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<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中的索引对

示例:

//实现直方图的反向投影
#include "stdafx.h"
#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("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<float>(i)*h / 255.0)),
Scalar(0, 0, 255), -1);
} //显示原图像和反向投影图像
imshow("反向投影图", backproj);
imshow("原图像", srcImage);
imshow("直方图", histImage);
//进行直方图均衡化
equalizeHist(backproj, backproj);
imshow("直方图均衡化后的直方图", backproj);
waitKey();
return 0;
}

程序运行结果如下所示:

参考资料:

图像处理基础(8):图像的灰度直方图、直方图均衡化、直方图规定化(匹配)

【OpenCV图像处理】十、图像的直方图及相关处理(上)

【OpenCV图像处理】十一、图像的直方图与相关处理(中)

【OpenCV图像处理】十二、图像的直方图与相关处理(下)

OpenCV-跟我一起学数字图像处理之直方图均衡化

OpenCV直方图(直方图、直方图均衡,直方图匹配,原理、实现)

12、OpenCV实现图像的直方图处理的更多相关文章

  1. OpenCV 学习(计算图像的直方图)

    OpenCV 计算图像的直方图 计算图像的直方图是图像处理领域一个非经常见的基本操作. OpenCV 中提供了 calcHist 函数来计算图像直方图.只是这个函数说实话挺难用的,研究了好久才掌握了些 ...

  2. OpenCV(7)-图像直方图

    直方图定义可参考这里.图像的直方图用来表示图像像素的统计信息,它统计了图像每一个通道(如果是多通道)中,每个像素的个数(比例). 计算直方图 OpenCV提供了直接计算直方图的函数 void calc ...

  3. OpenCV绘制图像中RGB三个通道的直方图

    一开始是看<OpenCV计算机视觉编程攻略(第2版)>这本书学做直方图,但是书本里说直方图的部分只详细说了黑白图像(单通道)的直方图绘制方法,RGB图像的直方图只说了如何计算,没有说计算完 ...

  4. 【图像处理】基于OpenCV实现图像直方图的原理

    背景 图像的直方图是衡量图像像素分布的一种方式,可以通过分析像素分布,使用直方图均衡化对图像进行优化,让图像变的清晰. opencv官方对图像直方图的定义如下: 直方图是图像中像素强度分布的图形表达方 ...

  5. 11、OpenCV实现图像的灰度变换

    1.灰度变换的基本概念 灰度变换指对图像的单个像素进行操作,主要以对比度和阈值处理为目的.其变换形式如下: s=T(r) 其中,T是灰度变换函数:r是变换前的灰度:s是变换后的像素.图像灰度变换的有以 ...

  6. OpenCV2+入门系列(四):计算图像的直方图,平均灰度,灰度方差

    本篇懒得排版,直接在网页html编辑器编辑 在图像处理时,我们常常需要求出图像的直方图.灰度平均值.灰度的方差,这里给出一个opencv2+自带程序,实现这些功能. 直方图 对于直方图,使用cv::c ...

  7. Java基于opencv实现图像数字识别(五)—投影法分割字符

    Java基于opencv实现图像数字识别(五)-投影法分割字符 水平投影法 1.水平投影法就是先用一个数组统计出图像每行黑色像素点的个数(二值化的图像): 2.选出一个最优的阀值,根据比这个阀值大或小 ...

  8. Java基于opencv实现图像数字识别(四)—图像降噪

    Java基于opencv实现图像数字识别(四)-图像降噪 我们每一步的工作都是基于前一步的,我们先把我们前面的几个函数封装成一个工具类,以后我们所有的函数都基于这个工具类 这个工具类呢,就一个成员变量 ...

  9. Java基于opencv实现图像数字识别(三)—灰度化和二值化

    Java基于opencv实现图像数字识别(三)-灰度化和二值化 一.灰度化 灰度化:在RGB模型中,如果R=G=B时,则彩色表示灰度颜色,其中R=G=B的值叫灰度值:因此,灰度图像每个像素点只需一个字 ...

随机推荐

  1. 解决VS2010自带的C/C++编译器CL找不到mspdb100.dll的问题

    https://www.cnblogs.com/dudu/archive/2011/05/21/2053104.html 更好解决方法是在命令行中运行vsvars32.bat: "C:\Pr ...

  2. Linux安装php yaml扩展

    1.首先得安装libyamlgit clone https://github.com/yaml/libyaml./bootstrap ./configure make make install 2.安 ...

  3. 原创:C++实现的可排序的双向链表

    学习C++有一周了,今天用C++设计了一个双向链表,这个链表有排序功能,默认按升序排列,接受的参数可以是数字,也可以是字符串.现在把自己写的代码,分享出来.如果链表中接受的对象为Lexeme,可以用于 ...

  4. SpringData like关键字不起作用

    使用springdata简单查询时,like关键字不起作用 Hibernate: select article0_.oId as oId1_2_, article0_.articleAbstract ...

  5. ubuntu安装mysql自动输入密码随笔记录

    sudo debconf-set-selections <<< 'mysql-server mysql-server/root_password password your_pass ...

  6. OpenCV reshape The Matrix is not continuous, thus its number of rows can not be changed

    When using OpenCV  reshape and gets this error: OpenCV Error: Image step is wrong (The matrix is not ...

  7. K8S中POD节点状态ContainerCreating原因排查

    现象: # kubectl get pods -n kube-system |grep dashboard kubernetes-dashboard-6685cb584f-dqkwk 0/1 Cont ...

  8. 使用IDEA运行CAS5.3服务器

    在上节中,我们运行CAS服务器是打成war包在tomcat中进行运行,这节介绍在IDEA中运行CAS服务器. 1.下载CAS 模板 Overlay Template,我这里使用 Apereo CAS ...

  9. 微信小程序的z-index在苹果ios无效

    1.在微信开发者工具可以正常显示 2.在安卓真机手机可以正常显示 3.在ios手机真机无法正常显示 原因:父级view的css属性有 position: fixed; ,把它注释掉即可

  10. ubuntu上安装python的ldap模块

    首先安装 apt-get install libldap2-dev 然后再安装 apt-get install libsasl2-dev 然后就可以继续安装你的python-ldap模块了 pip i ...