OpenCV实战之文档扫描判卷
import cv2 import numpy as np #图像显示 def cv_show(imgname,img): cv2.imshow(imgname,img) cv2.waitKey(0) cv2.destroyAllWindows() #排序坐标函数 def order_pts(pts): rect=np.zeros((4,2),dtype='float32') s = np.sum(pts,axis=1) rect[0] = pts[np.argmin(s)] rect[2] = pts[np.argmax(s)] n = np.diff(pts,axis=1) rect[1] = pts[np.argmin(n)] rect[3] = pts[np.argmax(n)] return rect #透视变换 def four_pts_change(img,pts): points = order_pts(pts) (tl,tr,bl,br) = points widthA = np.sqrt(((tr[1]-tl[1])**2)+((tr[0]-tl[0])**2)) widthB = np.sqrt(((br[1] - bl[1]) ** 2) + ((br[0] - bl[0]) ** 2)) width = max(int(widthA),int(widthB)) lengthA = np.sqrt(((tr[1]-br[1])**2)+((tr[0]-br[0])**2)) lengthB = np.sqrt(((tl[1] - bl[1]) ** 2) + ((tl[0] - bl[0]) ** 2)) length = max(int(lengthA),int(lengthB)) #输出图坐标 dst = np.array([ (0,0), (width-1,0), (width-1,length-1), (0,length) ],dtype='float32') M = cv2.getPerspectiveTransform(points,dst) wraped = cv2.warpPerspective(img,M,(width,length)) return wraped #正确答案 ANSWER_KEY={0:1,1:4,2:0,3:3,4:1} # ANSWER_KEY = {0:1,1:3,2:0,3:4,4:1} img = cv2.imread('./textcard.png') cv_show('img',img) orig = img.copy() gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY) #高斯模糊 gaussian = cv2.GaussianBlur(gray,(3,3),0) cv_show('gaussian',gaussian) #边缘检测 canny = cv2.Canny(gaussian,70,150) cv_show('canny',canny)
#轮廓检测 cnts = cv2.findContours(canny,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)[0] cnts = sorted(cnts,key=cv2.contourArea,reverse=True) # cv2.drawContours(orig,cnts,-1,(149,32,190),2) # cv_show('orig',orig) for i in cnts: perix = cv2.arcLength(i,True) approx = cv2.approxPolyDP(i,0.01*perix,True) if len(approx)==4: screen = approx break cv2.drawContours(orig,[screen],-1,(149,32,190),2) # cv_show('orig',orig) #透视变换 wraped = four_pts_change(img,screen.reshape(4,2)) cv_show('wraped',wraped) wraped_gray = cv2.cvtColor(wraped,cv2.COLOR_BGR2GRAY) #阈值处理 thresh = cv2.threshold(wraped_gray,0,255,cv2.THRESH_BINARY_INV|cv2.THRESH_OTSU)[1] cv_show('thresh',thresh) #检测轮廓 thresh_cnts=thresh.copy() cnts1 = cv2.findContours(thresh_cnts,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)[0] #白色填充轮廓 cv2.drawContours(thresh_cnts,cnts1,-1,(0,0,0),3) cv_show('WRAPED',thresh_cnts)
#检测圆形 questions = [] for cnt in cnts1: (x,y,w,h) = cv2.boundingRect(cnt) # print((x,y,w,h)) arc = w/float(h) if w>20 and h>20 and arc >0.6 and arc<1.1: questions.append(cnt) print('一共有{}个选项'.format(len(questions))) # print(questions) #从上到下排序 boundingBox =[ cv2.boundingRect(i) for i in questions] (questionCnts,boundingBox) = zip(*sorted(zip(questions,boundingBox),key=lambda b:b[1][1],reverse=False)) correct =0 #每行遍历 for (i,c) in enumerate(np.arange(0,len(questions),5)): #遍历每一行的第一个 返回值是[(1,0),(2,5),(3,10),(4,15),(5,20)] boundingBox = [cv2.boundingRect(a) for a in questionCnts[c:c+5]] #框出每一行的每个圆 (cnts,boundingBox) = zip(*sorted(zip(questionCnts[c:c+5],boundingBox),key=lambda b:b[1][0],reverse=False)) #每一行的圆的轮廓和框框按从左到右的顺序排列 bubble = None for (j,q) in enumerate(cnts): mask = np.zeros_like(thresh) cv2.drawContours(mask,[q],-1,255,-1) #白色填充圆 # cv_show('mask', mask) mask = cv2.bitwise_and(thresh,thresh,mask=mask) #抠出每一个选项 # cv_show('mask',mask) total = cv2.countNonZero(mask) if bubble is None or total>bubble[0]: bubble = (total,j) k = ANSWER_KEY[i] if k == bubble[1]: correct +=1 score = (correct/5)*100 print('[INFO]score : {:.2f}%'.format(score)) cv2.putText(wraped,'{:.2f}%'.format(score),(10,30),cv2.FONT_HERSHEY_SIMPLEX,0.9,(123,123,123),2) cv_show('score',wraped)