数字识别和其他的所有计算机视觉相关的应用都会分为两个步骤:ROI抽取和识别。

1. ROI抽取即将感兴趣的区域从原始图像中分离初来,这个步骤包括二值化,噪点的消除等
2. 识别即通过一些分类器将第一步中的结果进行分类,事实上属于机器学习的一个典型应用

数字识别步骤:

1.先处理图像:

转换为灰度值(灰度图较之原始图片,将三个维度的矩阵变成了一个维度)

转换为二值图(二值图即将灰度图转换成黑白图,每个点只有两种可能:非黑即白)

Mat srcImage = imread("number.png");
Mat dstImage, grayImage, Image;
cvtColor(srcImage, grayImage, COLOR_BGR2GRAY); threshold(grayImage, Image, , , CV_THRESH_BINARY_INV);

PS:48即为阈值,如果灰度高于48,那么该点会被认为是255,否则为0。

2.检测并勾勒轮廓:
   轮廓检测将二值图中的可连通的区域用一坨点表示,默认的轮廓检查会返回一个点的序列,使这个序列构成一个图形将该连通区域的所有点包围起来,比如四个点构成一个矩形。

特例:由于8这个数字中有两个圆圈,默认的轮廓检查会将这两个圆圈都检测到,8就会有三个轮廓,同样还可能出现这种情况的还有数字4,6,9。

因此需要指定findContours()函数仅搜索最外层的轮廓,而不关注内部可能出现的任何轮廓。

    vector<vector<Point>> contours;
vector<Vec4i> hierarchy;
findContours(Image,contours, hierarchy, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE);
drawContours(dstImage, contours, -, (,,) );

检测完轮廓后,使用contours迭代器遍历每一个轮廓,找到并画出包围这个轮廓的最小矩阵。

    vector<vector<Point>>::iterator It;
for(It = contours.begin();It < contours.end();It++){ //画出可包围数字的最小矩形
Point2f vertex[];
RotatedRect rect = minAreaRect(*It);
rect.points(vertex); for( int j = ; j < ; j++)
line(dstImage,vertex[j], vertex[ (j+)% ],Scalar(,,),);
}

但是,上述方法画出的矩形为旋转矩形(不一定水平) ,所以不采用这种方法。应使用boundingRect()画出矩形。

vector<vector<Point>>::iterator It;
for(It = contours.begin();It < contours.end();It++){ //画出可包围数字的最小矩形
Point2f vertex[];
Rect rect = boundingRect(*It);
vertex[] = rect.tl(); //矩阵左上角的点
vertex[].x = (float)rect.tl().x, vertex[].y = (float)rect.br().y; //矩阵左下方的点
vertex[] = rect.br(); //矩阵右下角的点
vertex[].x = (float)rect.br().x, vertex[].y = (float)rect.tl().y; //矩阵右上方的点 for( int j = ; j < ; j++)
line(dstImage,vertex[j], vertex[ (j+)% ],Scalar(,,),);

画出图像如下图

3.数字顺序整理:

由于轮廓检测时,不一定按照图中所给顺序进行检测,所以在检测轮廓时,要记录所给数字的坐标,根据x,y坐标进行排序。

由于用上述方法在同一行画出的矩形位于同一水平面,因此直接比较其某一点坐标即可。对此,我写出如下结构体:

struct con{
double x,y; //轮廓位置
int order; //轮廓向量contours中的第几个 bool operator<(con &m){
if(y > m.y) return false;
else if( y == m.y){
if(x < m.x) return true;
else return false;
}
else return true;
} }con[];

我按轮廓检测顺序的将矩阵的中心点存入结构体中,然后调用sort()函数。

con[i].x = (vertex[].x+vertex[].x+vertex[].x+vertex[].x) / 4.0;                 //根据中心点判断图像的位置
con[i].y = (vertex[].y+vertex[].y+vertex[].y+vertex[].y) / 4.0; //cout << i <<":"<< endl;
//cout << vertex[3].x<<" "<< vertex[3].y<<endl;
con[i].order = i;

但是用这种方法上图中的数字”4“一直在最前面,改了好久也没有结果,就先着手下一步。

