OpenCV2馬拉松第8圈——繪製直方圖

收入囊中
  • 灰度直方圖
  • 彩色直方圖

葵花寶典
直方圖的理論還是非常豐富的,應用也很多,諸如:
直方圖均衡化
直方圖匹配(meanshift,camshift)

在這裏,我先介紹基礎,如何繪製圖像的直方圖。
拿灰度圖像來說,直方圖就是不同的灰度對應的個數,橫軸(x)就是[0,256), 縱軸(y)就是對應的個數
如下圖,分別是灰度直方圖和彩色直方圖





初識API
C++: void calcHist(const Mat* images, int nimages, const int* channels, InputArray mask, SparseMat& hist, int dims, const int* histSize, const float** ranges, bool uniform=true, bool accumulate=false )
 
  • images – 是一個圖像數組,簡單起見,我們都只傳一個圖像. 
  • nimages – 圖片數組大小,我們固定爲1
  • channels – 是一個數組,灰度圖像爲0就可以,彩色圖像要0,1,2
  • mask – 這裏我們不需要,傳Mat()就可以
  • hist – 輸出的直方圖
  • dims – 計算彩色RGB要3,gray傳1
  • histSize – 是一個數組,一般來說內容就是256
  • ranges – 是一個二維數組,每個數組包括的都是一個範圍,[0,255]最常用,下面會看到
  • uniform – 在這裏用默認就足夠.
  • accumulate – 在這裏用默認就足夠.
假設我們要計算灰度圖像的直方圖,如下調用即可:
int histSize[1]; // number of bins
float hranges[2]; // min and max pixel value
const float* ranges[1];
int channels[1]; // only 1 channel used here

histSize[0]= 256;
hranges[0]= 0.0;
hranges[1]= 255.0;
ranges[0]= hranges;
channels[0]= 0; // by default, we look at channel 0

// Computes the 1D histogram.
Mat hist;
// Compute histogram
calcHist(&image,1,channels,Mat(),hist,1,histSize,ranges);



荷槍實彈
先看看設計的一個灰度類
第一個方法是構造函數
第二個方法是獲得直方圖
第三個方法是繪製直方圖
class Histogram1D {
private:
	int histSize[1]; // number of bins
	float hranges[2]; // min and max pixel value
	const float* ranges[1];
	int channels[1]; // only 1 channel used here
public:
	Histogram1D() {
		histSize[0]= 256;
		hranges[0]= 0.0;
		hranges[1]= 255.0;
		ranges[0]= hranges;
		channels[0]= 0; // by default, we look at channel 0
	}
	
	// Computes the 1D histogram.
	Mat getHistogram(const cv::Mat &image) {
		Mat hist;
		// Compute histogram
		calcHist(&image,1,channels,Mat(),hist,1,histSize,ranges);
		return hist;
	}
	
	Mat getHistogramImage(const cv::Mat &image){
		// Compute histogram first
		Mat hist= getHistogram(image);
		// Get min and max bin values
		double maxVal=0;
		double minVal=0;
		minMaxLoc(hist, &minVal, &maxVal, 0, 0);
		// Image on which to display histogram
		Mat histImg(histSize[0], histSize[0],CV_8U,Scalar(255));
		// set highest point at 90% of nbins
		int hpt = static_cast<int>(0.9*histSize[0]);
		// Draw a vertical line for each bin
		for( int h = 0; h < histSize[0]; h++ ) {
			float binVal = hist.at<float>(h);
			int intensity = static_cast<int>(binVal*hpt/maxVal);
			// This function draws a line between 2 points
			line(histImg,Point(h,histSize[0]),
			Point(h,histSize[0]-intensity),
			Scalar::all(0));
		}
		return histImg;
	}
};

然後,主函數調用
Mat image,gray;
image = imread( argv[1], 1 );
if( !image.data )
	return -1;
 cvtColor(image, gray, CV_BGR2GRAY);
    
Histogram1D h;
namedWindow("Histogram");
imshow("Histogram",h.getHistogramImage(gray));

灰度直方圖已經畫好,下面畫彩色直方圖
畫彩色直方圖的過程和灰度直方圖差不多,是把RGB三通道分別獨自出來,各自計算
/// Separate the image in 3 places ( B, G and R )
vector<Mat> bgr_planes;
split( src, bgr_planes );
現在,你就有了3個Mat存放在bgr_planes,再次強調下,OpenCV裏面彩色圖像的第一個通道是blue,BGR哦
然後,你用我們之前的Histogram1D類分別調用就可以了


舉一反三
有時候,我們還可以繪製不同色彩空間的直方圖
下面我們繪製2D Hue-Saturation histogram for a color image


由於我對HSV色彩空間的理論沒有學過,所以下面就貼出官方教程的代碼
#include <cv.h>
#include <highgui.h>

using namespace cv;

int main( int argc, char** argv )
{
    Mat src, hsv;
    if( argc != 2 || !(src=imread(argv[1], 1)).data )
        return -1;

    cvtColor(src, hsv, CV_BGR2HSV);

    // Quantize the hue to 30 levels
    // and the saturation to 32 levels
    int hbins = 30, sbins = 32;
    int histSize[] = {hbins, sbins};
    // hue varies from 0 to 179, see cvtColor
    float hranges[] = { 0, 180 };
    // saturation varies from 0 (black-gray-white) to
    // 255 (pure spectrum color)
    float sranges[] = { 0, 256 };
    const float* ranges[] = { hranges, sranges };
    MatND hist;
    // we compute the histogram from the 0-th and 1-st channels
    int channels[] = {0, 1};

    calcHist( &hsv, 1, channels, Mat(), // do not use mask
             hist, 2, histSize, ranges);
    double maxVal=0;
    minMaxLoc(hist, 0, &maxVal, 0, 0);

    int scale = 10;
    Mat histImg = Mat::zeros(sbins*scale, hbins*10, CV_8UC3);

    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/maxVal);
            rectangle( histImg, Point(h*scale, s*scale),
                        Point( (h+1)*scale - 1, (s+1)*scale - 1),
                        Scalar::all(intensity),
                        CV_FILLED );
        }

    namedWindow( "Source", 1 );
    imshow( "Source", src );

    namedWindow( "H-S Histogram", 1 );
    imshow( "H-S Histogram", histImg );
    waitKey();
}


計算機視覺討論羣:162501053
轉載請註明:http://blog.csdn.net/abcd1992719g



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章