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