当前位置: 首页 > news >正文

OpenCV图像处理——(实战)答题卡识别试卷

总目录

图像处理总目录←点击这里

二十、答题卡识别试卷

20.1、预处理

灰度图

输出灰度图+高斯滤波去噪

gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
blurred = cv2.GaussianBlur(gray, (5, 5), 0)

在这里插入图片描述

边缘检测

edged = cv2.Canny(blurred, 75, 200)

在这里插入图片描述

20.2、轮廓检测

找到原始图像中边框的四个矩形框的点

cnts = cv2.findContours(edged.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)[0]
cv2.drawContours(contours_img, cnts, -1, (0, 0, 255), 3)

在这里插入图片描述

20.3、透视变换

得到需要的图像范围

def four_point_transform(image, pts):
    # 获取输入坐标点
    rect = order_points(pts)
    (tl, tr, br, bl) = rect
 
    # 计算输入的w和h值
    widthA = np.sqrt(((br[0] - bl[0]) ** 2) + ((br[1] - bl[1]) ** 2))
    widthB = np.sqrt(((tr[0] - tl[0]) ** 2) + ((tr[1] - tl[1]) ** 2))
    maxWidth = max(int(widthA), int(widthB))

    heightA = np.sqrt(((tr[0] - br[0]) ** 2) + ((tr[1] - br[1]) ** 2))
    heightB = np.sqrt(((tl[0] - bl[0]) ** 2) + ((tl[1] - bl[1]) ** 2))
    maxHeight = max(int(heightA), int(heightB))

    # 变换后对应坐标位置
    dst = np.array([
        [0, 0],
        [maxWidth - 1, 0],
        [maxWidth - 1, maxHeight - 1],
        [0, maxHeight - 1]], dtype="float32")

    # 计算变换矩阵
    M = cv2.getPerspectiveTransform(rect, dst)
    warped = cv2.warpPerspective(image, M, (maxWidth, maxHeight))

    # 返回变换后结果
    return warped

在这里插入图片描述

20.4、阈值处理

处理已经选择的题目

thresh = cv2.threshold(warped, 0, 255, cv2.THRESH_BINARY_INV | cv2.THRESH_OTSU)[1]

在这里插入图片描述

20.5、轮廓检测(小)

每一个圆圈的轮廓检测

(涂答题卡可能涂到外面)

cnts = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)[0]
cv2.drawContours(thresh_Contours, cnts, -1, (0, 0, 255), 3)

在这里插入图片描述

20.6、筛选,排序,选项

筛选出符合条件的圆圈(通过宽高,宽高比例筛选)

将符合条件的进行排序(默认乱序)

  • 先从上到下找到所有行排序
  • 在从左到右对每一行排序
  • 根据像素点对比找到答案

# 遍历筛选
for c in cnts:
    # 计算比例和大小
    (x, y, w, h) = cv2.boundingRect(c)
    ar = w / float(h)

    # 根据实际情况指定标准
    if w >= 20 and h >= 20 and 0.9 <= ar <= 1.1:
        questionCnts.append(c)

# 排序
def sort_contours(cnts, method="left-to-right"):
    # ...
    boundingBoxes = [cv2.boundingRect(c) for c in cnts]
    return cnts, boundingBoxes

# 对所有行排序
questionCnts = sort_contours(questionCnts, method="top-to-bottom")[0]

# 循环,并找答案
for (q, i) in enumerate(np.arange(0, len(questionCnts), 5)):
	# 对每一行进行排序
	cnts = sort_contours(questionCnts[i:i + 5])[0]
	for (j, c) in enumerate(cnts):
		mask = np.zeros(thresh.shape, dtype="uint8")
		if bubbled is None or total > bubbled[0]:
    	# j为答案,total为像素点对比(选出最合适的)
    	bubbled = (total, j)
		# ... 

第一行跑完结果

由 12345与操作得出最后结果

在这里插入图片描述

20.7、结果

答案:BEADB

第4题选错

在这里插入图片描述

答案:BEADB

第1、2、4、5题选错
在这里插入图片描述
答案:BEADB

第1、4 题选错

在这里插入图片描述

答案:BEADB

第1、2、3、5题选错

在这里插入图片描述

答案:BEADB

正确

在这里插入图片描述

原图

# 正确答案 1 4 0 3 1  ------> B E A D B
ANSWER_KEY = {0: 1, 1: 4, 2: 0, 3: 3, 4: 1}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

相关文章:

  • 朋友电脑密码忘了,我当场拔了她的电源,结果。。。
  • SpringBoot: Controller层的优雅实现
  • LeetCode135. 分发糖果(贪心算法)
  • c语言:初识结构体
  • ASEMI肖特基二极管SBT40100VFCT规格,SBT40100VFCT封装
  • 谁能拒绝一个会动的皮卡丘挂件
  • docker 安装minio
  • 云原生系列 五【轻松入门容器基础操作】
  • Python - 异常处理
  • 经济师十大专业通过人数分析!选专业有谱了!
  • wpf布局学习二 wpf xaml 与android xml界面对比, C++图片旋转与缩放好复杂放弃
  • C语言百日千题系列之《忘情水题》第一日
  • CNN经典模型之ALexNet、ResNet、DenseNet总结和比较
  • 什么是固话号码认证?固话号码认证有用吗?
  • select......for update会锁表还是锁行?
  • 【强化学习论文清单】AAAI-2022 | 人工智能CCF-A类会议(附链接)
  • Android11 framework Handler
  • Linux系统漏洞本地提权
  • 【笔试强训】Day1
  • 【项目实战:核酸检测平台】第二章 大卸八块