2009年1月2日 星期五

OpenCV統計應用-直方圖比較

cvCompareHist(),是比較兩個統計直方圖的分布,總共有四個方法,被定義如下:

#define CV_COMP_CORREL 0
#define CV_COMP_CHISQR 1
#define CV_COMP_INTERSECT 2
#define CV_COMP_BHATTACHARYYA 3

而這些方法分別為相關係數,卡方,交集法以及在做常態分布比對的Bhattacharyya距離,這些方法都是用來做統計直方圖的相似度比較的方法,而且,都是根據統計學的概念,這邊就簡單的拿來用灰階統計直方圖來比較,而這部份的比較方式,是由圖形的色彩結構來著手,下面就簡單的用三種情況來分析它們距離比較的方式

直方圖比較實作
#include <cv.h>
#include <highgui.h>
#include <stdio.h>
#include <stdlib.h>

int HistogramBins = 256;
float HistogramRange1[2]={0,255};
float *HistogramRange[1]={&HistogramRange1[0]};

int main()
{
    IplImage *Image1=cvLoadImage("RiverBank.jpg",0);
    IplImage *Image2=cvLoadImage("DarkClouds.jpg",0);

    CvHistogram *Histogram1=cvCreateHist(1,&HistogramBins,CV_HIST_ARRAY,HistogramRange);
    CvHistogram *Histogram2=cvCreateHist(1,&HistogramBins,CV_HIST_ARRAY,HistogramRange);

    cvCalcHist(&Image1,Histogram1);
    cvCalcHist(&Image2,Histogram2);

    cvNormalizeHist(Histogram1,1);
    cvNormalizeHist(Histogram2,1);

    printf("CV_COMP_CORREL : %.4f\n",cvCompareHist(Histogram1,Histogram2,CV_COMP_CORREL));
    printf("CV_COMP_CHISQR : %.4f\n",cvCompareHist(Histogram1,Histogram2,CV_COMP_CHISQR));
    printf("CV_COMP_INTERSECT : %.4f\n",cvCompareHist(Histogram1,Histogram2,CV_COMP_INTERSECT));
    printf("CV_COMP_BHATTACHARYYA : %.4f\n",cvCompareHist(Histogram1,Histogram2,CV_COMP_BHATTACHARYYA));

    cvNamedWindow("Image1",1);
    cvNamedWindow("Image2",1);
    cvShowImage("Image1",Image1);
    cvShowImage("Image2",Image2);
    cvWaitKey(0);
}

原始圖片:





 

執行結果:

(1)RiverBank.jpg & DarkClouds.jpg

Output:
CV_COMP_CORREL : -0.1407
CV_COMP_CHISQR : 0.6690
CV_COMP_INTERSECT : 0.4757
CV_COMP_BHATTACHARYYA : 0.4490

(2)RiverBank.jpg & RiverBank.jpg

Output:
CV_COMP_CORREL : 1
CV_COMP_CHISQR : 0
CV_COMP_INTERSECT : 1
CV_COMP_BHATTACHARYYA : 0

(3)Black.jpg & White.jpg

Output:
CV_COMP_CORREL : 1
CV_COMP_CHISQR : 1
CV_COMP_INTERSECT : 0
CV_COMP_BHATTACHARYYA : 1

這邊的直方圖比較,則是將它們的統計直方圖用cvNormalizeHist()正規化成1,在由正規化的統計分佈來做直方圖的比較,從上面的輸出結果可以推測出,卡方法以及Bhattacharyya是數值越小圖形越相似,而相關係數則是看圖形的分佈程度,因此第三個Black.jpg&White.jpg所顯示相關係數的結果才會是1,這邊的圖形比較用的是統計學的方法,而一般的比較方式還有歐幾里德距離的方式,在這個cvCompareHist()的函式,也可以實作出多通道的多維度直方圖比較,而且支援CV_HIST_ARRAY及CV_HIST_SPARSE這兩種直方圖資料結構的格式,而這些比對方法的相關公式如下


相關係數法

而它的公式推導如下



再來是卡方的方式

這邊的卡方法跟一般的卡方檢定不太一樣,下面是一般適合度檢定(Goodness of fit test)的公式

o為觀察者次數,e為期望值次數


交集的方式就比較簡單了,兩個直方圖取最小的做累加



再來就是常態分配比對的Bhattacharyya距離






2008年12月29日 星期一

OpenCV統計應用-直方圖反向投影

