当前位置: 首页 > news >正文

竞赛选题 机器视觉的试卷批改系统 - opencv python 视觉识别

文章目录

  • 0 简介
  • 1 项目背景
  • 2 项目目的
  • 3 系统设计
    • 3.1 目标对象
    • 3.2 系统架构
    • 3.3 软件设计方案
  • 4 图像预处理
    • 4.1 灰度二值化
    • 4.2 形态学处理
    • 4.3 算式提取
    • 4.4 倾斜校正
    • 4.5 字符分割
  • 5 字符识别
    • 5.1 支持向量机原理
    • 5.2 基于SVM的字符识别
    • 5.3 SVM算法实现
  • 6 算法测试
  • 7 系统实现
  • 8 最后

0 简介

🔥 优质竞赛项目系列,今天要分享的是

基于机器视觉的试卷系统 - opencv python 视觉识别

该项目较为新颖,适合作为竞赛课题方向,学长非常推荐!

🧿 更多资料, 项目分享:

https://gitee.com/dancheng-senior/postgraduate

1 项目背景

机器视觉的发展对存在的作业批改问题, 提供了有效的解决方案。 通过基于机器视觉的作业批改系统可以对老师的教学工作进行辅助,改变传统的批改作业方式,
帮助老师减轻教学压力和工作负担, 老师可以快速完成批改过程,及时反馈给学生。 家长同样需要从繁重的重复性检查作业工作中解脱出来,
将更多的精力放在关注学生的学习情况和发现学习问题上。 学生可以通过自我批改作业中发现问题、加深理解, 培养自主学习意识, 提高分析问题和解决问题的能力。
因此, 自动批改作业系统在教育领域的应用表现出了无可比拟的教育价值和发展前景。
在这里插入图片描述

在这里插入图片描述

2 项目目的

在教育领域中人工智能应用愈加广泛, 作业在教学过程中起到重要的作用,当前作业批改存在着重复劳动、 效率低下等诸多问题,
这种传统的批改作业方式占据了老师宝贵的时间。 本文设计一种作业批改视觉系统, 将人工智能应用到教育领域中, 改变老师传统的批改作业方式,
实现自动批改数学算式作业的任务。

学长设计了一个系统系统,可以协助老师和家长完成繁重和重复的作业批改和检查工作, 提高工作效率。

3 系统设计

3.1 目标对象

学长这里以数学作业试卷识别为目标。

在这里插入图片描述

数学作业图像中一列包含多个算式, 字符主要包括印刷体的算式题目和手写体答案组成, 如上图 所示为一张数学算式作业图像。
本课题的难点在于如何有效的去除光线等外部干扰因素, 准确的提取到作业图像中的单个算式信息;选取有效的字符识别算法,
针对印刷体字符和手写体字符设计混合字符分类器,进行有效、 快速的识别; 选取适合的嵌入式设备, 进行软件与硬件的系统集成,实现视觉系统的基本功能,
完成稳定性的批改过程。

3.2 系统架构

通过对视觉系统的研究以及完成作业批改解决方案的设计目标, 采取 PC 平台与嵌入式平台相结合的设计方案。 针对 PC 平台进行软件设计与算法优化,
完成系统的功能要求后, 将程序移植到嵌入式系统中, 在嵌入式设备实现系统的便捷化应用。 对于设计的系统采取多平台测试分析, 保证系统在 PC
平台准确高效的运行, 同时保证嵌入式系统中表现出稳定的性能。 系统的总体结构框图如下。

在这里插入图片描述

首先按照系统功能需求进行分析, 确定要完成的设计任务和目标, 并对系统的功能和性能分析做出设计要求。 其次根据系统的功能划分, 选取基于 PC
平台的软件设计方案完成软件编程, 对系统实现的功能进行验证, 测试其功能和性能是否符合设计要求。 选取视觉系统的嵌入式开发平台,
进行硬件模块设计和开发环境及软件平台的搭建, 将系统软硬件集成在一起进行调试进行, 对系统存在的问题做出改进和优化。

最后通过系统测试, 分别对系统的功能和性能进行测试验证, 是否满足设计的要求。 最终构建一款多平台应用, 基于机器视觉的自动作业批改视觉系统。

3.3 软件设计方案

该系统基于机器视觉的图像处理和字符识别技术, 整个系统的核心是软件设计部分。 能否对作业有效和快速的批改,
很大程度上取决于软件设计部分图像处理的效果和字符识别的准确率。 软件设计主要完成系统相关的功能操作,设计流程可分为图 中的模块组成。

在这里插入图片描述

图像获取是将摄像头等设备获取的作业图像信息转化为数字图像信息; 预处理是对图像进行二值化转换, 去除多余噪声, 进行每一组算式提取,
分割获得单个清晰字符轮廓的过程; 特征提取是对预处理后的字符图像, 进行字符特征提取, 将提取好的特征量输入到分类器, 为字符识别做准备;
字符识别是系统的核心, 对字符分类器进行设计, 通过分析训练样本的特征, 将待预测的样本进行分类, 对字符完成准确识别;
结果输出是通过公式计算器计算印刷体算式结果与手写结果进行对比, 判断算式作业是否作答正确完成反馈的程。

