C++ opencv 数字识别

#include "cv.h"
#include "highgui.h"
#include "cxcore.h"
#include <stdlib.h>
#include <stdio.h>
#define N 5//载入数字图片个数

char *testPic[] = {"test1.jpg"};
int thres = 115;    //二值化阀值

int n_min = 80;       //识别数字轮廓长度的下限 单位(像素)  
int n_max = 400;      //识别数字轮廓长度的上限

//数字轮廓的长度和宽度  单位(像素)
int n_width_min = 5, n_width_max = 40;
int n_height_min = 30, n_height_max = 50;

// 数组成员之间的距离小于一个阀值视为一个数
int n_width_n_min = 15, n_width_n_max = 40;

char *picture[] = {"0.jpg", "1.jpg", "2.jpg", "3.jpg", "4.jpg"};  //数字图片集  这里储存白底黑子的数字图片 0 - 4;
CvSeq *pic[N];//储存数字图片轮廓

CvSeq* GetImageContour(IplImage* srcIn,int flag = 0)
{
    IplImage* src = cvCreateImage(cvGetSize(srcIn), 8, 1);

    //拷贝图像
    cvCopy(srcIn, src);

    //创建空间
    CvMemStorage* mem = cvCreateMemStorage(0);

    if(!mem){
        printf("mem is NULL!");
    }

    //二值化图像
    cvThreshold(src, src, thres, 255, CV_THRESH_BINARY_INV);

    //计算图像轮廓  0-只获取最外部轮廓  1-获取全部轮廓
    CvSeq* seq;//储存图片轮廓信息
    if(flag == 0){
        cvFindContours(src, mem, &seq, sizeof(CvContour), CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE, cvPoint(0,0));
    }
    if(flag == 1){
        cvFindContours(src, mem, &seq, sizeof(CvContour), CV_RETR_CCOMP, CV_CHAIN_APPROX_NONE, cvPoint(0,0));
    }

    //释放图像空间
    cvReleaseImage(&src);

    //返回轮廓信息
    return seq;
}

//数字图片轮廓计算
void Init(void)
{
    IplImage *src0, *src;
    for(int i = 0; picture[i] != 0; i++){
        src0 = cvLoadImage(picture[i], CV_LOAD_IMAGE_GRAYSCALE);
        if(!src0){
            printf("Couldn't load %s\n", picture[i]);
            exit(1);
        }
        src = cvCloneImage(src0);
        pic[i] = GetImageContour(src, 0);//只获取最外部轮廓
    }
}

int ReadNumber(CvSeq* contoursTemp)
{
    double tmp = 5,min = 5;
    int num = -1;
    for(int i = 0; i < N; i++){
        tmp = cvMatchShapes(contoursTemp,pic[i],1);   //匹配
        if(tmp < min){
            min = tmp;
            num = i;
        }
    }
    return num;
}

void Paixu(int numList[100][2], int count)    //将数字按横坐标从左往右排列
{
    int tem = 0;
    int newList[100] = {0};  //数字融合后的新序列
    for(int i = 0; i < count - 1; i++){
        for(int j = i + 1; j < count; j++){
            if(numList[i][1] > numList[j][1]){
                //交换坐标
                tem = numList[i][1];
                numList[i][1] = numList[j][1];
                numList[j][1] = tem;

                //交换数字
                tem = numList[i][0];
                numList[i][0] = numList[j][0];
                numList[j][0] = tem; 
            }
        }
    }
    if(count == 0){
        printf("no number!");
    }
    else{
        for(int i = 0; i < count; i++){
            printf("%d\t",numList[i][0]);
        }
    }

    //数字融合,可以自己改。。。。数字从左往右的顺序都在numList里面,[0]为数,[1]为坐标,自己可以根据数字间的距离判断是否为一个数
    if(count == 2){
        int width = 0;   //两数字间的距离
        width = numList[1][1] - numList[0][1];
        if((width < n_width_n_max) && (width > n_width_n_min)){
            tem = numList[0][0] * 10 + numList[1][0];
            newList[0] = tem;
        }

        printf("the number is %d\t",newList[0]);
    }
}

