2008年10月11日 星期六

OpenCV統計應用-極端值,cvReduce

在統計模型裡面,總是會有一些特別高或特別低的數值,這個數值已經脫離了統計分配的假設,不符合統計的模型,通常會有這種極大或是極小的極端值(outlier)有可能是因為量測誤差,或是在統計模型中那0.0001的機率發生,因此,在統計學裡,挑出極端值來做最精簡的量測也是很重要的,就好比一群低收入戶的人裡面出現了一位身價上億的人而拉高了一群人的平均所得,使得低收入戶的族群變成中高收入的族群,這邊要抓取那個最大最小值就用到了cvMinMaxLoc()的函式啦.在圖片裡面,也許很難有如此極端的事件產生,cvMinMaxLoc()可以處理一般的數據,亦可以處理圖片找出它最大最小值以及它的位置.

cvMinMaxLoc()的實作
#include <cv.h>
#include <highgui.h>
#include <stdio.h>


int main()
{
    IplImage *Image1=cvLoadImage("grotto.jpg",1);

    double MinValue;
    double MaxValue;

    CvPoint MinLocation;
    CvPoint MaxLocation;

    cvSetImageCOI(Image1,1);
    cvMinMaxLoc(Image1,&MinValue,&MaxValue,&MinLocation,&MaxLocation);

    printf("The Min number is : %.f\n",MinValue);
    printf("The position is : ( %d , %d )\n",MinLocation.x,MinLocation.y);
    printf("The Max number is : %.f\n",MaxValue);
    printf("The position is : (%d , %d )\n",MaxLocation.x,MaxLocation.y);

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

執行結果:


上面的結果是抓出這張圖片最大最小值的數據,選擇綠色這個通道,雖然這已經不算是極端值的意義了,不過它仍然是可以對一般圖形做處理,cvMinMaxLoc()可以同時找出最大最小值,也可以指出最大值的位置跟最小值的位置,而cvMinMaxLoc()必須對單通道做處理因此必須要用,cvSetImageCOI選定顏色,也可以支援ROI,甚至,cvMinMaxLoc()可以用遮罩的方式實作,使用的方法如下

#include <cv.h>
#include <highgui.h>
#include <stdio.h>


int main()
{
    IplImage *Image1=cvLoadImage("grotto.jpg",1);
    IplImage *MaskImage1=cvLoadImage("grotto_Threshold.bmp",0);
    double MinValue;
    double MaxValue;

    CvPoint MinLocation;
    CvPoint MaxLocation;

    cvSetImageCOI(Image1,1);
    cvMinMaxLoc(Image1,&MinValue,&MaxValue,&MinLocation,&MaxLocation,MaskImage1);

    printf("The Min number is : %.f\n",MinValue);
    printf("The position is : ( %d , %d )\n",MinLocation.x,MinLocation.y);
    printf("The Max number is : %.f\n",MaxValue);
    printf("The position is : (%d , %d )\n",MaxLocation.x,MaxLocation.y);

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

執行結果:


上面的程式也只對grotto_Threshold.bmp白色的部份做處理,從白色區域找出它的最大最小值以及它的位置,跟前面的程式差不多.

cvReduce()為將矩陣維度降低為向量的維度,也就勢將該行或該列做加總,平均,找出最大,最小值,cvReduce()可使用的參數或代號如下

#define CV_REDUCE_SUM 0
#define CV_REDUCE_AVG 1
#define CV_REDUCE_MAX 2
#define CV_REDUCE_MIN 3


而它的數據要從圖形縮成行或縮成列,則輸入的向量就必須要跟它的行或列相等長度,而且是一維的形式,下面這個是求出每一列的RGB值的平均數,並且用圖表的方式

cvReduce()平均數向量
#include <cv.h>
#include <highgui.h>
#include <stdio.h>


int main()
{
    IplImage *Image1=cvLoadImage("grotto.jpg",1);
    IplImage *AVGImage=cvCreateImage(cvSize(Image1->width,255),IPL_DEPTH_8U,3);
    CvMat *AVGVector=cvCreateMat(1,Image1->width,CV_32FC3);

    cvReduce(Image1,AVGVector,0,CV_REDUCE_AVG);


    for(int i=0;i<Image1->width;i++)
    {
        printf("%f\t%f\t%f\t\n",cvGet1D(AVGVector,i).val[0],cvGet1D(SumVector,i).val[1],cvGet1D(SumVector,i).val[2]);
        if(i!=Image1->width-1)
        {
            cvLine(AVGImage,cvPoint(i,(int)cvGet1D(AVGVector,i).val[0]),cvPoint(i+1,(int)cvGet1D(AVGVector,i+1).val[0]),CV_RGB(0,0,255));
            cvLine(AVGImage,cvPoint(i,(int)cvGet1D(AVGVector,i).val[1]),cvPoint(i+1,(int)cvGet1D(AVGVector,i+1).val[1]),CV_RGB(0,255,0));
            cvLine(AVGImage,cvPoint(i,(int)cvGet1D(AVGVector,i).val[2]),cvPoint(i+1,(int)cvGet1D(AVGVector,i+1).val[2]),CV_RGB(255,0,0));
        }

    }

    cvNamedWindow("grotto",1);
    cvShowImage("grotto",Image1);
    cvNamedWindow("AVG Image",1);
    cvShowImage("AVG Image",AVGImage);

    cvWaitKey(0);
}

執行結果:



cvReduce()可以支援多通道,而AVGVector則是通道三的向量,然後圖表的呈現以cvLine()實作,cvReduce()第一個引數為目標要被統計的圖片,第二個引數為要被計算出來的向量,而第二個引數有一些特殊的規定,它的使用條件如下

1.通道數必須與輸入圖片的數目相同
2.所需要計算的列或欄的長度必須要與輸入圖片
3.一定要用浮點數型別來輸入,如CV_32FC3,CV64FC3,CV_32FC1等,由於uchar型別在做像是CV_REDUCE_SUM時數據一定會超過,因此cvReduce()會做檢查驗證

而且而CV_REDUCE_SUM則是在二值化的圖片的計算比較具有意義,它可以求出二值化圖片的投影向量,並且以圖表的方式呈現.下面就是以列投影以及欄投影的方式來做圖表的呈現.

二值化圖形投影計算
#include <cv.h>
#include <highgui.h>
#include <stdio.h>

int main()
{
    IplImage *Image1=cvLoadImage("grotto_Threshold.bmp",0);
    IplImage *RowProjectionImage=cvCreateImage(cvSize(Image1->width,300),IPL_DEPTH_8U,3);
    IplImage *ColumnProjectionImage=cvCreateImage(cvSize(300,Image1->height),IPL_DEPTH_8U,3);
    CvMat *RowSumVector=cvCreateMat(1,Image1->width,CV_32FC1);
    CvMat *ColumnSumVector=cvCreateMat(Image1->height,1,CV_32FC1);

    RowProjectionImage->origin=1;
    cvReduce(Image1,RowSumVector,0,CV_REDUCE_SUM);
    cvReduce(Image1,ColumnSumVector,1,CV_REDUCE_SUM);

    for(int i=0;i<Image1->width;i++)
    {
        cvLine(RowProjectionImage,cvPoint(i,(int)cvGet1D(RowSumVector,i).val[0]/(2*255)),cvPoint(i,0),CV_RGB(255,255,255));
    }
    for(int i=0;i<Image1->height;i++)
    {
        cvLine(ColumnProjectionImage,cvPoint((int)cvGet1D(ColumnSumVector,i).val[0]/(2*255),i),cvPoint(0,i),CV_RGB(255,255,255));
    }

    cvNamedWindow("grotto Threshold",1);
    cvShowImage("grotto Threshold",Image1);

    cvNamedWindow("Row Projection Image",1);
    cvShowImage("Row Projection Image",RowProjectionImage);

    cvNamedWindow("Column Projection Image",1);
    cvShowImage("Column Projection Image",ColumnProjectionImage);
    cvWaitKey(0);
}

執行結果:


上面的程式,由於是二值化的圖片,只有0跟255,所以必須要將累加的數值除以255才能知道它所累積的個數,在將該數目除以二平移,使他在圖表呈現上不會太大,對於投影的實作,可以藉由選取較高的區間,提出二值化裡面有意義的資料區塊,在人臉辨識可以藉由這種方法提取特定臉部器官的特徵.

cvMinMaxLoc()
找出圖片或一組數據中最大值及最小值的數據,以及最大值及最小值的位置,第一個引數為輸入IplImage資料結構或CvMat資料結構,第二個引數為輸出最小值double型別數據,第三個引數為輸出最大值double型別數據,第四個引數為輸出最小值位置CvPoint資料結構,第五個引數為輸出最大值位置CvPoint資料結構.
cvMinMaxLoc(輸入IplImage或CvMat資料結構,輸出double型別最小值數據,輸出double型別最大值數據,輸出最小值CvPoint資料結構,輸出最大值CvPoint資料結構)

cvReduce()
將二維圖形縮減成列向量或欄向量,cvReduce()具有四個參數,分別為CV_REDUCE_SUM總和,CV_REDUCE_AVG平均,CV_REDUCE_MAX最大值,CV_REDUCE_MIN最小值,可以由多通道的輸入,但輸出的行向量或欄向量一定要符合圖形的長度.第一個引數為輸入目標IplImage資料結構或CvMat資料結構,第二個引數為輸出目標CvMat結構向量,第三個引數為縮減為列向量或欄向量,0為欄向量,1為列向量,第四個引數為cvReduce()的參數或代號.
cvReduce(輸入IplImage或CvMat資料結構,輸出CvMat向量資料結構,輸入縮減欄向量或列向量代號,目標參數或代號)



5 意見:

wa114040@gmail.com 提到...

對於二值化的圖形,一定要用bmp檔做存取,由於jpg檔案格式被經由破壞性壓縮,所以出現的值都不是0跟255二值化後的值,所以前面二值化Mask的都錯了orz,要把它改成cvLoadImage("xxx.bmp")的bmp檔案格式

匿名 提到...

如果最大、最小值不止一个呢?

匿名 提到...

在cvMinMaxLoc里面,我注意到你用的八位深度的图像做的处理,如果用32位浮点图像+mask的话效果如何?(mask!=null)

匿名 提到...

對同一張圖用了二次cvMinMaxLoc.其maxpoint結果郤不一樣.是什麼原因呢?

匿名 提到...

請問您二值化欄列統計圖為黑白分佈
但我做出來黑色部分視窗顯示為灰色
請問要怎麼改善?

Copyright 2008-2009,yester