zoukankan      html  css  js  c++  java
  • 12、OpenCV实现图像的直方图处理

    1、直方图

      一幅图像由不同灰度值的像素组成,图像中灰度的分布情况是该图像的一个重要特征。图像的灰度直方图就描述了图像中灰度分布情况,能够很直观的展示出图像中各个灰度级所占的多少。
    图像的灰度直方图是灰度级的函数,描述的是图像中具有该灰度级的像素的个数:其中,横坐标是灰度级,纵坐标是该灰度级出现的频率。

    不过通常会将纵坐标归一化到[0,1][0,1]区间内,也就是将灰度级出现的频率(像素个数)除以图像中像素的总数。灰度直方图的计算公式如下:

    其中

    • rk是像素的灰度级
    • nk是具有灰度rk的像素个数
    •  MN是图像中总的像素个数 

    直方图的计算是很简单的,无非是遍历图像的像素,统计每个灰度级的个数。OpenCV提供了一个可靠的直方图函数calcHist,其声明如下

    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 );
    

      该函数能够同时计算多个图像,多个通道,不同灰度范围的灰度直方图.

    其参数说明如下:

    images,输入图像的数组,这些图像要有相同大大小,相同的深度(CV_8U CV_16U CV_32F).
    nimages ,输入图像的个数
    channels,要计算直方图的通道个数。
    mask,可选的掩码,不使用时可设为空。要和输入图像具有相同的大小,在进行直方图计算的时候,只会统计该掩码不为0的对应像素
    hist,输出的直方图
    dims,直方图的维度
    histSize,直方图每个维度的大小
    ranges,直方图每个维度要统计的灰度级的范围
    uniform,是否进行归一化,默认为true
    accumulate,累积标志,默认值为false。

    示例:

    下面是一个使用caclHist的一个示例,这里对其进行了一个封装,只绘制灰度直方图

    #include "stdafx.h"
    #include <vector>
    #include <iostream>
    #include <opencv2corecore.hpp>
    #include <opencv2highguihighgui.hpp>
    #include <opencv2imgprocimgproc.hpp>
    
    using namespace cv;
    using namespace std;
    class Histogram1D
    {
    private:
    	int histSize[1]; // 项的数量
    	float hranges[2]; // 统计像素的最大值和最小值
    	const float* ranges[1];
    	int channels[1]; // 仅计算一个通道
    
    public:
    	Histogram1D()
    	{
    		// 准备1D直方图的参数
    		histSize[0] = 256;
    		hranges[0] = 0.0f;
    		hranges[1] = 255.0f;
    		ranges[0] = hranges;
    		channels[0] = 0;
    	}
    
    	MatND getHistogram(const Mat &image)
    	{
    		MatND hist;
    		// 计算直方图
    		calcHist(&image,// 要计算图像的
    			1,                // 只计算一幅图像的直方图
    			channels,        // 通道数量
    			Mat(),            // 不使用掩码
    			hist,            // 存放直方图
    			1,                // 1D直方图
    			histSize,        // 统计的灰度的个数
    			ranges);        // 灰度值的范围
    		return hist;
    	}
    
    	Mat getHistogramImage(const Mat &image)
    	{
    		MatND hist = getHistogram(image);
    
    		// 最大值,最小值
    		double maxVal = 0.0f;
    		double minVal = 0.0f;
    
    		minMaxLoc(hist, &minVal, &maxVal);
    
    		//显示直方图的图像
    		Mat histImg(histSize[0], histSize[0], CV_8U, Scalar(255));
    
    		// 设置最高点为nbins的90%
    		int hpt = static_cast<int>(0.9 * histSize[0]);
    		//每个条目绘制一条垂直线
    		for (int h = 0; h < histSize[0]; h++)
    		{
    			float binVal = hist.at<float>(h);
    			int intensity = static_cast<int>(binVal * hpt / maxVal);
    			// 两点之间绘制一条直线
    			line(histImg, Point(h, histSize[0]), Point(h, histSize[0] - intensity), Scalar::all(0));
    		}
    		return histImg;
    	}
    };
    int main()
    {
    	//图像的获取
    	Mat srcImage = imread("111.jpg");
    	if (!srcImage.data)
    	{
    		cout << "读入图片失败!" << endl;
    		return -1;
    	}
    	Histogram1D hist;
    	Mat histImg;
    	histImg = hist.getHistogramImage(srcImage);
    
    	imshow("Image", srcImage);
    	imshow("Histogram", histImg);
    	waitKey();
    	return 0;
    }

     运行结果如下:

            假如图像的灰度分布不均匀,其灰度分布集中在较窄的范围内,使图像的细节不够清晰,对比度较低。通常采用直方图均衡化直方图规定化两种变换,使图像的灰度范围拉开或使灰度均匀分布,从而增大反差,使图像细节清晰,以达到增强的目的。

    2、直方图均衡

          直方图均衡化就是对图像进行非线性拉伸,重新分配图像的灰度值,使一定范围内图像的灰度值大致相等,它是以累计分布函数变化法为基础的直方图修正法。它将当前的灰度分布通过一个变换函数,变换为范围更宽、灰度分布更均匀的图像。也就是将原图像的直方图修改为在整个灰度区间内大致均匀分布,因此扩大了图像的动态范围,增强图像的对比度。

        方图均衡化算法的步骤如下所示:

    • 计算原图像的灰度直方图
    • 其中n为像素总数,nk为灰度级Sk的像素个数
    • 计算原始图像的累积直方图 
    • 实现图像直方图均衡化: 其中Dj是目的图像的像素,CDF(Si)是源图像灰度为i的累积分布,L是图像中最大灰度级(灰度图为255)

    其代码实现如下:

    • 在上面中封装了求灰度直方图的类,这里直接应用该方法得到图像的灰度直方图;
    • 将灰度直方图进行归一化,计算灰度的累积概率;
    • 创建灰度变化的查找表
    • 应用查找表,将原图像变换为灰度均衡的图像

    代码实现如下所示:

    void equalization_self(const Mat &src, Mat &dst)
    {
        Histogram1D hist1D;
        MatND hist = hist1D.getHistogram(src);
    
        hist /= (src.rows * src.cols); // 对得到的灰度直方图进行归一化
        float cdf[256] = { 0 }; // 灰度的累积概率
        Mat lut(1, 256, CV_8U); // 灰度变换的查找表
        for (int i = 0; i < 256; i++)
        {
            // 计算灰度级的累积概率
            if (i == 0)
                cdf[i] = hist.at<float>(i);
            else
                cdf[i] = cdf[i - 1] + hist.at<float>(i);
    
            lut.at<uchar>(i) = static_cast<uchar>(255 * cdf[i]); // 创建灰度的查找表
        }
    
        LUT(src, lut, dst); // 应用查找表,进行灰度变化,得到均衡化后的图像
    
    }
    

      实际在OpenCV中也提供了灰度均衡化的函数equalizeHist,该函数的使用很简单,只有两个参数:输入图像,输出图像。

    示例:

     下面是一个直方图均衡化的示例

    #include "stdafx.h"
    #include <vector>
    #include <iostream>
    #include <opencv2corecore.hpp>
    #include <opencv2highguihighgui.hpp>
    #include <opencv2imgprocimgproc.hpp>
    
    using namespace cv;
    using namespace std;
    class Histogram1D
    {
    private:
    	int histSize[1]; // 项的数量
    	float hranges[2]; // 统计像素的最大值和最小值
    	const float* ranges[1];
    	int channels[1]; // 仅计算一个通道
    
    public:
    	Histogram1D()
    	{
    		// 准备1D直方图的参数
    		histSize[0] = 256;
    		hranges[0] = 0.0f;
    		hranges[1] = 255.0f;
    		ranges[0] = hranges;
    		channels[0] = 0;
    	}
    
    	MatND getHistogram(const Mat &image)
    	{
    		MatND hist;
    		// 计算直方图
    		calcHist(&image,// 要计算图像的
    			1,                // 只计算一幅图像的直方图
    			channels,        // 通道数量
    			Mat(),            // 不使用掩码
    			hist,            // 存放直方图
    			1,                // 1D直方图
    			histSize,        // 统计的灰度的个数
    			ranges);        // 灰度值的范围
    		return hist;
    	}
    
    	Mat getHistogramImage(const Mat &image)
    	{
    		MatND hist = getHistogram(image);
    
    		// 最大值,最小值
    		double maxVal = 0.0f;
    		double minVal = 0.0f;
    
    		minMaxLoc(hist, &minVal, &maxVal);
    
    		//显示直方图的图像
    		Mat histImg(histSize[0], histSize[0], CV_8U, Scalar(255));
    
    		// 设置最高点为nbins的90%
    		int hpt = static_cast<int>(0.9 * histSize[0]);
    		//每个条目绘制一条垂直线
    		for (int h = 0; h < histSize[0]; h++)
    		{
    			float binVal = hist.at<float>(h);
    			int intensity = static_cast<int>(binVal * hpt / maxVal);
    			// 两点之间绘制一条直线
    			line(histImg, Point(h, histSize[0]), Point(h, histSize[0] - intensity), Scalar::all(0));
    		}
    		return histImg;
    	}
    };
    void equalization_self(const Mat &src, Mat &dst)
    {
    	Histogram1D hist1D;
    	MatND hist = hist1D.getHistogram(src);
    
    	hist /= (src.rows * src.cols); // 对得到的灰度直方图进行归一化
    	float cdf[256] = { 0 }; // 灰度的累积概率
    	Mat lut(1, 256, CV_8U); // 灰度变换的查找表
    	for (int i = 0; i < 256; i++)
    	{
    		// 计算灰度级的累积概率
    		if (i == 0)
    			cdf[i] = hist.at<float>(i);
    		else
    			cdf[i] = cdf[i - 1] + hist.at<float>(i);
    
    		lut.at<uchar>(i) = static_cast<uchar>(255 * cdf[i]); // 创建灰度的查找表
    	}
    
    	LUT(src, lut, dst); // 应用查找表,进行灰度变化,得到均衡化后的图像
    
    }
    
    
    int main()
    {
    	//图像的获取
    	Mat srcImage = imread("111.jpg");
    	Mat srcgray;
    		cvtColor(srcImage, srcgray, CV_BGR2GRAY);
    	if (!srcImage.data)
    	{
    		cout << "读入图片失败!" << endl;
    		return -1;
    	}
    	Histogram1D hist;
    	Mat histImg;
    	histImg = hist.getHistogramImage(srcImage);
       //直方图均衡化
    	Mat equalize, equalizeImg, equalize1, equalizeImg1, srcgrayhist;
        //函数均衡
    	equalization_self(srcImage, equalize);
    	equalizeImg= hist.getHistogramImage(equalize);
    	//OpenCV自带函数均衡
    	srcgrayhist = hist.getHistogramImage(srcgray);
    	equalizeHist(srcgray, equalize1);
    	equalizeImg1 = hist.getHistogramImage(equalize1);
    	imshow("Image", srcImage);
    	imshow("Histogram", histImg);
    	imshow("equalizeImg", equalizeImg);
    	imshow("equalize", equalize);
    	imshow("srcgray", srcgray);
    	imshow("srcgrayhist", srcgrayhist);
    	imshow("equalizeImg1", equalizeImg1);
    	imshow("equalize1", equalize1);
    	waitKey();
    	return 0;
    }

    程序运行结果如下,左边两张图片是原图及我们写的函数的效果,右边两张图是灰度图及OpenCV自带函数的效果。

    需要注意的是,OpenCV自带的函数只能对单通道进行直方图均衡化,如果对原图进行均衡化,由于原图是三通道的,此时会报错。但是这并不意味着无法用equalizeHist对彩色图像进行处理,详情请看参考资料OpenCV直方图(直方图、直方图均衡,直方图匹配,原理、实现)

    3、直方图匹配(直方图规定化)

        直方图均衡化可以自动的确定变换函数,该函数寻求产生又均匀直方图的输出图像,需要自动增强时,这是一种好方法,因为这种方法得到的结果可以预知,并且这种方法的实现也很简单,但是在有些应用中这种自动的增强并不是最好的方法。有时候,需要图像具有

    某一特定的直方图形状(也就是灰度分布),而不是均匀分布的直方图,这时候可以使用直方图规定化

     直方图规定化,也叫做直方图匹配,用于将图像变换为某一特定的灰度分布,也就是其目的的灰度直方图是已知的。这其实和均衡化很类似,均衡化后的灰度直方图也是已知的,是一个均匀分布的直方图;而规定化后的直方图可以随意的指定,也就是在执行规定化操

    作时,首先要知道变换后的灰度直方图,这样才能确定变换函数。规定化操作能够有目的的增强某个灰度区间,相比于均衡化操作,规定化多了一个输入,但是其变换后的结果也更灵活。

       直方图的规定化也较为简单。可以利用均衡化后的直方图作为一个中间过程,然后求取规定化的变换函数。具体步骤如下:

    • 将原始图像的灰度直方图进行均衡化,得到一个变换函数
      其中s是均衡化后的像素,r是原始像素
    • 对规定的直方图进行均衡化,得到一个变换函数 其中v是均衡化后的像素,z是规定化的像素
    • 上式的逆变换为
       
      可通过均衡化后的灰度级v求出目标函数的灰度级z。由于对目标图像和原始图像都进行了均衡化处理,因此具有相同的分布密度,即
      因而可以用原始图像均衡化以后的灰度级s代表v,即
      所以可以依据原始图像均衡化后的图像的灰度值得到目标图像的灰度级z。

           直方图规定化在原理上很简单,在实践中,常见的困难是寻找T(r)和G-1的有意义表达式。幸运的是,在处理离散量时,问题可以大大得到简化。

    为了方便,这里将公式方法改写

      其中MN是图像的像素总数,nj是具有灰度值rj的像素数,L是图像中可能的灰度级数。

    类似的,给出个规定的Sk

          

    对一个q值,有

    则我们用反变换找到期望的值

    即对每一个S值给出一个Z值,这样就实现了从S到Z的一个映射,即在实践中,我们不需要计算G的反变换。

    即直方图的规定化过程如下:

    直方图规定化的实现

    直方图规定化的实现可以分为一下几步:

    • 计算原图像的累积直方图
    • 计算规定直方图的累积直方图
    • 计算两累积直方图的差值的绝对值
    • 根据累积直方图差值建立灰度级的映射
    void hist_specify(const Mat &src, const Mat &dst,Mat &result)
    {
        Histogram1D hist1D;
        MatND src_hist = hist1D.getHistogram(src);
        MatND dst_hist = hist1D.getHistogram(dst);
    
        float src_cdf[256] = { 0 };
        float dst_cdf[256] = { 0 };
    
        // 源图像和目标图像的大小不一样,要将得到的直方图进行归一化处理
        src_hist /= (src.rows * src.cols);
        dst_hist /= (dst.rows * dst.cols);
    
        // 计算原始直方图和规定直方图的累积概率
        for (int i = 0; i < 256; i++)
        {
            if (i == 0)
            {
                src_cdf[i] = src_hist.at<float>(i);
                dst_cdf[i] = dst_hist.at<float>(i);
            }
            else
            {
                src_cdf[i] = src_cdf[i - 1] + src_hist.at<float>(i);
                dst_cdf[i] = dst_cdf[i - 1] + dst_hist.at<float>(i);
            }
        }
        // 累积概率的差值
        float diff_cdf[256][256];
        for (int i = 0; i < 256; i++)
            for (int j = 0; j < 256; j++)
                diff_cdf[i][j] = fabs(src_cdf[i] - dst_cdf[j]);
        // 构建灰度级映射表
        Mat lut(1, 256, CV_8U);
        for (int i = 0; i < 256; i++)
        {
            // 查找源灰度级为i的映射灰度
            // 和i的累积概率差值最小的规定化灰度
            float min = diff_cdf[i][0];
            int index = 0;
            for (int j = 1; j < 256; j++)
            {
                if (min > diff_cdf[i][j])
                {
                    min = diff_cdf[i][j];
                    index = j;
                }
            }
            lut.at<uchar>(i) = static_cast<uchar>(index);
        }
    
        // 应用查找表,做直方图规定化
        LUT(src, lut, result);
    }

    示例:

    #include "stdafx.h"
    #include <vector>
    #include <iostream>
    #include <opencv2corecore.hpp>
    #include <opencv2highguihighgui.hpp>
    #include <opencv2imgprocimgproc.hpp>
    
    using namespace cv;
    using namespace std;
    class Histogram1D
    {
    private:
    	int histSize[1]; // 项的数量
    	float hranges[2]; // 统计像素的最大值和最小值
    	const float* ranges[1];
    	int channels[1]; // 仅计算一个通道
    
    public:
    	Histogram1D()
    	{
    		// 准备1D直方图的参数
    		histSize[0] = 256;
    		hranges[0] = 0.0f;
    		hranges[1] = 255.0f;
    		ranges[0] = hranges;
    		channels[0] = 0;
    	}
    
    	MatND getHistogram(const Mat &image)
    	{
    		MatND hist;
    		// 计算直方图
    		calcHist(&image,// 要计算图像的
    			1,                // 只计算一幅图像的直方图
    			channels,        // 通道数量
    			Mat(),            // 不使用掩码
    			hist,            // 存放直方图
    			1,                // 1D直方图
    			histSize,        // 统计的灰度的个数
    			ranges);        // 灰度值的范围
    		return hist;
    	}
    
    	Mat getHistogramImage(const Mat &image)
    	{
    		MatND hist = getHistogram(image);
    
    		// 最大值,最小值
    		double maxVal = 0.0f;
    		double minVal = 0.0f;
    
    		minMaxLoc(hist, &minVal, &maxVal);
    
    		//显示直方图的图像
    		Mat histImg(histSize[0], histSize[0], CV_8U, Scalar(255));
    
    		// 设置最高点为nbins的90%
    		int hpt = static_cast<int>(0.9 * histSize[0]);
    		//每个条目绘制一条垂直线
    		for (int h = 0; h < histSize[0]; h++)
    		{
    			float binVal = hist.at<float>(h);
    			int intensity = static_cast<int>(binVal * hpt / maxVal);
    			// 两点之间绘制一条直线
    			line(histImg, Point(h, histSize[0]), Point(h, histSize[0] - intensity), Scalar::all(0));
    		}
    		return histImg;
    	}
    };
    void equalization_self(const Mat &src, Mat &dst)
    {
    	Histogram1D hist1D;
    	MatND hist = hist1D.getHistogram(src);
    
    	hist /= (src.rows * src.cols); // 对得到的灰度直方图进行归一化
    	float cdf[256] = { 0 }; // 灰度的累积概率
    	Mat lut(1, 256, CV_8U); // 灰度变换的查找表
    	for (int i = 0; i < 256; i++)
    	{
    		// 计算灰度级的累积概率
    		if (i == 0)
    			cdf[i] = hist.at<float>(i);
    		else
    			cdf[i] = cdf[i - 1] + hist.at<float>(i);
    
    		lut.at<uchar>(i) = static_cast<uchar>(255 * cdf[i]); // 创建灰度的查找表
    	}
    
    	LUT(src, lut, dst); // 应用查找表,进行灰度变化,得到均衡化后的图像
    
    }
    
    
    void hist_specify(const Mat &src, const Mat &dst, Mat &result)
    {
    	Histogram1D hist1D;
    	MatND src_hist = hist1D.getHistogram(src);
    	MatND dst_hist = hist1D.getHistogram(dst);
    
    	float src_cdf[256] = { 0 };
    	float dst_cdf[256] = { 0 };
    
    	// 源图像和目标图像的大小不一样,要将得到的直方图进行归一化处理
    	src_hist /= (src.rows * src.cols);
    	dst_hist /= (dst.rows * dst.cols);
    
    	// 计算原始直方图和规定直方图的累积概率
    	for (int i = 0; i < 256; i++)
    	{
    		if (i == 0)
    		{
    			src_cdf[i] = src_hist.at<float>(i);
    			dst_cdf[i] = dst_hist.at<float>(i);
    		}
    		else
    		{
    			src_cdf[i] = src_cdf[i - 1] + src_hist.at<float>(i);
    			dst_cdf[i] = dst_cdf[i - 1] + dst_hist.at<float>(i);
    		}
    	}
    	// 累积概率的差值
    	float diff_cdf[256][256];
    	for (int i = 0; i < 256; i++)
    		for (int j = 0; j < 256; j++)
    			diff_cdf[i][j] = fabs(src_cdf[i] - dst_cdf[j]);
    	// 构建灰度级映射表
    	Mat lut(1, 256, CV_8U);
    	for (int i = 0; i < 256; i++)
    	{
    		// 查找源灰度级为i的映射灰度
    		// 和i的累积概率差值最小的规定化灰度
    		float min = diff_cdf[i][0];
    		int index = 0;
    		for (int j = 1; j < 256; j++)
    		{
    			if (min > diff_cdf[i][j])
    			{
    				min = diff_cdf[i][j];
    				index = j;
    			}
    		}
    		lut.at<uchar>(i) = static_cast<uchar>(index);
    	}
    
    	// 应用查找表,做直方图规定化
    	LUT(src, lut, result);
    }
    
    int main()
    {
    	//图像的获取
    	Mat srcImage = imread("111.jpg");
    	Mat srcImage1 = imread("222.jpg");
    	Mat srcgray, srcgray1;
    		cvtColor(srcImage, srcgray, CV_BGR2GRAY);
    		cvtColor(srcImage1, srcgray1, CV_BGR2GRAY);
    	Histogram1D hist;
    	Mat histImg, histImg1, histImg_result;
    	histImg = hist.getHistogramImage(srcImage);
    	histImg1 = hist.getHistogramImage(srcImage1);
       //直方图规定化
    	Mat result, equalizeImg, equalize1, equalizeImg1;
    	hist_specify(srcgray, srcgray1, result);
    	histImg_result = hist.getHistogramImage(result);
    	imshow("Image", srcImage);
    	imshow("Histogram", histImg);
    	imshow("Histogram1", histImg1);
    	imshow("histImg_result", histImg_result);
    	imshow("srcgray", srcgray);
    	imshow("srcgray1", srcgray1);
    	imshow("result", result);
    	waitKey();
    	return 0;
    }
    

      

     4、H-S直方图绘制

      图像的表示方法除了我们通常使用的RGB方法外,还可以通过HSV空间来进行表示,HSL和HSV都是一种将RGB色彩模型中的点在圆柱坐标系中的表示法。这两种表示法试图做到比RGB基于笛卡尔坐标系的几何结构更加直观。HSV即色相、饱和度、明度(英语:Hue, Saturation, Value),又称HSB,其中B即英语:Brightness。因此在分析图像的H-S直方图时,需要先将源RGB的图像转换到HSV颜色空间内,然后再将对应的H和S通道进行单元划分,再其二维空间上计算对应的直方图,最后再次通过计算直方图空间上的最大值,归一化绘制相应的直方图信息。

      H-S直方图通常是应用在目标检测、特征分析以及目标特征跟踪等场景中。其主要关注点为图像中的位置信息。

    示例:

    //实现直方图的反向投影
    #include "stdafx.h"
    #include <iostream>
    #include <opencv2corecore.hpp>
    #include <opencv2highguihighgui.hpp>
    #include <opencv2imgprocimgproc.hpp>
    
    using namespace cv;
    using namespace std;
    
    int main()
    {
    	Mat srcImage = imread("111.jpg");
    	if (!srcImage.data)
    	{
    		cout << "读入图片失败!" << endl;
    		return -1;
    	}
    	Mat hsvImage;
    	//将图像转换到HSV空间
    	cvtColor(srcImage, hsvImage, CV_BGR2HSV);
    
    	//初始化灰度阶参数
    	int hbins = 30, sbins = 32;
    	int histSize[] = { hbins, sbins };
    	//灰度变化范围设置
    	float hranges[] = { 0, 180 };
    	//饱和度变化范围
    	float sranges[] = { 0, 256 };
    
    	const float *ranges[] = { hranges, sranges };
    	MatND hist;
    	//选取计算直方图通道
    	int channels[] = { 0, 1 };
    	//计算当前通道直方图
    	calcHist(&hsvImage, 1, channels, Mat(), hist, 2, histSize, ranges, true, false);
    
    	double maxValue;
    	//找到直方图的最大值
    	minMaxLoc(hist, 0, &maxValue, 0, 0);
    	int scale = 10;
    	Mat histImage = Mat::zeros(sbins*scale, hbins * 10, CV_8UC3);
    	//遍历H和S通道
    	for (int h = 0; h < hbins; h++)
    	{
    		for (int s = 0; s < sbins; s++)
    		{
    			float binVal = hist.at<float>(h, s);
    			//根据最大值计算变换范围
    			int intensity = cvRound(binVal * 255 / maxValue);
    			//进行绘图
    			rectangle(histImage,
    				Point(h*scale, s*scale),
    				Point((h + 1)*scale - 1, (s + 1)*scale - 1),
    				Scalar::all(intensity),
    				CV_FILLED);
    		}
    	}
    	imshow("原图像", srcImage);
    	imshow("H-S直方图", histImage);
    	waitKey();
    	return 0;
    }
    

      

    5、直方图的反向投影

      如果一幅图像的区域中显示的是一种纹理结构或者一个独特的物体,那么这个区域的直方图可以看作是一个概率函数,其表现形式是某个像素属于该纹理或物体的概率。直方图的反向投影是利用直方图模型计算给定的像素点的特征。反向投影在某一位置的值是源图像在对应位置的像素值的累计。简单的讲就是首先计算某一特征的直方图模型,然后使用模型去寻找图像中存在的该特征的方法。主要用于在输入图像中查找与特定图像最匹配的点或者区域,即定位模板图像出现在图像中的位置。

      为了方便地计算直方图的反向投影,OpenCV中提供了一个用来简单计算hue通道的直方图反向投影的函数calcBackProject,下面对这个函数进行说明,函数原型为:

     void calcBackProject( const Mat* images, int nimages,const int* channels, InputArray hist,OutputArray backProject,const float** ranges,double scale=1, bool uniform=true );

    第一个参数 images表示输入图像源指针,需要注意的是,图像的源必须具有同样的深度信息,也就是说,可以是CV_8U或CV_32U,图像可以有任意的通道数。

    第二个参数nimages表示的是待计算图像源中图像的个数,通常单幅图像计算直方图时这个参数的值为1.

    第三个参数channels指的是需要同级的图像的通道维数数组索引,第一个数组的通道由0到arrays[0].channels()-1,第二个数组的通道从array[0].channels()到arrays[0].channels() + array[1].channels()-1。并以此类推。

    第四个参数hist表示输入源图像的直方图

    第五个参数backProject表示的是目标图像的反向投影图,这个图可以是单通道,与Image[0]具有同样的尺度和深度

    第六个参数ranges表示用于指出直方图每一维的每个bin上下界范围的数组,对于均匀直方图这个参数是一个包含两个元素的数组。

    第七个参数scale表示可选的输出反向投影的尺寸的参数

    第八个参数uniform是直方图统一显示的标志。

           在给出具体程序之前,先简单介绍一下会用到的一个OpenCV中的函数,函数声明如下:

    void mixChannels(const Mat* src, size_t nsrcs, Mat* dst, size_t ndsts, const int* fromTo, size_t npairs);
    CV_EXPORTS void mixChannels(const vector<Mat>& src, vector<Mat>& dst, const int* fromTo, size_t npairs);
    void mixChannels(InputArrayOfArrays src, InputArrayOfArrays dst,const vector<int>& fromTo);

    此函数为重排图像通道提供了比较先进的机制,这里函数实现的功能是通道复制到特定的通道

    参数src表示的是输入图像源组,被复制通道的输入图像数据

    参数nsrc指的是待输入图像源中图像的图像的个数

    参数dst表示的是输出目标图像数组,存储复制后的通道,所有的数组必须要事先进行分配,而且要和输入数组的大小和深度相同。

    参数ndsts指的是目标数组中图像的总数

    参数fromTo指的是通道索引对的数组,表示的是将输入图像数组复制到目标数组通道,如果是偶数表示输入矩阵索引,如果是奇数表示的是输出矩阵索引,如果是偶数而且其下标为负,则相应输出矩阵为0

    参数npairs表示fromTo中的索引对

    示例:

    //实现直方图的反向投影
    #include "stdafx.h"
    #include <iostream>
    #include <opencv2corecore.hpp>
    #include <opencv2highguihighgui.hpp>
    #include <opencv2imgprocimgproc.hpp>
    
    using namespace cv;
    using namespace std;
    
    int main()
    {
    	Mat srcImage = imread("111.jpg");
    	if (!srcImage.data)
    	{
    		cout << "图像打开失败!" << endl;
    		return -1;
    	}
    
    	// 将图像转换到HSV颜色空间
    	Mat hsvImage;
    	cvtColor(srcImage, hsvImage, CV_BGR2HSV);
    	//进行 hue 通道分离
    	Mat hueImage;
    	hueImage.create(hsvImage.size(), hsvImage.depth());
    	int ch[] = { 0, 0 };
    	mixChannels(&hsvImage, 1, &hueImage, 1, ch, 1);
    
    	//初始化直方图计算参数
    	int bins = 25;
    	MatND hist;
    	int histSize = MAX(bins, 2);
    	float hue_range[] = { 0, 100 };
    	const float*ranges = { hue_range };
    
    	//计算直方图并进行归一化操作
    	calcHist(&hueImage, 1, 0, Mat(), hist, 1,
    		&histSize, &ranges, true, false);
    	normalize(hist, hist, 0, 255, NORM_MINMAX, -1, Mat());
    
    	//计算反向投影
    	MatND backproj;
    	calcBackProject(&hueImage, 1, 0, hist, backproj, &ranges, 1, true);
    
    	//定义输出图像
    	int w = 320, h = 360;
    	int bin_w = cvRound((double)w / histSize);
    	Mat histImage = Mat::zeros(w, h, CV_8UC3);
    	for (int i = 0; i < bins; i++)
    	{
    		//绘制直方图
    		rectangle(histImage, Point(i*bin_w, h),
    			Point((i + 1)*bin_w, h - cvRound(hist.at<float>(i)*h / 255.0)),
    			Scalar(0, 0, 255), -1);
    	}
    
    	//显示原图像和反向投影图像
    	imshow("反向投影图", backproj);
    	imshow("原图像", srcImage);
    	imshow("直方图", histImage);
    	//进行直方图均衡化
    	equalizeHist(backproj, backproj);
    	imshow("直方图均衡化后的直方图", backproj);
    	waitKey();
    	return 0;
    }

    程序运行结果如下所示:

     

     参考资料:

    图像处理基础(8):图像的灰度直方图、直方图均衡化、直方图规定化(匹配)

    【OpenCV图像处理】十、图像的直方图及相关处理(上)

    【OpenCV图像处理】十一、图像的直方图与相关处理(中)

    【OpenCV图像处理】十二、图像的直方图与相关处理(下)

    OpenCV-跟我一起学数字图像处理之直方图均衡化

    OpenCV直方图(直方图、直方图均衡,直方图匹配,原理、实现)

  • 相关阅读:
    IsEmpty函数和IsNull函数之间的区别
    JavaScript的self和this使用小结
    PHP中读写文件实现代码
    ExtJs的数据代理proxy
    php递归列出所有文件和目录的代码
    解析Extjs与php数据交互(增删查改)
    MySql 批量创建、导入实例
    MySQL 进入 导入
    小知识:批量导入数据
    js 扩展实例
  • 原文地址:https://www.cnblogs.com/noticeable/p/10449867.html
Copyright © 2011-2022 走看看