影像處理的統計直方圖,可以知道一張圖片在該色彩空間的數據分布狀況,而這邊,就要介紹到直方圖反向投影的函式,直方圖反向投影,也就是將數據分布的狀況依照Look-up table的方式對應回去,實際上,這個函式是跟前面介紹到的cvLUT()是一樣的,只不過,差別是差異在cvLUT()的第三個引數改變成CvHistogram資料結構的輸入,直方圖反向投影,cvCalcBackProject()的第一個引數是輸入單通道IplImage資料結構,第二個引數是輸出單通道IplImage反向投影圖形資料結構,第三個引數是選定要被反向投影的CvHistogram直方圖資料結構,而cvCalcBackProject()把前面提到的Look-up table的計算方式包在cvCalcBackProject()函式的底層,因此,它可以整合CvHistogram這個資料結構做更多的應用,下面這個就是修改前面的範例"OpenCV統計應用-CvHistogram直方圖資料結構",來做直方圖反向投影的程式

灰階直方圖反向投影
#include <cv.h>
#include <highgui.h>
#include <stdio.h>


int HistogramBins = 50;
int HistogramBinWidth;
float HistogramRange1[2]={0,255};
float *HistogramRange[1]={&HistogramRange1[0]};

CvPoint Point1;
CvPoint Point2;

int main()
{
    IplImage *GrayImage1;
    IplImage *Image1;
    IplImage *Image2;
    IplImage *BackProjectImage;
    CvHistogram *Histogram1;
    IplImage *HistogramImage1;

    Image1=cvLoadImage("Riverbank.jpg",1);
    Image2=cvCreateImage(cvGetSize(Image1),IPL_DEPTH_8U,3);
    GrayImage1=cvLoadImage("Riverbank.jpg",0);
    BackProjectImage=cvCreateImage(cvGetSize(Image1),IPL_DEPTH_8U,1);
    Histogram1 = cvCreateHist(1,&HistogramBins,CV_HIST_ARRAY,HistogramRange);
    HistogramImage1 = cvCreateImage(cvSize(256,300),8,3);

    cvSetZero(HistogramImage1);
    HistogramImage1->origin=1;
    HistogramBinWidth=256/HistogramBins;

    cvCalcHist(&GrayImage1,Histogram1);
    cvNormalizeHist(Histogram1,5000);

    cvThreshHist(Histogram1,50);
    cvCalcBackProject(&GrayImage1,BackProjectImage,Histogram1);
    cvCopy(Image1,Image2,BackProjectImage);

    for(int i=0;i<HistogramBins;i++)
    {
        printf("%f\n",cvQueryHistValue_1D(Histogram1,i));
        Point1=cvPoint(i*HistogramBinWidth,0);
        Point2=cvPoint((i+1)*HistogramBinWidth,(int)        cvQueryHistValue_1D(Histogram1,i));

        cvRectangle(HistogramImage1,Point1,Point2,CV_RGB(127,127,127));
    }

    cvNamedWindow("Histogram1",1);
    cvNamedWindow("Riverbank",1);
    cvNamedWindow("Back Project RiverBank",1);
    cvShowImage("Riverbank",Image1);
    cvShowImage("Back Project RiverBank",Image2);
    cvShowImage("Histogram1",HistogramImage1);
    cvWaitKey(0);
}

執行結果:


這邊就是拿前面灰階去除較小直方圖區塊的程式碼做修改,然後將前面去除最小區塊的部份,對應到彩色的圖片去了,顯示的結果會是,只要是影像裡面灰階值分佈數量比較少的數據,全部都被對應成黑色的像素,也就是全部都變成0,而cvCalcBackProject(),就跟cvLUT()一樣直接拿直方圖的數據去對應,假設一個直方圖從頭開始的數據為254,129,80,70....那麼只要是圖片內像素值為1的數據就會對應到254,1的圖片裡面像素值的數據就會直接變成254,像素值為2的就會對應到129,2的數據就會直接變成129,以此類推,因此,用cvThreshHist()去除小於50的直方圖區段,讓小於50的全部歸0,再來,對他做一個圖片的反向投影,所對應出來的結果雖然不是0或255,可是它卻可以直接拿來當做是遮罩,也就是說,直接拿來給cvCopy()來做對應,因此,反向投影的結果就出來啦,在遮罩的部份就要參考"資料結構操作與運算-圖形的Mask遮罩實作",而這段程式碼則是用到"OpenCV統計應用-CvHistogram資料結構操作"裡面的部份.

在OpenCV Documentation的部份有提到cvCalcBackProject()可以對HSV色彩空間的Hue做反向投影,到底是怎麼實作出來呢?在OpenCV的Sample Code裡面有一個camshift.c的程式,就是用到這個反向投影的函式,而它的反向投影的簡單範例,原理就如下所示

