计算图像直方图 - sumpig/OpenCV GitHub Wiki
直方图是一个简单的表格,表示一幅图像(有时是一组图像)中具有某个值的像素的数量。
它是后面几节应用的基础。
代码实现:
cv::Mat image = cv::imread("H:\\opencv_c++\\group.jpg", 0);
Histogram1D h;
cv::Mat histo = h.getHistogram(image);
class Histogram1D {
private:
int histSize[1]; // 直方图中箱子的数量
float hranges[2]; // 值范围
const float* ranges[1]; // 值范围的指针
int channels[1]; // 要检查的通道数量
public:
Histogram1D() {
// 准备一维直方图的默认参数
histSize[0] = 256; // 256 个箱子
hranges[0] = 0.0; // 从0 开始(含)
hranges[1] = 256.0; // 到256(不含)
ranges[0] = hranges;
channels[0] = 0; // 先关注通道0
}
// 计算一维直方图
cv::Mat getHistogram(const cv::Mat& image) {
cv::Mat hist;
// 用calcHist 函数计算一维直方图
cv::calcHist(&image,
1, // 仅为一幅图像的直方图
channels, // 使用的通道
cv::Mat(), // 不使用掩码
hist, // 作为结果的直方图
1, // 这是一维的直方图
histSize, // 箱子数量
ranges // 像素值的范围
);
return hist;
}
打印直方图数值:
// 打印直方图箱子数值
for (int i = 0; i < 256; i++)
std::cout << "Value" << i << " = " << histo.at<float>(i) << std::endl;
显示直方图柱状图:
// 以图像形式显示直方图
cv::namedWindow("Histogram");
cv::imshow("Histogram", h.getHistogramImage(image));
// 计算一维直方图,并返回它的图像
cv::Mat getHistogramImage(const cv::Mat &image, int zoom=1) {
// 先计算直方图
cv::Mat hist= getHistogram(image);
// 创建图像
return getImageOfHistogram(hist, zoom);
}
// 创建一个表示直方图的图像(静态方法)
static cv::Mat getImageOfHistogram(const cv::Mat &hist, int zoom) {
// 取得箱子值的最大值和最小值
double maxVal = 0;
double minVal = 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 hpt = static_cast<int>(0.9*histSize);
// 为每个箱子画垂直线
for (int h = 0; h < histSize; h++) {
float binVal = hist.at<float>(h);
if (binVal > 0) {
int intensity = static_cast<int>(binVal*hpt / maxVal);
cv::line(histImg,
cv::Point(h*zoom, histSize*zoom), // 直线起点
cv::Point(h*zoom, (histSize - intensity)*zoom), // 直线终点
cv::Scalar(0), // 线条颜色
zoom); // 线条粗细
}
}
return histImg;
}
class ColorHistogram {
private:
int histSize[3]; // 每个维度的大小
float hranges[2]; // 值的范围(三个维度用同一个值)
const float* ranges[3]; // 每个维度的范围
int channels[3]; // 需要处理的通道
public:
// 准备用于彩色图像的默认参数
ColorHistogram() {
// 每个维度的大小和范围是相等的
histSize[0]= histSize[1]= histSize[2]= 256;
hranges[0]= 0.0; // BGR 范围为0~256
hranges[1]= 256.0;
ranges[0]= hranges; // 这个类中
ranges[1]= hranges; // 所有通道的范围都相等
ranges[2]= hranges;
channels[0]= 0; // 三个通道:B
channels[1]= 1; // G
channels[2]= 2; // R
}
// 计算直方图
cv::Mat getHistogram(const cv::Mat &image) {
cv::Mat hist;
// 计算直方图
cv::calcHist(&image, 1, // 单幅图像的直方图
channels, // 用到的通道
cv::Mat(), // 不使用掩码
hist, // 得到的直方图
3, // 这是一个三维直方图
histSize, // 箱子数量
ranges);// 像素值的范围
return hist;
}
如果选用含有256 个箱子的直方图,这个矩阵就有(256)^3 个元素,表示超过1600 万个项目。在很多应用程序中,最好在计算直方图时减少箱子的数量。
也可以使用数据结构cv::SparseMat 表示大型稀疏矩阵(即非零元素非常稀少的矩阵),这样不会消耗过多的内存。
// 计算直方图
cv::SparseMat getSparseHistogram(const cv::Mat &image) {
cv::SparseMat hist(3, // 维数
histSize, // 每个维度的大小
CV_32F);
// 计算直方图
cv::calcHist(&image, 1, // 单幅图像的直方图
channels, // 用到的通道
cv::Mat(), // 不使用掩码
hist, // 得到的直方图
3, // 这是三维直方图
histSize, // 箱子数量
ranges); // 像素值的范围
return hist;
}
灰度直方图尖峰往往分割了图像的背景和前景,利用阈值化处理可得到二值分割图像。
cv::Mat thresholded; // 输出二值图像
cv::threshold(image,
thresholded,
70, // 阈值
255, // 对超过阈值的像素赋值
cv::THRESH_BINARY); // 阈值化类型