持续创作,加快成长!这是我参加「日新方案 10 月更文应战」的第20天,点击检查活动详情

前言

一张图画由若干像素组成,每个像素假如包括一个值(一个通道),则能够组成一张灰度图画;或许假如每个像素包括三个值(三个通道),则能够组成一张五颜六色图画。每个通道的取值规模为0 到 255。根据图画的内容,每个灰度值具有不同数量。

核算图画直方图

图画直方图是一种反映图画色调分布的直方图,其绘制每个色调值的像素数,每个色调值的像素数也称为频率 (frequency)。因而,灰度图画的直方图有 256 个条目(柱条或 bin)。 bin 0 表明值为 0 的像素数,bin 1 表明值为 1 的像素数,依此类推。明显,假如对直方图的一切bin 求和,能够得到图画中的像素总数。直方图也能够归一化,使得 bin 的总和等于 1,在这种情况下,每个 bin 都表明图画中具有此色调值的像素数所占百分比。

运用 cv::calcHist()函数能够方便的核算图画直方图。这是一个通用函数,能够核算任何像素值类型和规模的多通道图画的直方图。

本节中,咱们经过为单通道灰度图画的情况创立一个直方图类来使其更易于运用。关于其他类型的图画,能够直接运用 cv::calcHist()函数。

1. 首要,创立一个直方图类Histogram1D

// 创立灰度图画直方图
class Histogram1D {
private:
int histSize[1]; // 直方图中 bin 的数量
float hranges[2]; // 值的规模
const float* ranges[1]; // 指向不同值规模的指针
int channels[1]; // 通道数量
public:
Histogram1D() {
// 默许参数
histSize[0] = 256; // 256 bins
hranges[0] = 0.0; // 从 0 开端
hranges[1] = 256.0; // 到 256 完毕
ranges[0] = hranges;
channels[0] = 0; // 运用通道 0
}

2. 运用定义的成员变量,能够运用以下办法完成灰度直方图的核算,该办法在 Histogram1D 类中完成:

// 核算 1D 直方图
cv:: Mat getHistogram(const cv::Mat& image) {
cv::Mat hist;
cv::calcHist(&image,
1, // 仅运用1张图画核算直方图
channels, // 所用通道
cv::Mat(), // 不运用掩码
hist, // 直方图
1, // 1D 直方图
histSize, // bins 的数量
ranges); // 像素规模
return hist;
}

3. 翻开一个图画,创立一个 Histogram1D 实例,并调用 getHistogram 办法:

// 读取输入图画
cv::Mat image = cv::imread("1.png", 0);
Histogram1D h;
// 核算直方图
cv::Mat histo = h.getHistogram(image);

4. 这里的 histo 对象是一个简单的一维数组,有 256 个条目。因而,能够经过简单地循环遍历此数组来读取每个 bin

for (int i=0; i<256; i++) {
cout << "Value" << i << " = " << histo.at<float>(i) << endl;
}

履行以上程序,某些像素值的像素数输出如下:

...
Value114 = 11
Value115 = 8
Value116 = 9
Value117 = 13
Value118 = 14
Value119 = 16
Value120 = 21
...

明显很难从这个值序列中提取任何直观的含义。因而,将直方图进行可视化有利于直观调查图画像素值分布。

5. 编写getHistogramImage 办法来可视化直方图:

// 核算 1D 直方图并返回其图画
cv::Mat getHistogramImage(const cv::Mat& image, int zoom=1) {
cv::Mat hist = getHistogram(image);
return Histogram1D::getImageOfHistogram(hist, zoom);
}
// 不创立用于表明图画的直方图
static cv::Mat getImageOfHistogram(const cv::Mat& hist, int zoom) {
double maxVal = 0;
double minVal = 0;
cv::minMaxLoc(hist, &minVal, &maxVal, 0, 0);
int histSize = hist.rows;
// 显现直方图
cv::Mat histImg(histSize*zoom, histSize*zoom, CV_8U, cv::Scalar(255));
// 设定图画高度
int hpt = static_cast<int>(0.9*histSize);
// 绘制每个 bin
for (int h=0; h<histSize; h++) {
float binVal = hist.at<float>(h);
if (binVal>0) {
int intensity = static_cast<int>(binVal*hpt/maxVal);
cv::line(histImg, cv::Point(h*zoom, histSize*zoom),
cv::Point(h*zoom, (histSize-intensity)*zoom),
cv::Scalar(0), zoom);
}
}
return histImg;
}

6. 运用 getImageOfHistogram 办法,能够以条形图的方式获取直方图的图画:

// 显现直方图
cv::namedWindow("Histogram");
cv::imshow("Histogram", h.getHistogramImage(image));

程序履行成果如下图所示:

OpenCV计算图像直方图