4 图像预处理

试卷字符识别过程中, 通过摄像头采集到的纸张作业图像信息由于受到光线产生的噪声、 书写的污点等干扰因素, 影响字符图像的提取效果。
为了得到完整的字符区域特征, 同时去除无关信息的干扰, 需要对图像进行预处理操作。

在这里插入图片描述

4.1 灰度二值化

灰度二值化是将图像先进行灰度处理, 再进行二值化处理。 经过灰度二值化处理的图像降低了像素的运算量, 同时突出图像中算式的特征。
灰度化是将采取到的彩色图像进行灰度值转换, 灰度化后的图像去除了彩色信息, 只保留了算式字符与背景之间的亮度信息, 图像中每个像素点都是介于 0 至 255
灰度值中的一种。

在这里插入图片描述

关键代码


#3、将 RGB 转为灰度图
def rgb2gray(rgb):
return np.dot(rgb[…,:3], [0.299, 0.587, 0.114])

gray = rgb2gray(lena)    
# 也可以用 plt.imshow(gray, cmap = plt.get_cmap('gray'))
plt.imshow(gray, cmap='Greys_r')
plt.axis('off')
plt.show()from scipy import misc
lena_new_sz = misc.imresize(lena, 0.5) # 第二个参数如果是整数,则为百分比,如果是tuple,则为输出图像的尺寸
plt.imshow(lena_new_sz)
plt.axis('off')
plt.show()附上imresize的用法
功能:改变图像的大小。
用法:
B = imresize(A,m)
B = imresize(A,m,method)
B = imresize(A,[mrows ncols],method)
B = imresize(...,method,n)
B = imresize(...,method,h)imrersize函数使用由参数method指定的插值运算来改变图像的大小。
method的几种可选值:
'nearest'(默认值)最近邻插值
'bilinear'双线性插值
'bicubic'双三次插值
B = imresize(A,m)表示把图像A放大m倍
B = imresize(...,method,h)中的h可以是任意一个FIR滤波器(h通常由函数ftrans2、fwind1、fwind2、或fsamp2等生成的二维FIR滤波器)。

4.2 形态学处理

形态学处理是通过一定形态的结构元素, 对图像产生基于形状的操作 。它可以在保持图像基本形状的基础上简化数据, 去除多余结构。
形态学运算主要包括开运算和闭运算, 这两个操作包含了膨胀和腐蚀。

在这里插入图片描述

算式图像经过形态学处理后, 实验效果如上图所示。 在图中可以看出左侧的算式图像经过形态学处理之后, 其断裂的乘号字符在右侧的算式图像中形成了连通区域。
形态处理后字符整体趋于完整, 边界变的平滑。

在手写字符识别的过程中, 由于手写字符的字迹大小、 粗细程度存在的随意性很大, 在特征提取的过程中, 相同字符的冗余度导致特征向量差异很大 。

因此对获取字符图像要进行适当的细化处理, 有利于特征提取的准确性。 图像细化指将二值图像进行骨架化操作的运算, 细化操作过程就是剥离字符图像上边缘轮廓的点,
细化操作要求字符骨架保持原有的笔画特征, 不能造成笔划断开, 同时具有连续性, 字符图像应尽量保留原始的结构特征。

在这里插入图片描述

关键代码


import cv2 as cv

img = cv.imread(r"C:\Users\Administrator\Desktop\chinese.png")
img_cvt = cv.cvtColor(img,cv.COLOR_BGR2GRAY)
ret,img_thr = cv.threshold(img_cvt,100,255,cv.THRESH_BINARY)
kernel = cv.getStructuringElement(cv.MORPH_RECT,(30,1)) #由于是1*30的矩阵,字体会被横向空隙的白色腐蚀掉,而下划线横向都是黑色,不会腐蚀
dst = cv.dilate(img_thr,kernel,iterations=1)  #由于是白底黑字,所有进行膨胀操作来去除黑色字体
cv.imshow("img_thr",img_thr)
cv.imshow("dst",dst)
cv.waitKey(0)
cv.destroyAllWindows()

4.3 算式提取

算式提取的主要任务是从纸张中找到其中一组算式的字符区域, 并将算式从所在的区域中提取出来。 经过算式提取操作,
可以针对每一组算式进行批改,同时也便于下一步的字符分割, 算式提取准确性对作业批改效果有直接的影响。二值化处理后的算式图像中算式的灰度值为 255,
背景的灰度值为 0。

采取基于投影的方法, 进行水平和垂直方向的投影对算式进行提取 , 由于字符图像和背景图像对比度较大, 背景几乎不存在噪音干扰,
因此投影分割可以取得较好的效果。

在这里插入图片描述

在这里插入图片描述

对图像进行列扫描, 得到垂直方向投影图, 投影后字符间隔的白色像素点的个数为 0, 在字符区域处形成波峰。 此时根据多个连续的波峰图像,
记录开始和结束的位置, 就可求得算式的左右边界, 进行分割得到仅包含一组算式区域的图像。

在这里插入图片描述
在这里插入图片描述

4.4 倾斜校正

