OpenCV计算和显示图像直方图

时间:2020-12-04 05:59:53

要在OpenCV中计算直方图,可调用函数calcHist(),

void calcHist(const Mat* images,//源图像
int nimages,//源图像的个数。设为1,则仅为一个图像的直方图
const int* channels,//使用的通道
InputArray mask, //掩码,(可设置哪些像素不参与直方图计算)
OutputArray hist, //作为结果的直方图
int dims, //直方图的维数
const int* histSize,//直方图箱子的数量
const float** ranges, //像素值的范围
bool uniform=true,
bool accumulate=false )

这是一个通用的直方图计算函数,可处理包含任何值类型和范围的多通道图像。为了简化使用,在这里构建两个计算直方图的类:分别处理单通道图像和彩色图像。

单通道图像

比如单通道图像可以这样调用类函数来计算直方图:

Histogram1D h;//直方图计算得算法类
h.setHistSize(256);//设置直方图的箱子数量
cv::Mat hist=h.getHistogram(image);//计算直方图
cv::Mat histImg = h.getHistogramImage(image, 1);//将直方图画出来,返回直方图图像
cv::imshow("histogram", histImg);

单通道图像计算直方图的算法类如下:

//头文件 Histogram1D.h
#pragma once
#include"opencv2/opencv.hpp"
#include"opencv2/core/core.hpp"
#include"opencv2/highgui/highgui.hpp"

class Histogram1D
{
private:
int channels[1];//使用的通道数量
int histSize[1];//直方图箱子(bin)的数量
const float* ranges[1];//像素值范围
float hranges[2];
public:
Histogram1D();
cv::Mat getHistogram(const cv::Mat& image);//得到直方图
void setHistSize(int n);//设置直方图箱子数量
int* getHistSize();
static cv::Mat applyLookUp(const cv::Mat& image,const cv::Mat& lookup);//应用查找表
cv::Mat getHistogramImage(const cv::Mat& image,int zoom);//得到直方图图像
~Histogram1D();
};
//实现文件 Histogram1D.cpp
#include "Histogram1D.h"

Histogram1D::Histogram1D()
{
histSize[0] = 256;//箱子个数设为256
channels[0] = 0;//使用一个通道,默认为0
hranges[0] = 0.0;
hranges[1] = 256.0;
ranges[0] = hranges;//值范围
}

Histogram1D::~Histogram1D()
{
}

cv::Mat Histogram1D::getHistogram(const cv::Mat& image)
{
cv::Mat hist;
cv::calcHist(&image,
1,//单幅图像的直方图
channels,
cv::Mat(),
hist,
1,//一维直方图
histSize,
ranges);
return hist;
}

cv::Mat Histogram1D::getHistogramImage(const cv::Mat& image, int zoom = 1)
{
cv::Mat hist = getHistogram(image);
//这里调用静态方法
return getImageOfHistogram(hist,zoom);
}

cv::Mat getImageOfHistogram(const cv::Mat& hist,
int zoom//设置缩放系数
)
{
double minVal = 0;
double maxVal = 0;
cv::minMaxLoc(hist,&minVal,&maxVal,0,0);

int histSize = hist.rows;

//用于显示直方图的方形图像
cv::Mat histImg(histSize*zoom,histSize*zoom,CV_8U,cv::Scalar(255));

//设置最高点为90%的箱子个数
int hmax = static_cast<int>(histSize*0.9);

//画线
for (int h = 0; h < histSize;h++)
{
float binVal= hist.at<float>(h);
if (binVal > 0)
{
int intensity = binVal*hmax / maxVal;
cv::line(histImg, cv::Point(h*zoom, histSize*zoom), cv::Point(h*zoom, (histSize-intensity)*zoom), cv::Scalar(0), zoom);
}
}
return histImg;
}
//应用查找表相关的函数
cv::Mat Histogram1D::applyLookUp(const cv::Mat& image, const cv::Mat& lookup)
{
cv::Mat result;
cv::LUT(image,lookup,result);
return result;
}
//get方法,获取成员变量histSize
int* Histogram1D::getHistSize()
{
return histSize;
}
//set方法,设置成员变量histSize
void Histogram1D::setHistSize(int n)
{
histSize[0] = n;
}

