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

基于Python+OpenCV的OMR答题卡检测:分割图像包围矩形生成问题

Fixing OMR Bounding Box Detection for Split Answer Sheets

Hey there! I’ve built a few OMR grading tools with OpenCV before, so let’s walk through how to get clean, accurate bounding boxes for your split answer sheet images. The key is nailing the preprocessing and contour filtering—here’s a step-by-step solution tailored to your 4-option per question setup:

1. Preprocess the Image for Better Contour Detection

First, we need to clean up the image to make the option boxes stand out clearly. Gaussian blur reduces noise, and adaptive thresholding handles uneven lighting (super common with scanned/photographed answer sheets):

import cv2
import numpy as np

# Load your split answer sheet image
img = cv2.imread("split_answer_sheet.jpg")
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# Denoise + Adaptive Binarization
blur = cv2.GaussianBlur(gray, (5, 5), 0)
thresh = cv2.adaptiveThreshold(
    blur, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY_INV, 11, 2
)

2. Filter Contours to Target Only Option Boxes

OpenCV will find all sorts of contours—we need to filter out anything that isn’t an option box. Adjust the area and aspect ratio thresholds to match your actual answer sheet’s dimensions:

# Find external contours (only the outer edges of boxes)
contours, _ = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

filtered_contours = []
for cnt in contours:
    area = cv2.contourArea(cnt)
    x, y, w, h = cv2.boundingRect(cnt)
    aspect_ratio = w / float(h)
    
    # Tweak these values to fit your option boxes (e.g., 20x20 boxes might have area ~300-600)
    if 300 < area < 600 and 0.8 < aspect_ratio < 1.2:
        filtered_contours.append(cnt)

3. Sort Contours to Match Reading Order

By default, contours are detected in random order. We’ll sort them top-to-bottom, left-to-right so the bounding boxes align with the question sequence:

def sort_contours(cnts):
    # Get bounding boxes for each contour
    bounding_boxes = [cv2.boundingRect(c) for c in cnts]
    # Sort by y-coordinate (row), then x-coordinate (column)
    sorted_pairs = sorted(zip(cnts, bounding_boxes), key=lambda b: (b[1][1], b[1][0]))
    return [c for c, _ in sorted_pairs]

sorted_contours = sort_contours(filtered_contours)

4. Draw Bounding Boxes on the Image

Finally, draw the green rectangles around each option box and save/show the result:

# Draw bounding boxes on the original image
for cnt in sorted_contours:
    x, y, w, h = cv2.boundingRect(cnt)
    cv2.rectangle(img, (x, y), (x + w, y + h), (0, 255, 0), 2)

# Save or display the output
cv2.imwrite("omr_bounding_boxes.jpg", img)
cv2.imshow("OMR Bounding Boxes", img)
cv2.waitKey(0)
cv2.destroyAllWindows()

Quick Tips to Refine Results

  • Adjust preprocessing parameters: If boxes are still fuzzy, try changing the Gaussian blur kernel size (e.g., (3,3) or (7,7)) or adaptive thresholding values.
  • Handle image skew: If your answer sheet is tilted, add a perspective correction step first using cv2.getPerspectiveTransform.
  • Batch process: For all 3 split images, wrap this code in a loop to process them automatically.

内容的提问来源于stack exchange,提问作者Akhilesh Chobey

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.20 10:08:26