先看一副官方图片:
假设有一副图片,以及像素值(上图左)。把像素值分为不同的等级,每个等级称为bin。如图把直方图分为16个等级。每个等级包含不同的像素值范围(像素值最大255)。然后统计每个bin中像素出现的频率。
一副灰度图像的像素范围按照上面的划分如下:
[ 0 ~ 255]=[0 ~ 15] ⋃ \bigcup ⋃ [16 ~ 31] ⋃ \bigcup ⋃ [ 32 ~ 47] ⋃ \bigcup ⋃ … … ⋃ \bigcup ⋃ [240 ~ 255]
r a n g e = b i n 1 ⋃ b i n 2 ⋃ b i n 3 . . . . . . ⋃ b i n 16 range=bin_1\bigcup bin_2\bigcup bin_3 ... ...\bigcup bin_{16} range=bin1⋃bin2⋃bin3......⋃bin16
Cv2.Split() 将多通道阵列的每个平面复制到专用阵列 (如:分割图像(把三通道分割为3个单通道))
返回值为:Mat[] (数组的数量必须与mtx.channels()匹配。如果需要,将重新分配数组本身)
参数 | 描述 |
---|---|
Mat src | The source multi-channel array(源多通道阵列) |
Cv2.CalcHist(): 计算一组图像的联合密集直方图。有一个重载方法
方法1:
参数 | 描述 |
---|---|
Mat[] images | 输入单通道图像集合 |
int[] channels | 图像在集合中对应的通道 : 0 ~ 2 之间 (0,1,2) |
InputArray mask | 掩膜 输入的InputArray 对象,可以为空,或者输入对应 images[i],要与通道对应 |
OutputArray hist | 输出计算后的直方图 OutputArray对象。 |
int dims | 维度 ,单通道为 1 |
int[] histSize | 直方图的级数,就是把像素分为几个等级。像素大小在 0 ~ 255之间,如果全部输出就是 256个等级。 |
float[][] ranges | 值域的范围。 |
bool uniform = true | 是否规范化。(每个bins大小取值等级一致,默认为true) |
bool accumulate = false | 是否累加(多通道的需要,默认值为 false) |
方法2:
参数 | 描述 |
---|---|
Mat[] images | 输入单通道图像集合 |
int[] channels | 图像在集合中对应的通道 : 0 ~ 2 之间 (0,1,2) |
InputArray mask | 掩膜对象 输入的InputArray 对象,可以为空,或者输入对应 images[i],要与通道对应 |
OutputArray hist | 输出计算后的直方图 OutputArray对象。 |
int dims | 维度 ,单通道为 1 |
int[] histSize | 直方图的级数,就是把像素分为几个等级。像素大小在 0 ~ 255之间,如果全部输出就是 256个等级。 |
Rangef[] ranges | 值域的范围。 |
bool uniform = true | 是否规范化。(每个bins大小取值等级一致,默认为true) |
bool accumulate = false | 是否累加(多通道的需要,默认值为 false) |
///
/// 直方图计算
///
///
private static void HistogramCalculation(string path)
{
using (Mat src = new Mat(path, ImreadModes.AnyColor | ImreadModes.AnyDepth))
using (Mat histogram = new Mat())
{
//计算直方图
Mat[] mats = Cv2.Split(src); //分割图像(把三通道分割为3个单通道)
Mat hist_B = new Mat();
Mat hist_G = new Mat();
Mat hist_R = new Mat();
int[] channels0 = { 0 };
int[] channels1 = { 1 };
int[] channels2 = { 2 };
int[] histSize = { 256 };
Rangef[] rangefs = new Rangef[]
{
new Rangef(0, 256),
};
//
// 摘要:
// computes the joint dense histogram for a set of images.
// 计算一组图像的联合密集直方图。
// 参数:
// images: 输入单通道图像集合,
//
// channels: 图像在集合中对应的通道 : 0 ~ 2 之间
//
// mask: 输入的InputArray 对象,可以为空,或者输入对应 images[i],要与通道对应
//
// hist: 输出计算后的直方图 OutputArray对象。
//
// dims: 维度 ,单通道为 1
//
// histSize: 直方图的级数,就是把像素分为几个等级。像素大小在 0 ~ 255之间,如果全部输出就是 256个等级。
//
// ranges: 值域的范围。
//
// uniform: 布尔值
//
// accumulate: 布尔值
Cv2.CalcHist(mats, channels0, new Mat(), hist_B, 1, histSize, rangefs, true, false);
Cv2.CalcHist(mats, channels1, new Mat(), hist_G, 1, histSize, rangefs, true, false);
Cv2.CalcHist(mats, channels2, new Mat(), hist_R, 1, histSize, rangefs, true, false);
int high = 400;
int width = 512;
int bin_w = width / 256;//每个bins的宽度 画布的宽度除以bins的个数
Mat histImage = new Mat(width, high, MatType.CV_8UC3, new Scalar(0, 0, 0)); //定义一个Mat对象,相当于一个画布
//归一化,像素值有可能数据量很大,压缩一下。是范围在定义画布的范围内。
//
// 摘要:
// scales and shifts array elements so that either the specified norm (alpha) or
// the minimum (alpha) and maximum (beta) array values get the specified values
// 缩放和移动数组元素,使指定的范数(alpha)或最小(alpha)和最大(beta)数组值得到指定的值
// 参数:
// src:
// The source array 源数据
//
// dst:
// The destination array; will have the same size as src 目标数组;会和src一样大
//
// alpha:
// The norm value to normalize to or the lower range boundary in the case of range
// normalization
// 在范围的情况下,要规格化到或较低范围边界的范数值归一化
// beta:
// The upper range boundary in the case of range normalization; not used for norm
// normalization
// 距离归一化情况下的上范围边界;不用于规范标准化
// normType:
// The normalization type 归一化类型
//
// dtype:
// When the parameter is negative, the destination array will have the same type
// as src, otherwise it will have the same number of channels as src and the depth
// =CV_MAT_DEPTH(rtype)
// 当参数为负时,目标数组将具有与src相同的类型,否则它将具有与src相同的通道数量,并且depth =CV_MAT_DEPTH(rtype)
// mask:
// The optional operation mask 可选操作掩码
Cv2.Normalize(hist_B, hist_B, 0, histImage.Rows, NormTypes.MinMax, -1, null);
Cv2.Normalize(hist_G, hist_G, 0, histImage.Rows, NormTypes.MinMax, -1, null);
Cv2.Normalize(hist_R, hist_R, 0, histImage.Rows, NormTypes.MinMax, -1, null);
//绘制直方图
for (int i = 1; i < 256; i++)//遍历直方图的级数
{
//B 画线,一条线有两个点组成。首先确定每个点的坐标(x,y) .遍历从1开始。0 ~ 1 两个点组成一条线,依次类推。
Cv2.Line(histImage, new Point(bin_w * (i - 1), high - Math.Round(hist_B.At(i - 1))), new Point(bin_w * (i - 1), high - Math.Round(hist_B.At(i))), new Scalar(255, 0, 0), 1, LineTypes.AntiAlias);
//G
Cv2.Line(histImage, new Point(bin_w * (i - 1), high - Math.Round(hist_G.At(i - 1))), new Point(bin_w * (i - 1), high - Math.Round(hist_G.At(i))), new Scalar(0, 255, 0), 1, LineTypes.AntiAlias);
//R
Cv2.Line(histImage, new Point(bin_w * (i - 1), high - Math.Round(hist_R.At(i - 1))), new Point(bin_w * (i - 1), high - Math.Round(hist_R.At(i))), new Scalar(0, 0, 255), 1, LineTypes.AntiAlias);
}
using (new Window("SRC", WindowMode.Normal, src))
using (new Window("histImage", WindowMode.Normal, histImage))
{
Cv2.WaitKey(0);
}
}
}