Counting Pixels with Histograms
Introduction
本次作業將實作六項主題:
Histogram: 繪製圖形的Histogram
Content Finder: 利用參考區域的Histogram製作機率表來尋找物件
Finder: 選定參考區域,並於另一張圖尋找相同的人臉
Retrieve: 比較兩張的相似度
Integral: 使用Adaptive Thresholding方法來讓圖片清晰
Tracking: 利用參考區域的Histogram,於水平範圍內找尋相同的物件
作業環境:
Windows 11
OpenCV 4.6.0
Visual Studio 2022
Histogram
Purpose
本節主要實踐五件事:
繪製圖片的Histogram
對圖片做threshold (Binary Image)
對圖片做 Equalization
對圖片做Stretched
對圖片做反向的灰階 (Negative image)
接下來我將分為五部分做原理分析及程式碼解析
以下將使用右圖做分析
照片來源 : Black Buck Coffee , Pinterest
part 1
Histogram X 軸的左至右代表由最黑(0)去到最白(255)的亮度值,而Y軸則代表pixel的數量,實踐原理很簡單,計算出0~255之中每個亮度值有幾個pixel,接著依照數據繪製出Histogram。Histogram可以判斷照片亮暗程度程度,常使用於攝影中觀察畫面是否過曝或是光線不足。
首先,先讀取照片,這隻程式碼主要僅分析單一channel,所以在讀取照片時直接以灰階的資料型態讀取,結果如左圖。
接著打印出使每個亮度值有多少
最後,piexl用已撰寫好的.h標頭檔來繪製Histogram,指令如下:
cv::Mat histo = h.getHistogram(image);
右側程式碼是這段中最關鍵的一個函式,使用cv::calcHist,來繪製單一個channel的亮度分布情形,接著並輸出到畫面中。
calcHist()可以計算BGR彩圖的channel分布情形,我有做嘗試要繪製BGR三種Histogram,但並未成功,所以本次結果仍以灰階呈現。
cv::calcHist(&image,
1, // histogram of 1 image only
channels, // the channel used
cv::Mat(), // no mask is used
hist, // the resulting histogram
1, // it is a 1D histogram
histSize, // number of bins
ranges); // pixel value range
Result
以左圖結果來判斷,光譜整體偏左,峰值在數值偏低的地方,可以知道這張圖整體畫面是偏黑的。這是我第一次操作包含.h標頭檔的程式,雖然沒有做出做出額外的變化,但在嘗試的過程中,可以理解程式碼如何運行、.h標頭檔程式如何使用。
part 2
這部分主要介紹threshold ,目的是將影像分割成一塊一塊的區塊,根據像素的相似性 (similarity) 把像素聚集成一塊塊區域 。
cv::threshold(image, thresholded,
43, // threshold value
255,// value assigned to pixels over threshold value
cv::THRESH_BINARY); // thresholding type
首先,先計計算出每個亮度值有幾個pixel。
接著開市做二值化分割,左側程式碼為這部分最關鍵的一個部分,使用threshold()將大於43的pixel改為255,小於43的值改為0。
最後繪製出Histogram,並使用cv::line()標示出門檻值。
Result
我首先使用excel計算數據的中位數,因為光譜分布不均,也不是完美的鍾型分布,所以我並非取平均值來做測試,而是計算中位數,欲讓得出來的二值化的圖形能接近黑白各半,以便後須觀察變化,會後計算得值43。因此我先使用43繪製一張Histogram,再將值調小、調大以觀察結果的變化。如下圖可以發現數值越小畫面越白,數值越大整體畫面顏色越黑,但越能判斷出物件。
cv::threshold(image, thresholded,
15, 255, cv::THRESH_BINARY);
cv::threshold(image, thresholded,
43, 255, cv::THRESH_BINARY);
cv::threshold(image, thresholded,
100, 255, cv::THRESH_BINARY);
part 3
一張曝光均勻的照片,一般而言Histogram 會呈現「鐘形分佈」,即大部份的像素集中於X軸中間,在最黑與最白之間的灰色位置。若是像本節模擬使用的圖一樣畫面得像數值集中於x軸兩側,圖片看起來偏暗或過曝,就需要進行Histogram Equalization,讓影像更加對比。
static cv::Mat equalize(const cv::Mat &image){
cv::Mat result;
cv::equalizeHist(image,result);
return result;}
那這部分的程式碼很單純直接使用.h標頭檔使用 equalize()來做 Equalization
Histogram Equalization的想法是讓每個像數值分布均勻,意思是讓 50% 的 piexl 低於128 (=256/2),讓 25% 的 piexl 低於64(=256/2)。
Result
這部分遇到最大的困難在於挑選照片,尤其是彩色照片比黑白照更不容易判斷亮度,為了讓效果容易觀察,我挑選了一張是有亮面和暗面,且整體偏暗,但像數點為正黑(0)正白(255)不多的照片。結果如下,可以發現經過Equalization後,整體顏色調亮,原本陰影面如牆角,視覺上變清晰更能判斷物件。
原圖
Equalization
原圖
Equalization
以Histogram來說明, 雖然沒變成鍾型分布,但原本聚集灰度區間視覺上變成均勻分佈。
part 4
接下來介紹另一種讓相片變對比的方式。影些照片並不會用到完整的0~255,所以將像數強度等比例放大到最低為0,最高為255,以下將實踐這個概念。
前幾部分所使用的圖片使用了完整的0~255的亮度值,所以這部分換了另一張圖以利觀察效果。
首先,需先了解這張圖像數強度分布的情形,如右圖可以發現value0 、value 235~255 這些區間是沒有 pixel 值的。那從這點可以判斷,使用Stretched方法欲讓這張圖顏色變對比是有效的。
這部分的程式碼中特別的部分如左圖所示。作者使用look up table的方式來撰寫,look up table的優點是執行時間很短效率高。
接下來,需要先建立table 。依 255.0*(i-imin)/(imax-imin); 將像數值放大到最小為0,最大為255。使用 cvRound() 將浮點數轉換成最接近的整數。
建立完table後,使用.at() 直接改寫數值。
Result
效果雖然沒有一眼就辨認出來但仔細觀察,在右下角樹林的部分,經過Stretched後變得更黑,下半部的天空變白,天空和101比較比較,看起來更黑白分明。
原圖
Stretched
將結果以Histogram表示,如左圖所示。
低像數值的部分不明顯,高像數值的的就很明顯可以看到Stretched後,最大值來到255。還可以觀察到Stretched後的圖形呈現一格一格,像是一條一條的柱狀圖,是使用cvRound()後的效果,數值均變成整數數值。
part 5
使用底片拍攝時,因爲記錄在底片上的的明暗與色調與原來景色相反,所以在底片上的影像稱之為「負像」 ( negative,相反的影像 )。影像反白 (image negative) 又稱為負片,影像反白的效果即愈白變愈黑、愈黑變愈白,以下將模擬底片相機的負片效果。
製作負片效果的原理是將每一個pixel值改寫成255 - 原像數值 = 新像數值,如果是彩色照片的話一樣,用 255 的數值 - 圖片中每個 pixel 的RGB顏色。
首先,要先製作一個反向的table,用255 - 每一個pixel值,並用.at直接改寫table裡的值。
接著輸出圖像,觀察負片效果,我在後面增加了繪製Histogram的指令,以觀察影像反白後像數值的變化。
在搜尋負片德原理時,發現負片效果並不局限於黑白照,彩圖也可以利用相同原理製作出負片。我將程式碼改寫為可用於彩色照片的負片效果,以了解用於彩圖會呈現的效果,因為彩色照片有RGB三個channel,所以我挑選色彩方非常簡單的圖片。
Result
下圖結果是使用在灰階相片的效果,原本相片整體篇昏暗,image negative後,整體偏白,愈白變愈黑、愈黑則變愈白。
原圖
negative image
將兩張圖放在一起相互比較,可以看出來兩張圖可以發現兩張呈現鏡像,左右顛倒。
以下是將彩色照片做image negative後的效果,可以清楚的看出來,反白後的顏色是左右兩色做相加。
原圖
negative image
原圖
negative image
Content Finder
Purpose
以下欲實現簡易的偵測。操作概念是找到參考區的Histogram分布狀況,Histogram代表一張影像的特徵一樣,再依這個特徵找和參考區的Histogram相符的pixel,並標示出來,若將所有相似的pixel均標示出來,便可大致判斷出物件的形狀。
為方便操作並判斷結果是否正確,我選擇的照片是背景單調,且和物件顏色差很多,物件的部分則挑選同物件顏色變化不大,不同物件顏色則差異大。
以下為圖片來源:
part 1
依原理首先要先找出參考點,為方便講解我將 PART 3 的圖移至這步驟作參考。如右圖,我選擇花瓣作參考點。使用 Rect(x-axis, y-axis, weight, height) 來選取參考點,並使用 calcHist()計算pixel值的分布情形,並繪製參考點的Histogram。
這步驟重要程式碼如下:
cv::Mat imageROI;
imageROI = image(cv::Rect(320, 120, 20, 20)); // Cloud region
在調整參數數值時,可以知道 Rect()的座標原點在圖片的左上角,向右為x-axis向下為y-axis
Result
所選得到的參考點如右,用此區繪製出的Histogram如左圖。Histogram近鐘形分布,數值均集中在中間區域,峰值也接近中央。
part 2
接下來要來找這張圖的Back projection(反投影)。依參考點所繪製出來的Histogram可以視為一個機率分布圖,接下來依照這著機率分布圖來重新標示圖片,機率越高的部分呈現越亮的顏色,機率越低的部分顏色越暗,最後再將整張圖作反白似負片效果。
首先,要先繪製一個機率函數,使用normalize(input,output,alpha),將Histogram標準化成總機率為 1 ,標準化後的數值為alpha = 1,意思是上界為1,而下界為0,程式碼如下:
cv::normalize(h,histogram,1.0);
接著,就可以依照機率圖重新來繪製圖形,程式碼如右,將機率值最大得像數點變成255(白色),機率值最低的伸縮成0(黑色),圖片像數值會會呈現,和參考區越相近的越白,越不相近的越黑。
最後,來製作反白的圖片。程式碼如下:
cv::Mat tmp;
result1.convertTo(tmp, CV_8U, -1.0, 255.0);
所代表的意思是 result1 = -1*(tmp)+255
先將全部pixel值反轉,在平移255至正值。
cv::calcBackProject(&image,
1, // we only use one image at a time
channels, // vector specifying what histogram dimensions belong to what image channels
histogram, // the histogram we are usin
result, // the resulting back projection image
ranges, // the range of values, for each dimension
255.0 // the scaling factor is chosen such that a histogram value of 1 maps to 255);
Result
結果如右圖,雖然我所選擇的參考點是花瓣,但畫面中的結果還是看的到葉子的形狀,可以知道以灰階顯示時,兩者的pixel是相似的。
由結果可以驗證,因為作過反白,所以結果為和參考點相近的花瓣和些許葉子為黑色,是高機率高的部分,其餘白色部分是和參考點無關,機率低的位置。
Part 3
接著實現另一種搜尋物件的方法,和上一部份不同的部分是,使用機率表示完後,不作負片校過,而是使用threshold ,將影像分割成一塊一塊的區塊,製作二值化圖形。
這部分重要程式碼如左圖所示。
先依照參考區域的Histogram製作機率函數,在依照機率函式重新繪製一張圖。和機率值越高的部分會呈現越亮,機率值越低的部分越暗。
接著再使用 threshold() 將高於255*0.12的pixel值改為255,低於255*0.12的pixel值變成0
Result
在左圖中圈出參考區域在輸入圖片的位置,結果為下圖,在參考區域裡最常出現的像數值會呈現白色(255),是高機率的部分,其餘黑色(0)部分是機率低的位置。
我嘗試不同的 Threshold值結果如下,取直越低所顯示出的特徵點越多,越的出物件的形狀,取直越低白點越少,只能看的物件大致的走向。
finder.setThreshold(0.1f);
finder.setThreshold(0.12f);
finder.setThreshold(0.2f);
Part 4
以下開始將輸入時改為以彩色相片的資料型態來進行模擬分析。這部分使用的觀念和前面很像,對兩張圖片重複相同的步驟,欲從兩張圖中找出和參考區域相似的物件。
首先,先對照片取參考區域,我改選葉子的部分,對應程式碼如下,取得的參考區域如映圖所示。
imageROI = color(cv::Rect(200, 700, 70, 50));
接著,繪製參考區域的Histogram,並使用 cv::normalize(h,histogram,1.0); 標準化後製作成機率表。
依機率表付值給圖片,機率越高的點像數值越高,機率越低的點像數值越低,。
cv::calcBackProject(&image, 1, channels, histogram,
result, ranges, 255.0);
最後,將pixel值大於255*0.05改為255,小於的值改為0,繪製成二元化圖形。
cv::threshold(result, result, 255.0*threshold,
255.0, cv::THRESH_BINARY);
image 1
Result
以下為模擬結果,兩張圖的綠色葉子的部分皆被標示出來,其餘部分皆呈現黑色。圖片由左至右分別為:操作在和參考區相同的照片上、第二張圖片、從第二張照片找出和參考區相同的物件。
result1 = finder.find(color);
cv::imshow("Color Detection Result", result1);
image 2
cv::Mat result2 = finder.find(color2);
cv::imshow("Result color (2)", result2);
我和前面一樣改變了不同的threshold ,操作在第一張圖上,並觀察中間的差異。
第一張圖是未使用threshold() ,明顯看得出來畫面沒有很清楚,第二張圖加上threshold()後,物件被凸顯出來,但第三張圖,我將threshold()值調得很小,但沒有很大的差異,可以推論葉子的部分顏色很相近。
未使用 threshold()
finder.setThreshold(0.05f);
finder.setThreshold(0.0005f);
Part 5
欲使用上一部份選定的參考需區域,將之轉換成機率分布圖後,在重新改寫圖片。在處理彩色照片和灰階不同處在於,彩色很多種表示法如RGB 、HSV 、HLS、和 CIE Lab等,在PART 4 使用RGB 色彩空間來表示色彩,在PART 5 則會使用到將使用HSV 和 CIE Lab 兩種色彩空間來找出兩張照片和參考區域相似的部分,因此結果總共有四張圖:使用CIE Lab操作在image 1 、使用CIE Lab操作在image 2、使用HSV操作在image 1、使用HSV操作在image 2。
首先,第一步是先繪製參考區域的二維Histogram。在彩色照片中在計算一維的Histogram 是使用RGB的色彩空間來表示,接下來欲繪製二維的Histogram,所以要將RGB色彩空間轉換成HSV色彩空間,在HSV色彩空間的參數表示如下:
channels = [0,1] 需要Hue(色相) 和 Saturation(飽和度)兩種
bins = [180,256] Hue(色相)有180區間, Saturation(飽和度)有256個區間
range = [0,180,0,256] Hue(色相)的範圍是0~180, Saturation(飽和度)的範圍是0~256
使用cv::calcHist 計算HSV圖像的二維直方圖程式碼如下
cv::calcHist(&lab,
1, // histogram of 1 image only
channels, // the channel used
cv::Mat(), // no mask is used
hist, // the resulting histogram
2, // it is a 2D histogram
histSize, // number of bins
ranges // pixel value range);
繪製出來的結果如右圖,橫軸為saturation 縱軸為Hue。
接著,就可以依照機率表來繪製圖形了,處理每張圖的步驟相同,和前面處理以BGM色彩空間表示的照片不同的地方是使用
cv::cvtColor(color, lab, cv::COLOR_BGR2Lab); // RGB轉換成CIE Lab
cv::cvtColor(color, hsv, cv::COLOR_BGR2HSV); //RGB轉換成HSV
將照片從RGB轉換成以CIE Lab和HSV色相空間表示。
之後的步驟便和 PART 3 一樣,先使用cv::normalize() 進行標準化成總機率為1機率表,再使用 cv::calcBackProject() 依照剛製作好的機率表,重新繪製,機率越大的部分越白,機率越小的部分越黑,最後使用cv::threshold() 將圖片轉換成二值化圖形。
Result
結果如下,和處理灰階時的結果比較,均使用 finder.setThreshold(0.05f); 為相同的參數值,但圖片看起來卻呈現一格一格的,左圖使用的照片和PART 4 相同,呈現出來特徵點沒有PART 4 的模擬結果多。我認為是在從RGB轉換成以CIE Lab、HSV色相空間表示時,有先顏色被縮減過,在依機率表繪製圖片時,鄰近的pixel值會被標示成一樣的數值。結果呈現的沒有PART4 一樣漂亮,但仍可以看的出來大致的形狀,黑色區域為機率小的,所以是和參考區無關的部分,相反的白色是機率高的,顏色和參考區相似的部分。
使用CIE Lab操作在image 1
使用CIE Lab操作在image 2
使用HSV操作在image 1
使用HSV操作在image 2
Finder
本節欲實現物件偵測的概念,實作概念是利用 meanshift() 來不斷更新位置,而meanshift() 更新位置的原理是,先製作參考區域的機率圖,再根據去機率圖改寫欲收尋的圖片,meanshif() 的更新方向是朝密度高的區域前進,最後的收斂點便是目標物件。
因為顏色的相似度會引響機率值,所以我選擇圖片的方式是,避免背景差異過大,所以找一系列同一個模特兒的照片,且為避免室外光線的變動因素大,照片是於攝影棚拍攝的照片,於戶外拍測的照片可能會造人像雖然是同一人,但因為光線分布不均導致程式執行時顏色誤差過大導致結果不夠清楚。
右圖為以下程式所使用的圖片
圖片來源:
Part 1
根據原理,首先要先擷取參考點,如右圖所示,程式碼如下:
cv::Rect rect(80, 30, 60, 80);
cv::rectangle(image, rect, cv::Scalar(0, 0, 255));
選擇完參考點後,要來繪製參考區域的Histogram並製作機率表,和前面不同的是,是使用HSV色相空間來計算,但當Saturation(飽和度)很低的時候,所對應的RGB顏色幾乎相同,所以須避免這個情況。
cv::threshold(v[1], mask, minSaturation, 255, cv::THRESH_BINARY);
再計算Histogram數值前,先設定將Saturation mask,將Saturation(飽和度)低於65的值都忽略,高於65的值Saturation(飽和度)便變成最大值(255)。
接下來,使用 cv::calcHist()來計算Histogram,再使用cv::normalize(),轉換成總機率為1的機率表。
接著坐著呈現了Saturation mask的形狀,程式碼如左圖。
將圖片從RGB轉換成HSV色彩空間,圖形分離,v[1]代表Saturation(飽和層),最後將Saturation(飽和層)65以上的pixel值降為0,高於65的值改為255。
Result
以下為模擬結果,當我將min Saturation調高時白色部分會變少,調整至 int minSat = 80;時 手上的物件也能辨認出形狀。數值調低時,指有人行的輪廓,像剪影一樣,便認不出來手上的花和臉型。
int minSat = 40;
int minSat = 65;
int minSat = 80;
Part 2
這部分,作者利用PART1 繪製好的參考區域的機率表,來繪製第二張圖的back projection。
先將第二張圖匯入後,從RGB轉換成HSV色彩空間後,使用繪製出back projection
cv::calcBackProject(&image, 1, channels, histogram, result, ranges, 255.0 );
結果如右圖,機率越大顏色越白接率越小顏色越黑,從圖可以大致辨認出人像的輪廓。
Part 3
接下來是本節最重要的部分,將使用PART 1 所得出的參考區域、參考區域機率表等來辨認在第二張圖相同的人臉。
首先,先在第二張圖片中設定初始範圍並標示出來(以紅色框表示)。
接著設定使用mean shiftg的迭代終止條件,設定在最大迴圈數為10,且希望收斂處和最後位置相差1個pixel以內。
開始辨識第二張圖人臉的位置,使用
int cv::meanShift(InputArray,Initial,criteria )
從初始位置開始往密度高的部分移動,也就是往機率值大的區域移動,並將迭代次數記錄下來。
Result
結果如右圖,成功的將和一張圖相同的人臉標示出來,紅色框回初始位置,綠色框為最終結果,迭代次數為5次。
中間我嘗試很多種照片,發現圖片的選擇會大大的影響效果。一開始使用電影劇照,同場景只是角色換一個角度站,但卻因為臉部光線不同,造成顏色差異,最後結果都不理想,所以最後改選擇平面攝影的照片。
Retrieve
本節目的在於比較兩張圖的關聯性,原理先計算兩張圖片的Histogram,要注意兩張圖片的大小需相同,比較兩張圖關聯性的方法有很多,作者是用的是HISTCMP_INTERSECT,尋找交集(Intersection),比較Histogram每隔刻度的數值,並將較小的數值留下,若有個pixel完全無關量,意思是其中一張圖數值為0,則返回量就會為0。
使用的照片一共有九張,我挑選的方式是,每張之間有一個部分有關聯,但又非全部有關連。找到照片後因為照片大小都不相同,所以均有經過裁切。
Image 1 pinterest
首先,我選擇image 1 當作參考圖片。
接著將欲相互比較的兩張圖分別使用cv::calcHist()計算Histogram,一格一格的刻度相互比較取最小值後輸出,並將結果顯示於螢幕上。
重要程式碼如下:
double compare(const cv::Mat& image) {
inputH= hist.getHistogram(image);
return cv::compareHist(refH,inputH,cv::HISTCMP_INTERSECT);}
Result
計算結果如右,我將數值轉為圖表比較方便觀察,可以發現和image1 最相關的是image2 最不相關的是image4,回去驗證圖片,實驗結果正確。
除了取最小值以外,cv::compareHist還有很多資料型態,我多嘗試兩種如下:
使用cv::HISTCMP_CORREL計算相關係數來比較兩張圖,數值會介於0~1之間,由結果可以知道和image1 最相關的是image2 最不相關的是image4,和前面結果相同
使用cv::Chi-Square計算Chi-Square,數字越小相關度越高,數字越高相關度越低,但計算出來的結果和前面兩個不同,和image1 最相關的是image3 最不相關的是image8
Integral Image
欲將圖片中的物件變得清晰,之前所使用的是二值化方法,但當圖片中光線分配不均有陰影的時候,二值化圖形的效果會變得很差,因此本節目的在實現使用Adaptive Thresholding來描繪物件。兩者間最大得差異在於,二值化方法是只會考慮單一點的值,直接去做閥值分析,但一張圖形中相鄰的像數值是有關連性的,所以Adaptive Thresholding 就是在找尋單一點與鄰近區域的關係。Adaptive Thresholding設定Threshold值有兩種做法:
cv.ADAPTIVE_THRESH_MEAN_C: Threshold值為鄰近區域pixel值平均
cv.ADAPTIVE_THRESH_GAUSSIAN_C:Threshold值為高斯平均值(gaussian-weighted sum )
本節主要測試光線分布不均的情況下Adaptive Thresholding 和二值化兩者間效果差異,所以我選擇的照片如右圖,光源在左上方,所以頭頂和左邊身體有曝光的問題,我還想了解每種作法呈現的細節程度,所以我選擇動物照片來操作。
Part 1
若想要旋轉照片,作者提供了兩種函式:
transpose():讓圖片像述職的x座標與y座標對調
flipCode():翻轉照片,若填0為上下翻轉,若值大於0為左右翻轉,若值小於0則為上下左右均翻轉
右圖為輸入相片,所以使用以下程式碼其轉正
cv::transpose(image, image);
cv::flip(image, image, 0);
Result
右圖為執行結果,跟輸入照片比較,向右旋轉了90度。
以下將測試transpose()、flipCode()兩種函式的效果,可以觀察到第一張圖僅變成灰階無旋轉,第二章圖像數值x軸和y軸對調,呈現像左旋轉90度,第三張圖執行上下翻轉。
無執行任何選轉指令
cv::transpose(image, image);
//cv::flip(image, image, 0);
//cv::transpose(image, image);
cv::flip(image, image, 0);
Part 2
接下來要開始實作Adaptive Thresholding 。
在Adaptive Thresholding 中的threshold值的計算方式是 :
T = mean(blockSize) – threshold ,區域像數值平均後減去一個常數。
程式碼如左圖。
Result
以下為實驗結果,左圖是使用二值化cv::threshold(image, binaryFixed, 70, 255, cv::THRESH_BINARY);將70以上橡樹點改為255低於70的改為0。兩張圖相互比較,可以發現因為光源是在左上方,所以在曝光的地方顯示不出來,但右圖呈現完整的整隻動物的形狀,連毛的線條也很清楚。
接下來我嘗試改變參數觀察在不同參數下的變化。
改變不同的區域範圍,由小到大,可以發現區域範圍越小(如左圖),呈現的相片的線條越細,細節也越多,畫面看起來較雜亂,若數值越大(如右圖),線條越粗,呈現較少的細節。
int blockSize = 9;
int threshold = 10;
int blockSize = 21;
int threshold = 10;
int blockSize = 51;
int threshold = 10;
改變不同的常數,減去的常數越小,畫面中越多細節,減去越大的成數畫面細節越少。
int blockSize = 21;
int threshold = 5;
int blockSize = 21;
int threshold = 10;
int blockSize = 21;
int threshold = 15;
Part 3
為了減少Adaptive Thresholding中計算平均時所花費的計算時間,作者提供一個方法使用 Integral Image(積分影像)的方式
積分影像的計算方法如右圖:
sum = integral(x+w, y+h) = integral(x,y+h)-integral(x,y)+integral(x,y)
接著,由以上概念,計算參考區域的像數值總和,程式碼如左圖
integral(x-axis, y-axis, width, height)
並將結果顯示於畫面上
Result
結果如右圖,像數值總值為115682。
由實驗結果可以了解到,計算像數值的平均是很龐大的計算量,若是計算很複雜的圖片,計算上就會耗費很多時間,因此想要減少運算時間,可以從運算平均數的部分來改善。
Part 4
以下將實現兩種概念並比較每種方法的效果:
使用Integral Image(積分影像)的方式來計算threshold值。
使用boxfilter() 將圖片做平均,且還有模糊降噪的效果
這個部分重要的程式碼如左圖。
首先,先創建一個矩陣,將圖片的pixel全部複製進去。
使用integral(input, output, type)來計算每個區域的像數值,接著再使用Integral Image(積分影像)的概念來計算總合成是概念如下圖描述。
計算完平均號再減去常數值,得到threshold,再將低於threshold 的數值改0,高於的數值改為255
最後,依照矩陣iimage改解原照片。
這部分使用boxfilter(input,output,type,kernelsize)來做平均。
接著,將比較原圖和經過filter後的圖形,若經過filter後的pixel減去常數後大於原圖的pixel值則改為白色(255),反之則改為黑色(0)
boxfilter()所使用的kernel如下:
Result
以下為使用三種不同方式的結果,三種方法是等效的,所以三張圖看不出差異,但第二章圖四周有白邊,因為在計算Integral Image(積分影像)時,出使用致無法計算,所以呈現一圈白框。
作者在每種方法都加入cv::getTickCount()計算所花費的時間(花費幾個clock),以下為計算結果:
time (adaptiveThreshold)= 154728
time integral= 27312
time filtered= 301382
相互比較所花費的時間:time filtered > adaptiveThreshold > time integral
cv::adaptiveThreshold()
cv::integral()
boxfilter()
Object Tracking
本節要實做物件偵測,假設物件是水平移動的,先繪製參考區域的Histogram,並依照Histogram在水平範圍內找尋最相近的Histogram。
我選擇的照片是,一隻狗往前奔跑的照片,且背景單調與狗的顏色差異很大。
照片來源:Here's What Guys Are Pinning on Pinterest (37 Photos) - Suburban Men , Pinterest
Part 1
作者在這邊提供了兩種計算參考費為內pixel總值的方法,因為是等效的結果,所以我加入了cv::getTickCount()計算所花費的時間(花費幾個clock)
第一種方法:直接使用cv::sum()做相加
第二種方法:使用Integral Image(積分影像)的方式,概念如下
Result
結果如右圖,兩種方法計算參考區域的pixel值結果相同,驗證兩者確實是等效。
速度的部分,在計算這張圖的情況下,使用cv::sum()做相加使用的clock比使用Integral Image(積分影像)的方式還要少
Part 2
接下來,要來繪製參考區域的Histogram和前面不同的是,這裡繪製的Histogram只有16格刻度,作者提供了兩種做法,第一種作法和前面相似使用cv::calcHist()來計算,第二種將參考區域轉換成binary image,概念是若integral image內的值只0和1(binary image),那pixel值的總和就會等於pixel的數量。
這部分的重點就會在於將圖片轉為binary image,程式碼如右圖。
首先,欲繪製16 bins 的Histogram,灰階為256 bins,所以將灰階每16格重新劃為一格,因此輸入值nPlanes=16。
接著,經過計算後mask=1111 1100
最後將經過mask的相片,和0000 0001做AND意思是,將數值改為0或1,變成integral image。
除了計算參考區域外,將選取區域往前移一些,猜測接下來會移動到地方,我選取區域的參數如右圖。使用的方法如上一部份相同,將參考區域轉換成binary image以計算Histogram。
Result
以下為執行結果,從結果可以確認兩種方式是等效的。
黑色框線是初始位置,白色框線則是下一步的位置,將第三張圖和前兩張做比較,會發現第三張圖右半部的pixel較高,左半部pixrl值較低,表示選取畫面比較白,驗證結果與圖形相符。
cv::calcHist()
convert To Binary Planes
下一步位置的Histogram
initial
下一步位置
Part 3
因為物體是呈現水平移動,所以將水平範圍內分為一區一區和參考區域的Histogram比效較相關性,找出和參考區域最像似的部分。
這部分重要的程式碼如左圖,我將樣測試的範圍設定的比參考範圍寬,並在畫面中標示出來。
接著使用 cv::compareHist()比較相關性。
最後再將結果打印出來。
Result
以下為執行結果,白色線是待測試範圍,黑色框線是為最計算後的最佳位置。底下圖表為距離和相關性的摺線圖,因為圖形不斷重複,所以我擷取一部份分析,範圍為distance(0, 266)~distance(480, 266),峰值位於中間偏左,觀察相片中的參考物件也位於中間偏左的位置,確認結果與圖片相符。
initial
result
Conclusion
本次作業最難的部分是在挑選照片,想要挑選到一張照片使結果符合預期,需先完全理解程式碼的運行方式。舉例來說,在第一個主題Histogram裡要使用Equalization將照片光線不足或過曝的狀況挑整成理想的亮度,我一開始挑的照片是有過多的pixel值很低,就算Equalization後Histogram也不會形成完美的鐘形分布,相片調整完的差異不明顯,之後上網看別人如何實作這個主題,統整他們選的照片的共通點,最後挑選了一張是有亮面和暗面,整體偏暗但沒有過多的pixel值為0(黑色),最終效果便很理想。還有挑選照片另一個困難點是,照片大多是彩色照片,但操作幾乎都是在灰階上操作,有時候物件和背景顏色差異很大,但轉灰階後顏色會很接近,在做物件偵測時,常會選到背景,或框線位置不精準,最後我選擇的照片多為背景單調的圖。
Reference
(C++) 標頭檔 , 2023/03/03 , Microsoft
學習看直方圖(Histogram) 助你判斷準確的曝光 , Canon
Histogram Calculation , OpenCV, 4.6.0 Open Source Computer Vision
9. 影像區塊分割 , D.-C. Tseng, DLCV Lab. in NCU
Basic Thresholding Operations, OpenCV, 4.6.0 Open Source Computer Vision
OpenCV 直方圖均衡化 , GitHub Gist
Utility and system functions and macros , OpenCV, 4.6.0 Open Source Computer Vision
cv::Rect_< _Tp > Class Template Reference , 4.6.0 Open Source Computer Vision
Operations on arrays , OpenCV, 4.6.0 Open Source Computer Vision
[convertTo]数据转换 , GitHub
Back Projection , OpenCV, 4.6.0 Open Source Computer Vision
2D Histogram , Published with MATLAB® R2017a
Histograms Image Processing , OpenCV, 4.6.0 Open Source Computer Vision
基礎的色彩學 , APTEC印刷科技研究中心有限公司
Meanshift and Camshift , OpenCV, 4.6.0 Open Source Computer Vision
cv::TermCriteria Class Reference , OpenCV, 4.6.0 Open Source Computer Vision
Object Tracking , OpenCV, 4.6.0 Open Source Computer Vision
Histogram Comparison , 4.6.0 Open Source Computer Vision
Python-Opencv中用compareHist函数进行直方图比较进行对比图片 , 2019-04-11 ,CSDn
Histograms , 4.6.0 Open Source Computer Vision
【沒錢ps,我用OpenCV!】Day 19 - 花式修圖1,OpenCV 的圖片自適應二值化,產生更好效果的黑白圖片!cv2.adaptiveThreshold , 2020-10-01
Image Thresholding, 4.6.0 Open Source Computer Vision
OpenCV-Python教程:几何空间变换~缩放、转置、翻转(resize,transpose,flip) , 2021年6月26日, 桔子菌
Miscellaneous Image Transformations , OpenCV, 4.6.0 Open Source Computer Vision
Integral Image 積分影像 , 2013-01-18
Image Filtering , 4.6.0 Open Source Computer Vision
Adaptive Thresholding with OpenCV ( cv2.adaptiveThreshold ), by Adrian Rosebrock, May 12, 2021