imgproc模塊--直方圖

1.目的
(1)如何使用OpenCV函數 split 將圖像分割成單通道數組。
(2)如何使用OpenCV函數 calcHist 計算圖像陣列的直方圖。
(3)如何使用OpenCV函數 normalize 歸一化數組。

2.原理
[1]直方圖
直方圖是對數據的集合 統計 ,並將統計結果分佈於一系列預定義的 bins 中。這裏的 數據 不僅僅指的是灰度值 (如上一篇您所看到的), 統計數據可能是任何能有效描述圖像的特徵。

假設有一個矩陣包含一張圖像的信息 (灰度值 0-255):如果我們按照某種方式去 統計 這些數字,會發生什麼情況呢? 既然已知數字的 範圍 包含 256 個值, 我們可以將這個範圍分割成子區域(稱作 bins), 如:
bins

然後再統計掉入每一個 bini 的像素數目。採用這一方法來統計上面的數字矩陣,我們可以得到下圖( x軸表示 bin, y軸表示各個bin中的像素個數)。
統計結果

以上只是一個說明直方圖如何工作以及它的用處的簡單示例。直方圖可以統計的不僅僅是顏色灰度, 它可以統計任何圖像特徵 (如 梯度, 方向等等)。

[2]直方圖的細節:
a. dims: 需要統計的特徵的數目, 在上例中, dims = 1 因爲我們僅僅統計了灰度值(灰度圖像)。
b. bins: 每個特徵空間 子區段 的數目,在上例中, bins = 16
c. range: 每個特徵空間的取值範圍,在上例中, range = [0,255]
PS:統計兩個特徵,直方圖是3維的,x,y分別代表一個特徵, z代表落入(x,y)中的樣本數目。同樣的方法適用於更高維的情形 。

3.部分代碼解釋
(1)calcHist

    /*
    calcHist參數解釋(10個參數)
    &rgb_planes[0]: 輸入數組(或數組集)
    1: 輸入數組的個數 (這裏我們使用了一個單通道圖像,我們也可以輸入數組集 )
    0: 需要統計的通道 (dim)索引 ,這裏我們只是統計了灰度 (且每個數組都是單通道)所以只要寫 0 就行了。    
    Mat(): 掩碼( 0 表示忽略該像素), 如果未定義,則不使用掩碼
    r_hist: 儲存直方圖的矩陣
    1: 直方圖維數
    histSize: 每個維度的bin數目
    histRange: 每個維度的取值範圍,必須是const,一個二維數組
    uniform 和 accumulate: bin大小相同,清除直方圖痕跡
    */
    calcHist(&rgb_plane[0], 1, 0, Mat(), r_hist, 1, &histSize, &histRange, uniform, accumulate);
    calcHist(&rgb_plane[1], 1, 0, Mat(), g_hist, 1, &histSize, &histRange, uniform, accumulate);
    calcHist(&rgb_plane[2], 1, 0, Mat(), b_hist, 1, &histSize, &histRange, uniform, accumulate);

(2)normalize

    /*
    normalize參數解釋
    r_hist:歸一化圖像
    r_hist:歸一化結果
    0-hist_image.rows:歸一化範圍
    NORM_MINMAX:歸一化方法
    -1:表示歸一化結果數組和歸一化前數組的類型一致
    Mat():掩碼,Mat()表示無掩碼操作
    */
    normalize(r_hist, r_hist, 0, hist_image.rows, NORM_MINMAX, -1, Mat());
    normalize(g_hist, g_hist, 0, hist_image.rows, NORM_MINMAX, -1, Mat());
    normalize(b_hist, b_hist, 0, hist_image.rows, NORM_MINMAX, -1, Mat());

4.完整代碼
(1)CommonInclude.h

#ifndef COMMON_INCLUDE
#define COMMON_INCLUDE
#include<iostream>
#include<vector>
#include<opencv2/core/core.hpp>
#include<opencv2/highgui/highgui.hpp>
#include<opencv2/imgproc/imgproc.hpp>
using namespace std;
using namespace cv;
#endif

