如何用Python OpenCV修正答题卡识别代码的错误?
答题卡OpenCV识别错误排查与修正方案
问题背景
使用Python OpenCV实现答题卡标记识别与判分功能,当前所有标记均正确的情况下,识别结果仍存在错误,经多方参考后无法推进。
核心需求
- 正确标记:用绿色框圈出,累加正确题数
- 错误标记:绿色框圈出正确选项,红色框圈出错误标记,累加错误题数
- 无标记:直接跳过该题
- 一行多标记:所有选项用红色框圈出,跳过该题判分
常见错误点排查
- 阈值处理不合理:固定阈值无法适配不同光照环境,导致标记区域漏检或误检
- 轮廓筛选逻辑缺失:未对轮廓的尺寸、宽高比做过滤,将非标记区域(如边框)纳入识别范围
- 多标记判断逻辑错误:未正确检测一行内的多个标记,导致判分逻辑混乱
- 答案映射偏差:题目行号与答案键的对应关系错误,导致正确标记被误判为错误
修正后的完整代码
import cv2 import numpy as np # 示例答案键:键为题目行索引,值为正确选项索引(0开始) ANSWER_KEY = {0: 1, 1: 3, 2: 0, 3: 2, 4: 1} def grade_exam(image_path): # 读取图像并预处理 img = cv2.imread(image_path) gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 高斯模糊降噪 blur = cv2.GaussianBlur(gray, (5, 5), 0) # 自适应阈值处理,适配光照变化 thresh = cv2.adaptiveThreshold(blur, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY_INV, 11, 2) # 提取外部轮廓 cnts, _ = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) question_contours = [] # 筛选题目选项轮廓(矩形、尺寸符合预期) for c in cnts: x, y, w, h = cv2.boundingRect(c) aspect_ratio = w / float(h) # 根据实际答题卡尺寸调整尺寸阈值 if 20 <= w <= 50 and 20 <= h <= 50 and 0.9 <= aspect_ratio <= 1.1: question_contours.append(c) # 按行排序轮廓(先按y坐标,再按x坐标) question_contours = sorted(question_contours, key=lambda x: (cv2.boundingRect(x)[1], cv2.boundingRect(x)[0])) correct_count = 0 incorrect_count = 0 # 按每行4个选项遍历(可根据实际选项数调整步长) for i in range(0, len(question_contours), 4): current_row = question_contours[i:i+4] marked_indices = [] # 检测每个选项是否被标记 for idx, contour in enumerate(current_row): # 创建掩码,仅保留当前选项区域 mask = np.zeros(thresh.shape, dtype="uint8") cv2.drawContours(mask, [contour], -1, 255, -1) # 计算标记区域的非零像素数量 filled_pixels = cv2.countNonZero(cv2.bitwise_and(thresh, thresh, mask=mask)) # 像素阈值根据实际标记大小调整 if filled_pixels > 300: marked_indices.append(idx) # 处理不同标记情况 current_question_idx = i // 4 correct_ans = ANSWER_KEY.get(current_question_idx) if len(marked_indices) == 0: # 无标记,跳过 continue elif len(marked_indices) > 1: # 多标记,全部红框 for contour in current_row: x, y, w, h = cv2.boundingRect(contour) cv2.rectangle(img, (x, y), (x+w, y+h), (0, 0, 255), 2) continue else: # 单个标记,判断对错 marked_ans = marked_indices[0] # 绿色框圈出正确选项 x_corr, y_corr, w_corr, h_corr = cv2.boundingRect(current_row[correct_ans]) cv2.rectangle(img, (x_corr, y_corr), (x_corr+w_corr, y_corr+h_corr), (0, 255, 0), 2) if marked_ans == correct_ans: correct_count += 1 else: incorrect_count += 1 # 红色框圈出错误标记 x_wrong, y_wrong, w_wrong, h_wrong = cv2.boundingRect(current_row[marked_ans]) cv2.rectangle(img, (x_wrong, y_wrong), (x_wrong+w_wrong, y_wrong+h_wrong), (0, 0, 255), 2) # 绘制统计结果 cv2.putText(img, f"Correct: {correct_count}", (15, 35), cv2.FONT_HERSHEY_SIMPLEX, 1.0, (0, 255, 0), 2) cv2.putText(img, f"Incorrect: {incorrect_count}", (15, 70), cv2.FONT_HERSHEY_SIMPLEX, 1.0, (0, 0, 255), 2) # 展示结果 cv2.imshow("Grading Result", img) cv2.waitKey(0) cv2.destroyAllWindows() return correct_count, incorrect_count # 调用示例 if __name__ == "__main__": grade_exam("answer_sheet.jpg")
关键修正说明
- 自适应阈值替换固定阈值:解决光照不均导致的标记识别失效问题
- 轮廓精准筛选:通过尺寸、宽高比过滤非选项区域,避免干扰
- 多标记逻辑完善:明确检测一行内的标记数量,触发对应处理规则
- 标记判断优化:基于掩码内的填充像素数判断标记,比单纯轮廓面积更可靠
- 答案映射校准:确保题目行索引与答案键的对应关系准确
内容的提问来源于stack exchange,提问作者Ali KAYA
相关产品推荐
相关产品推荐