PS:  最后发现了问题,如下:

sort(con,con+i);                                    //正确
sort(con,con+i+); //错误

4.切割各个数字:

使用ROI进行切割,关于ROI详见 http://www.cnblogs.com/farewell-farewell/p/5905107.html

我在此处写的ROI法分隔图片的方法如下,但是存在内存访问上的问题。

IplImage* num[];
for(int j = ; j < i; j++){
int k = con[i].order;
IplImage* src = cvLoadImage("number.jpg");
cvSetImageROI(src,rect[k]);
num[j] = cvCreateImage(cvSize(rect[k].width,rect[k].height),IPL_DEPTH_8U,);
cvCopy(src,num[j]);
cvResetImageROI(src);
}

最后换另一种方法,更简单,将其分割

    Mat num[];
for(int j = ; j < i; j++){
cout << "s "<<j<<endl;
int k = con[j].order;
cout << "k "<<k<<endl;
srcImage(rect[k]).copyTo(num[j]);
}

分割后的数字按顺序存放在num[10]图像数组中。

5.最后的识别

将按轮廓线切割好的数字放于程序文件中,然后采用逐点像素遍历的方法来进行对比

//两图象逐像素对比的函数
double compare(Mat &src, Mat &sample)
{
double same = 0.0, difPoint = 0.0;
Mat now;
resize(sample,now,src.size());
int row = now.rows;
int col = now.cols * now.channels();
for(int i = ; i < ; i++){
uchar * data1 = src.ptr<uchar>(i);
uchar * data2 = now.ptr<uchar>(i);
for(int j = ; j < row * col; j++){
int a = data1[j];
int b = data2[j];
if( a == b)same++;
else difPoint++;
}
}
return same/(same+difPoint) ;
}
//选取符合程度最高的数字
void deal(Mat &src,int order)
{ sample = imread("0.jpg");
Threshold(src,sample,); sample = imread("1.jpg");
Threshold(src,sample,); sample = imread("2.jpg");
Threshold(src,sample,); sample = imread("3.jpg");
Threshold(src,sample,); sample = imread("4.jpg");
Threshold(src,sample,); sample = imread("5.jpg");
Threshold(src,sample,); sample = imread("6.jpg");
Threshold(src,sample,); sample = imread("7.jpg");
Threshold(src,sample,); sample = imread("8.jpg");
Threshold(src,sample,); sample = imread("9.jpg");
Threshold(src,sample,); sort(result,result+); if(result[].bi > 0.6) {
cout << "第" << order << "个数字为 "<< result[]. num<<endl;
cout << "识别精度为 " << result[].bi <<endl;
}
else cout << "第" << order << "个数字无法识别"<<endl;
}
void Threshold(Mat &src,Mat &sample ,int m)
{
cvtColor(sample, sample, COLOR_BGR2GRAY);
threshold(sample, sample, , , CV_THRESH_BINARY_INV);
result[m].bi = compare(src,sample);
result[m].num = m;
} }con[]; struct result{
double bi;
int num; bool operator<(result &m){
if(bi < m.bi)return true;
else return false;
}
}result[];

大功告成~

完整的代码:

#include <opencv2/opencv.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <iostream>
using namespace cv;
using namespace std; struct con{
double x,y; //轮廓位置
int order; //轮廓向量contours中的第几个 bool operator<(con &m){
if(y > m.y) return false;
else if( y == m.y){
if(x < m.x) return true;
else return false;
}
else return true;
} }con[]; struct result{
double bi;
int num; bool operator<(result &m){
if(bi < m.bi)return true;
else return false;
}
}result[]; Mat num[];
Mat sample;
void deal(Mat &src,int order);
double compare(Mat &src, Mat &sample);
void Threshold(Mat &src,Mat &sample,int m); int main( )
{
Mat srcImage = imread("number.png");
Mat dstImage, grayImage, Image;
srcImage.copyTo(dstImage);
cvtColor(srcImage, grayImage, COLOR_BGR2GRAY);
threshold(grayImage, Image, , , CV_THRESH_BINARY_INV); //定义轮廓和层次结构
vector<vector<Point>> contours;
vector<Vec4i> hierarchy;
findContours(Image,contours, hierarchy, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE); int i = ;
Point2f pp[][];
vector<vector<Point>>::iterator It;
Rect rect[];
for(It = contours.begin();It < contours.end();It++){ //画出可包围数字的最小矩形
Point2f vertex[];
rect[i] = boundingRect(*It);
vertex[] = rect[i].tl(); //矩阵左上角的点
vertex[].x = (float)rect[i].tl().x, vertex[].y = (float)rect[i].br().y; //矩阵左下方的点
vertex[] = rect[i].br(); //矩阵右下角的点
vertex[].x = (float)rect[i].br().x, vertex[].y = (float)rect[i].tl().y; //矩阵右上方的点 for( int j = ; j < ; j++)
line(dstImage,vertex[j], vertex[ (j+)% ],Scalar(,,),); con[i].x = (vertex[].x+vertex[].x+vertex[].x+vertex[].x) / 4.0; //根据中心点判断图像的位置
con[i].y = (vertex[].y+vertex[].y+vertex[].y+vertex[].y) / 4.0;
con[i].order = i;
i++;
}
sort(con,con+i); for(int j = ; j < i; j++){
int k = con[j].order;
srcImage(rect[k]).copyTo(num[j]);
cvtColor(num[j], num[j], COLOR_BGR2GRAY);
threshold(num[j], num[j], , , CV_THRESH_BINARY_INV);
deal(num[j],j+);
} system("pause");
return ;
} void Threshold(Mat &src,Mat &sample ,int m)
{
cvtColor(sample, sample, COLOR_BGR2GRAY);
threshold(sample, sample, , , CV_THRESH_BINARY_INV);
result[m].bi = compare(src,sample);
result[m].num = m;
} void deal(Mat &src,int order)
{ sample = imread("0.jpg");
Threshold(src,sample,); sample = imread("1.jpg");
Threshold(src,sample,); sample = imread("2.jpg");
Threshold(src,sample,); sample = imread("3.jpg");
Threshold(src,sample,); sample = imread("4.jpg");
Threshold(src,sample,); sample = imread("5.jpg");
Threshold(src,sample,); sample = imread("6.jpg");
Threshold(src,sample,); sample = imread("7.jpg");
Threshold(src,sample,); sample = imread("8.jpg");
Threshold(src,sample,); sample = imread("9.jpg");
Threshold(src,sample,); sort(result,result+); if(result[].bi > 0.6) {
cout << "第" << order << "个数字为 "<< result[]. num<<endl;
cout << "识别精度为 " << result[].bi <<endl;
}
else cout << "第" << order << "个数字无法识别"<<endl;
} double compare(Mat &src, Mat &sample)
{
double same = 0.0, difPoint = 0.0;
Mat now;
resize(sample,now,src.size());
int row = now.rows;
int col = now.cols * now.channels();
for(int i = ; i < ; i++){
uchar * data1 = src.ptr<uchar>(i);
uchar * data2 = now.ptr<uchar>(i);
for(int j = ; j < row * col; j++){
int a = data1[j];
int b = data2[j];
if( a == b)same++;
else difPoint++;
}
}
return same/(same+difPoint) ;
}