运行效果如下:
输入图像
OpenCV计算和显示图像直方图
直方图
OpenCV计算和显示图像直方图


彩色图像

再比如,彩色图像可以这样调用类函数来计算直方图:

ColorHistogram hc;//直方图计算得算法类
hc.setSize(16);//设置每个通道直方图箱子数量
cv::Mat hist=hc.getHistogram(image);//计算直方图
//或者
//cv::Mat hist=hc.getHueHistogram(image);//计算H通道直方图
//cv::Mat histImg=hc.getHistImage(hist,16);//放大16倍,使图像能以256*256大小显示
//imshow("histImg",histImg);

彩色图像计算直方图的算法类如下:

//头文件 ColorHistogram.h
#pragma once
#include"opencv2/opencv.hpp"
#include"opencv2/core/core.hpp"
#include"opencv2/highgui/highgui.hpp"

class ColorHistogram
{
private:
int channels[3];//使用的通道数量
int histSize[3];//直方图箱子(bin)的数量
const float* ranges[3];//存放值范围(3个通道)
float hranges[2];
public:
ColorHistogram();
cv::Mat getHistogram(const cv::Mat& image);
cv::Mat getHueHistogram(const cv::Mat& image,int minSaturation);//设置饱和度阈值的原因是低饱和度图像RGB分量几乎相同, //很难确定准确颜色,故排除

void setSize(int n);//设置直方图箱子数量
~ColorHistogram();
};
//实现文件 ColorHistogram.cpp
#include "ColorHistogram.h"

ColorHistogram::ColorHistogram()
{
histSize[0] = 256;//箱子个数设为256
histSize[1] = 256;//箱子个数设为256
histSize[2] = 256;//箱子个数设为256

channels[0] = 0;//使用通道0
channels[1] = 1;//使用通道1
channels[2] = 2;//使用通道2

hranges[0] = 0.0;
hranges[1] = 256.0;
ranges[0] = hranges;//值范围
ranges[1] = hranges;//值范围
ranges[2] = hranges;//值范围
}

cv::Mat ColorHistogram::getHistogram(const cv::Mat& image)
{
cv::Mat hist;
cv::calcHist(&image,
1,//单个图像的直方图
channels,
cv::Mat(),
hist,
3,//3维直方图
histSize,
ranges);
return hist;
}

void ColorHistogram::setSize(int n)
{
histSize[0] = n;
histSize[1] = n;
histSize[2] = n;

}

cv::Mat ColorHistogram::getHueHistogram(const cv::Mat& image, int minSaturation)
{
cv::Mat hist;
cv::Mat hsv;
cv::cvtColor(image,hsv,CV_BGR2HSV);

//掩码,计算直方图时掩盖低饱和度像素贡献
cv::Mat mask;
if (minSaturation > 0)
{
std::vector<cv::Mat> v;
cv::split(hsv,v);
//饱和度通道
cv::threshold(v[1],mask,minSaturation,255,cv::THRESH_BINARY );
}

hranges[0] = 0.0;
hranges[1] = 180;
channels[0] = 0;

cv::calcHist(&hsv,
1,//单个图像的直方图
channels,
mask,
hist,
1,//3维直方图
histSize,
ranges);
return hist;
}

ColorHistogram::~ColorHistogram()
{
}

运行效果如下:
输入图像
OpenCV计算和显示图像直方图
这里插几句嘴。。

cv::Mat hist=hc.getHistogram(image);//计算直方图

上述方法返回的是三维的cv::Mat实例。如果选用含有256个箱子的直方图,这个矩阵的像素个数为256^3。形象的讲,它是一个正方体。所以无法显示。
类中计算直方图还有一种方法:

cv::Mat hist=hc.getHueHistogram(image);//计算H通道直方图

首先将输入图像转化为HSV格式,H代表色调,代表颜色信息。这样得到的是一维直方图,结果如下(这里将直方图箱子数量设为16):
OpenCV计算和显示图像直方图