如何使用OpenCV Python提取MCQ答题卡上的学生ID铅笔标记?
解决OpenCV提取铅笔标记学生ID的问题
问题描述
需要提取MCQ扫描图中的铅笔填写学生ID标记,调整形态学核尺寸后效果仍不理想。
原图:
当前使用代码:
# Assume student_area is the image posted above blur_img = cv2.GaussianBlur(student_area,(5,5),0) #cv2.imshow("gaussian_blur", blur_img) res, cut_image = cv2.threshold(blur_img, 160, 255, cv2.THRESH_BINARY_INV) #cv2.imshow("image", cut_image) # apply morphology close kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5,2)) morph = cv2.morphologyEx(cut_image, cv2.MORPH_CLOSE, kernel) morph = cv2.morphologyEx(morph, cv2.MORPH_OPEN, (20,10))
处理后图像:
问题分析
当前代码的核心问题是开运算的核尺寸(20,10)过大,直接把铅笔的细标记腐蚀掉了;另外固定阈值对铅笔这种灰度不均匀的标记适配性差,容易漏检或误检。
改进方案
1. 替换固定阈值为自适应阈值
自适应阈值能根据局部区域的灰度自动调整阈值,更适合铅笔这种边缘模糊、灰度波动的标记。
2. 调整形态学操作的核与顺序
先使用小核的闭合操作强化铅笔标记的连通性,再用匹配标记尺寸的开运算去除噪点,避免过度腐蚀有效标记。
3. 轮廓过滤与中心点计算
提取轮廓后,通过面积过滤掉小噪点,再用cv2.moments()计算每个有效标记的中心点。
改进后的代码
import cv2 import numpy as np # 假设student_area是输入的灰度图 student_area = cv2.imread("your_image_path.png", 0) # 1. 高斯模糊去噪 blur_img = cv2.GaussianBlur(student_area, (3, 3), 0) # 2. 自适应阈值二值化 - 适配铅笔标记的灰度不均 cut_image = cv2.adaptiveThreshold( blur_img, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY_INV, 11, 2 ) # 3. 形态学操作:先闭合强化标记,再开运算去噪 # 闭合核:横向略宽,强化铅笔标记的连通性 close_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (4, 2)) morph = cv2.morphologyEx(cut_image, cv2.MORPH_CLOSE, close_kernel) # 开运算核:尺寸匹配单个标记的大小,避免过度腐蚀 open_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (6, 4)) morph = cv2.morphologyEx(morph, cv2.MORPH_OPEN, open_kernel) # 4. 提取轮廓并计算中心点 contours, _ = cv2.findContours(morph, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) # 过滤小噪点轮廓(根据实际标记面积调整阈值) min_area = 50 valid_centers = [] for cnt in contours: area = cv2.contourArea(cnt) if area > min_area: M = cv2.moments(cnt) if M["m00"] != 0: cX = int(M["m10"] / M["m00"]) cY = int(M["m01"] / M["m00"]) valid_centers.append((cX, cY)) # 可选:在图上绘制中心点和轮廓 cv2.circle(student_area, (cX, cY), 3, (0, 0, 255), -1) cv2.drawContours(student_area, [cnt], -1, (0, 255, 0), 1) # 显示结果 cv2.imshow("Processed Image", morph) cv2.imshow("Centers Marked", student_area) cv2.waitKey(0) cv2.destroyAllWindows() # 输出有效中心点 print("提取到的标记中心点:", valid_centers)
关键调整说明
- 自适应阈值用
ADAPTIVE_THRESH_GAUSSIAN_C,结合邻域大小11和常数2,能更好保留铅笔的淡标记。 - 形态学核尺寸缩小到匹配单个标记的宽度(6,4),既去除噪点又保留有效标记。
- 增加轮廓面积过滤,排除小的噪点轮廓,确保只处理真实的铅笔标记。
内容的提问来源于stack exchange,提问作者Rohan Chhabra
相关产品推荐
相关产品推荐

