日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > python >内容正文

python

Python+Opencv实现自动化阅卷

發布時間:2023/12/15 python 33 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Python+Opencv实现自动化阅卷 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

目錄

    • 一、什么是自動化閱卷/網上閱卷?
    • 二、自動化閱卷有哪些優勢?
    • 三、 自動化閱卷的實現流程
    • 四、自動化閱卷代碼實戰
    • 五、自動化閱卷可視化及分析
    • 六、自動化閱卷效果展示
    • 七、問題探討
    • 參考資料
    • 注意事項

??在我們的日常生活中有很多自動化閱卷的需求,比如我們的期中考試、期末考試、中考、高考、四級、六級等等。總而言之,現實場景中我們有很多的考試,但是每一次考完試后,焦慮的不僅僅是學生,還有老師!老師們需要夜以繼日的去閱卷,盡快得出學生的考試成績,這是一個費人費力但又是剛需的一個任務。慶幸的是當前這個任務基本上已經被自動化啦,本文的任務就是來理解這其中的奧秘!

一、什么是自動化閱卷/網上閱卷?

百度的定義-網上閱卷全稱是網上閱卷系統,是指以計算機網絡技術和電子掃描技術為依托,實現客觀題自動閱卷,主觀題網上評卷的一種現代計算機系統。
這里面有幾點需要進行說明:

  • 當前的網上閱卷系統還是部分自動化的,即智能實現客觀題目的自動閱卷,最典型的就是選擇題!而主觀題還是需要進行網上閱卷的。
  • 當前的網上閱卷系統主要依托于兩個儀器,計算機+電子掃描儀,電子掃描儀的任務是將學生的答題卡錄入到電腦中去,充當了攝像機的功能。

二、自動化閱卷有哪些優勢?

