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
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 tabletable的值,透過一個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中
這樣的結果雖然並不算完美
但這在許多影像處理中都會用的到喔!!
下一篇將介紹簡單的影像偵測!!





沒有留言:
張貼留言