基於opencv的數字識別
最近學習了opencv,然後想通過其對圖片上的數字進行識別,參考了網上幾篇關於opencv數字識別的部落格,我自己也寫了一個程式玩玩。我是在vs2017和opencv3.4.1環境下實現的。
這裡先說一下我的思路和步驟:
- 載入需要識別的圖片,然後將其轉化為二值圖
- 尋找數字的外輪廓,切記不可以找全部輪廓,否則一個數字將會有多個輪廓,識別起來就很麻煩了
- 對輪廓進行排序,因為使用findcontours函式尋找到的輪廓順序不一定是我們想要的,因此需要對找到的數字輪廓根據輸入的圖片進行排序
- 按順序將數字輪廓分割,一個一個提取出來
- 進行識別和匹配,將提取出來的數字與模板數字(需要自己建立)進行比較,這裡採用兩幅圖片畫素相減的方法,尋找出差值最小的圖片即為最匹配的數字
一、圖片預處理
將輸入的圖片轉化為二值圖以便尋找輪廓
//輸入要識別的圖片,並顯示 Mat srcImage = imread("number.jpg"); imshow("原圖", srcImage); //對影象進行處理,轉化為灰度圖然後再轉為二值圖 Mat grayImage; cvtColor(srcImage, grayImage, COLOR_BGR2GRAY); Mat binImage; //第4個引數為CV_THRESH_BINARY_INV是因為我的輸入原圖為白底黑字 //若為黑底白字則選擇CV_THRESH_BINARY即可 threshold(grayImage, binImage, 100, 255, CV_THRESH_BINARY_INV);
二、尋找數字輪廓
//尋找輪廓,必須指定為尋找外部輪廓,不然一個數字可能有多個輪廓組成,比如4,6,8,9等數字 Mat conImage = Mat::zeros(binImage.size(), binImage.type()); vector<vector<Point>> contours; vector<Vec4i> hierarchy; //指定CV_RETR_EXTERNAL尋找數字的外輪廓 findContours(binImage, contours, hierarchy, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE); //繪製輪廓 drawContours(conImage, contours, -1, 255);
三、對輪廓進行排序
在排序之前呢,我們需要定義一個類,用來存放輪廓的外接矩陣以及方便後續的排序
class myRect
{
public:
myRect(){}
~myRect(){}
myRect(Rect &temp):myRc(temp){}
//比較矩形左上角的橫座標,以便排序
bool operator<(myRect &rect)
{
if (this->myRc.x < rect.myRc.x)
{
return true;
}
else
{
return false;
}
}
//過載賦值運算子
myRect operator=(myRect &rect)
{
this->myRc = rect.myRc;
return *this;
}
//獲取矩形
Rect getRect()
{
return myRc;
}
private:
Rect myRc;//存放矩形
};
有了這個類之後,我們可以將一個一個輪廓外接矩陣保存於該類中。而且類中過載了比較操作符,很容易對輪廓進行排序。
//將每個數字,分離開,儲存到容器中
vector<myRect> sort_rect;
for (int i = 0; i < contours.size(); i++)
{
//boundingRect返回輪廓的外接矩陣
Rect tempRect = boundingRect(contours[i]);
sort_rect.push_back(tempRect);
}
排序演算法的話,採用比較簡單的冒泡法
//對矩形進行排序,因為輪廓的順序不一定是數字真正的順序
for (int i = 0; i < sort_rect.size(); i++)
{
for (int j = i + 1; j < sort_rect.size(); j++)
{
if (sort_rect[j] < sort_rect[i])
{
myRect temp = sort_rect[j];
sort_rect[j] = sort_rect[i];
sort_rect[i] = temp;
}
}
}
這樣,sort_rect容器中的輪廓矩形是按我們輸入的圖片中的數字順序存放的。
四、載入(新建)數字模板
我們在進行數字匹配時,需要先載入模板進行比較。如果沒有就要先新建一個。新建模板需要輸入的是0-9的數字模板,不用修改程式,只需要新增以下程式碼。
/*載入模板,若沒有則需自己新建一個*/
//新建,執行一次就好,不過製作模板的材料為0-9十個數字的影象
for (int i = 0; i < 10; i++)
{
Mat ROI = conImage(sort_rect[i].getRect());
Mat dstROI;
resize(ROI, dstROI, Size(40, 50),0, 0, INTER_NEAREST);
char name[64];
sprintf(name, "C:/Users/Administrator/Desktop/number_recognition/number_recognition/image/%d.jpg", i);
//imshow(str, dstROI);
imwrite(name, dstROI);
}
製作模板其實就是將我們需要的ROI區域儲存為圖片,程式碼中的路徑是我的路徑,可以根據自己情況修改。
然後是載入模板,也就是從檔案中將各個模板圖片讀入,路徑根據自己的情況修改。
//載入模板
vector<Mat> myTemplate;
for (int i = 0; i < 10; i++)
{
char name[64];
sprintf(name, "C:/Users/Administrator/Desktop/number_recognition/number_recognition/image/%d.jpg", i);
Mat temp = imread(name, 0);
myTemplate.push_back(temp);
}
五、分割數字
分割數字比較容易,即通過輪廓外接矩形在二值圖片上尋找我們要使用的ROI,然後分別儲存下來,以供識別。根據排好序的sort_rect可以分割出待識別的數字。
//按順序取出和分割數字
vector<Mat> myROI;
for (int i = 0; i < sort_rect.size(); i++)
{
Mat ROI;
ROI = conImage(sort_rect[i].getRect());
Mat dstROI = Mat::zeros(myTemplate[0].size(),myTemplate[0].type());
resize(ROI, dstROI, myTemplate[0].size(), 0, 0, INTER_NEAREST);
myROI.push_back(dstROI);
}
六、比較和匹配
我採用的比較和匹配方法是,將absdiff計算模板和待識別數字的差值,然後比較出差值最小的即為最匹配的數字,從而實現匹配。在匹配前我們需要定義一個getPiexSum函式以計算兩幅圖片的差值的畫素之和。
//求圖片的畫素和
int getPiexSum(Mat &image)
{
int sum = 0;
for (int i = 0; i < image.cols; i++)
{
for (int j = 0; j < image.rows; j++)
{
sum += image.at<uchar>(j, i);
}
}
return sum;
}
下面進行匹配和輸出
//進行比較,將圖片與模板相減,然後求全部畫素和,和最小表示越相似,進而完成匹配
vector<int> seq;//順序存放識別結果
for (int i = 0; i < myROI.size(); i++)
{
Mat subImage;
int sum = 0;
int min = 100000;
int min_seq = 0;//記錄最小的和對應的數字
for (int j = 0; j < 10; j++)
{
//計算兩個圖片的差值
absdiff(myROI[i], myTemplate[j], subImage);
sum = getPiexSum(subImage);
if (sum < min)
{
min = sum;
min_seq = j;
}
sum = 0;
}
seq.push_back(min_seq);
}
//輸出結果
cout << "識別結果為:";
for (int i = 0; i < seq.size(); i++)
{
cout << seq[i];
}
cout << endl;
七、總結
識別的結果圖片:
存在的不足:這種識別方式有個不好的點就是,模板的數字大小(比如我用的是48號字型大小)不能與待識別的數字的大小相差太多,否則會降低識別準確率。如果相差不是很大的話是可以準確識別的。然後該程式也只能識別單行數字,暫時還不能識別多行。但是可以在本程式的基礎上做點修改就可以了。等以後有時間了再回來解決這兩個問題。
附上程式原始碼下載:https://download.csdn.net/download/m0_37543178/10662148