如何基于opencv實(shí)現(xiàn)簡(jiǎn)單的數(shù)字識(shí)別
由于自己學(xué)識(shí)尚淺,不能用python深度學(xué)習(xí)來識(shí)別這里的數(shù)字,所以就完全采用opencv來識(shí)別數(shù)字,然后在這里分享、記錄一下自己在學(xué)習(xí)過程中的一些所見所得和所想

要解決的問題
這是一個(gè)要識(shí)別的數(shù)字,我這里首先是對(duì)圖像進(jìn)行一個(gè)ROI的提取,提取結(jié)果就僅僅剩下數(shù)字,把其他的一些無關(guān)緊要的要素排除在外,

這是ROI圖片,我們要做的就是識(shí)別出該照片中的數(shù)字,
解決問題的思路
1、先把這個(gè)圖片中的數(shù)字分割,分割成為5張小圖片,每張圖片包含一個(gè)數(shù)字,為啥要分割呢?因?yàn)槲覀儧]辦法讓計(jì)算機(jī)知道這個(gè)數(shù)字是多少,所以只能根據(jù)特征,讓計(jì)算機(jī)去識(shí)別特征,然后每一個(gè)特征對(duì)應(yīng)一個(gè)值,首先貼出分割圖片的程序,然后在程序下方會(huì)有一段思路解釋
#include <opencv2/core/core.hpp>
#include <opencv.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/features2d/features2d.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <iostream>
#include <ctime>
using namespace std ;
using namespace cv;
#include <map>
Mat src_threshold;
Mat src_dil;
int sunImage(Mat &image);
vector<Mat>ROI_image;//待測(cè)圖片
int main()
{
clock_t start ,finish;
start=clock();
Mat src;
src=imread("D:\\vspic\\picture\\number6.jpg");
resize(src,src,Size(src.cols/7,src.rows/7));
imshow("src",src);
Mat src_gray;
cvtColor(src,src_gray,COLOR_BGR2GRAY);
//imshow("gsrc_ray",src_gray);
Mat src_blur;
blur(src_gray,src_blur,Size(9,9));
//GaussianBlur(src_gray,src_blur,Size(11,11),1,1);
Mat src_threshold;
threshold(src_blur,src_threshold,150,255,THRESH_OTSU);
//imshow("src_threshold",src_threshold);
Mat src_canny;
Canny(src_threshold,src_canny,125,255,3);
//imshow("src_canny",src_canny);
vector<vector<Point>>contours_src;
vector<Vec4i>hierarchy_src(contours_src.size());
findContours(src_canny,contours_src,hierarchy_src,RETR_EXTERNAL,CHAIN_APPROX_NONE);
Rect rect_s;
Rect choose_rect;
for (size_t i=0;i<contours_src.size();i++)
{
rect_s=boundingRect(contours_src[i]);
double width=rect_s.width;
double height= rect_s.height;
double bizhi=width/height;
if (bizhi>1.5&&height>50)
{
/*rectangle(src,rect_s.tl(),rect_s.br(),Scalar(255,255,255),1,1,0);*/
choose_rect=Rect(rect_s.x+20,rect_s.y+30,rect_s.x-30,rect_s.y-108);
}
}
Mat roi;
roi=src(choose_rect);
//imshow("src_",roi);
Mat img =roi;
Mat gray_img;
// 生成灰度圖像
cvtColor(img, gray_img, CV_BGR2GRAY);
// 高斯模糊
Mat img_gau;
GaussianBlur(gray_img, img_gau, Size(3, 3), 0, 0);
// 閾值分割
Mat img_seg;
threshold(img_gau, img_seg, 0, 255, THRESH_BINARY + THRESH_OTSU);
Mat element;
element=getStructuringElement(MORPH_RECT,Size(8,8));
erode(img_seg,src_dil,element);
//imshow("src_dil",src_dil);
// 邊緣檢測(cè),提取輪廓
Mat img_canny;
Canny(src_dil, img_canny, 200, 100);
//imshow("canny",img_canny);
vector<vector<Point>> contours;
vector<Vec4i> hierarchy(contours.size());
findContours(img_canny, contours, hierarchy, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE, Point());//尋找輪廓
int size = (int)(contours.size());//輪廓的數(shù)量
//cout<<size<<endl;6個(gè)
// 保存符號(hào)邊框的序號(hào)
vector<int> num_order;//定義一個(gè)整型int容器
map<int, int> num_map;//容器,需要關(guān)鍵字和模板對(duì)象兩個(gè)模板參數(shù),此處定義一個(gè)int作為索引,并擁有相關(guān)連的指向int的指針
for (int i = 0; i < size; i++)
{
// 獲取邊框數(shù)據(jù)
Rect number_rect = boundingRect(contours[i]);
int width = number_rect.width;//獲取矩形的寬
int height = number_rect.height;//獲取矩形的高
// 去除較小的干擾邊框,篩選出合適的區(qū)域
if (width > img.cols/20 )
{
rectangle(img,number_rect.tl(),number_rect.br(),Scalar(255,255,255),1,1,0);//繪制矩形
imshow("img",img);//顯示矩形框
num_order.push_back(number_rect.x);//把矩形的x坐標(biāo)放入number_order容器中,將一個(gè)新的元素添加到vector的最后面,
//位置為當(dāng)前元素的下一個(gè)元素
num_map[number_rect.x] = i;//向map中存入鍵值對(duì),number_rect.x是關(guān)鍵字,i是值
/*把矩形框的x坐標(biāo)與對(duì)應(yīng)的i值一起放入map容器中,形成一一對(duì)應(yīng)的鍵值對(duì)
*/
}
}
// 按符號(hào)順序提取
sort(num_order.begin(), num_order.end());/*把number_order容器中的內(nèi)容按照從小到大的順序排列,這里面是X的坐標(biāo)*/
for (int i = 0; i < num_order.size(); i++) {
Rect number_rect = boundingRect(contours[num_map.find(num_order[i])->second]);//num_order里面放的是坐標(biāo)
//cout<<"num_map的值是:"<<num_map.find(num_order[i])->second<<endl;
Rect choose_rect(number_rect.x, 0, number_rect.width, img.rows);//矩形左上角x,y的坐標(biāo)以及矩形的寬和高
Mat number_img = img(choose_rect);
resize(number_img,number_img,Size(30,100));//歸一化尺寸
ROI_image.push_back(number_img);//保存為待測(cè)圖片
//imshow("number" + to_string(i), number_img);
char name[50];
sprintf_s(name,"D:\\vs2012\\model\\%d.jpg",i);//保存模板
imwrite(name, number_img);
}
cout<<"圖片分割完畢"<<endl;
//加載模板
vector<Mat>temptImage;//存放模板
for (int i=0;i<4;i++)
{
char name[50];
sprintf_s(name,"D:\\vs2012\\model\\%d.jpg",i);
Mat temp;
temp=imread(name);
//cout<<"加載模板圖片通道數(shù):"<<temp.channels()<<endl;
temptImage.push_back(temp);
}
vector<int>seq;//存放順序結(jié)果
for (int i=0;i<ROI_image.size();i++)
{
Mat subImage;
int sum=0;
int min=50000;
int seq_min=0;//記錄最小的和對(duì)應(yīng)的數(shù)字
for (int j=0;j<4;j++)
{
absdiff(ROI_image[i],temptImage[j],subImage);//待測(cè)圖片像素減去模板圖片像素
sum=sunImage(subImage);//統(tǒng)計(jì)像素和
if (sum<min)
{
min=sum;
seq_min=j;
}
sum=0;
}
seq.push_back(seq_min);
}
cout<<"輸出數(shù)字匹配結(jié)果:";//endl是換行的意思
for (int i=0;i<seq.size();i++)//輸出結(jié)果,小數(shù)點(diǎn)固定在第3位
{
cout<<seq[i];
if (i==1)
{
cout<<".";
}
}
finish=clock();
double all_time=double(finish-start)/CLOCKS_PER_SEC;
/*cout<<"運(yùn)行總時(shí)間是:"<<all_time<<endl;*/
waitKey(0);
return 0;
}
//計(jì)算像素和
int sunImage(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;
}
整體思路是這樣子的:0-9這10個(gè)數(shù)字也都是已經(jīng)被分割好的,并且保存好了,也就是模板,然后我們把待測(cè)的圖片也分割掉,然后從0-9模板文件夾中去讀取模板圖片,讓待測(cè)的分割完畢的圖片去和10個(gè)模板逐個(gè)相減,然后去統(tǒng)計(jì)他們相減后的像素和,如果這個(gè)在這10個(gè)中最低,那么他們就是同一個(gè)數(shù)字,然后輸出值就可以了,分割后的大概是這樣

