OpenCV2编程手册笔记之 4.2计算图像的直方图

1.引子

    一个像素是由不同颜色的像素组成的,那么像素在图像中的分布就称为这个图像的重要特征。直方图不仅仅可以用来观察图像,还可以起到改善图像外观、描述直方图内容的作用。

2.直方图实现

    在一个单通道的灰度图像中,每个像素的值都介于0(黑色)——255(白色)之间。根据这点,灰度图像的直方图拥有256个条目,这些条目也称之为容器。分别称为0号容器到255号容器。

    现在,我们以灰度形式读取一张图片,并且定义一个直方图处理类来方便的处理直方图操作。

    在这个类中,我们先声明变量并编写构造函数:

private:
	int histSize[1];
	float hranges[2];
	const float *ranges[1];
	int channels[1];
public:
	Histogram1D()
	{
		histSize[0] = 256;
		hranges[0] = 0.0;
		hranges[1] = 255.0;
		ranges[0] = hranges;
		channels[0] = 0;
	}

    这是一个处理单通道灰度图像类的成员变量,之后我们使用以下方法计算灰度直方图。

    其中最主要的函数就是calcHist

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);
images是待处理的图像
nimages是图像的数量,输入1就是一张图片
channels是指针类型,因此在变量声明中使用数组进行声明,代表着图像的通道数,例如灰度图就是单通道
mask是掩码,这里可以默认为cv::Mat
hist是返回的直方图,推荐采用MatND进行定义
dims是维数,一维直方图就输入1就好
histSize是项的数量,也就是容器的数量。但注意它是指针类型,因此变量声明也采用了数组形式。这里有256个容器,因此构造时为256
ranges是像素范围,注意这是一个二维数组。在这个图像中,像素范围是0 - 255

    这里要注意,ranges是float型的,如果使用.at操作获取数值,<>中应为float,后面代码中会看到

    这样,我们只需要初始化对象并调用方法,就可以获取直方图了。

cv::Mat image = cv::imread("..\\group.jpg");
	Histogram1D h;
	cv::MatND histo = h.getHistogram(image);

    上面的代码获取到了直方图,但其是以向量方式储存起来的。我们需要将其以直方图的形式呈现使其变得更加直观。

    在这个类中,我们首先获取传入图像的直方图数据,并且获得直方图每个容器中最大值和最小值,这里采取minMaxLoc函数。之后,生成一个长宽都为256的白色底板图像histImg。

    由于每个直方图都必然有一个最高点,而这个最高点如果触顶了显得不那么美观。所以,我们设置一个直方图最高点(hpt)来避免这种事情。

    之后,就是逐点画出直线了,我们采取比例的办法。例如:

    某一个“容器”,例如128这个灰度值,它容纳了1000个点。上文已经知道,最大值是maxVal,那么我们的线的相对长度就是:

    hist.at(128) * hpt / maxVal ->128为例计算相对长度

    绘制出直方图后,我们可以看出,灰度的中间位置存在一个大的峰值,同时有大量深色像素。这两组像素对应的基本是图像的背景和前景,在区分时,我们可以使用此处的过渡阈值(峰值上升前的容器最小值)(灰度值为60)作为区分阈值。

OpenCV2编程手册笔记之 4.2计算图像的直方图_第1张图片

源代码:

.h文件
#pragma once
#include 
class Histogram1D
{
private:
	int histSize[1];
	float hranges[2];
	const float *ranges[1];
	int channels[1];
public:
	Histogram1D()
	{
		histSize[0] = 256;
		hranges[0] = 0.0;
		hranges[1] = 255.0;
		ranges[0] = hranges;
		channels[0] = 0;
	}
	cv::MatND getHistogram(const cv::Mat &image);
	cv::Mat getHistogramImage(const cv::Mat &image, int normal_type = 0);
};

.cpp文件

#include "stdafx.h"
#include "Histogram1D.h"

cv::MatND Histogram1D::getHistogram(const cv::Mat &image)
{
	cv::MatND hist;
	cv::calcHist(&image, 1, channels, cv::Mat(), hist, 1, histSize, ranges);
	return hist;
}

cv::Mat Histogram1D::getHistogramImage(const cv::Mat &image, int normal_type = 0)
{
	cv::MatND hist = getHistogram(image);
	if (normal_type != 0)
	{
		cv::normalize(hist, hist, 1.0);
	}
	double maxVal;
	double minVal;
	cv::minMaxLoc(hist, &minVal, &maxVal, 0, 0);
	cv::Mat histImg(histSize[0], histSize[0], CV_8U, cv::Scalar(255));
	int hpt = static_cast(0.9 * histSize[0]);
	for (int h = 0; h < histSize[0]; h++)
	{
		float binVal = hist.at(h);
		int intensity = static_cast(binVal * hpt / maxVal);
		cv::line(histImg, cv::Point(h, histSize[0]), cv::Point(h, histSize[0] - intensity), cv::Scalar::all(0));
	}
	return histImg;
}
//主函数

#include "stdafx.h"
#include 
#include "Histogram1D.h"


int main()
{
	cv::Mat image = cv::imread("..\\group.jpg");
	Histogram1D h;
	cv::MatND histo = h.getHistogram(image);
    return 0;
}




你可能感兴趣的:(OpenCV2,计算机视觉编程手册,学习笔记)