Update
代碼已經上傳到github上了,可以點這里
Cutting
一直說這要整理一下Computer Vision課程的大作業,拖了好久。這兩天忙著寫一個訂單處理的第三方庫,陷入了僵局,所以換個口味,把大作業整理一下。
Requirement
Water depth measurement.
實現目標:通過使用計算機視覺及圖像處理技術,通過正確檢測插入水體的標尺和水體水平面的刻度值來確定水位高度。圖像數據見附件。
可允許用戶輸入標尺最上端的高度值、照相機鏡頭距離標尺最上端的和水平面形成的夾角、刻度尺正面和照相機之間夾角值,以及標尺每個刻度的高度值。
評分標準:
- 能否解決存在的多種問題,其中包括:
a. 標尺刻度靠近水面的部分可能由于長期浸泡在污水中出現污漬而無法識別。
b. 水面可能出現的霧氣,造成識別困難。
c. 標尺可能有一定的弧度,造成精確度量存在問題。 - 計算效率:使用任意目前流行的Intel i3處理器及更快的處理器,每個4096*4096像素分辨率以內的圖像測量時間不超過20秒(包含圖像讀取及數據值輸出)。
3.系統完整性。
使用語言:Visual C++(可使用OpenCV)
部分附件
Train of Thought
整個過程大致分為四個階段:圖像預處理、識別、過濾、數據處理
1. 預處理
首先會進行一個直方圖均衡化的操作。再由于輸入的圖像差別較大,有如上圖這種十分清晰的,也存在模糊到人工識別也比較吃力的。所以顯然不同的清晰度應該有不同的處理方式。這里簡單的將清晰度分為清晰
、模糊
兩類。
對于清晰
的圖片,進行適度腐蝕膨脹操作,以進一步提高圖片中標尺的對比度。
//腐蝕、膨脹
int erosion_size = 3;
Mat element = getStructuringElement( MORPH_RECT,
Size( 2*erosion_size + 1, 2*erosion_size+1 ),
Point( erosion_size, erosion_size ) );
/// 腐蝕操作
erode( origin, origin, element );
dilate(origin, origin, element);
對于模糊
的圖片數據,先進行濾波,再提高對比度
//創建并初始化濾波模板
cv::Mat kernel(3,3,CV_32F,cv::Scalar(0));
kernel.at<float>(1,1) = 5.0;
kernel.at<float>(0,1) = -1.0;
kernel.at<float>(1,0) = -1.0;
kernel.at<float>(1,2) = -1.0;
kernel.at<float>(2,1) = -1.0;
cv::filter2D(origin,origin,origin.depth(),kernel);
int alpha = 1.5;
int beta = 50;
for( int y = 0; y < origin.rows; y++ )
{
for( int x = 0; x < origin.cols; x++ )
{
for( int c = 0; c < 3; c++ )
{
origin.at<Vec3b>(y,x)[c] = saturate_cast<uchar>( alpha*( origin.at<Vec3b>(y,x)[c] ) + beta );
}
}
}
對比效果:
2. 識別
考查了Haar Like
、SIFT
、LBP
、HOG
等算法。Haar Like
多用于人臉識別,LBP
多用于基于紋理特征的監測,所以不是很適合。SIFT
做了簡單測試,識別效果如下:
不是很理想,所以最后選定了
HOG
算法。
- 訓練素材準備
由于檢測的目標多位于水邊,環境多為山、天空、水、泥土等,所以額外加入了這些素材作為負樣本。由于負樣本創建有一定規格要求,所以使用Python腳本,批量裁剪,最后共得到負樣本42360個。 - 訓練識別過程
這一過程網上有不少代碼可供參考,主要是對于參數等選擇比較重要。這里我們定義部分參數如下:
//識別與檢測的參數
#define WIN_SIZE Size(64,64)
#define BLOCK_SIZE Size(8,8)
#define BLOCK_STRIDE Size(4,4)
#define CELL_SIZE Size(4,4)
#define BIN 12
2. 過濾
對于識別結果需要進行過濾。這里我們定義了一個Mask
算法,用以合并多個過濾算法多結果。
對于每一種過濾算法,都會有各自的保留區域,將它們疊加,通過某個大小的矩形掃描,如果該矩形區域內,每一層的保留區域面積占比大于一個可調參數Threshold
,則認為該矩形區域應該保留。
這里我們使用了兩種過濾算法:
-
ColorFilter
基于顏色特征的過濾。由于標尺上的顏色固定,故可以丟棄與之無關的顏色區域
顏色過濾 -
Canny邊緣檢測過濾
Canny算子多用于檢測物體的邊緣,我們通過保留邊緣區域后,并將邊緣鋪展開來,以得到保留區域。
邊緣檢測過濾
最后運用上面提到的多層Mask合并算法,得到最終的過濾保留區域。
Mask疊加
可以看到將過濾結果應用于識別結果時,大量的誤識別被過濾掉了。原圖非常大,綠色的框框即為識別結果。
-
單峰過濾
由于一張圖中僅有一個標尺,所以通過前面的過濾后,我們認為,矩形在圖片上的分布應該如圖所示。將除最高峰以外的矩形丟棄。
-
Rectangle修正
標尺中的"E"標示實際分布是均勻的,所以即使無法完全識別,也可以通過算法進行一個修正,自動補全出未被識別的"E"
效果如下:
修正效果
4. 數據處理
最后的處理是針對識別結果進行純數值分析的優化。我們認為識別系統穩定后,會存在一定的固有誤差,可以通過線性擬合的方式進行一個修正。將識別數據與真實數據進行擬合(這一點老師不是很贊成,認為沒有必要)。
Closing
這個大作業感覺還是很有難度的,部分的結果識別還是比較滿意的,但是也有一些圖片是偏差蠻大的。而且由于是直接調用的OpenCV
的庫,實際上對圖像處理的一些算法還是沒有很深入,有機會再回來搞搞視覺吧。