在图像获取的过程中, 由于摄像头拍摄角度和作业图像有时会产生一个倾斜角度, 此时图像会发生垂直倾斜, 如果不对算式图像进行倾斜校正处理,
可能会无法正确识别出字符。 因此算式提取后要对算式图像进行倾斜校正, 采用基于 Hough 变换的方法,
其原理为图像中的直线和曲线经过变换映射到参数空间上的一个点, 通过累加的峰值检验图像中的直线和曲线。 Hough
变换的实质是将图像中一定形状元素的点进行聚类, 通过解析式将参数空间对应的点联系起来。

在这里插入图片描述

4.5 字符分割

字符分割指是将一组算式中的多个字符图像根据字符之间的空隙, 分割成多张只包含单个字符的图像,
字符分割需要保证对每个字符进行完整的提取。作业字符图像是一连串的数字算式字符, 由于算式中包含除号和等号不连通的字符图像,
因此不便采取投影法对字符进行分割。

在这里插入图片描述

5 字符识别

支持向量机是一种新的解决分类问题的机器学习方法, 基于统计学习理论,采用结构风险最小原则。 其原理是在训练样本集通过少量支持向量,
自动构造分类函数建立一个最大间隔分类平面, 以此解决分类问题。 支持向量机不需要构建网络结构设计, 通过非线性变换解决高维空间中样本识别问题。
支持向量机越来越多的应用到了字符识别中, 表现出较好的字符识别效果。

5.1 支持向量机原理

支持向量机(Support Vector Machine, SVM), 是 Vapnik [35] 研究小组在统计学习理论基础上, 于 1995
年针对分类问题提出的最佳分类准则。 SVM 是一种基于统计学习理论的模式识别方法, 主要应用于解决分类和回归问题。
传统的统计学理论基于样本无穷大的统计性质, SVM 专门针对有限样本, 算法转化成一个二次型寻优问题, 得到的是全局最优解。 它具有解的唯一性,
经过非线性变化转化到高维特征空间, 其算法与样本的复杂度无关, 不依赖输入空间的维数,得到的最优解优于传统的学习方法 。 因此迅速的发展起来,
在手写字符识别领域取得了巨大的成功。

对于最优间隔平面分类问题, 根据样本分布的情况分为线性可分与非线性可分进行讨论。 在线性可分的情况下, 其目标就是寻找最优间隔超平面, 将样本准确的分开。
根据少量支持向量确定平面, 保证样本数据与超平面距离最大,如图所示。

最优分类面示意图
在这里插入图片描述

5.2 基于SVM的字符识别

数学算式作业中包含印刷体字符和手写体字符, 将这些字符全部放在一个分类器中会导致分类过于复杂, 类别过多会使识别速率降低。
因此按照字符的分布位置将分类器分为两种类型: 印刷体字符分类器和手写字符分类器。 采取一对一分类的方法对印刷体字符和手写体字符分别设计了二分类器,
对于算式中同时包含印刷体和手写体数字字符, 选用相应的分类器, 会提高识别的准确性和速率。 如图所示, 根据字符在算式中的位置, 选用对应的分类器。

在这里插入图片描述

每个分类器只能将一个字符与其他字符分开, 对于手写字符而言, 其中一类字符样本的特征向量作为正集(标签对应的值为+1), 其余 9
个样本的特征向量做负集(标签对应的值为-1)。 按照这种形式依次划分, 将训练集依次进行训练, 可得到 10 个二分类器, 测试阶段将未知样本输入到这 10
个分类器进行分类判断, 决策结果取相应结果的最大值。 若输出的值为+1, 则对应相应类的字符。

网格特征是字符识别中常用的特征提取方法之一, 体现了字符形状的整体分布。 其中粗网格特征提取的方法是将字符图像等分成多个网格区域, 进行特征提取。
首先将归一化的字符样本图像, 其中大小为 128 128, 等分成 16 16 个网格, 如下图所示。
统计每个网格中黑色像素点占整个网格图像的有效像素比例, 最后将特征值按照网格排列转换为向量形式。

在这里插入图片描述

5.3 SVM算法实现

