al+大数据每日学习笔记28

2026年8月26日

OpenCV 图像拼接、答题卡识别、扇子掩模提取 知识点总结

核心技术:SIFT 特征匹配、RANSAC 单应矩阵、图像拼接;轮廓检测 + 四点透视变换答题卡矫正;Canny 边缘、轮廓筛选、mask 掩码、按位与 ROI 提取

一、核心知识点梳理

1. SIFT 特征点检测与描述子

cv2.SIFT_create() :SIFT 特征提取器,检测关键点 + 生成特征描述符,对旋转、缩放具备鲁棒性。

sift.detectAndCompute(gray, None)

输入灰度图,返回kps关键点对象,des描述子数组。

kp.pt 关键点浮点坐标(x,y)。

一般输入灰度图像,减少计算量。

2 .特征匹配器

BFMatcher 暴力匹配 :逐个描述子距离计算,小数据集效果好;

FlannBasedMatcher 快速近似匹配 :大数据特征点,速度更快。

matcher.knnMatch(des1,des2,k=2) :k 近邻匹配,每个特征返回前 k 个匹配。

Lowe 比率测试 :m0.distance < ratio * m1.distance,过滤错误匹配,ratio 常用 0.4‑0.8。distance 为描述符欧式距离,数值越小相似度越高。

**cv2.drawMatchesKnn():**绘制匹配连线,可设置标记关键点样式。

3 .单应矩阵 & RANSAC & 图像拼接

cv2.findHomography(srcPoints,dstPoints,method,threshold)

求解单应矩阵 H(3×3 透视变换矩阵),支持多组点对。

cv2.RANSAC随机抽样一致性算法: 剔除外点(错误匹配点),返回H变换矩阵与mask内外点掩码。

ransacReprojThreshold: 重投影误差阈值,超过该值判定为外点。

cv2.warpPerspective(src, H, dsize): 执行透视变换,将图像按照矩阵 H 投影到新画布。

图像拼接逻辑:B 图透视变换到 A 图坐标系,画布扩大,再把 A 图拷贝到画布左侧,完成拼接。

至少需要 4 组有效匹配点才能求解单应矩阵。

4. 轮廓相关全套 API(答题卡 + 扇子提取共用)

cv2.Canny(blur, t1, t2): Canny 边缘检测,双阈值,输出二值边缘图。

cv2.findContours()轮廓查找

cv2.RETR_EXTERNAL: 只找最外层轮廓;

cv2.CHAIN_APPROX_SIMPLE: 压缩轮廓点,减少点数量。

cv2.contourArea(cnt): 计算轮廓面积;max(contours,key=cv2.contourArea)获取面积最大轮廓。

cv2.arcLength(cnt,closed=True): 轮廓周长。

cv2.approxPolyDP(cnt,epsilon,closed) 轮廓多边形近似;答题卡中用来找 4 角答题卡轮廓。

cv2.drawContours()

thickness=-1: 填充轮廓内部,用于生成 mask 掩模。

cv2.boundingRect(cnt):轮廓外接矩形,返回x,y,w,h;可以计算长宽比ar=w/h筛选圆形答题框。

5. 四点透视变换(答题卡矫正)

order_points(): 对 4 个角点重排序:左上、右上、右下、左下。

cv2.getPerspectiveTransform(src_pts,dst_pts): 输入 4 对对应点,得到透视变换矩阵 M。

cv2.warpPerspective(img,M,(w,h))做透视变换,把倾斜答题卡矫正成俯视正视图。

6. 掩码 mask 与按位运算(答题卡、扇子提取)

np.zeros_like(img) 创建和原图同尺寸全黑掩码。

cv2.bitwise_and(img,img,mask=mask): 按位与操作,只保留 mask 白色区域图像,黑色区域置 0 黑色,实现 ROI 目标区域提取。

**cv2.countNonZero(binary_img)**统计二值图中非 0 像素个数;答题卡中用来判断圆圈是否被涂黑。

7 .图像基础操作

cv2.resize(img,(w,h)) 缩放图像;注意参数顺序:(宽,高)。

cv2.rotate(img, cv2.ROTATE_90_COUNTERCLOCKWISE) 逆时针旋转 90°。

cv2.GaussianBlur(img,(ksize,ksize),sigma) 高斯模糊,降噪,前置处理,提升边缘检测效果。

cv2.threshold() 二值化

cv2.THRESH_BINARY_INV | cv2.THRESH_OTSU :Otsu 自动阈值 + 反向二值化,适合答题卡。

cv2.imwrite() 保存图片。

**cv2.imshow()+cv2.waitKey(0)**图像显示。

8 .轮廓排序

根据外接矩形坐标,对轮廓从上到下、从左到右排序,答题卡用来按行读取答题选项。

二、代表性关键代码片段

片段 1:SIFT 特征匹配 + RANSAC 求单应矩阵(图像拼接)

