You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用Python OpenCV修正答题卡识别代码的错误?

答题卡OpenCV识别错误排查与修正方案

问题背景

使用Python OpenCV实现答题卡标记识别与判分功能,当前所有标记均正确的情况下,识别结果仍存在错误,经多方参考后无法推进。

核心需求

  • 正确标记:用绿色框圈出,累加正确题数
  • 错误标记:绿色框圈出正确选项,红色框圈出错误标记,累加错误题数
  • 无标记:直接跳过该题
  • 一行多标记:所有选项用红色框圈出,跳过该题判分

常见错误点排查

  1. 阈值处理不合理:固定阈值无法适配不同光照环境,导致标记区域漏检或误检
  2. 轮廓筛选逻辑缺失:未对轮廓的尺寸、宽高比做过滤,将非标记区域(如边框)纳入识别范围
  3. 多标记判断逻辑错误:未正确检测一行内的多个标记,导致判分逻辑混乱
  4. 答案映射偏差:题目行号与答案键的对应关系错误,导致正确标记被误判为错误

修正后的完整代码

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")

关键修正说明

  1. 自适应阈值替换固定阈值:解决光照不均导致的标记识别失效问题
  2. 轮廓精准筛选:通过尺寸、宽高比过滤非选项区域,避免干扰
  3. 多标记逻辑完善:明确检测一行内的标记数量,触发对应处理规则
  4. 标记判断优化:基于掩码内的填充像素数判断标记,比单纯轮廓面积更可靠
  5. 答案映射校准:确保题目行索引与答案键的对应关系准确

内容的提问来源于stack exchange,提问作者Ali KAYA

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.04 15:08:19