opencv 手写选择题阅卷 (一)表格设计与识别

(一)答题表格设计与识别

实际设计好的表格如下图

为了图像精确,表格和四角的标记都是由程序生成的,文字和数据是后期排版软件添加上去的.

图中四角的四个黑方块主要用来定位表格,然后就可以切割出每个单元格,最后去做字符识别.

具体步骤为:

1,灰度化并二值化;

2,查找轮廓,把找出四个定位标记;

3,透视变换,校正变形;

4,切割表格,分别识别每个表格;

实际操作中发现最关键的是表格一定要平整,变形对识别影响较大;

部分代码:

int table_recognition(IplImage* img,unsigned char * result)
{
    //大图二值化
    IplImage* bin_img = cvCloneImage(img);
    image_threshold(bin_img);
    //去噪
    IplImage* tmp_img = cvCloneImage(bin_img);
    cvErode(tmp_img, tmp_img, NULL, 1); //腐蚀
    cvDilate(tmp_img, tmp_img, NULL, 1); //膨胀
    //查找轮廓
    CvSeq* contours;
    CvMemStorage * storage = cvCreateMemStorage(0);

    cvSetImageROI(tmp_img, cvRect(0, 0, bin_img->width, bin_img->height));
    cvFindContours(tmp_img, storage, &contours, sizeof(CvContour), CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE, cvPoint(0, 0));

    int ids[5];
    int rotates[5];
    int vals[5];
    CvPoint points[5][4];
    int n = 0;
    // 检测每个轮廓
    for (; contours; contours = contours->h_next)
    {
        //用指定精度逼近多边形曲线
        CvSeq* result;
        result = cvApproxPoly(contours, sizeof(CvContour), storage, CV_POLY_APPROX_DP, cvContourPerimeter(contours)*0.01, 0);

        //不是四边形的不要
        if (result->total != 4)
            continue;
        //不是凸多边形不要
        if (!cvCheckContourConvexity(result))
            continue;
        //面积大小或小于指定值的排除
        double s = fabs(cvContourArea(result, CV_WHOLE_SEQ, 0));
        if (s<60)
            continue;

        //解码每个轮廓标志,正确的保存下来**********************************************
        CvPoint2D32f  srcQuad[4];
        for (int i = 0; i < 4; i++){
            CvPoint* pt = (CvPoint*)cvGetSeqElem(result, i);//取标记四边形的四个顶点
            points[n][i] = *pt;
            srcQuad[i].x = (float)pt->x;
            srcQuad[i].y = (float)pt->y;
        }
        //透视变换取出marker
        IplImage * mark_img = cvCreateImage(cvSize(40,40), 8, 1);
        perspective(bin_img, mark_img, srcQuad);

        //
        int rt = marker_decode(mark_img, &ids[n], &rotates[n], &vals[n]);
        if (rt !=0)
            continue;

        //
        n++;
        if (n>4)
            break;
    }
    if (n != 4)//发现四个标记
        return -1;
    //if (rotates[0] != rotates[1] || rotates[1] != rotates[2] || rotates[2] != rotates[3])//四个标记旋转一致
    //    return -1;
    //marker 0123
    if (ids[0] != 0 && ids[1] != 0 && ids[2] != 0 && ids[3] != 0)
        return -1;
    if (ids[0] != 1 && ids[1] != 1 && ids[2] != 1 && ids[3] != 1)
        return -1;
    if (ids[0] != 2 && ids[1] != 2 && ids[2] != 2 && ids[3] != 2)
        return -1;
    if (ids[0] != 3 && ids[1] != 3 && ids[2] != 3 && ids[3] != 3)
        return -1;

    //确定表格四个点
    CvPoint2D32f  pts[4];
    for (int i = 0; i < 4; i++)
    {
        int id = ids[i];
        int rotate = rotates[i];
        CvPoint pt;
        if (id == 0){
            pt = points[i][(1 + rotate)%4];
        }
        else if (id == 1){
            pt = points[i][(0 + rotate) % 4];
        }
        else if (id == 2){
            pt = points[i][(3 + rotate) % 4];
        }
        else if (id == 3){
            pt = points[i][(2 + rotate) % 4];
        }
        pts[id].x = pt.x;
        pts[id].y = pt.y;
    }

    //CvPoint2D32f tmp_ptf = pts[1];
    //pts[1] = pts[3];
    //pts[3] = tmp_ptf;

    IplImage * table_img = cvCreateImage(cvSize(64*15, 64*4+32), 8, 1);
    perspective(img, table_img, pts);

    //表格分割
    int nt = 0;
    IplImage* gird_img = cvCreateImage(cvSize(64, 64), 8, 1);
    for (int j = 1; j < 4; j+=2)
    {
        for (int i = 0; i < 15; i++)
        {
            cvSetImageROI(table_img, cvRect(0+64*i, 15+64*j, 64, 64));
            cvCopy(table_img, gird_img);
#ifdef _WIN32
            save_gird(gird_img, nt);
#endif
            int rt = svm_recognition(gird_img);
            result[nt] = rt;
            nt++;
        }
    }

    //cvNamedWindow("Image", CV_WINDOW_NORMAL);
    //cvShowImage("Image", gird_img);
    //cvWaitKey(0);

    cvReleaseImage(&bin_img);
    cvClearMemStorage(storage);

    return 0;
}
时间: 2024-12-18 17:13:18

