opencv数字识别

来源:互联网 发布:java基础讲解实战 编辑:程序博客网 时间:2024/05/22 01:28

这是基于opencv写的数字识别程序 对于记事本里的宋体常规数字能够完美的识别

思路:   放大图片→灰度化→二值化→开运算→寻找外轮廓→轮廓排序→遍历像素与模板匹配→得到数字

最后为了验证识别率,读入txt文件与识别图片后的输出结果对比

总结:

1.图片放大可以把各个数字分开一点,防止数字黏在一起导致找轮廓时出错

2.开运算,进一步把各个数字分开  开运算(膨胀->腐蚀)//去除图像中较小区域

3.寻找外轮廓,必须是外轮廓,不可以找所有轮廓,这样就可以等到各个数字

4.根据轮廓找出各个轮廓的最小矩形

5.根据最小矩形左上角的坐标为各个矩形排序,这样就可以知道各个字符输出顺序了

6,.把事先分割好的模板图片进行匹配(把这个程序改改就可以成一个分割数字的程序了)

7.最后读入txt文件与图片输出结果对比

#include<opencv2/highgui/highgui.hpp>#include<opencv2/imgproc/imgproc.hpp>#include<iostream>#include <fstream>#include <cassert>#include <string>using namespace std;using namespace cv;const int NUM = 10000;#define WINDOW_NAME "二值图"int g_nThreshold = 200;static void on_Threshold(int, void*);void getNumber();//分割字符char getNumChar(Mat&);//识别单个字符图片Mat g_srcImage, gray, image_threshold;Mat open;char result_num[NUM];//txt文件中的数字char result_true[NUM];//识别后的数字/*读取txt文件*/void readTxt(string file){ifstream infile;infile.open(file.data());   //将文件流对象与文件连接起来 assert(infile.is_open());   //若失败,则输出错误消息,并终止程序运行 int i = 0;char c;while (!infile.eof()){infile >> c;result_true[i] = c;result_true[i + 1] = '\0';i++;}infile.close();}int main(){readTxt("123.txt");cout << "文件值为:";for (unsigned int i = 0; i < strlen(result_true)-1; i++)//读取txt输出数字{cout << result_true[i];}cout << endl;g_srcImage = imread("1.png");//放大原图,图片太小数字可能会连在一起resize(g_srcImage, g_srcImage, Size(g_srcImage.cols * 2, g_srcImage.rows * 2), 0, 0, INTER_LINEAR);cvtColor(g_srcImage, gray, COLOR_BGR2GRAY);namedWindow(WINDOW_NAME, WINDOW_AUTOSIZE);createTrackbar("阈值", WINDOW_NAME, &g_nThreshold, 255, on_Threshold);on_Threshold(g_nThreshold, 0);waitKey(0);return 0;}/*阈值操作*/static void on_Threshold(int, void*){threshold(gray, image_threshold, g_nThreshold, 255, 1);imshow(WINDOW_NAME, image_threshold);Mat element = getStructuringElement(MORPH_RECT, Size(3, 3));morphologyEx(image_threshold, open, MORPH_OPEN, element);//开运算,防止图片连在一起getNumber();}/*单字符识别*/char getNumChar(Mat &temp){int min = 10000;int min_i = 0;int total = 0;for (int n = 0; n < 10; n++)//n对应图片n.jpg{total = 0;char a[100];sprintf(a, "number/%d.jpg", n);//读取number模板文件夹图片,名字对应数字string name = a;Mat match = imread(name); cvtColor(match, match, COLOR_BGR2GRAY);threshold(match, match, 254, 255, 1);resize(temp, temp, match.size(), 0, 0, INTER_LINEAR);//转换图片大小和模板图片一样for (int i = 0; i < match.rows; i++)//遍历所有像素与n.jpg对比{uchar *data_match = match.ptr<uchar>(i);uchar *data_temp = temp.ptr<uchar>(i);for (int j = 0; j < match.cols; j++){if (*data_match == *data_temp){total++;}*data_match++;*data_temp++;}}//读入的图片二值化后与原图二值化相同像素为0//读入的图片二值化与程序运行过程中二值化的产生图片刚好相反//所以相同的像素越少图片相似度越高//在这里match二值化后与temp二值化图相反即黑白正好颠倒了if (total < min){min = total;min_i = n;}}char a = min_i+'0';return a;}/*字符分割*/void getNumber(){Mat image_threshold_clone = image_threshold.clone();vector<vector<Point>>contours;vector<Vec4i>hierarchy;findContours(image_threshold_clone, contours, hierarchy,CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE);//找出图片外轮廓,不要连内轮廓也找出来int contour_size = contours.size();int average_height = 0;int count = 0;Rect rc[NUM];int min_y =100000;int max_y = 0;//找出字符平均高度,最上方的数字的y,最下方的数字的yfor (int i = 0; i < contour_size; i++){rc[i] = boundingRect(contours.at(i));if (rc[i].y<min_y){min_y = rc[i].y;}if (rc[i].y>max_y){max_y = rc[i].y;}average_height += rc[i].height;}average_height /= contour_size;Rect sort_rc[NUM];Rect rc_y[NUM];int _t = 0;//由于轮廓顺序不是字符顺序所以要排序,主要根据矩形左上方坐标排序//排序从上到下,从左到右,每隔一个字符高度获取一次,当超出图片边界时停止for (int n = 0;; n++){int t = 0;for (int i = 0; i < contour_size; i++){if (min_y - average_height*0.5 < rc[i].y&& rc[i].y < min_y + average_height*0.5){rc_y[t] = rc[i];t++;}}if (min_y>max_y){break;}if (t == 0){_t += t;min_y += average_height;continue;}for (int i = 0; i < t; i++){for (int j = i+1; j < t; j++){if (rc_y[i].x>rc_y[j].x){Rect temp = rc_y[i];rc_y[i] = rc_y[j];rc_y[j] = temp;}}}for (int i = 0; i < t; i++){sort_rc[i + _t] = rc_y[i];}_t += t;min_y += average_height;}//把排序后的图片一一识别for (int i = 0; i < contour_size; i++){Mat ROI = image_threshold(sort_rc[i]);resize(ROI, ROI, Size(40, 56), 0, 0, INTER_LINEAR);result_num[i] = getNumChar(ROI);result_num[i + 1] = '\0';}//与txt文件的数字进行比较int equal_num = 0;cout << "结果值为:";for (unsigned int i = 0; i < strlen(result_num); i++){cout << result_num[i];if (result_num[i] == result_true[i])equal_num++;}cout << "\n字符数为:" << contour_size << endl;cout << "正确率为:" << (double)equal_num / contour_size *100<<"%"<< endl;}
以下为输出结果:


当然识别数字后你想干嘛 就是你自己的是了


原创粉丝点击