文案网站编辑怎么做,局部刷新 文章列表 wordpress,高端网站建设的公司,做网站开发学什么目录 1.答题卡自动批阅整体实现思路
2.关键技术步骤与原理
答题卡区域提取
①轮廓检测并排序
②执行透视变换
③找到每一个圆圈轮廓
④先对所有圆圈轮廓从上到下排序
⑤再通过循环每次只提取出五个轮廓再进行从左到右的排序
3.完整代码 1.答题卡自动批阅整体实现思路
…目录 1.答题卡自动批阅整体实现思路
2.关键技术步骤与原理
答题卡区域提取
①轮廓检测并排序
②执行透视变换
③找到每一个圆圈轮廓
④先对所有圆圈轮廓从上到下排序
⑤再通过循环每次只提取出五个轮廓再进行从左到右的排序
3.完整代码 1.答题卡自动批阅整体实现思路
首先通过图像处理技术将含答题卡的图片中的答题区域单独提取出来使其变为一个标准的矩形。然后针对处理后的答题卡区域进行分析逐一识别每一道题目的各个选项A, B, C, D, E是否被学生正确涂黑。最终根据判断结果计算出学生的得分并以百分比和具体答案串等形式展示。
2.关键技术步骤与原理
答题卡区域提取
该过程采用“轮廓查找”与“透视变换”。通过灰度化、二值化等预处理利用cv2.findContours找到答题卡的最外层轮廓随后对轮廓进行近似得到四个角点最后应用透视变换将答题卡区域整齐地裁剪出来。
imagecv2.imread(r./images/test_04.png)
contours_imgimage.copy()
graycv2.cvtColor(image,cv2.COLOR_BGR2GRAY)
blurredcv2.GaussianBlur(gray,(5,5),0)
cv_show(blurred,blurred)
edgedcv2.Canny(blurred,75,200)
cv_show(edged,edged)
简单的预处理操作 ①轮廓检测并排序
首先通过轮廓的边界框bounding box对所有选项轮廓进行从大到小的初步排序。
#轮廓检测
cntscv2.findContours(edged.copy(),cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)[-2]
cv2.drawContours(contours_img,cnts,-1,(0,0,255),3)
cv_show(contours_img,contours_img)
donCntNone
#根据轮廓大小排序准备透视变换
cntssorted(cnts,keycv2.contourArea,reverseTrue)
for c in cnts:#遍历每一个轮廓pericv2.arcLength(c,True)approxcv2.approxPolyDP(c,0.02*peri,True)if len(approx)4:donCntapproxbreak
approx的长度必须为四得到答题卡区域轮廓的四个近似点坐标矩阵数组 ②执行透视变换
#执行透视变换
warped_tfour_point_transform(image,donCnt.reshape(4,2))
warped_newwarped_t.copy()
cv_show(warped_t,warped_t)
二值化
threshcv2.threshold(warped,0,255,cv2.THRESH_BINARY_INV|cv2.THRESH_OTSU)[1]
cv_show(thresh,thresh)
thresh_Contoursthresh.copy() ③找到每一个圆圈轮廓
先找到所有轮廓
#找到每一个圆圈轮廓
cntscv2.findContours(thresh,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)[-2]
warped_Contourscv2.drawContours(warped_t,cnts,-1,(0,255,0),1)
cv_show(warped_Contours,warped_Contours) 对所有轮廓做外接矩形条件判断,筛选出所有圆形答题轮廓共25个
questionCnts[]
for c in cnts:(x,y,w,h)cv2.boundingRect(c)arw/float(h)if w20 and h20 and 0.9ar1.1:questionCnts.append(c)
print(len(questionCnts))#25
④先对所有圆圈轮廓从上到下排序
questionCntssort_contours(questionCnts,methodtop-to-bottom)[0]
correct0
⑤再通过循环每次只提取出五个轮廓再进行从左到右的排序
#每排有五个选项
for (q,i) in enumerate(np.arange(0,len(questionCnts),5)):cntssort_contours(questionCnts[i:i5])[0]bubbledNone#遍历每一个结果for (j,c) in enumerate(cnts):#使用mask来判读结果masknp.zeros(thresh.shape,dtypeuint8)cv2.drawContours(mask,[c],-1,255,-1)#-1表示填充cv_show(mask,mask)#通过计算非零点数量来算是否选择这个答案#利用掩膜(mask)进行与操作只保留mask位置中的内容thresh_mask_andcv2.bitwise_and(thresh,thresh,maskmask)cv_show(thresh_mask_and,thresh_mask_and)totalcv2.countNonZero(thresh_mask_and)#统计灰度值不为零的像素数量if bubbled is None or totalbubbled[0]:bubbled(total,j)#对比正确答案color(0,0,255)answerANSWER_KRY[q]if answerbubbled[1]:#判断正确color(0,255,0)correct1cv2.drawContours(warped_new,[cnts[answer]],-1,color,3)cv_show(warpeding,warped_new) 选项识别与答案分析
使用逐个建立掩膜mask的方式从原图中精确抠取出每个选项的图像。计算抠取出的每个选项区域内的白色像素点数量。通过比较不同选项的白色像素点数量确定学生实际作答的选项数量最多的即为作答的区域。 标准答案比对与评分
系统内部维护一个标准答案对照表answer key记录每道题的正确选项。 ANSWER_KRY{0:1,1:4,2:0,3:3,4:1}#正确答案 将程序识别出的答案与标准答案进行比对。若答案正确则计为1分并使用绿色边框对正确选项进行标注若答案错误则不加分并用红色边框标注正确选项。最终系统将根据总分和题目数量计算出最终得分并在图片上显示。
score(correct/5.0)*100
print([INFO] score:{:.2f}%.format(score))
cv2.putText(warped_new,{:.2f}%.format(score),(10,30),cv2.FONT_HERSHEY_SIMPLEX,0.9,(0,0,255),2)
cv_show(Original,image)
cv_show(Exam,warped_new)#[INFO] score:20.00% 3.完整代码
import cv2
import numpy as npdef order_points(pts):rectnp.zeros((4,2),dtypefloat32)#按顺序找到对应坐标0123即左上右上右下左下spts.sum(axis1)#每行求和rect[0]pts[np.argmin(s)]rect[2]pts[np.argmax(s)]diffnp.diff(pts,axis1)#每行求差rect[1]pts[np.argmin(diff)]rect[3]pts[np.argmax(diff)]return rect
def four_point_transform(image,pts):#获取输入坐标点rectorder_points(pts)(tl,tr,br,bl)rect#计算输入的w和h值widthAnp.sqrt(((br[0]-bl[0])**2(br[1]-bl[1])**2))widthBnp.sqrt(((tr[0]-tl[0])**2(tr[1]-tl[1])**2))maxWidthmax(int(widthA),int(widthB))heightAnp.sqrt(((tr[0]-br[0])**2)(tr[1]-br[1])**2)heightBnp.sqrt(((tl[0]-bl[0])**2)(tl[1]-bl[1])**2)maxHeightmax(int(heightA),int(heightB))#变换对应的坐标位置dstnp.array([[0,0],[maxWidth-1,0],[maxWidth-1,maxHeight-1],[0,maxHeight-1]],dtypefloat32)Mcv2.getPerspectiveTransform(rect,dst)#M是获取到的转换之间的关系warpedcv2.warpPerspective(image,M,(maxWidth,maxHeight))#返回变换后结果return warped
def sort_contours(cnts,methodleft-to-right):reverseFalsei0if methodright-to-leftor methodbottom-to-top:reverseTrueif methodtop-to-bottomor methodbottom-to-top:i1boundingBoxes[cv2.boundingRect(c) for c in cnts](cnts,boundingBoxes)zip(*sorted(zip(cnts,boundingBoxes),keylambda a:a[1][i],reversereverse))return cnts,boundingBoxes
def cv_show(name,img):cv2.imshow(name,img)cv2.waitKey(0)ANSWER_KRY{0:1,1:4,2:0,3:3,4:1}#正确答案imagecv2.imread(r./images/test_04.png)
contours_imgimage.copy()
graycv2.cvtColor(image,cv2.COLOR_BGR2GRAY)
blurredcv2.GaussianBlur(gray,(5,5),0)
cv_show(blurred,blurred)
edgedcv2.Canny(blurred,75,200)
cv_show(edged,edged)
#轮廓检测
cntscv2.findContours(edged.copy(),cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)[-2]
cv2.drawContours(contours_img,cnts,-1,(0,0,255),3)
cv_show(contours_img,contours_img)
donCntNone
#根据轮廓大小排序准备透视变换
cntssorted(cnts,keycv2.contourArea,reverseTrue)
for c in cnts:#遍历每一个轮廓pericv2.arcLength(c,True)approxcv2.approxPolyDP(c,0.02*peri,True)if len(approx)4:donCntapproxbreak
#执行透视变换
warped_tfour_point_transform(image,donCnt.reshape(4,2))
warped_newwarped_t.copy()
cv_show(warped_t,warped_t)
warpedcv2.cvtColor(warped_t,cv2.COLOR_BGR2GRAY)
threshcv2.threshold(warped,0,255,cv2.THRESH_BINARY_INV|cv2.THRESH_OTSU)[1]
cv_show(thresh,thresh)
thresh_Contoursthresh.copy()
#找到每一个圆圈轮廓
cntscv2.findContours(thresh,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)[-2]
warped_Contourscv2.drawContours(warped_t,cnts,-1,(0,255,0),1)
cv_show(warped_Contours,warped_Contours)questionCnts[]
for c in cnts:(x,y,w,h)cv2.boundingRect(c)arw/float(h)if w20 and h20 and 0.9ar1.1:questionCnts.append(c)
print(len(questionCnts))
#按照从上到下进行排序
questionCntssort_contours(questionCnts,methodtop-to-bottom)[0]
correct0
#每排有五个选项
for (q,i) in enumerate(np.arange(0,len(questionCnts),5)):cntssort_contours(questionCnts[i:i5])[0]bubbledNone#遍历每一个结果for (j,c) in enumerate(cnts):#使用mask来判读结果masknp.zeros(thresh.shape,dtypeuint8)cv2.drawContours(mask,[c],-1,255,-1)#-1表示填充cv_show(mask,mask)#通过计算非零点数量来算是否选择这个答案#利用掩膜(mask)进行与操作只保留mask位置中的内容thresh_mask_andcv2.bitwise_and(thresh,thresh,maskmask)cv_show(thresh_mask_and,thresh_mask_and)totalcv2.countNonZero(thresh_mask_and)#统计灰度值不为零的像素数量if bubbled is None or totalbubbled[0]:bubbled(total,j)#对比正确答案color(0,0,255)answerANSWER_KRY[q]if answerbubbled[1]:#判断正确color(0,255,0)correct1cv2.drawContours(warped_new,[cnts[answer]],-1,color,3)cv_show(warpeding,warped_new)
score(correct/5.0)*100
print([INFO] score:{:.2f}%.format(score))
cv2.putText(warped_new,{:.2f}%.format(score),(10,30),cv2.FONT_HERSHEY_SIMPLEX,0.9,(0,0,255),2)
cv_show(Original,image)
cv_show(Exam,warped_new)