import numpy as npimport randomimport matplotlib.pyplot as plt'''类名称:dataStruct功能:用于存储一些需要保存或者初始化的数据'''class dataStruct:def __init__(self,dataMatIn,labelMatIn,C,toler,eps):self.dataMat = dataMatIn                        #样本数据self.labelMat = labelMatIn                      #样本标签self.C = C                                      #参数Cself.toler = toler                              #容错率self.eps = eps                                  #乘子更新最小比率self.m = np.shape(dataMatIn)[0]                 #样本数self.alphas = np.mat(np.zeros((self.m,1)))      #拉格朗日乘子alphas,shape(m,1),初始化全为0self.b = 0                                      #参数b,初始化为0self.eCache = np.mat(np.zeros((self.m,2)))      #误差缓存,'''函数名称:loadData函数功能:读取文本文件中的数据,以样本数据和标签的形式返回输入参数:filename       文本文件名返回参数:dataMat        样本数据labelMat       样本标签'''def loadData(filename):dataMat = [];labelMat = []fr = open(filename)for line in fr.readlines():                 #逐行读取lineArr = line.strip().split('\t')      #滤除行首行尾空格,以\t作为分隔符,对这行进行分解num = np.shape(lineArr)[0]     dataMat.append(list(map(float,lineArr[0:num-1])))#这一行的除最后一个被添加为数据labelMat.append(float(lineArr[num-1]))#这一行的最后一个数据被添加为标签dataMat = np.mat(dataMat)labelMat = np.mat(labelMat).Treturn dataMat,labelMat'''函数名称:takeStep函数功能:给定alpha1和alpha2,执行alpha1和alpha2的更新,执行b的更新输入参数:i1            alpha1的标号i2            alpha2的标号dataMat       样本数据labelMat      样本标签返回参数:如果i1==i2 or L==H or eta<=0 or alpha更新前后相差太小,返回0正常执行,返回1'''   def takeStep(i1,i2,dS):#如果选择了两个相同的乘子,不满足线性等式约束条件,因此不做更新if(i1 == i2):print("i1 == i2")return 0#从数据结构中取得需要用到的数据alpha1 = dS.alphas[i1,0]alpha2 = dS.alphas[i2,0]y1 = dS.labelMat[i1]y2 = dS.labelMat[i2]#如果E1以前被计算过,就直接从数据结构的cache中读取它,这样节省计算量,#如果没有历史记录,就计算E1if(dS.eCache[i1,0] == 1):E1 = dS.eCache[i1,1]else:u1 = (np.multiply(dS.alphas,dS.labelMat)).T * np.dot(dS.dataMat,dS.dataMat[i1,:].T) + dS.b     #计算SVM的输出值u1E1 = float(u1 - y1)    #误差E1#dS.eCache[i1] = [1,E1] #存到cache中#如果E2以前被计算过,就直接从数据结构的cache中读取它,这样节省计算量,#如果没有历史记录,就计算E2if(dS.eCache[i2,0] == 1):E2 = dS.eCache[i2,1]else:u2 = (np.multiply(dS.alphas,dS.labelMat)).T * np.dot(dS.dataMat,dS.dataMat[i2,:].T) + dS.b     #计算SVM的输出值u2E2 = float(u2 - y2)    #误差E2#dS.eCache[i2] = [1,E2] #存到cache中        s = y1*y2#计算alpha2的上界H和下界Lif(s==1):       #如果y1==y2L = max(0,alpha1+alpha2-dS.C)H = min(dS.C,alpha1+alpha2)elif(s==-1):    #如果y1!=y2L = max(0,alpha2-alpha1)H = min(dS.C,dS.C+alpha2-alpha1)if(L==H):print("L==H")return 0#计算学习率etak11 = np.dot(dS.dataMat[i1,::],dS.dataMat[i1,:].T)k12 = np.dot(dS.dataMat[i1,::],dS.dataMat[i2,:].T)k22 = np.dot(dS.dataMat[i2,::],dS.dataMat[i2,:].T)eta = k11 - 2*k12 +k22if(eta > 0):#正常情况下eta是大于0的,此时计算新的alpha2,新的alpha2标记为a2a2 = alpha2 + y2*(E1-E2)/eta#这个公式的推导,曾经花费了我很多精力,现在写出来却是如此简洁,数学真是个好东西#对a2进行上下界裁剪if(a2 < L):a2 = Lelif(a2 > H):a2 = Helse:#非正常情况下,也有可能出现eta《=0的情况print("eta<=0")return 0'''Lobj = Hobj = if(Lobj < Hobj-eps):a2 = Lelif(Lobj > Hobj+eps):a2 = Helse:a2 = alpha2'''#如果更新量太小,就不值浪费算力继续算a1和b,不值得对这三者进行更新if(abs(a2-alpha2) < dS.eps*(a2+alpha2+dS.eps)):print("so small update on alpha2!")return 0#计算新的alpha1,标记为a1a1 = alpha1 + s*(alpha2 - a2)#计算b1和b2,并且更新bb1 = -E1 + y1*(alpha1 - a1)*np.dot(dS.dataMat[i1,:],dS.dataMat[i1,:].T) + y2*(alpha2 - a2)*np.dot(dS.dataMat[i1,:],dS.dataMat[i2,:].T) + dS.bb2 = -E2 + y1*(alpha1 - a1)*np.dot(dS.dataMat[i1,:],dS.dataMat[i2,:].T) + y2*(alpha2 - a2)*np.dot(dS.dataMat[i2,:],dS.dataMat[i2,:].T) + dS.bif(a1>0 and a1<dS.C):dS.b = b1elif(a2>0 and a2<dS.C):dS.b = b2else:dS.b = (b1 + b2) / 2#用a1和a2更新alpha1和alpha2dS.alphas[i1] = a1dS.alphas[i2] = a2#由于本次alpha1、alpha2和b的更新,需要重新计算Ecache,注意Ecache只存储那些非零的alpha对应的误差validAlphasList = np.nonzero(dS.alphas.A)[0]   #所有的非零的alpha标号列表dS.eCache = np.mat(np.zeros((dS.m,2)))#要把Ecache先清空for k in validAlphasList:#遍历所有的非零alphauk = (np.multiply(dS.alphas,dS.labelMat).T).dot(np.dot(dS.dataMat,dS.dataMat[k,:].T)) + dS.byk = dS.labelMat[k,0]Ek = float(uk-yk)dS.eCache[k] = [1,Ek]print ("updated")return 1'''函数名称:examineExample函数功能:给定alpha2,如果alpha2不满足KKT条件,则再找一个alpha1,对这两个乘子进行一次takeStep输入参数:i2            alpha的标号dataMat       样本数据labelMat      样本标签返回参数:如果成功对一对乘子alpha1和alpha2执行了一次takeStep,返回1;否则,返回0'''def examineExample(i2,dS):#从数据结构中取得需要用到的数据y2 = dS.labelMat[i2,0]alpha2 = dS.alphas[i2,0]#如果E2以前被计算过,就直接从数据结构的cache中读取它,这样节省计算量,#如果没有历史记录,就计算E2if(dS.eCache[i2,0] == 1):E2 = dS.eCache[i2,1]else:u2 = (np.multiply(dS.alphas,dS.labelMat)).T * np.dot(dS.dataMat,dS.dataMat[i2,:].T) + dS.b#计算SVM的输出值u2E2 = float(u2 - y2)#误差E2#dS.eCache[i2] = [1,E2]r2 = E2*y2#如果当前的alpha2在一定容忍误差内不满足KKT条件,则需要对其进行更新if((r2<-dS.toler and alpha2<dS.C) or (r2>dS.toler and alpha2>0)):'''#随机选择的方法确定另一个乘子alpha1,多执行几次可可以收敛到很好的结果,就是效率比较低i1 = random.randint(0, dS.m-1)if(takeStep(i1,i2,dS)):return 1'''#启发式的方法确定另一个乘子alpha1nonZeroAlphasList = np.nonzero(dS.alphas.A)[0].tolist()#找到所有的非0的alphanonCAlphasList = np.nonzero((dS.alphas-dS.C).A)[0].tolist()#找到所有的非C的alphanonBoundAlphasList = list(set(nonZeroAlphasList)&set(nonCAlphasList))#所有非边界(既不=0,也不=C)的alpha#如果非边界的alpha数量至少两个,则在所有的非边界alpha上找到能够使\E1-E2\最大的那个E1,对这一对乘子进行更新if(len(nonBoundAlphasList)  > 1):maxE = 0maxEindex = 0for k in nonBoundAlphasList:if(abs(dS.eCache[k,1]-E2)>maxE):maxE = abs(dS.eCache[k,1]-E2)maxEindex = ki1 = maxEindexif(takeStep(i1,i2,dS)):return 1#如果上面找到的那个i1没能使alpha和b得到有效更新,则从随机开始处遍历整个非边界alpha作为i1,逐个对每一对乘子尝试进行更新randomStart = random.randint(0,len(nonBoundAlphasList)-1)for i1 in range(randomStart,len(nonBoundAlphasList)):if(i1 == i2):continueif(takeStep(i1,i2,dS)):return 1for i1 in range(0,randomStart):if(i1 == i2):continueif(takeStep(i1,i2,dS)):return 1#如果上面的更新仍然没有return 1跳出去或者非边界alpha数量少于两个,这种情况只好从随机开始的位置开始遍历整个可能的i1,对每一对尝试更新 randomStart = random.randint(0,dS.m-1)for i1 in range(randomStart,dS.m):if(i1 == i2):continueif(takeStep(i1,i2,dS)):return 1for i1 in range(0,randomStart):if(i1 == i2):continueif(takeStep(i1,i2,dS)):return 1   '''i1 = random.randint(0,dS.m-1)if(takeStep(i1,i2,dS)):return 1 '''#如果实在还更新不了,就回去重新选择一个alpha2吧,当前的alpha2肯定是有毒    return 0'''函数名称:SVM_with_SMO函数功能:用SMO写的SVM的入口函数,里面采用了第一个启发式确定alpha2,即在全局遍历和非边界遍历之间来回repeat,直到不再有任何更新输入参数:dS            dataStruct类的数据返回参数:None'''def SVM_with_SMO(dS):#初始化控制变量,确保第一次要全局遍历numChanged = 0examineAll = 1#显然,如果全局遍历了一次,并且没有任何更新,此时examineAll和numChanged都会被置零,算法终止while(numChanged > 0 or examineAll):numChanged = 0if(examineAll):for i in range(dS.m):numChanged += examineExample(i,dS)else:for i in range(dS.m):if(dS.alphas[i] == 0 or dS.alphas[i] == dS.C):continuenumChanged += examineExample(i,dS)if(examineAll == 1):examineAll = 0elif(numChanged == 0):examineAll = 1'''函数名称:cal_W函数功能:根据alpha和y来计算W输入参数:dS         dataStruct类的数据返回参数:W          超平名的法向量W            '''def cal_W(dS):W = np.dot(dS.dataMat.T,np.multiply(dS.labelMat,dS.alphas))return W'''函数名称:showClassifer函数功能:画出原始数据点、超平面,并标出支持向量输入参数:dS         dataStruct类的数据W          超平名的法向量W    返回参数:None'''    def showClassifer(dS,w):#绘制样本点dataMat = dS.dataMat.tolist()data_plus = []                                  #正样本data_minus = []                                 #负样本for i in range(len(dataMat)):if dS.labelMat[i,0] > 0:data_plus.append(dataMat[i])else:data_minus.append(dataMat[i])data_plus_np = np.array(data_plus)              #转换为numpy矩阵data_minus_np = np.array(data_minus)            #转换为numpy矩阵plt.scatter(np.transpose(data_plus_np)[0], np.transpose(data_plus_np)[1], s=30, alpha=0.7, c='r')   #正样本散点图plt.scatter(np.transpose(data_minus_np)[0], np.transpose(data_minus_np)[1], s=30, alpha=0.7,c='g') #负样本散点图#绘制直线x1 = max(dataMat)[0]x2 = min(dataMat)[0]a1, a2 = wb = float(dS.b)a1 = float(a1[0])a2 = float(a2[0])y1, y2 = (-b- a1*x1)/a2, (-b - a1*x2)/a2plt.plot([x1, x2], [y1, y2])#找出支持向量点for i, alpha in enumerate(dS.alphas):if abs(alpha) > 0.000000001:x, y = dataMat[i]plt.scatter([x], [y], s=150, c='none', alpha=0.7, linewidth=1.5, edgecolor='red')plt.xlabel("happy 520 day, 2018.06.13")plt.savefig("svm.png")plt.show()if __name__ == '__main__':dataMat,labelMat = loadData("testSet.txt")dS = dataStruct(dataMat, labelMat, 0.6, 0.001, 0.01)#初始化数据结构 dataMatIn, labelMatIn,C,toler,epsfor i in range(0,1):#只需要执行一次,效果就非常不错SVM_with_SMO(dS)W = cal_W(dS)showClassifer(dS,W.tolist())

