图像直方图是图像处理中非常重要的像素统计结果,图像直方图不再表征任何的图像纹理信息,而是对图像像素的统计。由于同一物体无论是旋转还是平移在图像中都具有相同的灰度值,因此直方图具有平移不变性、放缩不变性等优点,因此可以用来查看图像整体的变化形式,例如图像是否过暗、图像像素灰度值主要集中在哪些范围等,在特定的条件下也可以利用图像直方图进行图像的识别,例如对数字的识别。

图像直方图简单来说就是统计图像中每个灰度值的个数,之后将图像灰度值作为横轴,以灰度值个数或者灰度值所占比率作为纵轴绘制的统计图。通过直方图可以看出图像中哪些灰度值数目较多,哪些较少,可以通过一定的方法将灰度值较为集中的区域映射到较为稀疏的区域,从而使得图像在像素灰度值上分布更加符合期望状态。通常情况下,像素灰度值代表亮暗程度,因此通过图像直方图可以分析图像亮暗对比度,并调整图像的亮暗程度。

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:需要统计的通道索引数组,第一个图像的通道索引从0到images[0].channels()-1,第二个图像通道索引从images[0].channels()到images[0].channels()+images[1].channels()-1,以此类推。
  • mask:可选的操作掩码,如果是空矩阵则表示图像中所有位置的像素都计入直方图中,如果矩阵不为空,则必须与输入图像尺寸相同且数据类型为CV_8U。
  • hist:输出的统计直方图结果,是一个dims维度的数组。
  • dims:需要计算直方图的维度,必须是整数,并且不能大于CV_MAX_DIMS,在OpenCV 4.0和OpenCV 4.1版本中为32。
  • histSize:存放每个维度直方图的数组的尺寸。
  • ranges:每个图像通道中灰度值的取值范围。
  • uniform:直方图是否均匀的标志符,默认状态下为均匀(true)。
  • accumulate:是否累积统计直方图的标志,如果累积(true),则统计新图像的直方图时之前图像的统计结果不会被清除,该同能主要用于统计多个图像整体的直方图。

该函数用于统计图像中每个灰度值像素的个数,例如统计一张CV_8UC1的图像,需要统计灰度值从0到255中每一个灰度值在图像中的像素个数,如果某个灰度值在图像中没有,那么该灰度值的统计结果就是0。

绘制灰度图像直方图示例:

 

//
// Created by smallflyfly on 2021/6/10.
//

#include "opencv2/opencv.hpp"
#include "opencv2/highgui.hpp"

#include <iostream>

using namespace cv;
using namespace std;

int main() {
    // circle
    Mat im = imread("test.jpg", IMREAD_GRAYSCALE);
    resize(im, im, Size(0, 0), 0.5, 0.5);

    Mat hist;
    const int channels[1] = {0};
    float inRange[2] = {0, 255};
    const float *ranges[1] = {inRange};
    const int bins[1] = {256};
    cv::calcHist(&im, 1, channels, Mat(), hist, 1, bins, ranges);
    // 绘制直方图
    int histW = 512;
    int histH = 400;
    int width = 2;
    Mat histImage = Mat::zeros(histH, histW, CV_8UC3);
    for (int i = 1; i <= hist.rows; ++i) {
        rectangle(histImage, Point(width * (i-1), histH-1),
                  Point(width * i - 1, histH - cvRound(hist.at<float>(i-1) / 5)),
                  Scalar(255, 255, 255), -1);
    }

    imshow("im", im);
    imshow("hist", histImage);

    waitKey(0);

    destroyAllWindows();

    return 0;
}