OpenCV【4】---calcHist 计算图像的直方图

1 直方图

  直方图:给出了一幅图像或一组图像中拥有给定数值的像素数量。灰度图图像(单通道)的直方图有256个条目。0号条目给出值为0的像素个数,1号条目给出值为1的像素个数…。当然直方图可以归一化,归一化后的所有条目之和等于1,每一个条目是该特定值像素在图像中所占的比例。大多数情况下,直方图是一个单通道或者三通道的图像。
  直方图能够有效地描述图像的内容。既然能够有效的描述图像的内容,那么将图像化为直方图就能够做内容检测的工作。比如物体检测,相似度检测等。

OpenCV【4】---calcHist 计算图像的直方图_第1张图片

2 calcHist函数

  calcHist函数是用来计算一个图像数组集合的直方图。

  C++函数调用形式:

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)

  Python调用形式:

cv2.calcHist(images, channels, mask, histSize, ranges[, hist[, accumulate]]) → hist

  C调用形式:

void cvCalcHist(IplImage** image, CvHistogram* hist, int accumulate=0, const CvArr* mask=NULL)

  参数说明:

images — 源矩阵指针(输入图像,可以多张)。 但都应当有同样的深度(depth), 比如CV_8U 或者 CV_32F ,和同样的大小。每张图片可以拥有任意数量的通道。
nimages — 源图像的数量,如一幅图像则为1。
channels — 通道的数量,每个通道都有编号,灰度图为一通道,即channel[1]=0;对应着一维。BGR三通道图像编号为channels[3]={0,1,2};分别对应着第一维,第二维,第三维。
mask — 掩码图像:要对图像处理时,先看看这个像素对应的掩码位是否为屏蔽,如果为屏蔽,就是说该像素不处理(掩码值为0的像素都将被忽略)
hist — 返回的直方图。
dims — 直方图维度。
histSize — 直方图每一维的条目个数的数组,灰度图一维有256个条目。
ranges — 每一维的像素值的范围,传递数组,与维数相对应,灰度图一维像素值范围为0~255。

3 获取直方图代码

3.1 单通道图像直方图

Histogram1D.h
#ifndef HISTOGRAM1D_H
#define HISTOGRAM1D_H

#include <iostream>
#include <QDebug>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>


using namespace cv;
using namespace std;

class Histogram1D
{
public:
    Histogram1D();
    MatND getHistogram(const Mat& image);
    Mat getHistogramImage(const Mat& image);
    Mat applyLookUp(const Mat&image,const Mat&lookup);
private:
    int histSize[1];        //项的数量
    float hranges[2];       //像素的最小值和最大值
    const float* ranges[2]; //
    int channels[1];        //仅用到一个通道
};

#endif // HISTOGRAM1D_H
Histogram1D.cpp
#include "histogram1d.h"

Histogram1D::Histogram1D()
{
    //准备一维直方图的参数
    histSize[0] = 256;
    hranges[0] = 0.0;
    hranges[1] = 255.0;
    ranges[0] = hranges;
    //默认情况下,只考察0号通道
    channels[0] = 0;
}

// 获取直方图
MatND Histogram1D::getHistogram(const Mat &image)
{
    MatND hist;
    calcHist(&image,
             1,
             channels,
             Mat(),
             hist,
             1,
             histSize,
             ranges
    );
    return hist;
}

// 将直方图用图表形式显示出来
Mat Histogram1D::getHistogramImage(const Mat&image)
{
    //计算直方图
    MatND hist = getHistogram(image);
    //获取最大值和最小值
    double  maxValue = 0.0;
    double  minValue = 0.0;
    int maxIdx = 0;
    int minIdx = 0;

    //cout << hist << endl;
    minMaxIdx(hist,&minValue,&maxValue,&minIdx,&maxIdx);
    qDebug()<< minValue << " " << maxValue << " " <<minIdx << " " << maxIdx;
    qDebug()<< hist.channels();
    //显示直方图的图像
    Mat histImg(histSize[0],histSize[0],CV_8U,Scalar(255));
    //设置最高点为nbins的90%
    int highPoint = static_cast<int>(0.9*histSize[0]);
    //每个条目都绘制一条垂直线
    qDebug() << hist.size;
    for(int h = 0;h < histSize[0];h++){
        float binVal = hist.at<float>(h);
        int intensity = static_cast<int>(binVal*highPoint/maxValue);
        //两点之间绘制一条直线
        line(histImg,Point(h,histSize[0]),
                     Point(h,histSize[0]-intensity),
                     Scalar::all(0));
    }
    return histImg;
}