6 算法测试

输入图像

在这里插入图片描述

预处理结果

在这里插入图片描述

识别结果

在这里插入图片描述

7 系统实现

系统主要流程如下

在这里插入图片描述

对在 PC 软件平台通过 MFC 界面中实现各模块操作, 系统界面如图所示。

系统界面采用模块化设计, 按照界面分布分为图像显示模块、 按键功能模块、 图像预处理模块、 批改结果输出四个模块组成。

主要内容包括:

  • 显示获取作业图像的基本信息;
  • 通过按键控制相应功能;
  • 显示预处理后图像的效果;输出识别的字符信息和批改的结果。

在这里插入图片描述
图像显示模块, 通过打开摄像头按键, 将摄像头获取到的纸张作业图像实时信息传送到计算机中, 获取的图像显示在界面左侧窗口, 界面运行结果如图所示。

在这里插入图片描述

按键功能模块, 通过算式提取按键, 对纸张中单个算式整体区域进行选框提取, 运行结果如图所示,
此时算式检测的结果在原图像上用矩形框标记,在界面右侧显示提取到的算式效果。

在这里插入图片描述

图像处理模块, 通过检测识别按键完成字符分割和识别, 在界面右侧窗口显示预处理后的图像效果。 批改结果输出模块,
在界面下框中显示字符的识别结果以及手写的计算结果, 同时在右下角窗口显示解答正误, 输出得到的批改信息。 同时对整个过程运行的时间进行统计,
最后保存按键将错误的批改结果保存, 便于后期修改。 此时系统运行界面如图所示。