(2)Hist.cpp

#include"CommonInclude.h"

int main(int argc, char** argv){
    if(argc < 2){
        cout << "more parameters are required!!!" << endl;
        return(-1);
    }
    Mat src;
    vector<Mat> rgb_plane;
    Mat r_hist, g_hist, b_hist;
    src = imread(argv[1]);
    if(!src.data){
        cout << "error to read image!!!" << endl;
        return(-1);
    }
    //分離rgb通道
    split(src, rgb_plane);
    //直方圖參數
    int histSize = 255;//bins個數
    float range[] = {0,255};//取值範圍
    const float* histRange = {range};//必須是const,否則calcHist會報錯
    bool uniform = true;
    bool accumulate = true;
    /*
    calcHist參數解釋(10個參數)
    &rgb_planes[0]: 輸入數組(或數組集)
    1: 輸入數組的個數 (這裏我們使用了一個單通道圖像,我們也可以輸入數組集 )
    0: 需要統計的通道 (dim)索引 ,這裏我們只是統計了灰度 (且每個數組都是單通道)所以只要寫 0 就行了。    
    Mat(): 掩碼( 0 表示忽略該像素), 如果未定義,則不使用掩碼
    r_hist: 儲存直方圖的矩陣
    1: 直方圖維數
    histSize: 每個維度的bin數目
    histRange: 每個維度的取值範圍,必須是const,一個二維數組
    uniform 和 accumulate: bin大小相同,清除直方圖痕跡
    */
    calcHist(&rgb_plane[0], 1, 0, Mat(), r_hist, 1, &histSize, &histRange, uniform, accumulate);
    calcHist(&rgb_plane[1], 1, 0, Mat(), g_hist, 1, &histSize, &histRange, uniform, accumulate);
    calcHist(&rgb_plane[2], 1, 0, Mat(), b_hist, 1, &histSize, &histRange, uniform, accumulate);

    //創建畫布  
    int h = 400, w = 400;
    int bin_w = cvRound((double)w/histSize);
    Mat hist_image = Mat(w, h, CV_8UC3, Scalar(0,0,0));

    //歸一化
    /*
    normalize參數解釋
    r_hist:歸一化圖像
    r_hist:歸一化結果
    0-hist_image.rows:歸一化範圍
    NORM_MINMAX:歸一化方法
    -1:表示歸一化結果數組和歸一化前數組的類型一致
    Mat():掩碼,Mat()表示無掩碼操作
    */
    normalize(r_hist, r_hist, 0, hist_image.rows, NORM_MINMAX, -1, Mat());
    normalize(g_hist, g_hist, 0, hist_image.rows, NORM_MINMAX, -1, Mat());
    normalize(b_hist, b_hist, 0, hist_image.rows, NORM_MINMAX, -1, Mat());

    //繪製直方圖
    for(int i=1; i<histSize; i++){
        line(hist_image, Point(bin_w*(i-1), h-cvRound(r_hist.at<float>(i-1))), Point(bin_w*i, h-cvRound(r_hist.at<float>(i))), Scalar(0,0,255), 1, 8);
        line(hist_image, Point(bin_w*(i-1), h-cvRound(g_hist.at<float>(i-1))), Point(bin_w*i, h-cvRound(g_hist.at<float>(i))), Scalar(0,255,0), 1, 8);
        line(hist_image, Point(bin_w*(i-1), h-cvRound(b_hist.at<float>(i-1))), Point(bin_w*i, h-cvRound(b_hist.at<float>(i))), Scalar(255,0,0), 1, 8);
    }
    namedWindow("RGB Histogram", CV_WINDOW_AUTOSIZE);
    imshow("RGB Histogram", hist_image);
    waitKey(0);
    return(0);
}

參考文獻
1.http://www.opencv.org.cn/opencvdoc/2.3.2/html/doc/tutorials/imgproc/histograms/histogram_calculation/histogram_calculation.html#histogram-calculation

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