Python答題卡識(shí)別并給出分?jǐn)?shù)的實(shí)現(xiàn)代碼
哈嘍大家好,這里是滑稽研究所??催^(guò)我們圖像處理系列的朋友,應(yīng)該知道識(shí)別答題卡那期文章。其中利用opencv框架,完美的實(shí)現(xiàn)了答題卡填涂區(qū)域的識(shí)別。在后臺(tái)有小伙伴想要我完善一下判斷選項(xiàng)對(duì)錯(cuò)并打分的功能,本期我們就來(lái)實(shí)現(xiàn)一下。
那么我們來(lái)復(fù)習(xí)一下往期的代碼原理。我們需要對(duì)圖片素材進(jìn)行灰度化處理、透視變換、輪廓檢測(cè)、腐蝕膨脹處理、區(qū)域分割、邊框計(jì)算、區(qū)域計(jì)算。實(shí)際上我們是通過(guò)像素面積的過(guò)濾、填涂區(qū)域優(yōu)化和獲取選項(xiàng)坐標(biāo)來(lái)完成答題卡的識(shí)別的。
素材:
那么在獲取到答題卡的填涂區(qū)域之后就好辦了。我們首先分隔答題卡,去除干擾項(xiàng),然后把不同的區(qū)域打上標(biāo)簽。我們的答題卡是自上而下排序的。那么我們獲取到的填涂項(xiàng)的x坐標(biāo)即橫坐標(biāo)就派上了用場(chǎng)。選項(xiàng)A~E一定是占據(jù)了五個(gè)不同的區(qū)域。我們已經(jīng)為不同區(qū)域打上了標(biāo)簽。剩下的就是交給我們的if判斷語(yǔ)句了。這時(shí)我們已經(jīng)為填涂項(xiàng)賦上了實(shí)際的意義。即從像素坐標(biāo)轉(zhuǎn)換成了具有實(shí)際意義的選項(xiàng)。
那y坐標(biāo)就沒(méi)有用了嗎?非也。經(jīng)過(guò)上面的處理我們只是得到了填涂區(qū)域?qū)?yīng)的選項(xiàng)。但是我們還沒(méi)有進(jìn)行排序。大家知道無(wú)序的選項(xiàng)是沒(méi)有意義的。而剛剛我們說(shuō)了該答題卡的題號(hào)順序是自上而下的。因?yàn)槲覀儽闅v選項(xiàng)時(shí),是同時(shí)得到x、y坐標(biāo)的,因此我們可以保證得到的坐標(biāo)是配對(duì)的。
其中橫縱坐標(biāo)分別填入兩個(gè)list中,然后使用zip方法合并list。這時(shí)我們?cè)侔凑彰總€(gè)list的第二個(gè)元素也就是縱坐標(biāo)進(jìn)行由小到大的排序,就可以得到正確的順序。
這時(shí)我們才真正獲取到了需要的數(shù)據(jù)。即考生填涂的選項(xiàng)順序,我們?cè)傩陆ㄒ粋€(gè)list放正確的答案,與考生的答案進(jìn)行對(duì)比,經(jīng)計(jì)算得出考生的正確率,并給出分?jǐn)?shù)。
好,思路清晰,上代碼!
import cv2 import numpy as np path = './test_01.png' img = cv2.imread(path) imgGray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY) imgBlur = cv2.GaussianBlur(imgGray,(3,3),1) imgCanny = cv2.Canny(imgBlur,100,120) cv2.imshow("O", imgCanny) imgContour = img.copy() cnts = cv2.findContours(imgCanny, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)[0] for cnt in cnts: area = cv2.contourArea(cnt) # 這個(gè)輸出各個(gè)輪廓的面積 #print(area) # if area >= 500: cv2.drawContours(imgContour, cnt, -1, (255, 0, 0), 3) peri = cv2.arcLength(cnt, True) # 找出輪廓的突變值 approx = cv2.approxPolyDP(cnt, 0.02 * peri, True) # approx找到的是一個(gè)輪廓有幾個(gè)突變值,有幾個(gè)角就會(huì)有幾個(gè)突變值 # 返回的是一個(gè)list,輸出他的長(zhǎng)度,就可以知道到底有幾個(gè)角 #print(approx) a1,a2,a3,a4 = list(approx[0][0]),list(approx[1][0]),list(approx[2][0]),list(approx[3][0]) #cv2.imshow("Canny Image",imgContour) mat1 = np.array([a1,a2,a3,a4],dtype=np.float32) #透視變換 #計(jì)算矩形寬高 width = 402#int(((a4[0]-a1[0])+(a3[0]-a2[0]))/2) height = 518#int(((a2[1]-a1[1])+(a3[1]-a4[1]))/2) #計(jì)算還原后的坐標(biāo) new_a1 = [0,0] new_a2 = [0,height] new_a3 = [width,height] new_a4 = [width,0] mat2 = np.array([new_a1,new_a2,new_a3,new_a4],dtype=np.float32) #計(jì)算變換矩陣 mat3 = cv2.getPerspectiveTransform(mat1,mat2) #進(jìn)行透視變換 res = cv2.warpPerspective(imgCanny,mat3,(width,height)) res1 = cv2.warpPerspective(img,mat3,(width,height)) imgxx = cv2.cvtColor(res1,cv2.COLOR_BGR2GRAY) binary = cv2.threshold(imgxx,0,255,cv2.THRESH_BINARY_INV | cv2.THRESH_OTSU )[1] #變換完成 #cv2.imshow("Output",res1) cntss = cv2.findContours(res, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)[0] for cnt1 in cntss: area1 = cv2.contourArea(cnt1) # 這個(gè)輸出各個(gè)輪廓的面積 #print(area) # if area1 >= 1500 and area1<=1700: #把圓的輪廓畫(huà)成黑色 cv2.drawContours(binary, cnt1, -1, (0, 0, 0), 10) kernel = np.ones((5, 5), np.uint8) imgDialation = cv2.dilate(binary, kernel, iterations=1) cv2.imshow("Out", imgDialation) cntsss = cv2.findContours(imgDialation, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)[0] l1 = [] l2 = [] l3 = ['B','E','A','D','B'] for cnt2 in cntsss: area2 = cv2.contourArea(cnt2) #print(area) if area2 <= 1200 and 800<=area2: #cv2.drawContours(res1, cnt, -1, (0, 255, 0), 5) #輪廓長(zhǎng) peri = cv2.arcLength(cnt2, True) # 找出輪廓的突變值 approx1 = cv2.approxPolyDP(cnt2, 0.02 * peri, True) x, y, w, h = cv2.boundingRect(approx1) #外接矩形 #print(x+w//2,y+h//2) m = x+w//2 n = y+h//2 l1.append(m) l2.append(n) #拼接兩個(gè)一維列表,使x,y坐標(biāo)配對(duì)。 mix1 = list(zip(l1,l2)) #按列表第二個(gè)元素升序,即按y值由小到大排列。 #這是我們得到的答案為正確順序。 mix1.sort(key=lambda x: x[1]) if 400>x>80 and 50<y<350: cv2.rectangle(res1, (x, y), (x + w, y + h), (0, 0, 255), 2) #圓心 # (圖像,x.y位置,半徑,顏色,輪廓粗細(xì)) cv2.circle(res1, (x+w//2,y+h//2), 1, (255, 0, 0), 5) l4 = [] for i in mix1: if 75 < i[0] < 130: print("A") l4.append('A') elif 130 < i[0] < 185: print("B") l4.append('B') elif 185 < i[0] < 240: print("C") l4.append('C') elif 240 < i[0] < 295: print("D") l4.append('D') elif 295 < i[0] < 350: print("E") l4.append('E') print('正確答案:',l3) print('考生答案',l4) h = 0 for i in range(0, len(l3)): if l3[i] == l4[i]: h=h+1 print('得分:',str(h/5*100)+'分') cv2.imshow("cc Image",res1) cv2.imshow("dd Image",binary) cv2.waitKey(0)
運(yùn)行結(jié)果:
以上為兩個(gè)圖片素材的運(yùn)行結(jié)果,我們只放出其中一部分。剩余的素材大家自行實(shí)驗(yàn)。
可以看到,程序成功的識(shí)別了考生填涂的答題卡,并給出了考生答案、正答案和考生最后的得分。
綜上功能實(shí)現(xiàn),任務(wù)完成。大家學(xué)會(huì)了嗎?
以上就是Python識(shí)別答題卡并給出分?jǐn)?shù)的詳細(xì)內(nèi)容,更多關(guān)于Python識(shí)別答題卡的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
- python OpenCV實(shí)現(xiàn)答題卡識(shí)別判卷
- python實(shí)現(xiàn)百萬(wàn)答題自動(dòng)百度搜索答案
- python實(shí)現(xiàn)用戶(hù)答題功能
- 答題輔助python代碼實(shí)現(xiàn)
- python3.5+tesseract+adb實(shí)現(xiàn)西瓜視頻或頭腦王者輔助答題
- 從0到1使用python開(kāi)發(fā)一個(gè)半自動(dòng)答題小程序的實(shí)現(xiàn)
- Python沖頂大會(huì) 快來(lái)答題!
- python利用opencv如何實(shí)現(xiàn)答題卡自動(dòng)判卷
相關(guān)文章
python自動(dòng)化之如何利用allure生成測(cè)試報(bào)告
這篇文章主要給大家介紹了關(guān)于python自動(dòng)化之如何利用allure生成測(cè)試報(bào)告的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-05-05Python 實(shí)現(xiàn)將numpy中的nan和inf,nan替換成對(duì)應(yīng)的均值
這篇文章主要介紹了Python 實(shí)現(xiàn)將numpy中的nan和inf,nan替換成對(duì)應(yīng)的均值,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-06-06python定位xpath 節(jié)點(diǎn)位置的方法
今天小編就為大家分享一篇python定位xpath 節(jié)點(diǎn)位置的方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2019-08-08Python 實(shí)現(xiàn)簡(jiǎn)單的shell sed替換功能(實(shí)例講解)
下面小編就為大家?guī)?lái)一篇Python 實(shí)現(xiàn)簡(jiǎn)單的shell sed替換功能(實(shí)例講解)。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-09-09Python利用多線(xiàn)程枚舉實(shí)現(xiàn)獲取wifi信息
這篇文章主要為大家詳細(xì)介紹了Python如何利用枚舉字典的方式來(lái)實(shí)現(xiàn)獲取wifi信息,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以了解一下2022-12-12基python實(shí)現(xiàn)多線(xiàn)程網(wǎng)頁(yè)爬蟲(chóng)
python是支持多線(xiàn)程的, 主要是通過(guò)thread和threading這兩個(gè)模塊來(lái)實(shí)現(xiàn)的,本文主要給大家分享python實(shí)現(xiàn)多線(xiàn)程網(wǎng)頁(yè)爬蟲(chóng),需要的朋友可以參考下2015-09-09