2012年10月14日 星期日

EmguCV Image Process: Counting the Pixels with Histograms part4

再度攜手步入森林

EmguCV Image Process: Counting the Pixels with Histograms

參考OpenCV 2 Computer Vision Application Programming Cookbook第四章

介紹內容如下:

Computing the image histogram

Applying look-up tables to modify image appearance

Equalizing the image histogram

Backprojecting a histogram to detect specific image content

Using the mean shift algorithm to find an object

上一篇提到如何利用backprojection來做影像偵測

而backprojection的結果就是一張機率影像(probability map)

也就是說結果的機率影像,越亮的部分就代表越接近要搜尋的影像內容

因此可以給定一個window大小的範圍來搜尋機率影像

透過不斷的移動window來找到範圍中機率最大的區塊

以此來決定搜尋影像的正確位置

而這個方法就是著名的:mean shift algorithm


首先要決定對影像中哪個區塊感興趣

並以此來做搜尋目標

一樣以封面這張影像來做測試

這裡對新娘的區塊作搜尋

位置在:x=100、y=280,範圍:width=50、height=50

藍色圈選區域就是要搜尋的區塊!

而通常在做影像搜尋時

採用的影像色域為:HSV

利用Hue的色域來做搜尋

這裡提供一個方法,需要傳入來源影像,偵測區塊

然後回傳搜尋結果區塊

Rectangle MeanShift(Image<Bgr,Byte> image,Rectangle ROI)
{
    //Convert to HSV color space
    Image<Hsv, Byte> HSVimage = image.Convert<Hsv, Byte>();
    //Get Hue, Saturation, Value image
    Image<Gray, Byte>[] HSVimages = HSVimage.Split();
    //Set the ROI
    HSVimages[0].ROI = ROI;
    //Get the Hue histogram
    DenseHistogram HueHist = GetHistogram(HSVimages[0]);
    //Reset the ROI
    HSVimages[0].ROI = Rectangle.Empty;
    //Get backprojection of Hue histogram
    Image<Gray, Byte> result = HueHist.BackProject(new Image<Gray, Byte>[] { HSVimages[0] });
    MCvConnectedComp connectedComp;
    //MeanShift algorithm
    CvInvoke.cvMeanShift(result, ROI, new MCvTermCriteria(10, 0.01), out connectedComp);
    //Get the result area
    return connectedComp.rect;
}
利用mean shift algorithm來搜尋影像

這邊傳入的影像色域為一般的RGB

透過Convert<Hsv, Byte>()把色域轉成HSV

然後利用Split()把H、S、V區分出來

然後取用Hue影像來做histogram的擷取和計算backprojection

把backprojection的結果、偵測區塊(window)的起始位置(也就是偵測區塊的位置)、

MCvTermCriteria的模式、跟回傳結果

傳入cvMeanShift的方法中,並把結果回寫到connectedComp裡面

而偵測到的區塊就是connectedComp.rect

透過這樣的方式,就可以取得偵測結果

而叫用的方式如下:

//Read reference image
Image<Bgr, Byte> image = new Image<Bgr, Byte>(@"..\image.jpg");
//Bride area
Rectangle ROI = new Rectangle(100,280,50,50);
//Get the detection result
Rectangle result = MeanShift(image, ROI);
取得偵測的結果

把偵測的結果印出來

結果如下:
紅色區塊就是偵測結果

偵測結果多少都會有些誤差

那要如何提升準確率呢?

當取用Hue的影像時,Saturation的值其實也相當重要

當Saturation(飽和度)的值很低時,Hue的資訊會變得很不穩定也不可靠

而R、G、B的部分也是如此

而更進一步的方式

可以把low Saturation的部分忽略掉

設定一個最低Saturation的閥值,建立一張mask的影像

因此在建立histogram的時候需要帶入此mask

public DenseHistogram GetHistogram(Image<Gray, Byte> image,Image<Gray, Byte> mask)
{
    //Create a histogram
    DenseHistogram histogram = 
        new DenseHistogram(
            //number of bins
            256,               
            //pixel value range
             new RangeF(0, 255)
            );
    Image<Gray, byte>[] images = new Image<Gray, byte>[] { image };
    //Compute histogram
    histogram.Calculate(
        images, //Split current Image into an array of gray scale images
        true, //If it is true, the histogram is not cleared in the beginning
        mask  //no mask is used
        );
    return histogram;
}
將part1所提到的GetHistogram加入一個mask的引數

並代入Calculate的方法中

這時候再修正剛剛MeanShift的方法如下:

Rectangle MeanShift(Image<Bgr, Byte> image, Rectangle ROI, int minSat)
{
    //Convert to HSV color space
    Image<Hsv, Byte> HSVimage = image.Convert<Hsv, Byte>();
    //Get Hue, Saturation, Value image
    Image<Gray, Byte>[] HSVimages = HSVimage.Split();
    //Mask to be used
    Image<Gray, Byte> lowSatnMask = HSVimages[1].ThresholdBinary(new Gray(minSat), new Gray(255));
    //Set the ROI
    HSVimages[0].ROI = ROI;
    lowSatnMask.ROI = ROI;
    //Get the Hue histogram
    DenseHistogram HueHist = GetHistogram(HSVimages[0], lowSatnMask);
    //Reset the ROI
    HSVimages[0].ROI = Rectangle.Empty;
    lowSatnMask.ROI = Rectangle.Empty;
    //Get backprojection of Hue histogram
    Image<Gray, Byte> result = HueHist.BackProject(new Image<Gray, Byte>[] { HSVimages[0] });
    //Eliminate low saturation pixels
    result._And(lowSatnMask);

    MCvConnectedComp connectedComp;
    //MeanShift algorithm
    CvInvoke.cvMeanShift(result, ROI, new MCvTermCriteria(10, 0.01), out connectedComp);
    //Get the result area
    return connectedComp.rect;
}
此時除了加入low saturation mask外

這時候叫用MeanShift的方法要加入min saturation的閥值

Rectangle result = MeanShift(image, ROI, 60);
代入min saturation的值,這裡設定為60

這時候再跑一次結果:
這時候紅色的偵測區塊已經完全偏離原先的藍色區塊

雖然書上這樣寫

但如果實際測試就會發現,加入閥值反而效果變差

原因大概就是因為所偵測的影像區塊

大部分屬於白色,白色的saturation極低

所以若是把較低的saturation濾掉,反而導致效果變差!

所以在saturation這部分要根據影像內容來做調整
(此張偵測的影像區塊的閥直設為0最佳~XD)


mean shift algorithm能夠用來做影像追蹤

在一連串的序列影像中

去搜尋指定的影像內容區塊

然後不斷地在下一張影像中搜尋

如果此時是拍連續的組圖,新娘不斷的往前走去

此時將每一張新的影像都mean shift algorithm去做偵測

紅色的框框也會不斷的移動,追蹤新娘的影像區塊

就能達到追蹤的效果

Rectangle MeanShift(Image<Bgr, Byte> sourceImage, Image<Bgr, Byte> targetImage, Rectangle ROI, int minSat)
{
    //Convert to HSV color space
    Image<Hsv, Byte> HSVimage = sourceImage.Convert<Hsv, Byte>();
    Image<Hsv, Byte> HSVimage2 = targetImage.Convert<Hsv, Byte>();
    //Get Hue, Saturation, Value image
    Image<Gray, Byte>[] HSVimages = HSVimage.Split();
    Image<Gray, Byte>[] HSVimages2 = HSVimage2.Split();
    //Mask to be used
    Image<Gray, Byte> lowSatnMask = HSVimages[1].ThresholdBinary(new Gray(minSat), new Gray(255));
    //Set the ROI
    HSVimages[0].ROI = ROI;
    lowSatnMask.ROI = ROI;
    //Get the Hue histogram
    DenseHistogram HueHist = GetHistogram(HSVimages[0], lowSatnMask);
    //Reset the ROI
    HSVimages[0].ROI = Rectangle.Empty;
    lowSatnMask = HSVimages2[1].ThresholdBinary(new Gray(minSat), new Gray(255));
    //Get backprojection of Hue histogram
    Image<Gray, Byte> result = HueHist.BackProject(new Image<Gray, Byte>[] { HSVimages2[0] });
    //Eliminate low saturation pixels
    result._And(lowSatnMask);
    MCvConnectedComp connectedComp;
    //MeanShift algorithm
    CvInvoke.cvMeanShift(result, ROI, new MCvTermCriteria(10, 0.01), out connectedComp);
    //Get the result area
    return connectedComp.rect;
}
加入target image的設定

利用source image的影像區塊

並且在target image中做搜尋的動作

取得最接近的影像範圍

這裡利用source image做裁切來測試,結果如下:


利用原圖做裁切後的偵測

此圖的左邊和底部都經過裁切

所以新娘的位置稍微往左上移動了一些

藍色區塊是原始影像新娘的位置

紅色區塊則是搜尋這張裁切過的影像的位置

因此可以發現位置偵測的還算正確!!


這時候就可以自行嘗試做影像偵測和追蹤的效果了!!


沒有留言:

張貼留言