【opencv】答题卡判分实验

实验环境: anaconda、jupyter notebook

实验用的包:numpy、matplotlib、opencv

实验的目的还是以熟悉图像的透视变换、轮廓特征提取为主要目的

关于如何判断答题卡被选项:通过几个覆盖备选项的掩膜与原二值图像想与,最终整个图像中白色像素点多的就是被选择的项

根据我的亲身体验cv2.bitwise_and(src1,src2)会把src1中与src2不同的点置为0!!!我排查了一下午,可恶啊!!!!

一、实验使用的图像

实验图片

一、引入包

import cv2
import numpy as np
import matplotlib.pyplot as plt

二、读入图像预处理为二值图片

answer_sheet = cv2.imread('answer_sheet.png')
# 灰度图
answer_sheet_gray = cv2.cvtColor(answer_sheet, cv2.COLOR_BGR2GRAY)

# 二值图
answer_sheet_bin = cv2.threshold(answer_sheet_gray, 127,255,cv2.THRESH_BINARY)[1]
answer_sheet_bin_inv = cv2.threshold(answer_sheet_gray, 127,255,cv2.THRESH_BINARY_INV)[1]

plt.imshow(answer_sheet_bin, 'gray')
plt.show()

二值图

三、获取答题卡轮廓

# 获取轮廓
binary, answer_sheet_contours, hierarchy = cv2.findContours(answer_sheet_bin, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)

answer_sheet_contour = None
answer_sheet_contour_length = 0
for c in answer_sheet_contours:
    # 做近似
    epsilon = 0.1 * cv2.arcLength(c, True)
    approx = cv2.approxPolyDP(c, epsilon, True)
    # 求周长
    answer_sheet_contour_length_temp = cv2.arcLength(approx,True)
    # 找周长最大的轮廓
    if answer_sheet_contour_length_temp > answer_sheet_contour_length:
        answer_sheet_contour_length = answer_sheet_contour_length_temp
        answer_sheet_contour = approx

# 展示轮廓
answer_sheet_temp = answer_sheet.copy()
res = cv2.drawContours(answer_sheet_temp, [answer_sheet_contour], -1, (0,0,255),3)

plt.imshow(cv2.cvtColor(res, cv2.COLOR_BGR2RGB))
plt.show()

轮廓展示

四、构建透视变换的两个矩阵

# 取到四个点
answer_sheet_contour_deal = np.float32(answer_sheet_contour[:,0,:])
A,B,C,D = answer_sheet_contour_deal 


# 在原始图像上画轮廓
answer_sheet_temp = answer_sheet.copy()
answer_sheet_contour_deal_temp = np.array([[np.int32(A)],[np.int32(B)],[np.int32(C)],[np.int32(D)]])
cv2.drawContours(answer_sheet_temp, [answer_sheet_contour_deal_temp], -1, (0,255,0),10)
plt.imshow(cv2.cvtColor(answer_sheet_temp, cv2.COLOR_BGR2RGB))
plt.show()

W1 = np.sqrt((A[0] - B[0]) ** 2 + (A[1] -B[1]) ** 2)
W2 = np.sqrt((C[0] -D[0]) ** 2 + (C[1] -D[1]) ** 2)
W = max(int(W1), int(W2))

H1 = np.sqrt((A[0] - C[0]) ** 2 + (A[1] -C[1]) ** 2)
H2 = np.sqrt((B[0] -D[0]) ** 2 + (B[1] -D[1]) ** 2)
H = max(int(H1), int(H2))

# 目标坐标
dest = np.array([
    [0,0],
    [0,H],
    [W,H],
    [W,0]
], dtype=np.float32)


# 在原始图像上画轮廓
answer_sheet_temp = answer_sheet.copy()
answer_sheet_contour_deal_temp = np.array([[np.int32(dest[0])],[np.int32(dest[1])],[np.int32(dest[2])],[np.int32(dest[3])]])
cv2.drawContours(answer_sheet_temp, [answer_sheet_contour_deal_temp], -1, (0,255,0),10)
plt.imshow(cv2.cvtColor(answer_sheet_temp, cv2.COLOR_BGR2RGB))
plt.show()

透视变换矩阵

五、透视变换

# 透视变换
M = cv2.getPerspectiveTransform(answer_sheet_contour_deal, dest)
answer_sheet_warped = cv2.warpPerspective(answer_sheet_gray, M, (int(W),int(H)))

# 转为二值图
answer_sheet_warped_bin = cv2.threshold(answer_sheet_warped, 0, 255, cv2.THRESH_BINARY_INV|cv2.THRESH_OTSU)[1]

plt.imshow(answer_sheet_warped_bin, cmap='gray')
plt.show()

透视变换

六、获取每个答案的轮廓

# 获取每个选项的外轮廓
cnts = cv2.findContours(answer_sheet_warped_bin.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)[1]