复制代码
# 1. SIFT提取特征
sift = cv2.SIFT_create()
kpsA, desA = sift.detectAndCompute(grayA, None)
kpsB, desB = sift.detectAndCompute(grayB, None)

# 2.BF匹配+Lowe筛选
matcher = cv2.BFMatcher()
rawMatches = matcher.knnMatch(desB, desA, k=2)
good = []
for m in rawMatches:
    if len(m)==2 and m[0].distance < 0.65*m[1].distance:
        good.append(m)

# 3.RANSAC求解单应矩阵
if len(good)>4:
    ptsB = np.float32([kps_floatB[m[0].queryIdx] for m in good])
    ptsA = np.float32([kps_floatA[m[0].trainIdx] for m in good])
    H, mask = cv2.findHomography(ptsB, ptsA, cv2.RANSAC,10)
    # 透视变换
    result = cv2.warpPerspective(imageB, H, (imageA.shape[1]+imageB.shape[1],imageA.shape[0]))
    result[0:imageA.shape[0],0:imageA.shape[1]] = imageA

片段 2:四点透视变换核心(答题卡矫正)

复制代码
def order_points(pts):
    rect = np.zeros((4,2),dtype=np.float32)
    s = pts.sum(axis=1)
    rect[0]=pts[np.argmin(s)]
    rect[2]=pts[np.argmax(s)]
    diff = np.diff(pts,axis=1)
    rect[1]=pts[np.argmin(diff)]
    rect[3]=pts[np.argmax(diff)]
    return rect

# 获取变换矩阵
rect = order_points(docCnt.reshape(4,2))
dst = np.array([[0,0],[w-1,0],[w-1,h-1],[0,h-1]],np.float32)
M = cv2.getPerspectiveTransform(rect,dst)
warped = cv2.warpPerspective(image,M,(w,h))

片段 3:mask 掩码 + bitwise_and 提取目标区域(扇子案例)

复制代码
# Canny边缘检测
blur = cv2.GaussianBlur(gray,(5,5),0)
edge = cv2.Canny(blur,50,150)
# 找最大轮廓
contours,_ = cv2.findContours(edge,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
max_cnt = max(contours,key=cv2.contourArea)
# 创建掩码,填充轮廓内部为白色
mask = np.zeros_like(gray)
cv2.drawContours(mask,[max_cnt],-1,255,-1)
# 按位与提取目标
roi_img = cv2.bitwise_and(img_rot,img_rot,mask=mask)
cv2.imwrite("shanzi.png",roi_img)

片段 4:答题卡判断填涂(mask+countNonZero)

复制代码
mask = np.zeros(thresh.shape,np.uint8)
cv2.drawContours(mask,[c],-1,255,-1)
masked_img = cv2.bitwise_and(thresh,thresh,mask=mask)
total = cv2.countNonZero(masked_img) #统计涂黑像素

三、整体流程对比

图像拼接流程

读取两张图 → SIFT提取关键点描述子 → knn匹配+Lowe筛选 → RANSAC计算单应矩阵H → warpPerspective透视变换 → 画布合并完成拼接

答题卡识别流程

读取图片 →高斯模糊→Canny边缘→轮廓检测→多边形近似找到答题卡四角→四点透视矫正→灰度+Otsu二值化→筛选圆形答题框轮廓→轮廓从上到下排序→mask掩码统计非零像素判断填涂→对比标准答案计算分数

扇子目标提取流程

读取fan.jpg → resize(640,480) →逆时针旋转90° →灰度+高斯模糊 →Canny边缘检测 →查找外轮廓,取面积最大扇子轮廓 →生成填充mask掩码 →bitwise_and按位与提取扇子ROI →保存shanzi.png

相关推荐
FakeOccupational3 小时前
【github 有趣项目】OpenPLC: 支持通用硬件的开源 PLC 软件平台‌
笔记
xian_wwq8 小时前
【学习笔记】-深度认知系列-第2讲-大模型到底是什么?——拆解“参数、训练、推理”
笔记·学习·深度认知
lzhdim10 小时前
12、JavaScript常见的内存泄露问题 - JavaScript学习系列文章
开发语言·前端·javascript·学习·ecmascript
Oll Correct11 小时前
Adobe illustrator 案例九:百宝箱图标绘制
笔记·ui·adobe·illustrator
那年窗外下的雪.11 小时前
AIDC 学习日志|第 15 天|EVPN 多归属故障收敛与撤销机制
网络·学习·php
M78佐菲12 小时前
Linux学习笔记:进程
linux·笔记·学习·算法
我命由我1234514 小时前
人脸识别 - 勒克斯(光线照射到物体表面上的明亮程度)
android·java·学习·java-ee·人脸识别·学习方法·android runtime
乐橙开放平台15 小时前
养老 SaaS 笔记:setMessageCallback 事件桥接 + msgType 映射 P0/P1,先 ACK 再分流值班流
笔记·物联网·音视频·智能家居
Logintern0915 小时前
【每天学习一点点】MPI是什么?
学习·mpi·多机训练