網(wǎng)站首頁 編程語言 正文
使用Python3和Opencv識別一張標準的答題卡。大致的過程如下:
1.讀取圖片
2.利用霍夫圓檢測,檢測出四個角的黑圓位置,從確定四個角的位置
3.利用透視變換和四個角的位置,矯正圖片(直接用的網(wǎng)上的圖片,沒有拍照,所以這一步?jīng)]有實現(xiàn))
4.裁剪四個邊框,獲取邊框上小黑格的位置
5.根據(jù)小黑格的位置確定每個涂卡區(qū)域的位置
6.將答題卡腐蝕和膨脹,遍歷所有的格子的區(qū)域,計算每個區(qū)域內(nèi)像素值為0的個數(shù),若數(shù)量達到某個值,那么就確認這個格子是被黑筆涂過,并記錄該位置的題目選項。
具體的實現(xiàn)
一、讀取圖片,用是imread函數(shù)。
二、利用霍夫圓檢測位置,這里注意的是HoughCircles的param參數(shù),調(diào)的不準,所以檢測出來還有其他的干擾圓,最后可以通過設(shè)置半徑閾值,將四個角的圓篩選出來。最后這個函數(shù)返回四個圓的位置和半徑。
#檢測圖中的圓,并返回每個圓的位置和半徑 def detect_circles_demo(image): ? ? temp = image.copy() ? ? rows,cols,channels = temp.shape ? ? print(rows,cols) ? ? location_vcol = [] ? ? dst = cv.pyrMeanShiftFiltering(image,10,100) ? ? cimage = cv.cvtColor(dst,cv.COLOR_BGR2GRAY) ? ? #不同的圖片,Parmal的值是不一樣的 ? ? circles = cv.HoughCircles(cimage,cv.HOUGH_GRADIENT,1,20,param1= 27,param2=30,minRadius=0,maxRadius=0) ? ? circles = np.uint16(np.around(circles)) ? ? for i in circles[0,:]: ? ? ? ? if i[2] < 20 and i[2] > 10: ? ? ? ? ? ? cv.circle(image,(i[0],i[1]),i[2],(0,0,255),2) ? ? ? ? ? ? location_vcol.append((i[0],i[1],i[2])) ? ? # 畫出圖中圓的位置 ? ? cv.imshow("image",image) ? ? return location_vcol
三、先將圖片整體二直化,再在二直化的圖片將四個邊框裁剪下來,直接根據(jù)四個邊角的位置向外或者向里增加或者較少一個半徑,確定邊框矩形的對角位置。
#獲取四個邊角的位置 location_RT = location_vcol[0] location_RB = location_vcol[1] location_LT = location_vcol[2] location_LB = location_vcol[3] ? # 先將原圖二值化,注意閾值的取范圍,再將二值化圖片轉(zhuǎn)換成BGR,方便后面標出小方格的位置 gray = cv.cvtColor(temp, cv.COLOR_BGR2GRAY) ret, binary = cv.threshold(gray, 100, 255, cv.THRESH_BINARY_INV) Matimage = cv.cvtColor(binary, cv.COLOR_GRAY2BGR) ?cv.imshow("Matimage",Matimage) ? #將圖片的四條邊裁剪出來 ?roilLeft = Matimage[location_LT[1] - location_LT[2]:location_LB[1] + location_LB[2],location_LT[0] -location_LT[2]:location_LB[0] + location_LB[2]] ?roilRight = Matimage[location_RT[1] - location_RT[2]:location_RB[1] + location_RB[2],location_RT[0] - location_RT[2]:location_RB[0]+location_RB[2]] ?roilTop = Matimage[location_LT[0] - location_LT[2]:location_RT[1]+location_RT[2],location_LT[0] - location_LT[2]:location_RT[0] +location_RT[2]] ?roilBottom = Matimage[location_RB[1]-location_RB[2]:location_RB[1] + location_RB[2],location_LB[0] -location_LB[2]:location_RB[0]+location_RB[2]] ? # 展示四條邊的圖片 cv.imshow("left",roilLeft) cv.imshow("right", roilRight) cv.imshow("top", roilTop) cv.imshow("bottom", roilBottom)
四、根據(jù)裁剪的邊框確定邊框上小白格的位置。每個方位的計算方式是一樣的。每個函數(shù)返回的是小格子在源圖中的位置。
先來說底部的。這個過程是把截圖看成一個坐標系,以行長為橫坐標,列高為中坐標,計算統(tǒng)計每個橫坐標下,有多少個點是白色的,并將該數(shù)值存放于列表vcol中。根據(jù)這種算法,能夠得出這個列表中應(yīng)該是rows個元素,而且每個元素的值不超過列高,不低于0。
遍歷vcol列表,如果某一個位置為0,后一個位置不為0,那么這個位置就是小白格的起始位置。并記錄起始位置在原圖中的位置,方便后期遍歷整個涂卡區(qū)域。
其他的三個方向是同樣的道理,
#計算底部小黑格的位置 def sure_bottom(roilBottom,src,a): ? ? rows, cols, channels = roilBottom.shape ? ? src_rows,src_cols,src_channels = src.shape ? ? itemp = 0 ? ? vcol = [] ? ? bottom_vcol = [] ? ? for i in range(0, cols): ? ? ? ? for j in range(0, rows): ? ? ? ? ? ? if roilBottom[j, i][0] == 255 and roilBottom[j, i][1] == 255 and roilBottom[j, i][2] == 255: ? ? ? ? ? ? ? ? itemp = itemp + 1 ? ? ? ? vcol.append(itemp) ? ? ? ? itemp = 0 ? ? for i in range(vcol.__len__()): ? ? ? ? if 0 == vcol[i] and vcol[i + 1] > 0: ? ? ? ? ? ? cv.line(roilBottom, (i, 0), (i, rows), (0, 0, 255), 2) ? ? ? ? ? ? cv.line(src, (i + a, src_rows), (i + a, src_rows - rows), (0, 255, 0), 2) ? ? ? ? ? ? bottom_vcol.append(i + a) ? ? cv.imshow("src-1", roilBottom) ? ? return bottom_vcol ? #計算右部小黑格的位置 def sure_Right(roilRight,src,a): ? ? rows, cols, channels = roilRight.shape ? ? src_rows, src_cols, src_channels = src.shape ? ? itemp = 0 ? ? vcol = [] ? ? right_vcol = [] ? ? for i in range(0, rows): ? ? ? ? for j in range(0, cols): ? ? ? ? ? ? if roilRight[i, j][0] == 255 and roilRight[i, j][1] == 255 and roilRight[i, j][2] == 255: ? ? ? ? ? ? ? ? itemp = itemp + 1 ? ? ? ? vcol.append(itemp) ? ? ? ? itemp = 0 ? ? print(vcol) ? ? for i in range(vcol.__len__()): ? ? ? ? if vcol[i] == 0 and vcol[i + 1] > 0: ? ? ? ? ? ? cv.line(roilRight, (0, i), (cols, i), (0, 0, 255), 2) ? ? ? ? ? ? cv.line(src, (src_cols - cols, i + a ), (src_cols, i + a), (0, 0, 255), 2) ? ? ? ? ? ? right_vcol.append(i + a) ? ? cv.imshow("src", src) ? ? return right_vcol ? #計算左邊小黑格的位置 def sure_Left(roilLeft,src,a): ? ? rows, cols, channels = roilLeft.shape ? ? print(rows, cols) ? ? itemp = 0 ? ? vcol = [] ? ? left_vcol = [] ? ? for i in range(0, rows): ? ? ? ? for j in range(0, cols): ? ? ? ? ? ? if roilLeft[i, j][0] == 255 and roilLeft[i, j][1] == 255 and roilLeft[i, j][2] == 255: ? ? ? ? ? ? ? ? itemp = itemp + 1 ? ? ? ? vcol.append(itemp) ? ? ? ? itemp = 0 ? ? for i in range(vcol.__len__()): ? ? ? ? if vcol[i] == 0 and vcol[i + 1] > 0: ? ? ? ? ? ? cv.line(roilLeft, (0, i), (cols, i), (0, 0, 255), 2) ? ? ? ? ? ? cv.line(src, (16, i + a), (16 + cols, i + a), (0, 0, 255), 2) ? ? ? ? ? ? left_vcol.append(i + a) ? ? cv.imshow("src", src) ? ? return left_vcol ? # 確定頂部小黑格的位置 def sure_Top(roilTop,src,a): ? ? rows, cols, channels = roilTop.shape ? ? src_rows, src_cols, src_channels = src.shape ? ? itemp = 0 ? ? vcol = [] ? ? top_vcol = [] ? ? for i in range(0, cols): ? ? ? ? for j in range(0, rows): ? ? ? ? ? ? if roilTop[j, i][0] == 255 and roilTop[j, i][1] == 255 and roilTop[j, i][2] == 255: ? ? ? ? ? ? ? ? itemp = itemp + 1 ? ? ? ? vcol.append(itemp) ? ? ? ? itemp = 0 ? ? for i in range(vcol.__len__()): ? ? ? ? if vcol[i] == 0 and vcol[i + 1] > 0: ? ? ? ? ? ? cv.line(roilTop, (i, 0), (i, rows), (0, 0, 255), 2) ? ? ? ? ? ? cv.line(src, (i + a, 12), (a + i,48), (0, 0, 255), 2) ? ? ? ? ? ? top_vcol.append(i + a) ? ? cv.imshow("src", src) ? ? return top_vcol ? #獲取原圖位置 ? ? bottom_vcol = sure_bottom(roilBottom,temp,location_LB[0] -location_LB[2]) ? ? right_vcol = sure_Right(roilRight,temp,location_RT[1] - location_RT[2]) ? ? left_vcol = sure_Left(roilLeft,temp,location_LT[1] - location_LT[2]) ? ? top_vcol = sure_Top(roilTop,temp,location_LT[0] - location_LT[2])
五、在原圖中全出每個小格子。在此之前要將圖片做個預(yù)處理,腐蝕和膨脹。這個程序先圈出來的是答題區(qū)域的小格子,所以i和j 的范圍分別是底部格子數(shù)和右邊格子數(shù)。
先將每個格子的區(qū)域找出來,然后遍歷這個格子的全部像素,判斷是否為0 ,如果為0,那么就isum+1,最后遍歷結(jié)束,看看isum的個數(shù)有沒有達到總像素個數(shù)的10%,如果達到,那么就可以判定這個區(qū)域是被涂過的,記錄這個區(qū)域?qū)?yīng)的題號和選項,以備后面計分。
dst = sure_if_fill(temp) ? ? for i in range(0,20): ? ? ? ? for j in range(10,26): ? ? ? ? ? ? rect = dst[bottom_vcol[i]:bottom_vcol[i] + 9,right_vcol[i]:right_vcol[j] + 3] ? ? ? ? ? ? rect_up = (bottom_vcol[i],right_vcol[j]) ? ? ? ? ? ? rect_down = (bottom_vcol[i] + 9,right_vcol[j] + 3) ? ? ? ? ? ? ? # 判斷ROI區(qū)域是否被填充 ? ? ? ? ? ? isum = 0 ? ? ? ? ? ? for ii in range(rect.shape[0]): ? ? ? ? ? ? ? ? for jj in range(rect.shape[1]): ? ? ? ? ? ? ? ? ? ? if dst[ii,jj] == 0: ? ? ? ? ? ? ? ? ? ? ? ? isum = isum + 1 ? ? ? ? ? ? # if isum > 0.1 * rect.shape[0] * rect.shape[1]: ? ? ? ? ? ? cv.rectangle(temp,rect_down,rect_up,(0,255,0),2) ? ? ? ? ? ? isum = 0 ? ? cv.imshow("dst",temp) ? ? #檢查空格是否被填充 def sure_if_fill(image): ? ? temp = image.copy() ? ? gray = cv.cvtColor(temp,cv.COLOR_BGR2GRAY) ? ? ret,binary = cv.threshold(gray,100,255,cv.THRESH_BINARY) ? ? kernel = cv.getStructuringElement(cv.MORPH_RECT,(3,3)) ? ? dst = cv.erode(binary,kernel= kernel) ? ? cv.imshow("dst",dst) ? ? return dst
這是腐蝕后的圖片
這是圈出所有答題區(qū)域的圖片
原文鏈接:https://blog.csdn.net/qq_33531400/article/details/85690148
相關(guān)推薦
- 2022-05-13 kafka-server-stop.sh關(guān)閉Kafka失敗
- 2022-10-19 Beego?AutoRouter工作原理解析_Golang
- 2023-09-12 Spring Boot自動配置介紹
- 2022-04-27 詳解linux里的backlog參數(shù)_Linux
- 2022-07-21 配置nacos持久化
- 2022-06-22 Git的基礎(chǔ)文件操作初始化查看添加提交示例教程_其它綜合
- 2022-08-03 goFrame的隊列g(shù)queue對比channel使用詳解_Golang
- 2022-10-28 C++異步操作future和aysnc與function和bind_C 語言
- 最近更新
-
- window11 系統(tǒng)安裝 yarn
- 超詳細win安裝深度學習環(huán)境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎(chǔ)操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區(qū)別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權(quán)
- redisson分布式鎖中waittime的設(shè)
- maven:解決release錯誤:Artif
- restTemplate使用總結(jié)
- Spring Security之安全異常處理
- MybatisPlus優(yōu)雅實現(xiàn)加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務(wù)發(fā)現(xiàn)-Nac
- Spring Security之基于HttpR
- Redis 底層數(shù)據(jù)結(jié)構(gòu)-簡單動態(tài)字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應(yīng)用詳解
- 聊聊消息隊列,發(fā)送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支