questionCnts = []
for c in cnts:
    (x, y, w, h) = cv2.boundingRect(c)
    ar = w / float(h)
    # 筛选
    if w >= 20 and h >= 20 and ar >= 0.9 and ar <=1.1:
        yt = np.sum(c[:,:,1])
        xt = np.sum(c[:,:,0])
        questionCnts.append(c)

# 行排序
for i in range(len(questionCnts) - 1):
    for j in range(len(questionCnts) - 1 - i):
        if questionCnts[j][0][0][1] > questionCnts[j + 1][0][0][1]:
            questionCnts[j],questionCnts[j + 1] = questionCnts[j + 1],questionCnts[j]
# 列排序
for i in range(5):
    temp_array = questionCnts[i * 5 : (i + 1) * 5]
    for j in range(len(temp_array) - 1):
        for k in range(len(temp_array) - 1 - j):
            if temp_array[k][0][0][0] > temp_array[k + 1][0][0][0]:
                temp_array[k],temp_array[k + 1] = temp_array[k + 1],temp_array[k]
    questionCnts[i * 5 : (i + 1) * 5] = temp_array

# 展示排序结果
answer_sheet_warped_bin_temp = answer_sheet_warped_bin.copy()
for c in questionCnts:
    cv2.drawContours(answer_sheet_warped_bin_temp, np.array([c]),-1,(0,255,0),3)
    plt.imshow(answer_sheet_warped_bin_temp, cmap='gray')
    plt.show()

可以看到排序后按从左到右,从上到下的顺序排列轮廓

排序

七、统计分数

sum = 0
for i in range(5):
    max_count = 0
    choose = 0
    temp_array = questionCnts[i * 5 : (i + 1) * 5]
    for (j,c) in enumerate(temp_array):
        #掩码图
        # 全黑
        mask = np.ones(answer_sheet_warped_bin.shape, dtype='uint8')
        # 在全黑图上画出白色圈
        cv2.drawContours(mask,[c], -1, 255, -1)
        # plt.imshow(mask, cmap='gray')
        # plt.show()

        t = cv2.bitwise_and(answer_sheet_warped_bin,mask)
        # 去除t里的杂色点
        for x in range(len(t)):
            for y in range(len(t[i])):
                if t[x][y] == 1:
                    t[x][y] = 0

        # 非0像素点最多的就是所选项
        total = cv2.countNonZero(t)
        if total > max_count:
            max_count = total
            choose = j
    # 假定答案为全A
    if choose == 0:
        sum += 20
answer_sheet_temp = answer_sheet_warped.copy()
cv2.putText(answer_sheet_temp, "{}%".format(sum), (0, 25), cv2.FONT_HERSHEY_SIMPLEX, 0.65, (0,0,255),2)
plt.imshow(answer_sheet_temp, 'gray')
plt.show()

统计分数

相关推荐

  1. 记录-ORACLE

    2024-05-16 14:18:13       37 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-05-16 14:18:13       16 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-05-16 14:18:13       16 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-05-16 14:18:13       15 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-05-16 14:18:13       18 阅读

热门阅读

  1. 漫谈:C C++ 嵌套包含与前置声明

    2024-05-16 14:18:13       11 阅读
  2. 【postgresql】PostgreSQL中的pgrowlocks插件介绍

    2024-05-16 14:18:13       11 阅读
  3. 机器学习 - 朴素贝叶斯

    2024-05-16 14:18:13       10 阅读
  4. 解决el-dialog弹框出现后页面滚动条可滚动问题

    2024-05-16 14:18:13       13 阅读
  5. nginx中,location匹配规则解析

    2024-05-16 14:18:13       11 阅读
  6. ubuntu 修改网卡名

    2024-05-16 14:18:13       7 阅读
  7. .net 框架基础(一) 字符、字符串

    2024-05-16 14:18:13       12 阅读
  8. leensa邀请码

    2024-05-16 14:18:13       10 阅读
  9. AI绘画原理及工具介绍

    2024-05-16 14:18:13       14 阅读
  10. Vue.js介绍

    2024-05-16 14:18:13       10 阅读
  11. 【Leetcode 每日一题】20. 有效的括号

    2024-05-16 14:18:13       13 阅读
  12. 【哈希】Leetcode 242. 有效的字母异位词【简单】

    2024-05-16 14:18:13       10 阅读
  13. Linux- cron调度进程

    2024-05-16 14:18:13       11 阅读
  14. 深度解析Kubernetes网络模型

    2024-05-16 14:18:13       10 阅读
  15. FunASR语音识别快速上手指南

    2024-05-16 14:18:13       10 阅读
  16. SDL系列(二)—— 渲染窗口与BMP图片

    2024-05-16 14:18:13       12 阅读
  17. Gone框架介绍19 -如何进行单元测试?

    2024-05-16 14:18:13       12 阅读