日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

OpenCV机读卡识别

發布時間:2023/12/29 编程问答 37 豆豆
生活随笔 收集整理的這篇文章主要介紹了 OpenCV机读卡识别 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

簡單介紹

編寫一個基于OpenCV的小程序,用于識別下圖所示機讀卡。

步驟回顧

圖像處理

圖像二值化

圖像識別離不開圖像的處理。用相機拍攝的機讀卡基本都是三通道的彩色圖像,而這里需要用到的處理方法就是“圖像二值化”,整個圖像呈現出明顯的只有黑和白的視覺效果,便于進行圖像分割。現將圖片先轉換為灰度圖,然后再進行二值化。

Mat loadImage(char *path) {Mat src = imread(path);if (src.empty()){cout << "IMAGE LOAD FAILED!" << endl;exit(0);}Mat gray, binary;cvtColor(src, gray, CV_BGR2GRAY);adaptiveThreshold(gray, binary, 255, 0, 1, 101, 10);return binary; }

處理效果:

ROI分割

想要獲得數字信息和選擇題答案,就要定位到ROI(region of interest,感興趣區域)將其單獨分割出來。顯而易見,圖1的ROI即矩形框框住的區域,也是整個圖像中連通域最大的區域。獲得最大連通域的方法如下:

Mat gaintComponent(Mat src) {//查找連通域vector<vector<Point>>contours;findContours(src, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE);//最大連通域vector<Point>maxContour;double maxArea = 0.0;for (size_t i = 0; i < contours.size(); i++){double area = cv::contourArea(contours[i]);if (area > maxArea){maxArea = area;maxContour = contours[i];}}//轉換為矩形框(boundingbox)Rect maxRect = boundingRect(maxContour);Mat result = src(maxRect);return result; }

分割結果:

選擇題部分分割

根據圖3的分割效果將圖像反色,再次提取最大連通域即可定位到選擇題方框,接著再次反色讓背景變成黑色。然后對得到的圖像進行簡單的裁剪,去掉上方的分割線,降低識別誤差。
ps:二值圖中白色為1,黑色為0。處理二值圖圖像時計算機只看得見白色,所以需要反色操作。

//定位到選擇題部分 Mat roiBox = gaintComponent(src); Mat choiceBox = gaintComponent(~roiBox); choiceBox = ~choiceBox; //選擇題部分邊界處理 choiceBox = choiceBox(Rect(0, 15, choiceBox.cols, choiceBox.rows / 2 - 22));

分割效果:

數字部分分割
//定位選擇題上方數字信息 Mat roiImg = gaintComponent(src); Mat infoBox = roiImg(Rect(0, 10, roiImg.cols, roiImg.rows / 4 - 10)); vector<vector<cv::Point>> contours, numBox; cv::findContours(infoBox, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE); for (size_t i = 0; i < contours.size(); i++) {double area = cv::contourArea(contours[i]);if (area > 100)numBox.push_back(contours[i]); } vector<Mat>num; for (size_t i = 0; i < numBox.size(); i++) {cv::Rect rect = cv::boundingRect(numBox[i]);cv::Mat result = infoBox(rect);num.push_back(result); }

該代碼的問題:對于上方數字的分割用的是固定值的方法,只適用于圖1大小的機讀卡圖片,不能自適應的調整分割區域

分割效果:

識別

選擇題識別

根據圖4的分割效果,可以明顯的看出涂抹了選項的選項部分連通域明顯大于未涂抹部分,所以這里可以很輕易的得到40道選擇題的涂抹區域的相對位置。將圖4分割為10x20的矩陣,每個位置對于一個區域信息,從而得到涂抹結果。

代碼實現:

//定位涂抹區間連通域vector<vector<cv::Point>> contours, answer;cv::findContours(choiceBox, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE);for (size_t i = 0; i < contours.size(); i++){double area = cv::contourArea(contours[i]);if (area > 20 && area < 60)answer.push_back(contours[i]);}//存儲涂抹中心點vector<Moments> mu(answer.size());for (int i = 0; i < answer.size(); i++)mu[i] = moments(answer[i], false);// 計算中心矩:vector<Point2f> mc(answer.size());for (int i = 0; i < answer.size(); i++)mc[i] = Point2f(mu[i].m10 / mu[i].m00, mu[i].m01 / mu[i].m00);//得到答案(使用選擇題模板,涂抹部分的答案用小寫字母標識)char answers[10][20] = {{ ' ','A','B','C','D',' ','A','B','C','D', ' ','A','B','C','D', ' ','A','B','C','D' },{ ' ','A','B','C','D',' ','A','B','C','D', ' ','A','B','C','D', ' ','A','B','C','D' },{ ' ','A','B','C','D',' ','A','B','C','D', ' ','A','B','C','D', ' ','A','B','C','D' },{ ' ','A','B','C','D',' ','A','B','C','D', ' ','A','B','C','D', ' ','A','B','C','D' },{ ' ','A','B','C','D',' ','A','B','C','D', ' ','A','B','C','D', ' ','A','B','C','D' },{ ' ','A','B','C','D',' ','A','B','C','D', ' ','A','B','C','D', ' ','A','B','C','D' },{ ' ','A','B','C','D',' ','A','B','C','D', ' ','A','B','C','D', ' ','A','B','C','D' },{ ' ','A','B','C','D',' ','A','B','C','D', ' ','A','B','C','D', ' ','A','B','C','D' },{ ' ','A','B','C','D',' ','A','B','C','D', ' ','A','B','C','D', ' ','A','B','C','D' },{ ' ','A','B','C','D',' ','A','B','C','D', ' ','A','B','C','D', ' ','A','B','C','D' } };int x = choiceBox.cols / 20;int y = choiceBox.rows / 10;for (size_t i = 0; i < mc.size(); i++){int x_index = mc[i].x / x;int y_index = mc[i].y / y;answers[y_index][x_index] += 32;}for (int i = 0; i < 10; i++){for (int j = 0; j < 20; j++){if (answers[i][j] >= 'a'&&answers[i][j] <= 'd')cout << answers[i][j] << " ";}cout << endl;}

數字識別

數字識別相較于選擇題識別更加復雜一點,需要進行模板匹配,當然也可以用機器學習的相關方法實現。這里我使用的是較為簡單一點的模板識別。這是我自己做的一個匹配模板:

按照選擇題識別的方法,將0~9的數字放在一個圖像上,通過OpenCV提供的匹配函數定位識別區域中心的位置。為了使識別更精準,現將待匹配的數字圖像提取其最大連通域進行匹配。下面是代碼實現的過程:

//method: //CV_TM_SQDIFF =0, //CV_TM_SQDIFF_NORMED =1, //CV_TM_CCORR =2, //CV_TM_CCORR_NORMED =3, //CV_TM_CCOEFF =4, //CV_TM_CCOEFF_NORMED =5 int numMatch(Mat src, int method) {//模板簡單處理Mat model = imread("model.jpg", CV_BGR2GRAY);resize(model, model, Size(src.cols * 10, src.rows));//調整模板至便于匹配的大小//取數字部分連通域,排除干擾因素src = gaintComponent(~src);src = ~src;//單獨識別1,因為1的連通域最小,長寬比大if (src.rows / src.cols > 2)return 1;//帶識別圖像處理(單通道變三通道)cv::Mat three_ch = Mat::zeros(src.rows, src.cols, CV_8UC3);vector<Mat>channels;for (int i = 0; i < 3; i++)channels.push_back(src);merge(channels, three_ch);//用matchTemplate()函數匹配數字Mat result;matchTemplate(model, three_ch, result, method);normalize(result, result, 0, 1, NORM_MINMAX, -1, Mat());//通過匹配的區域的中心點坐標定位數字double minVal, maxVal;Point minLoc, maxLoc;minMaxLoc(result, &minVal, &maxVal, &minLoc, &maxLoc, Mat());int ans = (minLoc.x + three_ch.cols / 2) / (model.cols / 10);return ans; }

運行結果

轉載于:https://www.cnblogs.com/linzijie1998/p/11031965.html

總結

以上是生活随笔為你收集整理的OpenCV机读卡识别的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。