核心内容摘要
TurboDiffusion技术拆解:双模型架构在I2V中的协同机制
总体概述这是一个完整的答题卡自动识别和评分系统主要流程包括图像预处理→答题卡定位→透视变换→选项检测→答案判断→评分输出。
详细分析
准备工作ANSWER_KEY {0: 1, 1: 4, 2: 0, 3: 3, 4: 1} # 正确答案存储标准答案题目索引→正确选项索引0-based这里表示第0题正确答案是B索引1第1题正确答案是E索引4等
关键函数定义(
order_points()- 坐标排序def order_points(pts): # 找出4个坐标位置 rect np.zeros((4,
, dtypefloat
s pts.sum(axis
rect[0] pts[np.argmin(s)] # 左上 rect[2] pts[np.argmax(s)] # 右下 diff np.diff(pts, axis
rect[1] pts[np.argmin(diff)] # 右上 rect[3] pts[np.argmax(diff)] # 左下 return rect功能将4个点按顺序排列为左上、右上、右下、左下原理左上点xy最小右下点xy最大右上点x-y最小左下点x-y最大(
four_point_transform()- 透视变换def four_point_transform(image, pts): # 获取输入坐标点并做透视变换 rect order_points(pts) # 找出4个坐标位置 (tl, tr, br, bl) rect # 计算输入的w和h值 widthA np.sqrt(((br[0] - bl[0]) **
((br[1] - bl[1]) **
) widthB np.sqrt(((tr[0] - tl[0]) **
((tr[1] - tl[1]) **
) maxWidth max(int(widthA), int(widthB)) heightA np.sqrt(((tr[0] - br[0]) **
((tr[1] - br[1]) **
) heightB np.sqrt(((tl[0] - bl[0]) **
((tl[1] - bl[1]) **
) maxHeight max(int(heightA), int(heightB)) # 变换后对应坐标位置 dst np.array([[0, 0], [maxWidth - 1, 0], [maxWidth - 1, maxHeight - 1], [0, maxHeight - 1]], dtypefloat
# 计算变换矩阵 M cv
getPerspectiveTransform(rect, dst) warped cv
warpPerspective(image, M, (maxWidth, maxHeight)) return warped # 返回变换后结果功能将倾斜的答题卡矫正为正面视图步骤计算原始四边形和变换后矩形的对应关系使用cv
getPerspectiveTransform()计算变换矩阵应用透视变换得到矫正后的图像(
sort_contours()- 轮廓排序def sort_contours(cnts, methodleft-to-right): # 对轮廓进行排序 reverse False i 0 if method right-to-left or method bottom-to-top: reverse True if method top-to-bottom or method bottom-to-top: i 1 boundingBoxes [cv
boundingRect(c) for c in cnts] (cnts, boundingBoxes) zip(*sorted(zip(cnts, boundingBoxes), keylambda b: b[1][i], reversereverse)) return cnts, boundingBoxes功能按指定方向左→右、上→下等对轮廓排序实现通过bounding box的坐标进行排序
主流程分析第一阶段图像预处理和答题卡定位1读取和灰度化image cv
imread(rtest_
png) contours_img image.copy() gray cv
cvtColor(image, cv
COLOR_BGR2GRAY)2高斯模糊降噪blurred cv
GaussianBlur(gray, (5,
,
cv_show(blurred, blurred)3Canny边缘检测edged cv
Canny(blurred, 75,
cv_show(edged, edged)4寻找轮廓cnts cv
findContours(edged.copy(), cv
RETR_EXTERNAL, cv
CHAIN_APPROX_SIMPLE)[-2] cv
drawContours(contours_img, cnts, -1, (0, 0,
,
cv_show(contours_img, contours_img) docCnt None使用Canny边缘检测找出所有边缘筛选最大的轮廓应该是答题卡的外框第二阶段透视变换1寻找四边形轮廓# 根据轮廓大小进行排序准备透视变换 cnts sorted(cnts, keycv
contourArea, reverseTrue) for c in cnts: # 遍历每一个轮廓 peri cv
arcLength(c, True) approx cv
approxPolyDP(c,
02 * peri, True) # 轮廓近似 if len(approx) 4: docCnt approx break2执行透视变换warped_t four_point_transform(image, docCnt.reshape(4,
) warped_newwarped_t.copy() cv_show(warped, warped_t) warped cv
cvtColor(warped_t, cv
COLOR_BGR2GRAY)通过cv
approxPolyDP()近似多边形找到4个顶点的轮廓作为答题卡边界透视变换得到正视图第三阶段选项检测1阈值处理thresh cv
threshold(warped, 0, 255, cv
THRESH_BINARY_INV | cv
THRESH_OTSU)[1] cv_show(thresh, thresh) thresh_Contours thresh.copy()2寻找所有轮廓# 找到每一个圆圈轮廓 cnts cv
findContours(thresh, cv
RETR_EXTERNAL, cv
CHAIN_APPROX_SIMPLE)[-2] warped_Contours cv
drawContours(warped_t, cnts, -1, (0, 255,
,
cv_show(warped_Contours, warped_Contours)3筛选圆形选项轮廓questionCnts [] for c in cnts: # 遍历轮廓并计算比例和大小 (x, y, w, h) cv
boundingRect(c) ar w / float(h) # 根据实际情况指定标准 if w 20 and h 20 and
9 ar
1: questionCnts.append(c) print(len(questionCnts))筛选标准宽高≥20像素排除噪声宽高比
9-
1接近圆形通过这两个条件筛选出选项圆圈第四阶段答案识别和评分1按行排序每题5个选项for (q, i) in enumerate(np.arange(0, len(questionCnts),
): cnts sort_contours(questionCnts[i:i 5])[0] # 排序 bubbled None2逐题处理for (j, c) in enumerate(cnts): # 使用mask来判断结果 mask np.zeros(thresh.shape, dtypeuint
cv
drawContours(mask, [c], -1, 255, -
# -1表示填充 cv_show(mask, mask) # 通过计算非零点数量来算是否选择这个答案 # 利用掩膜mask进行“与”操作只保留mask位置中的内容 thresh_mask_and cv
bitwise_and(thresh, thresh, maskmask) cv_show(thresh_mask_and, thresh_mask_and) total cv
countNonZero(thresh_mask_and) # 统计灰度值不为0的像素数3判断选择状态if bubbled is None or total bubbled[0]: # 通过阈值判断保存灰度值最大的序号 bubbled (total, j)4与正确答案对比if k bubbled[1]: # 判断正确 color (0, 255,
correct 1 cv
drawContours(warped_new, [cnts[k]], -1, color,
# 绘图 cv_show(warpeding, warped_new)关键算法 - 判断哪个选项被选中掩膜技术为每个选项创建单独的掩膜区域统计在掩膜区域内统计非零像素数量决策逻辑被涂黑的选项有最多的非零像素因为是二值化后的白色具体实现# 创建选项掩膜 mask np.zeros(thresh.shape, dtypeuint
cv
drawContours(mask, [c], -1, 255, -
# 只保留该选项区域 thresh_mask_and cv
bitwise_and(thresh, thresh, maskmask) # 统计非零像素涂黑的部分 total cv
countNonZero(thresh_mask_and)
可视化过程代码中通过cv_show()函数展示多个中间结果blurred模糊后的图像edged边缘检测结果contours_img所有轮廓warped透视变换后thresh二值化结果mask单个选项的掩膜thresh_mask_and掩膜应用后的效果
评分和标记score (correct /
5.