<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          啪啪,幾步搞定!用Python實(shí)現(xiàn)答題卡識(shí)別!

          共 2732字,需瀏覽 6分鐘

           ·

          2022-04-14 02:47

          來(lái)源丨h(huán)ttps://blog.csdn.net/qq_44805233?type=blog


          答題卡素材圖片:

          思路

          1. 讀入圖片,做一些預(yù)處理工作。
          2. 進(jìn)行輪廓檢測(cè),然后找到該圖片最大的輪廓,就是答題卡部分。
          3. 進(jìn)行透視變換,以去除除答題卡外的多余部分,并且可以對(duì)答題卡進(jìn)行校正。
          4. 再次檢測(cè)輪廓,定位每個(gè)選項(xiàng)。
          5. 對(duì)選項(xiàng)圓圈先按照豎坐標(biāo)排序,再按照行坐標(biāo)排序,這樣就從左到右從上到下的獲得了每個(gè)選項(xiàng)輪廓。
          6. 對(duì)每個(gè)選項(xiàng)輪廓進(jìn)行檢查,如果某個(gè)選項(xiàng)輪廓中的白色點(diǎn)多,說(shuō)明該選項(xiàng)被選中,否則就是沒(méi)被選上。細(xì)節(jié)部分看過(guò)程:

          1、預(yù)處理(去噪,灰度,二值化)

          img?=?cv2.imread("1.png",1)
          #高斯去噪
          img_gs?=?cv2.GaussianBlur(img,[5,5],0)
          #?轉(zhuǎn)灰度
          img_gray?=?cv2.cvtColor(img_gs,cv2.COLOR_BGR2GRAY)
          #?自適應(yīng)二值化
          _,binary_img?=?cv2.threshold(img_gray,0,255,cv2.THRESH_OTSU|cv2.THRESH_BINARY)

          注:cv2.THRESH_OTSU|cv2.THRESH_BINARY,該參數(shù)指的是自適應(yīng)閾值+反二值化,做自適應(yīng)閾值的時(shí)候閾值要設(shè)置為0

          2、輪廓檢測(cè)

          #?找輪廓
          contours,?hierarchy?=?cv2.findContours(binary_img,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_NONE)
          #?按照輪廓的面積從大到小排序
          cnts?=?sorted(contours,key?=?cv2.contourArea,reverse=True)
          #?畫(huà)輪廓
          draw_img?=?cv2.drawContours(img.copy(),cnts[0],-1,(0,255,255),2)

          注:findContours函數(shù),傳入的圖像應(yīng)該是二值圖像,cv2.RETR_EXTERNAL指的是只檢測(cè)外部輪廓,cv2.CHAIN_APPROX_NONE指的返回輪廓上的所有點(diǎn)。

          #?輪廓近似
          #?閾值,一般為輪廓長(zhǎng)度的2%
          alpha?=?0.02*cv2.arcLength(cnts[0],True)
          approxCurve?=?cv2.approxPolyDP(cnts[0],alpha,True)
          draw_img?=?cv2.drawContours(img.copy(),[approxCurve],-1,(255,0,0),2)

          這里做輪廓近似的目的是,之前檢測(cè)到的輪廓看似是一個(gè)多邊形,其實(shí)本質(zhì)上是只是點(diǎn)集。

          cv2.approxPolyDP(contour,epsilon,True),多邊形逼近,第一個(gè)參數(shù)是點(diǎn)集,第二個(gè)參數(shù)是精度(原始輪廓的邊界點(diǎn)與擬合多邊形之間的最大距離),第三個(gè)參數(shù)指新產(chǎn)生的輪廓是否需要閉合,返回值approxCurve為多邊形的點(diǎn)集(按照逆時(shí)針排序)。與該函數(shù)類(lèi)似的函數(shù)還有cv2.boundingRect(矩形包圍框)cv2.minAreaRect(最小包圍矩形框),cv2.minEnclosingCircle(最小包圍圓形)cv2.filtEllipse(最優(yōu)擬合橢圓)cv2.filtLine(最優(yōu)擬合直線),cv2.minEnclosingTriangle(最小外包三角形)

          3、透視變換

          #透視變換
          #?矩形的四個(gè)頂點(diǎn)為approxCurve[0][0],approxCurve[1][0],approxCurve[2][0],approxCurve[3][0]
          #?分別表示矩形的TL,BL,BR,TR四個(gè)點(diǎn)
          a1?=?list(approxCurve[0][0])
          a2?=?list(approxCurve[1][0])
          a3?=?list(approxCurve[2][0])
          a4?=?list(approxCurve[3][0])
          #?原始矩陣
          mat1?=?np.array([a1,a2,a3,a4],dtype?=?np.float32)

          #?計(jì)算矩形的w和h
          w1?=?int(np.sqrt((a1[0]-a4[0])**2+(a1[1]-a4[1])**2))
          w2?=?int(np.sqrt((a2[0]-a3[0])**2+(a2[1]-a3[1])**2))
          h1?=?int(np.sqrt((a1[0]-a2[0])**2+(a1[1]-a2[1])**2))
          h2?=?int(np.sqrt((a3[0]-a4[0])**2+(a3[1]-a4[1])**2))
          w,h=max(w1,w2),max(h1,h2)
          #?計(jì)算透視變換后的坐標(biāo)
          new_a1?=?[0,0]
          new_a2?=?[0,h]
          new_a3?=?[w,h]
          new_a4?=?[w,0]
          #?目標(biāo)矩陣
          mat2?=?np.array([new_a1,new_a2,new_a3,new_a4],dtype?=?np.float32)
          #?透視變換矩陣
          mat?=?cv2.getPerspectiveTransform(mat1,mat2)
          #?進(jìn)行透視變換
          res?=?cv2.warpPerspective(img,mat,(w,h))
          imshow((res))

          透視變換的計(jì)算步驟:

          1. 首先獲取原圖多邊形的四個(gè)頂點(diǎn),注意頂點(diǎn)順序。
          2. 然后構(gòu)造原始頂點(diǎn)矩陣。
          3. 計(jì)算矩形長(zhǎng)寬,構(gòu)造變換后的目標(biāo)矩陣。
          4. 獲取原始矩陣到目標(biāo)矩陣的透視變換矩陣
          5. 進(jìn)行透視變換

          4、輪廓檢測(cè),檢測(cè)每個(gè)選項(xiàng)

          res_gray?=?cv2.cvtColor(res,cv2.COLOR_BGR2GRAY)
          _,binary_res?=?cv2.threshold(res_gray,0,255,cv2.THRESH_OTSU|cv2.THRESH_BINARY_INV)
          contours?=?cv2.findContours(binary_res,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_NONE)[0]
          dst?=?cv2.drawContours(res.copy(),contours,-1,(0,0,255),1)
          imshow(dst)

          篩選選項(xiàng)輪廓

          #?挑選合適的輪廓
          def?check(contours):
          ????ans?=?[]
          ????for?i?in?contours:
          ????????area?=?float(cv2.contourArea(i))
          ????????length?=?float(cv2.arcLength(i,True))
          ????????if?area<=0?or?length<=0:
          ????????????continue
          ????????if?area/length?>7.05?and?area/length<10.5:
          ????????????ans.append(i)
          ????return?ans
          ans_contours?=?check(contours)
          dst_new?=?cv2.drawContours(res.copy(),ans_contours,-1,(0,255,255),3??)
          imshow(dst_new)

          5、畫(huà)輪廓的外接圓,排序,定位每個(gè)選項(xiàng)

          #?遍歷每一個(gè)圓形輪廓,畫(huà)外接圓
          circle?=?[]
          for?i?in?ans_contours:
          ????(x,y),r?=?cv2.minEnclosingCircle(i)
          ????center?=?(int(x),int(y))
          ????r?=?int(r)
          ????circle.append((center,r))
          #?按照外接圓的水平坐標(biāo)排序center[1],也就是圓心的高度h,或者y坐標(biāo)
          circle.sort(key?=?lambda?x:x[0][1])
          A?=?[]
          for?i?in?range(1,6):
          ????now?=?circle[(i-1)*5:i*5]
          ????now.sort(key?=?lambda?x:x[0][0])
          ????A.extend(now)

          每個(gè)選項(xiàng)按照?qǐng)A心從左到右,從上到下的順序保存在了A中

          6、選項(xiàng)檢測(cè)

          思路:對(duì)于A中的每個(gè)選項(xiàng)圓,計(jì)算它有所覆蓋的坐標(biāo),然后判斷這些坐標(biāo)在二值圖像中對(duì)應(yīng)的值,統(tǒng)計(jì)白色點(diǎn)的個(gè)數(shù), 如果白色點(diǎn)所占的比例比較大的話,說(shuō)明該選項(xiàng)被選中。

          def?dots_distance(dot1,dot2):
          ????#計(jì)算二維空間中兩個(gè)點(diǎn)的距離
          ????return?((dot1[0]-dot2[0])**2+(dot1[1]-dot2[1])**2)**0.5
          def?count_dots(center,radius):
          ????#輸入圓的中心點(diǎn)與半徑,返回圓內(nèi)所有的坐標(biāo)
          ????dots?=?[]
          ????for?i?in?range(-radius,radius+1):
          ????????for?j?in?range(-radius,radius+1):
          ????????????dot2?=?(center[0]+i,center[1]+j)
          ????????????if?dots_distance(center,dot2)?<=?radius:
          ????????????????dots.append(dot2)
          ????return?dots
          ?
          da?=?[]
          for?i?in?A:
          ????dots?=?count_dots(i[0],i[1])
          ????all_dots?=?len(dots)
          ????whilt_dots?=?0
          ????for?j?in?dots:
          ????????if?binary_res[j[1]][j[0]]?==?255:
          ????????????whilt_dots?=?whilt_dots+1
          ????if?whilt_dots/all_dots>=0.4:
          ????????da.append(1)
          ????else:
          ????????da.append(0)
          da?=?np.array(da)
          da?=?np.reshape(da,(5,5))

          這樣每個(gè)答題卡就轉(zhuǎn)換成了一個(gè)二維數(shù)組,接下來(lái)在做一些簡(jiǎn)單的收尾工作就可以了。


          萬(wàn)水千山總是情,點(diǎn)個(gè)????行不行

          Python機(jī)器人公眾號(hào)正式上線了




          推薦閱讀:

          入門(mén):?最全的零基礎(chǔ)學(xué)Python的問(wèn)題? |?零基礎(chǔ)學(xué)了8個(gè)月的Python??|?實(shí)戰(zhàn)項(xiàng)目?|學(xué)Python就是這條捷徑


          干貨:爬取豆瓣短評(píng),電影《后來(lái)的我們》?|?38年NBA最佳球員分析?|? ?從萬(wàn)眾期待到口碑撲街!唐探3令人失望? |?笑看新倚天屠龍記?|?燈謎答題王?|用Python做個(gè)海量小姐姐素描圖?|碟中諜這么火,我用機(jī)器學(xué)習(xí)做個(gè)迷你推薦系統(tǒng)電影


          趣味:彈球游戲? |?九宮格? |?漂亮的花?|?兩百行Python《天天酷跑》游戲!


          AI:?會(huì)做詩(shī)的機(jī)器人?|?給圖片上色?|?預(yù)測(cè)收入?|?碟中諜這么火,我用機(jī)器學(xué)習(xí)做個(gè)迷你推薦系統(tǒng)電影


          小工具:?Pdf轉(zhuǎn)Word,輕松搞定表格和水印!?|?一鍵把html網(wǎng)頁(yè)保存為pdf!|??再見(jiàn)PDF提取收費(fèi)!?|?用90行代碼打造最強(qiáng)PDF轉(zhuǎn)換器,word、PPT、excel、markdown、html一鍵轉(zhuǎn)換?|?制作一款釘釘?shù)蛢r(jià)機(jī)票提示器!?|60行代碼做了一個(gè)語(yǔ)音壁紙切換器天天看小姐姐!


          年度爆款文案

          點(diǎn)閱讀原文,看原創(chuàng)200個(gè)趣味案例!

          瀏覽 39
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  亚洲高清无码一区 | 日韩一级操逼大片 | 青青草人妻 | 操逼视频在线看 | 成人黄色免费 |