??正如文章開頭所講,現實的場景中我們有各種各樣的考試,老師們需要進行大量的閱卷工作,因而解決這個問題具有重大的意義。

  • 高效-相比于傳統的教師閱卷而言,自動化閱卷系統的速度更快,這是毋庸置疑的,因為人不可能一下子記住所有的選擇題答案,需要不斷的查看標準答案,這很費時;
  • 魯棒-相比于傳統的教師閱卷而言,自動化閱卷系統更加理智,不會夾雜一些情感信息,同時不會受到外界條件的干擾,不會因為勞累等原因給出錯誤的判斷,即使是在復雜的干擾環境下,仍然能得到正確的結果;
  • 準確-相比于傳統的教師閱卷而言,自動化閱卷系統能夠獲得更加準確的結果,只要設備正常,答題卡正常,就能準確的得出答案;
  • 省時省力-自動化閱卷系統可以節省大量的人力和物力,這是毋庸置疑的!
  • 三、 自動化閱卷的實現流程

    步驟1-檢測圖片中的答題卡;
    步驟2-采取透視變換取得答題卡自上而下的俯視圖.;
    步驟3-從第二步結果中獲取泡泡集(例如可能的答案選項);
    步驟4-按行進行排序處理;
    步驟5-確認每行用戶填寫的答案
    步驟6-查找正確的答案,判斷用戶選擇是否正確
    步驟7-遍歷所有的問題,重復以上的步驟。

    四、自動化閱卷代碼實戰

    # coding=utf-8 # 導入一些python包 from imutils.perspective import four_point_transform from imutils import contours import numpy as np import argparse import imutils import cv2# 配置一些需要改變的參數 ap = argparse.ArgumentParser() ap.add_argument("-i", "--image", required=True,help="path to the input image") args = vars(ap.parse_args())# 定義將問題編號映射到正確答案的答案鍵,即正確的問題編號 ANSWER_KEY = {0: 1, 1: 4, 2: 0, 3: 3, 4: 1}# 讀取輸入圖片 image = cv2.imread(args["image"]) # 輸入圖片灰度化 gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) # 進行高斯濾波 blurred = cv2.GaussianBlur(gray, (5, 5), 0) # 進行邊緣檢測處理 edged = cv2.Canny(blurred, 75, 200)# 在邊緣圖像中發現輪廓 cnts = cv2.findContours(edged.copy(), cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE) cnts = imutils.grab_contours(cnts) 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)# 如果我們在近似輪廓中發現4個點,表明我們發現了答題卡if len(approx) == 4:docCnt = approxbreak# 將變換關系應用到原始的輸入圖像和灰度圖像中,從而獲得一個答題卡的鳥瞰圖 paper = four_point_transform(image, docCnt.reshape(4, 2)) warped = four_point_transform(gray, docCnt.reshape(4, 2))# 在warped圖像上面使用Otsu方法進行閾值分割 thresh = cv2.threshold(warped, 0, 255,cv2.THRESH_BINARY_INV | cv2.THRESH_OTSU)[1]# 在二值圖像中尋找輪廓 cnts = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE) cnts = imutils.grab_contours(cnts) questionCnts = []# 遍歷每一個輪廓 for c in cnts:# 計算輪廓的BB,并根據BB獲得橫縱比(x, y, w, h) = cv2.boundingRect(c)ar = w / float(h)# 為了將輪廓標記為問題,區域應足夠寬、足夠高,且長寬比約等于1# 即選擇滿足條件的結果if w >= 20 and h >= 20 and ar >= 0.9 and ar <= 1.1:questionCnts.append(c)# 對問題輪廓進行排序 questionCnts = contours.sort_contours(questionCnts,method="top-to-bottom")[0] correct = 0# 對于每一個問題而言,有5個可能的答案,遍歷每一個可能的答案 for (q, i) in enumerate(np.arange(0, len(questionCnts), 5)):# 對每一個問題的5個答案進行排序cnts = contours.sort_contours(questionCnts[i:i + 5])[0]bubbled = None# 循環遍歷每一個輪廓for (j, c) in enumerate(cnts):# 為每行的答案創建一個mask模板mask = np.zeros(thresh.shape, dtype="uint8")cv2.drawContours(mask, [c], -1, 255, -1)# 將mask應用到閾值圖像中,然后計算氣泡區域中的非零像素數mask = cv2.bitwise_and(thresh, thresh, mask=mask)total = cv2.countNonZero(mask)# 如果它大于該非0像素,我們認為它是答案if bubbled is None or total > bubbled[0]:bubbled = (total, j)# 初始化輪廓顏色和正確答案的索引color = (0, 0, 255)k = ANSWER_KEY[q]# 將用戶的答案和標準答案進行對比,進行統計if k == bubbled[1]:color = (0, 255, 0)correct += 1# 在圖像中繪制結果cv2.drawContours(paper, [cnts[k]], -1, color, 3)# 計算正確率并打印得分 score = (correct / 5.0) * 100 print("[INFO] score: {:.2f}%".format(score)) # 顯示原始圖片和測試后的結果并顯示得分 cv2.putText(paper, "{:.2f}%".format(score), (10, 30),cv2.FONT_HERSHEY_SIMPLEX, 0.9, (0, 0, 255), 2) cv2.imshow("Original", image) cv2.imshow("Exam", paper) cv2.waitKey(0)

    五、自動化閱卷可視化及分析


    ??上圖展示了整個算法運行的一些中間輸出結果,其中第1行第1列表示輸入的原圖,對應于程序中的image;第1行第2列表示灰度化的結果,對應于程序中的gray;第1行第3列表示邊緣檢測的結果,對應于程序中的edged;第1行第4列是對原圖中所有檢測到的輪廓的可視化,我們可以觀察到基本上所有的答題區域都可以檢測到;第2行第1列表示的使用坐標變換后的原圖,即從原圖中裁剪出來的答題卡圖片,對應于程序中的paper;第2行第2列表示的灰度化的圖片的變換結果,對應于程序中的warped;第2行第3列表示的在paper中檢測到的所有輪廓,我們需要的答題區域都可以檢測出來,答過的結果和沒有答過的結果差距較大;第2行第2列表示最終的輸出結果,可以看出該答題卡的答案和正確的答案完全符合,因此得100分。


    ??上圖展示了整個mask的生成過程,使用.gif動態圖進行展示,我們可以觀察到整個mask從上到下,從左到右遍歷所有的答題位置,并判斷當前的結果是否和標準答案相同。

    # 添加中間結果可視化 # coding=utf-8 # 導入一些python包 from imutils.perspective import four_point_transform from imutils import contours import numpy as np import argparse import imageio import imutils import cv2# 配置一些需要改變的參數 ap = argparse.ArgumentParser() ap.add_argument("-i", "--image", required=True,help="path to the input image") args = vars(ap.parse_args())# 定義將問題編號映射到正確答案的答案鍵,即正確的問題編號 ANSWER_KEY = {0: 1, 1: 4, 2: 0, 3: 3, 4: 1}# 讀取輸入圖片 image = cv2.imread(args["image"]) image1 = cv2.imread(args["image"]) # 輸入圖片灰度化 gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) cv2.imwrite("vis\gray.png", gray) # 進行高斯濾波 blurred = cv2.GaussianBlur(gray, (5, 5), 0) cv2.imwrite("vis\blurred.png", blurred) # 進行邊緣檢測處理 edged = cv2.Canny(blurred, 75, 200) cv2.imwrite("vis\edged.png", edged)ret,binary = cv2.threshold(gray.copy(), 127, 255, cv2.THRESH_BINARY) contour, hierarchy = cv2.findContours(binary, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE) cv2.drawContours(image1,contour,-1,(0,255,0),3) cv2.imwrite("vis\Contours1.png", image1)# 在邊緣圖像中發現輪廓 cnts = cv2.findContours(edged.copy(), cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE) cnts = imutils.grab_contours(cnts) 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)# 如果我們在近似輪廓中發現4個點,表明我們發現了答題卡if len(approx) == 4:docCnt = approxbreak# 將變換關系應用到原始的輸入圖像和灰度圖像中,從而獲得一個答題卡的鳥瞰圖 paper = four_point_transform(image, docCnt.reshape(4, 2)) paper1 = four_point_transform(image, docCnt.reshape(4, 2)) cv2.imwrite("vis\paper1.png", paper1) warped = four_point_transform(gray, docCnt.reshape(4, 2)) cv2.imwrite("vis\warped.png", warped)# 在warped圖像上面使用Otsu方法進行閾值分割 thresh = cv2.threshold(warped, 0, 255,cv2.THRESH_BINARY_INV | cv2.THRESH_OTSU)[1]# 在二值圖像中尋找輪廓 cnts = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE) cnts = imutils.grab_contours(cnts) questionCnts = []ret1,binary1 = cv2.threshold(warped.copy(), 127, 255, cv2.THRESH_BINARY) contour, hierarchy = cv2.findContours(binary1, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE) cv2.drawContours(paper1,contour,-1,(255,0,0),3) cv2.imwrite("vis\Contours2.png", paper1)# 遍歷每一個輪廓 for c in cnts:# 計算輪廓的BB,并根據BB獲得橫縱比(x, y, w, h) = cv2.boundingRect(c)ar = w / float(h)# 為了將輪廓標記為問題,區域應足夠寬、足夠高,且長寬比約等于1# 即選擇滿足條件的結果if w >= 20 and h >= 20 and ar >= 0.9 and ar <= 1.1:questionCnts.append(c)# 對問題輪廓進行排序 questionCnts = contours.sort_contours(questionCnts,method="top-to-bottom")[0] correct = 0# 對于每一個問題而言,有5個可能的答案,遍歷每一個可能的答案 img_mask = [] for (q, i) in enumerate(np.arange(0, len(questionCnts), 5)):# 對每一個問題的5個答案進行排序cnts = contours.sort_contours(questionCnts[i:i + 5])[0]bubbled = None# 循環遍歷每一個輪廓for (j, c) in enumerate(cnts):# 為每行的答案創建一個mask模板mask = np.zeros(thresh.shape, dtype="uint8")cv2.drawContours(mask, [c], -1, 255, -1)# 將mask應用到閾值圖像中,然后計算氣泡區域中的非零像素數mask = cv2.bitwise_and(thresh, thresh, mask=mask)total = cv2.countNonZero(mask)img_mask.append(mask)# 如果它大于該非0像素,我們認為它是答案if bubbled is None or total > bubbled[0]:bubbled = (total, j)# 初始化輪廓顏色和正確答案的索引color = (0, 0, 255)k = ANSWER_KEY[q]# 將用戶的答案和標準答案進行對比,進行統計if k == bubbled[1]:color = (0, 255, 0)correct += 1# 在圖像中繪制結果cv2.drawContours(paper, [cnts[k]], -1, color, 3)# 計算正確率并打印得分 score = (correct / 5.0) * 100 print("[INFO] score: {:.2f}%".format(score)) # 顯示原始圖片和測試后的結果并顯示得分 cv2.putText(paper, "{:.2f}%".format(score), (10, 30),cv2.FONT_HERSHEY_SIMPLEX, 0.9, (0, 0, 255), 2) cv2.imshow("Original", image) cv2.imwrite("vis\paper.png", paper) cv2.imshow("Exam", paper) imageio.mimsave("vis\mask.gif", img_mask) cv2.waitKey(0)

    六、自動化閱卷效果展示






    ??上面幾幅圖展示了整個算法的實際測試效果,整體來看,整個算法的準確率高,基本可以滿足實時場景的需要。

    七、問題探討

    ??盡管從上面的測試結果來看該算法比較魯棒、可靠,但是在現實場景中會遇到一些其它的問題,比如

    1、空白答題卡

    ??對于這種情況而言,當計算cv2.countNonZero時,我們可以設置一個最小的閾值(108行),如果這個值足夠大,我們就可以將泡泡標記為"已填涂".相反,如果total太小,我們就跳過那個泡泡.如果直到行結尾,沒有泡泡具有足夠大的閾值計數,就將該問題標記為測試者"跳過".
    2、每個問題填寫了多個答案

    ??對于問題2而言,我們采用類似的方法,我們追蹤一個問題具有total的泡泡數量是否超過預定義值,我們將其標記為無效問題或者是錯誤答案。
    3、對于復雜的答題卡該如何處理呢?

    ??上圖僅僅是答題卡的一種,對于答題卡而言,基本每一個考試會有一種類型,對于這些類型我們不可能分別設計一個算法吧,這是基于深度學習的方法就具有獨特的優勢啦。具體的效果大家可以去實現并測試一些歐。

    參考資料

    1、參考博客

    注意事項

    [1] 該博客是本人原創博客,如果您對該博客感興趣,想要轉載該博客,請與我聯系(qq郵箱:1575262785@qq.com),我會在第一時間回復大家,謝謝大家的關注。
    [2] 由于個人能力有限,該博客可能存在很多的問題,希望大家能夠提出改進意見。
    [3] 如果您在閱讀本博客時遇到不理解的地方,希望您可以聯系我,我會及時的回復您,和您交流想法和意見,謝謝。
    [4] 本文測試的圖片可以通過該鏈接進行下載。測試圖片鏈接- 提取碼:mj3s
    [5] 本人業余時間承接各種本科畢設設計和各種小項目,包括圖像處理(數據挖掘、機器學習、深度學習等)、matlab仿真、python算法及仿真等,有需要的請加QQ:1575262785詳聊!!!

    創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎

    總結

    以上是生活随笔為你收集整理的Python+Opencv实现自动化阅卷的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。