在这里插入图片描述

8 最后

🧿 更多资料, 项目分享:

https://gitee.com/dancheng-senior/postgraduate

相关文章:

竞赛选题 机器视觉的试卷批改系统 - opencv python 视觉识别

文章目录 0 简介1 项目背景2 项目目的3 系统设计3.1 目标对象3.2 系统架构3.3 软件设计方案 4 图像预处理4.1 灰度二值化4.2 形态学处理4.3 算式提取4.4 倾斜校正4.5 字符分割 5 字符识别5.1 支持向量机原理5.2 基于SVM的字符识别5.3 SVM算法实现 6 算法测试7 系统实现8 最后 0…...

第15届蓝桥STEMA测评真题剖析-2023年8月20日Scratch编程中级组

[导读]&#xff1a;超平老师的《Scratch蓝桥杯真题解析100讲》已经全部完成&#xff0c;后续会不定期解读蓝桥杯真题&#xff0c;这是Scratch蓝桥杯真题解析第155讲。 第15届蓝桥第1次STEMA测评&#xff0c;这是2023年8月20日举办的STEMA&#xff0c;比赛仍然采取线上形式。这…...

html5 checkbox

1 选中状态的设置与获取 &#xff08;1&#xff09;获取checkbox是否选中&#xff1a; $("#checkbox").is(":checked");$("#checkbox-id")get(0).checked$(#checkbox-id).attr(checked)&#xff08;2&#xff09;设置checkbox是否选中 $("…...

安装matplotlib_

安装pip 安装matplotlib 安装完毕 导入出现bug.........

STM32复习笔记(六):STM32远程升级BootLoader相关

目录 Preface&#xff1a; &#xff08;一&#xff09;STM32上电启动流程 &#xff08;二&#xff09;BootLoader相关 &#xff08;三&#xff09;Clion配置 Preface&#xff1a; 有关STM32的BootLoader主要还是参考了许多大佬的文章&#xff0c;这里只是简单地列举一下&am…...