// 对image应用查找表lookup,得到新的图像result
Mat Histogram1D::applyLookUp(const Mat&image,const Mat&lookup)
{
    Mat result;
    LUT(image,lookup,result);

    return result;
}

3.2 三通道图像直方图

ColorHistogram.h
#ifndef COLORHISTOGRAM_H
#define COLORHISTOGRAM_H

#include <QDebug>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>

using namespace cv;

class ColorHistogram
{
public:
    ColorHistogram();
    MatND getHistogram(const Mat& image);
    Mat getHistogramImage(const Mat&image);
    void colorReduce(Mat& image,int div=64);
private:
    int histSize[3];
    float hranges[2];
    const float* ranges[3];
    int channels[3];
};

#endif // COLORHISTOGRAM_H
ColorHistogram.cpp
#include "colorhistogram.h"

ColorHistogram::ColorHistogram()
{
    //准备彩色直方图的参数
    histSize[0] = histSize[1] = histSize[2] = 256;
    //BGR的范围
    hranges[0] = 0.0;
    hranges[1] = 255.0;
    //所有通道拥有相同的范围
    ranges[0] = hranges;
    ranges[1] = hranges;
    ranges[2] = hranges;
    //三个通道
    channels[0] = 0;
    channels[1] = 1;
    channels[2] = 2;
}

//获取直方图
//因为彩色的色彩数目较多,则直方图的栏目则也很多
MatND ColorHistogram::getHistogram(const Mat& image)
{
    MatND hist;
    calcHist(&image,            //图像矩阵指针,可多个图像
             1,                 //图像的数量,1张
             channels,          //通道的数量,一张图片(3通道的)为0~2,两张为3~5,三张为...
             Mat(),             //掩码图像。(不使用掩码图像)
             hist,              //计算返回的直方图
             3,                 //三维直方图
             histSize,          //每个维度的直方图数组的大小
             ranges,            //像素值的范围,BGR三个通道的像素值相同
             true,
             false
    );
    return hist;
}

// 将直方图用图表形式显示出来
Mat ColorHistogram::getHistogramImage(const Mat&image)
{
    //计算直方图
    MatND hist = getHistogram(image);

    //获取最大值和最小值
    double  maxValue[3] = {0};
    double  minValue[3] = {0};
    int maxIdx[3] = {0};
    int minIdx[3] = {0};

    minMaxIdx(hist,minValue,maxValue,minIdx,maxIdx);
    qDebug()<< hist.channels();
    //显示直方图的图像
    Mat histImg(histSize[0],histSize[0],CV_8U,Scalar(255));
    //设置最高点为nbins的90%
    int highPoint = static_cast<int>(0.9*histSize[0]);
    //每个条目都绘制一条垂直线
    qDebug() << hist.size;
    for(int h = 10000;h < histSize[0]+10000;h++){
        float binVal = hist.at<float>(h);
        int intensity = static_cast<int>(binVal*highPoint/maxValue[0]);
        //两点之间绘制一条直线
        line(histImg,Point(h,histSize[0]),
                     Point(h,histSize[0]-intensity),
                     Scalar::all(0));
    }
    return histImg;
}

// 减少图像的色彩数目
void ColorHistogram::colorReduce(Mat& image,int div)
{
    int n1 = image.rows;
    int nc = image.cols;
    //图像是连续存储的吗?
    if(image.isContinuous()){
        //没有则对行进行填补
        nc = nc*n1;
        //一维数组
        n1 = 1;
    }
    int n = static_cast<int>(log(static_cast<double>(div))/log(2.0));
    //用来对像素值进行取整的二进制掩模
    uchar mask = 0xFF << n;
    for(int j = 0;j < n1;j++){
        uchar* data = image.ptr<uchar>(j);
        for(int i = 0;i < nc;i++){
            *(data++) = (*data)&mask + div/2;
            *(data++) = (*data)&mask + div/2;
            *(data++) = (*data)&mask + div/2;
        }
    }
}

参考资料:
http://www.opencv.org.cn/opencvdoc/2.3.2/html/modules/imgproc/doc/histograms.html?highlight=calchist#void calcHist(const Mat* arrays, int narrays, const int* channels, InputArray mask, OutputArray hist, int dims, const int* histSize, const float** ranges, bool uniform, bool accumulate)

你可能感兴趣的:(qt,opencv,直方图,calcHist)