OpenCV 计算图像的直方图

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

calcHist 函数 C++ 的函数原型例如以下:

void calcHist(const Mat* images, 
    int nimages, 
    const int* channels, 
    InputArray mask, 
    SparseMat& hist, 
    int dims, 
    const int* histSize, 
    const float** ranges, 
    bool uniform=true, 
    bool accumulate=false )

各个參数的含义例如以下:

  • images:输入的图像的指针。能够是多幅图像。可是全部的图像必须有相同的深度(CV_8U or CV_32F)。一副图像能够有多个 channels。

  • nimages:输入的图像的个数。

  • channels:用来计算直方图的 channels 的数组。

  • mask:掩码。假设mask不为空,那么它必须是一个8位(CV_8U)的数组,并且它的大小的和输入的图像的大小相同,值非 0 的点将用来计算直方图。

  • hist:输出參数,计算出来的直方图。

  • dims:直方图的维数,不能大于 CV_MAX_DIMS (在现有版本号中是 32)。

  • histSize: 在每一维上直方图的元素个数。

  • ranges: 所需计算直方图的每一维的范围。

    ranges 是个指向数组的数组。

    我们称它指向的那些数组为 ranges 的元素,元素大小也就是这些数组的长度。

假设參数 uniform 为 true,这此时的ranges 里元素的大小为 2(也就是说 ranges 指向一系列长度为 2 的数组),存储的是每一维的上下限这两个数字。假设參数 uniform 为 false,则此时的 ranges 的元素大小为 bin 的个数,存储的是一个个颜色值,即每一维的坐标值不一定是均匀的,须要人为指定。

  • uniform: 假设为 true 的话,则说明所需计算的直方图的每一维依照它的范围和尺寸大小均匀取值;假设为 false 的话,说明直方图的每一维不是均匀分布取值的,參考參数 ranges 的解释。

  • accumulate: 表示是否对传入的 hist 清零。不清零的话能够将多幅图像的直方图累加。

看上面这个解释大家应该就能感觉出这个函数有多麻烦了吧。只是好在我们一般仅仅会用到一些最主要的功能。

比方计算个单通道灰度图像的直方图。

直方图的取值范围一般来说也会是 0 到 255。 这时我们能够把这个函数再进行一次封装。使其更好用一些。

以下的代码来自 《OpenCV 2 Computer Vision Application Programming Cookbook》。

书的作者写了一个类。叫做 Histogram1D。

这个类的声明例如以下:

class Histogram1D
{
public:
    Histogram1D()
    {
        // Prepare arguments for 1D histogram
        histSize[0] = 256;
        hranges[0] = 0.0;
        hranges[1] = 255.0;
        ranges[0] = hranges;
        channels[0] = 0; // by default, we look at channel 0
    }
    ~Histogram1D();
    // Computes the 1D histogram and returns an image of it.
    cv::Mat getHistogramImage(const cv::Mat &image);
    // Computes the 1D histogram.
    cv::MatND getHistogram(const cv::Mat &image);
private:
    int histSize[1]; // number of bins
    float hranges[2]; // min and max pixel value
    const float* ranges[1];
    int channels[1]; // only 1 channel used here

};

getHistogram 函数用来计算直方图。实现例如以下:

// Computes the 1D histogram.
cv::MatND Histogram1D::getHistogram(const cv::Mat &image)
{
    cv::MatND hist;
    // Compute histogram
    cv::calcHist(&image,
        1, // histogram from 1 image only
        channels, // the channel used
        cv::Mat(), // no mask is used
        hist, // the resulting histogram
        1, // it is a 1D histogram
        histSize, // number of bins
        ranges // pixel value range
    );
    return hist;
}

getHistogramImage 函数用来生成直方图的图像:

// Computes the 1D histogram and returns an image of it.
cv::Mat Histogram1D::getHistogramImage(const cv::Mat &image)
{
    // Compute histogram first
    cv::MatND hist = getHistogram(image);
    // Get min and max bin values
    double maxVal = 0;
    double minVal = 0;
    cv::minMaxLoc(hist, &minVal, &maxVal, 0, 0);
    // Image on which to display histogram
    cv::Mat histImg(histSize[0], histSize[0], CV_8U, cv::Scalar(255));
    // set highest point at 90% of nbins
    int hpt = static_cast<int>(0.9 * histSize[0]);
    // Draw a vertical line for each bin
    for( int h = 0; h < histSize[0]; h++ )
    {
        float binVal = hist.at<float>(h);
        int intensity = static_cast<int>(binVal * hpt / maxVal);
        // This function draws a line between 2 points
        cv::line(histImg, cv::Point(h, histSize[0]),
        cv::Point(h,histSize[0]-intensity), cv::Scalar::all(0));
    }
    return histImg;
}

还有另外一个类能够计算彩色图像的直方图。

只是这个用途不是非常大。由于彩色图像的颜色空间太大了。并且形成的直方图是个三维的数组。

用起来也不方便。为了完整,这里还是把代码列了出来。

class ColorHistogram
{
public:
    ColorHistogram()
    {
        // Prepare arguments for a color histogram
        histSize[0] = histSize[1] = histSize[2] = 256;
        hranges[0] = 0.0; // BRG range
        hranges[1] = 255.0;
        ranges[0] = hranges; // all channels have the same range
        ranges[1] = hranges;
        ranges[2] = hranges;
        channels[0] = 0; // the three channels
        channels[1] = 1;
        channels[2] = 2;
    }
    cv::MatND getHistogram(const cv::Mat &image) ;
    cv::SparseMat getSparseHistogram(const cv::Mat &image) ;
private:
    int histSize[3];
    float hranges[2];
    const float* ranges[3];
    int channels[3];
};

这里实现了两个函数 getHistogram 和 getSparseHistogram。 唯一的差别就是 getSparseHistogram 返回的是个稀疏矩阵。

cv::MatND ColorHistogram::getHistogram(const cv::Mat &image)
{
    cv::MatND hist;
    // Compute histogram
    cv::calcHist(&image,
                 1, // histogram of 1 image only
                 channels, // the channel used
                 cv::Mat(), // no mask is used
                 hist, // the resulting histogram
                 3, // it is a 3D histogram
                 histSize, // number of bins
                 ranges // pixel value range
                 );
    return hist;
}

cv::SparseMat ColorHistogram::getSparseHistogram(const cv::Mat &image)
{
    cv::SparseMat hist(3,histSize,CV_32F);
    // Compute histogram
    cv::calcHist(&image,
                 1, // histogram of 1 image only
                 channels, // the channel used
                 cv::Mat(), // no mask is used
                 hist, // the resulting histogram
                 3, // it is a 3D histogram
                 histSize, // number of bins
                 ranges // pixel value range
                 );
    return hist;
}
posted on 2017-07-03 19:54  yutingliuyl  阅读(504)  评论(0编辑  收藏  举报