2012年9月28日 星期五

EmguCV Image Process: Counting the Pixels with Histograms part2

再度攜手步入森林

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

上一篇介紹了histogram,通常中文翻譯成直方圖

可以發現直方圖是透過每個pixel的intensity value來呈現一張影像

所以分析一張影像中pixel的分佈對於修改或是改善影像是很有用的資訊

這裡介紹如何用一個簡單的mapping function

透過一個look-up table來修改影像中一些pixels的值

一個look-up table是一個簡單的一對一或是多對一的函式

其中定義了如何將pixels的值轉換成新的值

若是以灰階的影像來說

一個一維的陣列,包含256個entries

在table的第i個entry則傳出相對應灰階值的新值

newIntensity = Lookup[oldIntensity];
像這樣的對應方式

在EmguCV中並無實作出Lookup table的方法

但可透過CvInvoke來呼叫

使用Matrix<Byte>的型別來表示look-up table

用法如下:

Image<Gray, Byte> ApplyLookUp(Image<Gray, Byte> image, Matrix<Byte> lookupTable)
{
    //the output image
     Image<Gray,byte> result = image.CopyBlank();
    //apply lookup table
     CvInvoke.cvLUT(image, result, lookupTable);
     return result;
}
用法非常簡單!!

這裡要注意就是傳入的result要先建立成跟來源影像同大小的影像才行

然後把來源影像、目的影像跟look-up table傳入cvLUT這個方法就可以了!

這裡來試試一個inverse的效果

//Load input image
Image<Gray, Byte> image = new Image<Gray, Byte>(@"image.jpg");
//Create an image inversion table
Matrix<Byte> lookupTable = new Matrix<Byte>(new Size(1, 256));
for (int i = 0; i < 256; i++)
{
    lookupTable[i, 0] = (Byte)(255 - i);
}
Image<Gray, Byte> result = ApplyLookUp(image, lookupTable);
建立一個inversion look-up table

table的值,透過一個for迴圈塞入反向的值(255-i)

就可以取得inverse後的影像

結果影像,看起來是不是很詭異~XD


這時候就可以跟上一篇介紹的直方圖整合來做一些影像處理

這邊示範如何加強影像的對比

首先取得原始影像的直方圖

透過直方圖可以很輕鬆發現影像所有pixels的值的分佈位於0~255那些區間

可以找出那些區間是沒有pixels存在的(可能沒有全白(255)或是全黑(0)的pixel)

這時候將直方圖做延伸(stretch),就能夠擴大影像的對比度

講白一點,就是讓影像深色的部分更深,淺色的部分更淺,就會增加對比!!
(黑的更黑~白得更白!!)

前提是我們必須知道直方圖裡頭最小的值(min)和最大的值(max)落在哪裡

然後將最小的值擴展到真正的最小值0

最大的值擴展到真正最大值255

簡單的公式如下: 255.0 * ((i - min) / (max - min) + 0.5);

那就來看程式怎麼寫:

Image<Gray, Byte> Stretch(Image<Gray, Byte> image, int minValue)
{
    //Compute histogram first
    DenseHistogram histogram = GetHistogram(image);
    float[] hist = new float[256];
    histogram.MatND.ManagedArray.CopyTo(hist, 0);
    //find left extremity of the histogram
    int min = 0;
    for (; min < hist.Length; min++)
    {
        if (hist[min] > minValue)
            break;
    }
    //find right extremity of the histogram
    int max = hist.Length - 1;
    for (; max >= 0; max--)
    {
        if (hist[max] > minValue)
            break;
    }

    //Create lookup table
    Matrix<Byte> lookupTable = new Matrix<Byte>(new Size(1, 256));
    for (int i = 0; i < 256; i++)
    {
        //stretch between minLocation and maxLocation
        if (i < min) lookupTable[i, 0] = 0;
        else if (i > max) lookupTable[i, 0] = 255;
        else lookupTable[i, 0]=
            (Byte)(255.0 * (i - min) / (max - min) + 0.5);
    }
    //Apply lookup table
    return ApplyLookUp(image, lookupTable);
}
由傳入的引數minValue來決定左右邊界的閥值

這邊minValue設定成0的話

影像中若是剛好有一個黑(0)點、一個白點(255)

這張影像就不會受到處理

所以可以帶入一個較大的值來取得較好的結果

//Ignore starting and ending bins with less than 100 pixels
Image<Gray, Byte> result = Stretch(image,100);
若是設定成100,就會忽略掉左邊、右邊小於100個pixels的位置


由於我們原始提供的影像的對比度相當完美

分佈均勻、0~255皆有值分佈,所以跑出來的結果如下:

原本對比就很高,再去做stretch也還是一樣!!


所以這邊再提供一張降低對比的影像來做測試:

可以發現影像變得比較黯淡

這時候一樣再透過stretch來處理

有沒有發現,好像又變成原始那張影像一樣了!!


但是其實仔細看還是會發現有些地方不一樣

尤其是樹林的部分

原本的樹林在影像中屬於比較暗的部分

若是在一開始提供的影像

就已經在暗部失去細節的話

就算透過影像處理,拉高對比,也還是救不回來!!!


Equalizing the image histogram

也就是說,一個品質好的影像

應該在所有的intensity中(0~255)平均的分佈

上面的stretch的方法

並不會改變每一個bin中的pixels數量

而在許多的影像處理中

都會提到的histogram equalization

就是用上述的概念來實作的

這個方法會重新把pixels更平均的分佈在各個bin中

在EmguCV能夠很輕易的實作這個方法:

image._EqualizeHist();
就這麼一行!

這裡一樣讀入經過降低對比的影像

結果如下:

就是讓pixels更平均的分佈在每個bin中


這樣的結果雖然並不算完美

但這在許多影像處理中都會用的到喔!!

下一篇將介紹簡單的影像偵測!!

沒有留言:

張貼留言