十二、OpenCV實現圖像的直方圖處理 OpenCV直方圖(直方圖、直方圖均衡,直方圖匹配,原理、實現) 圖像處理基礎(8):圖像的灰度直方圖、直方圖均衡化、直方圖規定化(匹配) OpenCV-跟我一

一、直方圖

  一幅圖像由不一樣灰度值的像素組成,圖像中灰度的分佈狀況是該圖像的一個重要特徵。圖像的灰度直方圖就描述了圖像中灰度分佈狀況,可以很直觀的展現出圖像中各個灰度級所佔的多少。
圖像的灰度直方圖是灰度級的函數,描述的是圖像中具備該灰度級的像素的個數:其中,橫座標是灰度級,縱座標是該灰度級出現的頻率。
html

不過一般會將縱座標歸一化到[0,1][0,1]區間內,也就是將灰度級出現的頻率(像素個數)除以圖像中像素的總數。灰度直方圖的計算公式以下:ios

 

其中算法

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

  該函數可以同時計算多個圖像,多個通道,不一樣灰度範圍的灰度直方圖.函數

其參數說明以下:post

images,輸入圖像的數組,這些圖像要有相同大大小,相同的深度(CV_8U CV_16U CV_32F).
nimages ,輸入圖像的個數
channels,要計算直方圖的通道個數。
mask,可選的掩碼,不使用時可設爲空。要和輸入圖像具備相同的大小,在進行直方圖計算的時候,只會統計該掩碼不爲0的對應像素
hist,輸出的直方圖
dims,直方圖的維度
histSize,直方圖每一個維度的大小
ranges,直方圖每一個維度要統計的灰度級的範圍
uniform,是否進行歸一化,默認爲true
accumulate,累積標誌,默認值爲false。ui

示例:

下面是一個使用caclHist的一個示例,這裏對其進行了一個封裝,只繪製灰度直方圖url

#include "stdafx.h"
#include <vector>
#include <iostream>
#include <opencv2\core\core.hpp>
#include <opencv2\highgui\highgui.hpp>
#include <opencv2\imgproc\imgproc.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;
}

 運行結果以下:spa

        假如圖像的灰度分佈不均勻,其灰度分佈集中在較窄的範圍內,使圖像的細節不夠清晰,對比度較低。一般採用直方圖均衡化直方圖規定化兩種變換,使圖像的灰度範圍拉開或使灰度均勻分佈,從而增大反差,使圖像細節清晰,以達到加強的目的。.net

二、直方圖均衡

      直方圖均衡化就是對圖像進行非線性拉伸,從新分配圖像的灰度值,使必定範圍內圖像的灰度值大體相等,它是以累計分佈函數變化法爲基礎的直方圖修正法。它將當前的灰度分佈經過一個變換函數,變換爲範圍更寬、灰度分佈更均勻的圖像。也就是將原圖像的直方圖修改成在整個灰度區間內大體均勻分佈,所以擴大了圖像的動態範圍,加強圖像的對比度。

    方圖均衡化算法的步驟以下所示:

  • 計算原圖像的灰度直方圖
  •  

    其中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 <opencv2\core\core.hpp>
#include <opencv2\highgui\highgui.hpp>
#include <opencv2\imgproc\imgproc.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直方圖(直方圖、直方圖均衡,直方圖匹配,原理、實現)

 

三、直方圖匹配(直方圖規定化)

    直方圖均衡化能夠自動的肯定變換函數,該函數尋求產生又均勻直方圖的輸出圖像,須要自動加強時,這是一種好方法,由於這種方法獲得的結果能夠預知,而且這種方法的實現也很簡單,可是在有些應用中這種自動的加強並非最好的方法。有時候,須要圖像具備

某一特定的直方圖形狀(也就是灰度分佈),而不是均勻分佈的直方圖,這時候可使用直方圖規定化

 直方圖規定化,也叫作直方圖匹配,用於將圖像變換爲某一特定的灰度分佈,也就是其目的的灰度直方圖是已知的。這其實和均衡化很相似,均衡化後的灰度直方圖也是已知的,是一個均勻分佈的直方圖;而規定化後的直方圖能夠隨意的指定,也就是在執行規定化操

做時,首先要知道變換後的灰度直方圖,這樣才能肯定變換函數。規定化操做可以有目的的加強某個灰度區間,相比於均衡化操做,規定化多了一個輸入,可是其變換後的結果也更靈活。

   直方圖的規定化也較爲簡單。能夠利用均衡化後的直方圖做爲一箇中間過程,而後求取規定化的變換函數。具體步驟以下:

  • 將原始圖像的灰度直方圖進行均衡化,獲得一個變換函數
    其中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 <opencv2\core\core.hpp>
#include <opencv2\highgui\highgui.hpp>
#include <opencv2\imgproc\imgproc.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;
}

  

 

 四、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 <opencv2\core\core.hpp>
#include <opencv2\highgui\highgui.hpp>
#include <opencv2\imgproc\imgproc.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;
}

  

五、直方圖的反向投影

  若是一幅圖像的區域中顯示的是一種紋理結構或者一個獨特的物體,那麼這個區域的直方圖能夠看做是一個機率函數,其表現形式是某個像素屬於該紋理或物體的機率。直方圖的反向投影是利用直方圖模型計算給定的像素點的特徵。反向投影在某一位置的值是源圖像在對應位置的像素值的累計。簡單的講就是首先計算某一特徵的直方圖模型,而後使用模型去尋找圖像中存在的該特徵的方法。主要用於在輸入圖像中查找與特定圖像最匹配的點或者區域,即定位模板圖像出如今圖像中的位置。

  爲了方便地計算直方圖的反向投影,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 <opencv2\core\core.hpp>
#include <opencv2\highgui\highgui.hpp>
#include <opencv2\imgproc\imgproc.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圖像處理】10、圖像的直方圖及相關處理(上)

【OpenCV圖像處理】11、圖像的直方圖與相關處理(中)

【OpenCV圖像處理】12、圖像的直方圖與相關處理(下)

OpenCV-跟我一塊兒學數字圖像處理之直方圖均衡化

OpenCV直方圖(直方圖、直方圖均衡,直方圖匹配,原理、實現)

相關文章
相關標籤/搜索