HSV色彩空間反向投影
#include <cv.h>
#include <highgui.h>
#include <stdio.h>


IplImage *Image1,*Image2;
IplImage *HSVImage;
IplImage *HueImage;
IplImage *BackProjectHueImage,*BackProjectImage;
CvHistogram *Histogram1;
IplImage *HistogramImage1;
CvPoint Point1,Point2;

int HueValue=0;

int HistogramBins = 180;
int HistogramBinWidth;
float HistogramRange1[2]={0,180};
float *HistogramRange[1]={&HistogramRange1[0]};
void onTrackbar(int position);

int main()
{

    Image1 =cvLoadImage("Riverbank.jpg",1);
    HSVImage = cvCreateImage( cvGetSize(Image1),8,3);
    HueImage = cvCreateImage( cvGetSize(Image1),8,1);

    BackProjectHueImage = cvCreateImage( cvGetSize(Image1),8,1);
    BackProjectImage = cvCreateImage( cvGetSize(Image1),8,3);

    Histogram1 = cvCreateHist(1,&HistogramBins,CV_HIST_ARRAY,HistogramRange);
    HistogramImage1 = cvCreateImage(cvSize(180,300),8,3);
    HistogramImage1->origin=1;

    cvCvtColor( Image1, HSVImage, CV_BGR2HSV );
    cvSplit(HSVImage,HueImage,0,0,0);
    cvCalcHist( &HueImage, Histogram1);
    cvNormalizeHist(Histogram1,5000);
    cvZero( HistogramImage1 );
    cvNot(HistogramImage1,HistogramImage1);
    HistogramBinWidth = HistogramImage1->width/HistogramBins;
    for(int i=0;i<HistogramBins;i++)
    {

        Point1=cvPoint(i,0);
        Point2=cvPoint(i,(int)cvQueryHistValue_1D(Histogram1,i));
        printf("%d\n",(int)cvQueryHistValue_1D(Histogram1,i));
        cvLine(HistogramImage1,Point1,Point2,CV_RGB(127,127,127));
    }
    cvNamedWindow("Riverbank",1 );
    cvNamedWindow("Hue Histogram",1);
    cvCreateTrackbar("Hue Thresh","Riverbank",&HueValue,250,onTrackbar);
    cvShowImage("Riverbank",Image1);
    cvShowImage("Hue Histogram",HistogramImage1);
    cvWaitKey(0);

}

void onTrackbar(int position)
{
    IplImage *Image2=cvCreateImage( cvGetSize(Image1),8,3);
    CvHistogram *Histogram2= cvCreateHist(1,&HistogramBins,CV_HIST_ARRAY,HistogramRange);
    cvCopyHist(Histogram1,&Histogram2);

    cvThreshHist(Histogram2,position);
    cvCalcBackProject(&HueImage, BackProjectHueImage, Histogram2);
    cvCopy(Image1,Image2,BackProjectHueImage);

    cvZero( HistogramImage1 );
    cvNot(HistogramImage1,HistogramImage1);
    HistogramBinWidth = HistogramImage1->width/HistogramBins;
    for(int i=0;i<HistogramBins;i++)
    {

        Point1=cvPoint(i,0);
        Point2=cvPoint(i,(int)cvQueryHistValue_1D(Histogram2,i));
        printf("%d\n",(int)cvQueryHistValue_1D(Histogram2,i));
        cvLine(HistogramImage1,Point1,Point2,CV_RGB(127,127,127));
    }
    cvShowImage("Hue Histogram",HistogramImage1);
    cvShowImage("Riverbank",Image2);
}

執行結果:


在OpenCV裡面,HSV色彩空間,色調(Hue)值的範圍在0~180,飽和度(Saturation)的範圍在0~255,亮度(Value)的範圍在0~255,而這邊就只取色調(Hue)值在做反向投影,開啟了一個Track bar的功能,並且利用cvCvtColor()將BGR的色彩空間轉換成HSV,並且用cvSplit()通道分割取Hue通道的圖片,計算Hue值的直方圖,在onTrackbar()的部份,則是用Trackbar來調整去除cvThreshHist()的最小區塊的臨界值,去除之後在反向投影到原始的Hue的圖片,在由反向投影的結果當做遮罩,直接跟彩色圖片做對應.

而cvCalcBackProject()不單單只有這樣的功能,它可以對多維度空間的色彩直方圖做對應,cvCalcBackProject()提供了一個當CvHistogram資料結構維度為3的時候的一個反向投影,下面的這個例子就以HSV的色彩空間為例,建構一個三維的CvHistogram資料結構