上邊是第一種方法,然后還有第二種,是穿針引線的方法,是根據(jù)晶體管數(shù)字特征來識(shí)別的

這是晶體管數(shù)字的特征,每個(gè)0-9每個(gè)數(shù)字都是不一樣的,我們下一篇文章再做詳細(xì)的介紹
總結(jié)
到此這篇關(guān)于如何基于opencv實(shí)現(xiàn)簡(jiǎn)單的數(shù)字識(shí)別的文章就介紹到這了,更多相關(guān)opencv實(shí)現(xiàn)數(shù)字識(shí)別內(nèi)容請(qǐng)搜索本站以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持本站!
版權(quán)聲明:本站文章來源標(biāo)注為YINGSOO的內(nèi)容版權(quán)均為本站所有,歡迎引用、轉(zhuǎn)載,請(qǐng)保持原文完整并注明來源及原文鏈接。禁止復(fù)制或仿造本網(wǎng)站,禁止在非maisonbaluchon.cn所屬的服務(wù)器上建立鏡像,否則將依法追究法律責(zé)任。本站部分內(nèi)容來源于網(wǎng)友推薦、互聯(lián)網(wǎng)收集整理而來,僅供學(xué)習(xí)參考,不代表本站立場(chǎng),如有內(nèi)容涉嫌侵權(quán),請(qǐng)聯(lián)系alex-e#qq.com處理。
關(guān)注官方微信