赞
踩
- import cv2
- import numpy as np
-
- ANSWER_KEY={0:1,1:4,2:0,3:3,4:1}
-
- # 获取坐标点
- def order_points(pts):
- # 一共4个坐标点
- rect = np.zeros((4, 2), dtype="float32")
-
- # 按顺序找到对应坐标0123分别是 左上,右上,右下,左下
- # 计算左上,右下
- s = pts.sum(axis=1)
- rect[0] = pts[np.argmin(s)]
- rect[2] = pts[np.argmax(s)]
-
- # 计算右上和左下
- diff = np.diff(pts, axis=1)
- rect[1] = pts[np.argmin(diff)]
- rect[3] = pts[np.argmax(diff)]
-
- return rect
-
-
- def four_point_transform(image, pts):#pts是原始图像的四个坐标点
- # 获取输入坐标点
- rect = order_points(pts)
- (tl, tr, br, bl) = rect
-
- #计算输入的w和h值
- widthA = np.sqrt(((br[0] - bl[0]) ** 2) + ((br[1] - bl[1]) ** 2))#根号下(x3-x4)^2+(y3-y4)^2
- widthB = np.sqrt(((tr[0] - tl[0]) ** 2) + ((tr[1] - tl[1]) ** 2))#根号下(x2-x1)^2+(y2-y1)^2
- maxWidth = max(int(widthA), int(widthB))#取得两个中的最大值,作为目标图像的宽
-
- heightA = np.sqrt(((tr[0] - br[0]) ** 2) + ((tr[1] - br[1]) ** 2))#根号下(x4-x1)^2+(y4-y1)^2
- heightB = np.sqrt(((tl[0] - bl[0]) ** 2) + ((tl[1] - bl[1]) ** 2))#根号下(x3-x2)^2+(y3-y2)^2
- maxHeight = max(int(heightA), int(heightB))#取得两个中的最大值,作为目标图像的高
-
- # 变换后对应坐标位置:目标图像中对应的四个点
- dst = np.array([
- [0, 0],#目标图像中的tl坐标
- [maxWidth - 1, 0],#目标图像中tr坐标
- [maxWidth - 1, maxHeight - 1],#目标图像的br坐标
- [0, maxHeight - 1]], dtype="float32")#目标图像的bl坐标
-
- # 计算变换矩阵
- M = cv2.getPerspectiveTransform(rect, dst) #通过输入和输出的四个坐标,就可以计算出变换矩阵
- warped = cv2.warpPerspective(image, M, (maxWidth, maxHeight))#dsize是目标图像的大小
- # 返回变换后结果
- return warped
-
- def sort_contours(contours,method="left-to-right"):
- reverse = False
- i = 0
- if method == "right-to-left" or method == "bottom-to-top":
- reverse = True
- if method == "top-to-bottom" or method == "bottom-to-top":
- i = 1
-
- # 把找到的形状用最小的矩形包起来,外接矩形,x,y,h,w
- boundingBoxes = [cv2.boundingRect(c) for c in contours]
- # 直接用x来判断出来轮廓的排列顺序
- (contours, boundingBoxes) = zip(*sorted(zip(contours, boundingBoxes), key=lambda b: b[1][i], reverse=reverse))
-
- return contours, boundingBoxes
-
-
- #图像的预处理操作
- image=cv2.imread("/Users/macbook/Desktop/project3.png")#读入原始图像
- contours_img=image.copy()
- gray=cv2.cvtColor(image,cv2.COLOR_BGR2GRAY)#将BGR图像转换成灰度图像
- blurred=cv2.GaussianBlur(gray,(5,5),0)#对灰度图像进行高斯滤波操作,去除噪点
- edged=cv2.Canny(blurred,75,200)#对滤波后进行边缘检测操作
- cv2.imshow("edged",edged)#显示边缘检测后的图像
-
- #检测外轮廓
- cnts,hierarchy=cv2.findContours(edged.copy(),cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
- cv2.drawContours(contours_img,cnts,-1,(0,0,255),3)#在原始图像上用红色画笔画出轮廓
-
- docCnt=None
- if len(cnts)>0:#确保检测到了轮廓
- cnts=sorted(cnts,key=cv2.contourArea,reverse=True)#根据面积的大小对检测的外轮廓按照面积的大小进行排序处理
- for c in cnts:
- peri = cv2.arcLength(c, True)
- approx = cv2.approxPolyDP(c,0.02*peri,True)#c表示输入的点集,True表示闭合
- if(len(approx)==4):#表示检测到了矩形的轮廓,直接退出循环即可
- docCnt=approx
- break
-
- #透视变换
- warped=four_point_transform(gray,docCnt.reshape(4,2))
- thresh=cv2.threshold(warped,0,255,cv2.THRESH_BINARY_INV+cv2.THRESH_OTSU)[1]#自定义阈值,二值化操作
- cv2.imshow("thresh",thresh)#显示二值化图像处理的结果
- # 学生涂的区域白色部分比较多,就是非0区域面积比较大,没涂的部分非0区域比较少
-
- thresh_Contours=thresh.copy()
-
- #找到每一个圆圈选项的轮廓:对应有各自的掩码图像
- Cnts,hierarchy=cv2.findContours(thresh.copy(),cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
- cv2.drawContours(thresh_Contours,Cnts,-1,(0,0,255),3)
- cv2.imshow("thresh_Contours",thresh_Contours)
-
- questionCnts=[]
- #遍历每一个轮廓:筛选轮廓
- for c in Cnts:
- #计算比例和大小
- (x,y,w,h)=cv2.boundingRect(c)#对每一个轮廓用最小的外包矩形来包围
- ar=w/float(h)
- if w>=20 and h>=20 and ar>=0.9 and ar<=1.1:#根据实际情况制定标准,因为学生涂卡时可能没涂满,也有可能涂到外面了
- questionCnts.append(c)
-
-
- #对得到的轮廓进行排序
- questionCnts=sort_contours(questionCnts,method="top-to-bottom")[0]
-
- correct=0#表示正确的题目数量
-
- for(q,i) in enumerate(np.arange(0,len(questionCnts),5)):#每道题目有五个选项:5
- cnts=sort_contours(questionCnts[i:i+5])[0]
- bubbled=None
- for(j,c)in enumerate (cnts):
- mask = np.zeros(thresh.shape, dtype='uint8')#作出与二进制图像大小相同的掩码图像
- cv2.drawContours(mask, [c], -1, 255, -1)#在掩码图像上对轮廓所处的区域用白色填充好
- cv2.imshow("mask2",mask)#展示此时的掩码图像
- mask=cv2.bitwise_and(thresh,thresh,mask=mask)#进行与操作,掩码白色区域的thresh图像保留下来,其他变成黑色
- cv2.imshow("mask3",mask)#展示此时的掩码图像
- total=cv2.countNonZero(mask)#计算掩码区域非零的值,涂黑区域的非零值会更多
-
- if bubbled is None or total>bubbled[0]:
- #表示是每一题的第一个选项或者现在的非零区域更多,就把bubbled的值换成目前的total值
- bubbled=(total,j)#j表示q题学生填写答案的列数
-
- #对比正确答案:
- color=(0,0,255)
- k=ANSWER_KEY[q]#q代表题数,从0开始,ANSWER_KEY[k]就是q题目对应的正确答案的列数
-
- if k==bubbled[1]:#表示填写答案与正确答案相同
- color=(0,255,0)#绿色表示正确
- correct+=1
-
- #绘图
- cv2.drawContours(warped,[cnts[k]],-1,color,3)
-
- #计算正确率
- score=(correct/5.0)*100
- print("[INFO] score: {:.2f}%".format(score))
- cv2.putText(warped, "{:.2f}%".format(score), (10, 30),cv2.FONT_HERSHEY_SIMPLEX, 0.9, (0, 0, 255), 2)
- result=cv2.cvtColor(warped,cv2.COLOR_GRAY2BGR)
- cv2.imshow("result",result)
-
- cv2.waitKey(0)
- cv2.destroyAllWindows()

Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。