int main()
{
    Init();//初始化,在pic中储存所有图片轮廓
    IplImage* img = cvLoadImage(testPic[0], CV_LOAD_IMAGE_GRAYSCALE);

    int travel = 1;//如果为0,识别中间数字,如果为1,识别右边数字.其他数字,全部识别
    if(travel == 0){
        CvRect ins;
        ins.x = 170;
        ins.y = 140;
        ins.width = 300;
        ins.height = 200;
        cvSetImageROI(img, ins);
    }
    if(travel == 1){
        CvRect ins;
        ins.x = 470;
        ins.y = 140;
        ins.width = 165;
        ins.height = 200;
        cvSetImageROI(img, ins);
    }
    
    IplImage* imgColor = cvCreateImage(cvGetSize(img), 8, 3);
    IplImage* contoursImage = cvCreateImage(cvSize(img->width,img->height), 8, 1);

    CvSeq* contours = 0, *contoursTemp = 0;
    cvZero(contoursImage);
    contours = GetImageContour(img, 1);//获取轮廓信息
    contoursTemp = contours;

    //对轮廓进行循环
    int count=0;   //数字轮廓个数
    int numList[100][2];  //一幅图像中的数字序列  一维是数字,二维是数字所在横坐标
    for(; contoursTemp != 0; contoursTemp = contoursTemp->h_next){
        //如果符合数字轮廓长度条件 保留并画出
        if(contoursTemp->total > n_min && contoursTemp->total < n_max){
            int num = -1;//识别一幅图像中的一个数字
            CvRect rect = cvBoundingRect(contoursTemp, 1);  //根据序列,返回轮廓外围矩形

            //如果满座数字轮廓长宽
            if((rect.width >= n_width_min && rect.width <= n_width_max) && (rect.height >= n_height_min && rect.height <= n_height_max)){
                //匹配该轮廓数字
                num = ReadNumber(contoursTemp);

                //计算矩形顶点
                CvPoint pt1,pt2;
                pt1.x = rect.x;
                pt1.y = rect.y;
                pt2.x = rect.x + rect.width;
                pt2.y = rect.y + rect.height;

                if(num >= 0){
                    numList[count][0] = num;         //一维存数字
                    numList[count][1] = rect.x;      //二维存数字横坐标
                }

                //在原图上绘制轮廓外矩形
                cvRectangle(imgColor, pt1, pt2, CV_RGB(0,255,0), 2);

                //提取外轮廓 上的所以坐标点
                for(int i = 0; i < contoursTemp->total; i++){
                    CvPoint * pt = (CvPoint*)cvGetSeqElem(contoursTemp, i); // 读出第i个点。

                    cvSetReal2D(contoursImage, pt->y , pt->x , 255.0);
                    cvSet2D(imgColor, pt->y, pt->x,cvScalar(0, 0, 255, 0));
                }
                count++;//数字轮廓+1
            }
        }
    }

    Paixu(numList, count);
    for(int i = 0; i < count; i++){
        printf("%d(%d)\t", numList[i][0], numList[i][1]);
    }

    cvNamedWindow("image", 1);
    cvShowImage("image", imgColor);

    cvNamedWindow("contours");
    cvShowImage("contours", contoursImage);

    cvWaitKey(5000);//停顿5秒钟
    cvResetImageROI(imgColor);
    cvResetImageROI(img);
    cvReleaseImage(&img);
    cvReleaseImage(&contoursImage);
    cvReleaseImage(&imgColor);
    return 0;
}

由于代码是从网上下载下来的,所以整理了一下发布出来。可以运行。

配置环境:VS2013  opencv3.0.0

缺点:识别率不高

 源码下载地址:http://download.csdn.net/detail/sz76211822/8986931

posted @ 2015-08-10 18:50  QQ76211822  阅读(1888)  评论(0编辑  收藏  举报