opencv 手写选择题阅卷 (一)表格设计与识别的相关文章

opencv 手写选择题阅卷 (二)字符识别

选择题基本上只需要识别ABCD和空五个内容,理论上应该识别率比较高的,识别代码参考了网上搜索的代码,因为参考的网址比较多,现在也弄不清是参考何处的代码了,在这里就不一一感谢了. 基本步骤: 一,识别函数接受一般64X64的灰度图像; 二,二值化并反色为黑底白字; 三,找出字符的最小包围矩形,并大小归一化为32X32; 四,计算图像的HOG特征; 五,用SVM分类器对HOG特征进行识别,从而确定当前图像属于ABCD还是空白; 整个识别代码还是比较简单的.这得得益于opencv 对分类器的封装,除了

放弃antd table,基于React手写一个虚拟滚动的表格

缘起 标题有点夸张,并不是完全放弃antd-table,毕竟在react的生态圈里,对国人来说,比较好用的PC端组件库,也就antd了.即便经历了2018年圣诞彩蛋事件,antd的使用者也不仅不减,反而有所上升. 客观地说,antd是开源的,UI设计得比较美观(甩出其他组件库一条街),而且是蚂蚁金服的体验技术部(一堆p7,p8,p9,基本都是大牛级的)在持续地开发维护,质量可以信任. 不过,antd虽好,但一些组件在某一些场景下,是很不适用的.例如,以表格形式无限滚动地展示大量数据(1w+)时,

opencv实现KNN手写数字的识别

人工智能是当下很热门的话题,手写识别是一个典型的应用.为了进一步了解这个领域,我阅读了大量的论文,并借助opencv完成了对28x28的数字图片(预处理后的二值图像)的识别任务. 预处理一张图片: 首先采用opencv读取图片的构造函数读取灰度的图片,再采用大津法求出图片的二值化的阈值,并且将图片二值化. 1 int otsu(const IplImage* src_image) { 2 double sum = 0.0; 3 double w0 = 0.0; 4 double w1 = 0.0

【OpenCV】opencv3.0中的SVM训练 mnist 手写字体识别

前言: SVM(支持向量机)一种训练分类器的学习方法 mnist 是一个手写字体图像数据库,训练样本有60000个,测试样本有10000个 LibSVM 一个常用的SVM框架 OpenCV3.0 中的ml包含了很多的ML框架接口,就试试了. 详细的OpenCV文档:http://docs.opencv.org/3.0-beta/doc/tutorials/ml/introduction_to_svm/introduction_to_svm.html mnist数据下载:http://yann.l

学习OpenCV——SVM 手写数字检测

转自http://blog.csdn.net/firefight/article/details/6452188 是MNIST手写数字图片库:http://code.google.com/p/supplement-of-the-mnist-database-of-handwritten-digits/downloads/list 其他方法:http://blog.csdn.net/onezeros/article/details/5672192 使用OPENCV训练手写数字识别分类器 1,下载训

手写数字识别【QT+OpenCV】

[说明] 手写数字识别的实现方式很多. 本文尽量将其简化,以让大家能够快速了解怎样实现一个动起来的系统. [截图] [思路] 1.特征提取 将图像划分为5*5大小的区域,然后计算该区域内黑色(或白色)的像素点所占比例. 将需要测试的图像.用来分类的图像都进行特征提取. 2.计算当前的测试图像与用来分类的图像之间的欧氏距离. 3.找出欧式距离最小的值即为与当前测试图像最匹配的图像,即将该图像所代表的数字作为当前测试图像的结果. 4.为了处理上的方便,做了简化处理如下: 4.1仅仅选用10幅用来分类

透彻理解Spring事务设计思想之手写实现

前言 事务,是描述一组操作的抽象,比如对数据库的一组操作,要么全部成功,要么全部失败.事务具有4个特性:Atomicity(原子性),Consistency(一致性),Isolation(隔离性),Durability(持久性).在实际开发中,我们对事务应用最多就是在数据库操作这一环,特别是Spring对数据库事务进行了封装管理.Spring对事务的支持,确实很强大,但是从本质上来讲:事务是否生效取决数据库底层是否支持(比如MySQL的MyISAM引擎就不支持事务,Spring能奈何!),同时一

UI到底应该用xib/storyboard完成,还是用手写代码来完成?

UI到底应该用xib/storyboard完成,还是用手写代码来完成? 文章来源:http://blog.csdn.net/libaineu2004/article/details/45488665 参考文章: <关于代码手写UI,xib和StoryBoard> http://blog.csdn.net/likendsl/article/details/38731333 <代码手写UI,xib和StoryBoard间的博弈,以及Interface Builder的一些小技巧> ht

安装MXnet包,实现MNIST手写数体识别

我想写一系列深度学习的简单实战教程,用mxnet做实现平台的实例代码简单讲解深度学习常用的一些技术方向和实战样例.这一系列的主要内容偏向于讲解实际的例子,从样例和代码里中学习解决实际问题.我会默认读者有一定神经网络和深度学习的基础知识,读者在这里不会看到大段推导和理论阐述.基础理论知识十分重要,如果读者对理论知识有兴趣,可以参看已有的深度学习教程补充和巩固理论基础,这里http://deeplearning.net/reading-list/tutorials/有一些不错的理论教程,相关的理论知