opencv基本圖片操作:
因為opencv有2.0 和 3.0 的版本區別,所以網上搜到的函數或類型都是兩種格式,建議用新版的,什么impImage* 類型的都是2.0版本的寫法,我全部使用的是Mat。一定要統一好,不要一會新的一會舊的,會報錯的。
讀圖片imread,顯示imshow,等待waitKey等等,這些要先熟悉
opencv的強大之處在于幾乎所有的圖像操作它都有現成的函數可供調用,非常方便。多谷歌,一定會有函數已經實現了你想完成的功能。
二值化:不論是原圖還是有劃痕或噪點的圖,背景都不干凈,這對識別的影響還是挺不好的,所以要先二值化,把黑白像素點區分的開一些。但是圖片右側明顯要比左側更暗,所以在閾值選取的時候比較難辦,很難用一個固定的值將兩部分圖像都二值化得很理想,所以就用到了逼格更高的自適應二值化(adaptiveThreshold),tips:二值化前先直方圖均衡一下效果會更好。
中值濾波:針對有噪點和有劃痕的圖像,中值濾波是非常好的處理方案,中值的參數可調,可以很好的消除噪音的影響。缺點就是參數不好調啊,調的想死。。
模板匹配:模板的來源可以是自己從待識別的圖片中摳圖,不過我們作業提供了模板圖片,所以這一步就可以省掉了。opencv提供了非常強大的matchTemplate函數,可以將給定圖片與模板按照你規定的計算方法計算一個相似度的值,并將對應的坐標存儲下來,你需要做的只是將值比較大(或小,與你規定計算相似度的函數有關)的圖像框出來即可
窗口掃描:為了提高識別率,我設定了一個窗口對原圖進行掃描,掃描窗口的移動設定了一點規則,就是如果前一個窗口沒有匹配到數字就微調窗口位置,如果匹配到數字就將窗口左軸移動到匹配到的數字的右側,再重復掃描。
以下是基于OpenCV實現簡單的數字識別。這里以游戲Angry Birds為例,通過以下幾個主要步驟對其中右上角的分數部分進行自動識別。
1. 學習分類器
根據訓練樣本,選取模型訓練產生數字分類器。這里的樣本可以是通用的數字樣本庫(如NIST等),也可以是針對應用場景而制作的專門訓練樣本。前者優在泛化性,后者強在準確率,當然常用做法是將這兩者結合,即在通用數字庫基礎上做修改。另外這里由于模式并不復雜,計算量也不大,所以不對樣本進行特征提取,對原始樣本作簡單變換后直接作為訓練樣本。
具體地,首先是生成訓練樣本矩陣,一般樣本是以二維矩陣的方式存在文件當中,現在要將它們讀出來,進行適當的預處理,然后生成OpenCV能理解的數據結構。
train_X = cvCreateMat(sample_num * class_num, size * size, CV_32FC1);
train_Y = cvCreateMat(sample_num * class_num, 1, CV_32FC1);
for(i = 0; i 《 class_num; i++){
for(j = 0; j 《 sample_num; j++){
src_image = cvLoadImage(file,0);
pimage = preprocessing(src_image, size, size);
。。。
cvGetRow(train_X, &row, i * sample_num + j);
row_vec = cvReshape(&data, &mathdr, 0, 1);
cvCopy(row_vec, &row, NULL);
。。。
cvGetRow(train_Y, &row, i * sample_num + j);
cvSet(&row, cvRealScalar(i));
}
}
訓練樣本中的數字位置形態各異,因此讀入時需要進行規整化。主要方法是先找到數字的邊界框,然后以寬和高中大的一邊為基準進行縮放和拉伸,從而使得其可以占滿整個表示單個樣本的矩陣。
IplImage preprocessing(IplImage* img, int w, int h){
。。。
bb = findBoundingBox(img);
cvGetSubRect(img, &data, cvRect(bb.x, bb.y, bb.width, bb.height));
size = (bb.width 》 bb.height) ? bb.width : bb.height;
res = cvCreateImage(cvSize(size, size), 8, 1);
x = floor((float)(size - bb.width) / 2.0f);
y = floor((float)(size - bb.height) / 2.0f);
cvGetSubRect(res, &subdata, cvRect((int)x, (int)y, bb.width, bb.height));
cvCopy(&data, &subdata, NULL);
ret = cvCreateImage(cvSize(w, h), 8, 1);
cvResize(res, ret, CV_INTER_NN);
return *ret;
}
假設單個樣本可表示為0/1矩陣,那findBoundingBox()只要從x和y方向分別掃描最大最小的非0值就可以了。 訓練樣本準備好后,在OpenCV中創建相應的分類器非常方便。這里用的是KNN,當然除了KNN外還有其它很多封裝好的分類器(如NN, SVM等)。
knn = new CvKNearest(train_X, train_Y, 0, false, K);
評論