HSV三維直方圖反向投影
#include <cv.h>
#include <highgui.h>
#include <stdio.h>


IplImage *Image1,*Image2;
IplImage *HSV;
IplImage *HueImage,*SaturationImage,*ValueImage;
IplImage *ImageArray[3];
IplImage *BackProjectImage;
CvHistogram *Histogram1;
IplImage *HistogramImage1;
CvPoint Point1,Point2;

int HueValue=0;

int HistogramBins[3] ={180,256,256};
int HistogramBinWidth;
float HistogramRange1[6]={0,180,0,255,0,255};
float *HistogramRange[3]={&HistogramRange1[0],&HistogramRange1[2],&HistogramRange1[4]};
void onTrackbar(int position);

int main()
{

    Image1 =cvLoadImage("Riverbank.jpg",1);
    HSV = cvCreateImage( cvGetSize(Image1),8,3 );
    HueImage = cvCreateImage( cvGetSize(Image1),8,1 );
    SaturationImage = cvCreateImage( cvGetSize(Image1),8,1 );
    ValueImage = cvCreateImage( cvGetSize(Image1),8,1);
    ImageArray[0]=HueImage;
    ImageArray[1]=SaturationImage;
    ImageArray[2]=ValueImage;

    BackProjectImage = cvCreateImage( cvGetSize(Image1),8,3 );

    Histogram1 = cvCreateHist(3,HistogramBins,CV_HIST_SPARSE,HistogramRange);


    cvCvtColor( Image1, HSV, CV_BGR2HSV );
    cvSplit(HSV,HueImage,SaturationImage,ValueImage,0);

    cvCalcHist( ImageArray, Histogram1);


    cvNamedWindow("Riverbank",1 );
    cvCreateTrackbar("Hue Thresh","Riverbank",&HueValue,200,onTrackbar);
    cvShowImage("Riverbank",Image1);
    cvWaitKey(0);

}

void onTrackbar(int position)
{
    CvHistogram *Histogram2= cvCreateHist(3,HistogramBins,CV_HIST_SPARSE,HistogramRange);
    IplImage *Image2=cvCreateImage( cvGetSize(Image1),8,3 );
    IplImage *BackProjectImage = cvCreateImage( cvGetSize(Image1),8,1 );

    cvCopyHist(Histogram1,&Histogram2);

    cvThreshHist(Histogram2,position);
    cvCalcBackProject( ImageArray, BackProjectImage, Histogram2);
    cvCopy(Image1,Image2,BackProjectImage);

    cvShowImage("Riverbank",Image2);
}

執行結果:


在三維空間的作法上面,就要參考到前面"OpenCV統計應用-CvHistogram直方圖資料結構"關於三維空間製作的部份,除了用cvCvtColor()將色彩空間轉換,用cvSplit()將通道做分割,還要做個圖形陣列(ImageArray)來讓cvCalcHist()這個函式做運算,計算出來的結果為一個CvHistogram的三維空間稀疏矩陣直方圖,而在onTrackbar()的部份,cvCalcBackProject()直方圖反向投影亦是同樣要用ImageArray做輸入,而輸出則是一個單通道的圖形,在稀疏矩陣裡面,由於維度為三維,所以他所形成的統計直方圖數值都是極小,所以門檻值只要一點點就快要全部都分佈了,而這個三維空間的反向投影可以如此建構,是基於Look-up table的功能來實現,只不過他的缺點是,每一個維度的Look-up table範圍是0~255,因此如果是像Hue值的範圍0~180,它的數值就會被模糊化,也就是數據會被些許位移,但是仍不會影響它出來結果的精確度

在這個直方圖反向投影的部份,也可以結合連通成分來做去除某一門檻值的連通分量

cvCalcBackProject()
將統計直方圖的分布數據根據Look-up table對應回去,也就是說,當今天CvHistogram資料結構內的數據分佈是243,110,0,60...則使用cvCalcBackProject()函式單通道的圖片像素值會是,當遇到像素值為1的時候變243,像素值為2的時候變110,依此類推,cvCalcBackProject()直方圖反向投影可以根據多維度設計,而cvCalcBackProject()第一個引數為輸入單通道IplImage或CvMat資料結構,第二個引數為輸入單通道反向投影IplImage或CvMat資料結構,第三個引數為輸入CvHistogram資料結構
cvCalcBackProject(輸入單通道IplImage或CvMat資料結構,輸入單通道反向投影IplImage或CvMat資料結構,輸入CvHistogram資料結構)



Copyright 2008-2009,yester