OpenCV——识别印刷体数字的更多相关文章

  1. OpenCV——识别手写体数字

    这个是树莓派上运行的, opencv3 opencv提供了一张手写数字图片给我们,如下图所示,可以作为识别手写数字的样本库. 0到9共十个数字,每个数字有五行,一行100个数字.首先要把这5000个数 ...

  2. Adaline网络识别印刷体数字0到9-java实现

    本篇只给出实现的代码,下一篇将讲一讲实现的原理,及其Adline网络中的LMS算法原理. 包含两个类: package com.cgjr.com; import java.security.Diges ...

  3. Java基于opencv实现图像数字识别(五)—投影法分割字符

    Java基于opencv实现图像数字识别(五)-投影法分割字符 水平投影法 1.水平投影法就是先用一个数组统计出图像每行黑色像素点的个数(二值化的图像): 2.选出一个最优的阀值,根据比这个阀值大或小 ...

  4. Java基于opencv实现图像数字识别(四)—图像降噪

    Java基于opencv实现图像数字识别(四)-图像降噪 我们每一步的工作都是基于前一步的,我们先把我们前面的几个函数封装成一个工具类,以后我们所有的函数都基于这个工具类 这个工具类呢,就一个成员变量 ...

  5. Java基于opencv实现图像数字识别(三)—灰度化和二值化

    Java基于opencv实现图像数字识别(三)-灰度化和二值化 一.灰度化 灰度化:在RGB模型中,如果R=G=B时,则彩色表示灰度颜色,其中R=G=B的值叫灰度值:因此,灰度图像每个像素点只需一个字 ...

  6. Java基于opencv实现图像数字识别(二)—基本流程

    Java基于opencv实现图像数字识别(二)-基本流程 做一个项目之前呢,我们应该有一个总体把握,或者是进度条:来一步步的督促着我们来完成这个项目,在我们正式开始前呢,我们先讨论下流程. 我做的主要 ...

  7. Java基于opencv实现图像数字识别(一)

    Java基于opencv实现图像数字识别(一) 最近分到了一个任务,要做数字识别,我分配到的任务是把数字一个个的分开:当时一脸懵逼,直接百度java如何分割图片中的数字,然后就百度到了用Buffere ...

  8. 转载:使用 OpenCV 识别 QRCode

    原文链接:http://coolshell.cn/articles/10590.html#jtss-tsina 识别二维码的项目数不胜数,每次都是开箱即用,方便得很. 这次想用 OpenCV 从零识别 ...

  9. OpenCV识别技术

    OpenCV识别技术# 老师:james 20181019 # 识别技术# Pycharm + Python3 + OpenCV """ 一.识别技术: 什么是OpenC ...

随机推荐

  1. js实现楼层效果

    今天自己写个楼层效果,有一点烦躁,小地方犯错误.各位大神来修改不足啊!!! <!DOCTYPE html><html lang="en"><head& ...

  2. css3实现各种渐变效果,比较适合做手机触屏版

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...

  3. mac qq截图功能失效后,如何重启截图功能?

    在finder中打开应用程序目录,找到QQ,右键单击QQ,选择显示包内容,此时会打开一个文件夹. 进入以下路径Library/LoginItems然后双击ScreenCapture这个进程,截图功能即 ...

  4. 自定义VIew基础

    一.坐标 ①.通过View获取坐标,通过调用getLeft().getRight()...方法获取坐标. 1.获取到的是相对于View父控件的位置 2.指的是左上角和右下角的x,y值 3.View还提 ...

  5. ECSHOP_百度收录网址后面有?from=rss

    ecshop的feed.xml文件中间添加了?from=rss,百度蜘蛛抓取收录后,会出现frome页面,导致商品列表重复抓取,访问出错. 修改方法简单: 在根目录下的feed.php找到(八处), ...

  6. Lonely Integer

    https://www.hackerrank.com/challenges/lonely-integer def main(): n = int(raw_input()) s = dict() a = ...

  7. Unicode的解救方案 - Windows程序设计(SDK)002

    Unicode的解救方案 让编程改变世界 Change the world by program 内容节选: 早期的Windows也是使用ASCII字符集,ASCII好处多多,但ASCII的第一个字母 ...

  8. Django的 select_related 和 prefetch_related 函数对 QuerySet 查询的优化(三)

    4.一些实例 如果我们想要获得所有家乡是湖北的人,最无脑的做法是先获得湖北省,再获得湖北的所有城市,最后获得故乡是这个城市的人.就像这样: 1 2 3 4 5 >>> hb = Pr ...

  9. js 对url字符转译全解

    1.js 对url进行字符解码设计到3个方法 escape , encodeURI , encodeURIComponent eg: var url='http://baidu.com';encode ...

  10. Xcode中C、C++、Object-C3种语言的混编

    转自: http://hi.baidu.com/onejw/item/f34390c997cdc226a1b50ae http://www.cocoachina.com/ask/questions/s ...