MASA MAUI iOS 文件下载与断点续传

文章目录 背景介绍方案及代码1、新建MAUI项目2、建立NSUrlSession会话连接3、使用NSUrlSessionDownloadTask 创建下载任务4、DidWriteData 监听下载5、DidFinishDownloading 完成下载6、CancelDownload (取消/暂停)下载7、ResumeDownload 恢复下载8、杀死进程-恢复下载 效果图总…...

NPDP产品经理知识(产品创新流程)

1.复习组合管理&#xff1a; 组合管理的目标 > 价值最大化&#xff0c;项目平衡&#xff0c;战略一致&#xff0c;管道平衡(资源需求和供给)&#xff0c;盈利充分 (实现财务目标) 产品创新流程就是管理风险的过程。 模糊前端&#xff1a; 产品创新章程&#xff1a;PIC 包…...

Android日常——记一次Android事件分发需求的实现

背景&#xff1a;在一个事件分发复杂的view中&#xff0c;插入一个可点击的控件&#xff0c;且不能影响到本身的事件分发。 尝试: 1.对view本身设置点击事件&#xff1b;由于view整体是交由root view去处理分发&#xff0c;存在滑动、边界处理、调出其他界面等复杂操作。设置点…...

【Python】函数(function)和方法(method)的区别

这里先说结论&#xff0c;为了满足心急的小伙伴&#xff1a;method与function的最大区别就是参数有无进行绑定。 自定义类Test&#xff1a; 首先先来一个自定义类&#xff1a; class Test:def Func_normal(arg):print(Func_normal:,arg)staticmethoddef Func_static(arg):pri…...

linux入门---信号的理解

目录标题 如何理解计算机中的信号如何查看计算机中的信号初步了解信号的保存和发送如何向目标进程发送信号情景一&#xff1a;使用键盘发送信号情景二&#xff1a;系统调用发送信号情景三&#xff1a;硬件异常产生信号情景四&#xff1a;软件条件产生信号 核心转储信号的两个问…...

nn.Linear(d, num_units, bias=True)设置bias和不设置bias有什么区别?

nn.Linear(d, num_units, biasTrue)是PyTorch中定义的一个全连接线性层。其中&#xff0c;d是输入特征的数量&#xff0c;num_units是输出特征的数量&#xff0c;而bias参数决定是否在这个线性变换中添加一个偏置项。 设置biasTrue与biasFalse的区别如下&#xff1a; 数学表示…...

代码随想录 Day10 栈与队列 LeetCode T239 滑动窗口的最大值 T347 前K个高频元素

简要介绍一下单调队列和优先级队列的不同 元素顺序的处理&#xff1a;单调队列中&#xff0c;元素的顺序是单调的&#xff0c;也就是说&#xff0c;队列中的元素按照特定的单调性&#xff08;递增或递减&#xff09;排列。这种特性使得单调队列在处理一些问题时非常高效&#…...

vue/自定义指令

需求&#xff1a; 页面有个input元素&#xff0c;现在要鼠标光标聚焦在上面&#xff0c;让每个页面上的标签都可以聚焦光标&#xff0c;比如&#xff0c;从A页面跳转到B页面的时候&#xff0c;我们依然要聚焦。如果要一遍遍地操作dom就会很麻烦。 这个时候&#xff0c;为了方便…...

借用binlog2sql工具轻松解析MySQL的binlog文件,再现Oracle的闪回功能

借用binlog2sql工具轻松解析MySQL的binlog文件 简介依赖配置用户权限选项配置案例&#xff1a;误UPDATE表数据回滚binlog2sql VS mysqlbinlog 看腻文章了就来听听视频演示吧&#xff1a;https://www.bilibili.com/video/BV1Zj411k7VW/ 简介 binlog2sql是美团大众点评开源的一…...

一次解决Pytorch训练时损失和参数出现Nan或者inf的经历

目前在做实验&#xff0c;参考了一个新的网络架构之后发现训练时损失出现Nan&#xff0c;参数了出现了inf的情况&#xff0c;先说说我的排查经历。 首先肯定是打印损失&#xff0c;损失是最容易出现Nan的&#xff0c;有各种原因&#xff0c;网上也有很多解决办法&#xff0c;我…...

【python入门篇】列表简介及操作(2)

列表是什么&#xff1f; 列表是由一系列按特定顺序排列的元素组成。你可以创建包含字母表中的所有字母、数字 0~9 或所有家庭成员的列表&#xff1b;也可以将任何东西加入列表中&#xff0c;其中的元素之间可以没有任何关系。列表通常包含多个元素&#xff0c;因此给列表指定一…...

数据结构与算法——19.红黑树

这篇文章我们来讲一下红黑树。 目录 1.概述 1.1红黑树的性质 2.红黑树的实现 3.总结 1.概述 首先&#xff0c;我们来大致了解一下什么是红黑树 红黑树是一种自平衡的二叉查找树&#xff0c;是一种高效的查找树。红黑树具有良好的效率&#xff0c;它可在 O(logN) 时间内完…...

js题解(三)

文章目录 柯里化模块乘法改变上下文 柯里化 已知 fn 为一个预定义函数&#xff0c;实现函数 curryIt&#xff0c;调用之后满足如下条件&#xff1a; 1、返回一个函数 a&#xff0c;a 的 length 属性值为 1&#xff08;即显式声明 a 接收一个参数&#xff09; 2、调用 a 之后&a…...

CompletableFuture异步回调

CompletableFuture异步回调 CompletableFutureFuture模式CompletableFuture详解1.CompletableFuture的UML类关系2.CompletionStage接口3.使用runAsync和supplyAcync创建子任务4.设置子任务回调钩子5.调用handle()方法统一处理异常和结果6.线程池的使用 异步任务的串行执行thenA…...

Python中匹配模糊的字符串

嗨喽~大家好呀&#xff0c;这里是魔王呐 ❤ ~! python更多源码/资料/解答/教程等 点击此处跳转文末名片免费获取 如何使用thefuzz 库&#xff0c;它允许我们在python中进行模糊字符串匹配。 此外&#xff0c;我们将学习如何使用process 模块&#xff0c;该模块允许我们在模糊…...

PHP图片文件管理功能系统源码

文件图库管理单PHP源码直接解压就能用&#xff0c;单文件&#xff0c;indexm.php文件可以重新命名&#xff0c;上传到需要访问的目录中&#xff0c; 可以查看目录以及各个文件&#xff0c;图片等和下载及修改管理服务。 源码下载&#xff1a;https://download.csdn.net/downloa…...

(枚举 + 树上倍增)Codeforces Round 900 (Div. 3) G

Problem - G - Codeforces 题意&#xff1a; 思路&#xff1a; 首先&#xff0c;目标值和结点权值是直接联系的&#xff0c;最值不可能直接贪心&#xff0c;一定是考虑去枚举一些东西&#xff0c;依靠这种枚举可以遍历所有的有效情况&#xff0c;思考的方向一定是枚举 如果去…...

websocket逆向【python实现websocket拦截】

python实现websocket拦截 前言一、拦截的优缺点优点:缺点:二、实现方法1.环境配置2.代码三、总结前言 开发者工具F12,筛选ws后,websocket的消息是这样显示的,如何获取这里面的消息呢? 以下是本篇文章正文内容 一、拦截的优缺点 主要讲解一下websocket拦截的实现,现在…...

软件测试自动化的成本效益分析

随着软件测试技术的发展&#xff0c;人们已经从最初的手工测试转变为手工和自动化技术相结合的测试方法。目前&#xff0c;人们更多的是关心自动化测试框架、自动化测试工具以及脚本研究等技术方面&#xff0c;而在软件自动化测试方案的效益分析方面涉及较少。 软件测试的目的是…...

【Java】状态修饰符 final static

目录 final 修饰我们的成员方法、成员变量、类 示例代码&#xff1a; final 修饰的局部变量 示例代码&#xff1a; static 示例代码&#xff1a; static 访问特点&#xff1a; 示例代码&#xff1a; static关键字的用途 示例代码&#xff1a; static 修饰常量 示例…...

笔试编程ACM模式JS(V8)、JS(Node)框架、输入输出初始化处理、常用方法、技巧

目录 考试注意事项 先审完题意&#xff0c;再动手 在本地编辑器&#xff08;有提示&#xff09; 简单题515min 通过率0%&#xff0c;有额外log 常见输入处理 str-> num arr&#xff1a;line.split( ).map(val>Number(val)) 初始化数组 new Array(length).fill(v…...

learn掩码张量

目录 1、什么是掩码张量 2、掩码张量的作用 3、代码演示 &#xff08;1&#xff09;、定义一个上三角矩阵&#xff0c;k0或者 k默认为 0 &#xff08;2&#xff09;、k1 &#xff08;3&#xff09;、k-1 4、掩码张量代码实现 &#xff08;1&#xff09;、输出效果 &…...

激活函数介绍

介绍 神经网络当中的激活函数用来提升网络的非线性&#xff0c;以增强网络的表征能力。它有这样几个特点&#xff1a;有界&#xff0c;必须为非常数&#xff0c;单调递增且连续可求导。我们常用的有sigmoid或者tanh&#xff0c;但我们都知道这两个都存在一定的缺点&#xff0c…...

docker方式启动一个java项目-Nginx本地有代码,并配置反向代理

文章目录 案例导入说明1.安装MySQL1.1.准备目录1.2.运行命令1.3.修改配置1.4.重启 2.导入SQL3.导入Demo工程3.1.分页查询商品&#xff08;仔细看代码&#xff0c;很多新的MP编程技巧&#xff09;3.2.新增商品3.3.修改商品3.4.修改库存3.5.删除商品3.6.根据id查询商品3.7.根据id…...

前端和后端是Web开发选哪个好?

前端和后端是Web开发中的两个不同的领域&#xff0c;哪一种更适合学习&#xff1f;前景更广呢&#xff1f; 一、引言 Web前端开发就像装饰房间的小瓦匠&#xff0c;勤勤恳恳&#xff0c;仔仔细细&#xff0c;粉饰墙壁&#xff0c;妆点家具。会 HTML,CSS&#xff0c;懂点 JS。…...