【OpenCV 计算机视觉实战:从图像分割到特征匹配,全流程实战教程】

目录

一、扇子图像分割

[二、SIFT 特征匹配 + 图像拼接](#二、SIFT 特征匹配 + 图像拼接)

​编辑

三、答题卡自动阅卷系统


本篇博客基于 OpenCV-Python 实现三大经典计算机视觉任务:

  1. 扇子图像分割(边缘检测 + 轮廓提取 + 掩模 + 按位与抠图)
  2. SIFT 特征匹配与图像拼接
  3. 答题卡自动阅卷

一、扇子图像分割

读取扇子图片 → 缩放旋转预处理 → Canny 边缘检测 → 提取最大外轮廓 → 生成掩模 → 按位与抠图 → 保存透明背景扇子图

python 复制代码
import cv2
import numpy as np

# 图像显示工具函数:窗口名 + 图像,按任意键继续
def cv_show(name,img):
    cv2.imshow(name,img)
    cv2.waitKey(0)

# ===================== 1. 图像读取与预处理 =====================
# 读取原始图片
fan1=cv2.imread("fan.jpg")
# 统一缩放尺寸 640x480
fan2=cv2.resize(fan1,(640,480))
cv_show('fan2',fan2)

# 逆时针旋转90度,调整扇子方向
fan3=np.rot90(fan2,k=1)
cv_show('fan3',fan3)
cv2.waitKey(0)

# ===================== 2. 灰度化 + Canny边缘检测 =====================
# 彩色图转灰度图(边缘检测必须灰度图)
fan4=cv2.cvtColor(fan3,cv2.COLOR_BGR2GRAY)
# Canny边缘提取,100/200为高低阈值
fan5=cv2.Canny(fan4,100,200)
cv_show("canny边缘检测",fan5)

# ===================== 3. 二值化 + 轮廓检测 =====================
# 自适应二值化,强化边缘
ref = cv2.threshold(fan5,100,255,cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]
# 查找所有轮廓
cnts = cv2.findContours(ref.copy(), cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)[-2]
cv2.waitKey(0)

# ===================== 4. 筛选最大轮廓(扇子外轮廓) =====================
# 按轮廓面积从大到小排序
cnts = sorted(cnts,key=cv2.contourArea,reverse=True)

max_circle_cnt = None
max_radius =0

# 遍历轮廓,找到外接圆最大的轮廓(扇子整体)
for c in cnts:
    (x,y),radius =cv2.minEnclosingCircle(c)
    if radius>max_radius:
        max_radius=radius
        max_circle_cnt=c

# 绘制最大外轮廓(红色线)
fan1_contour=cv2.drawContours(fan3.copy(),[max_circle_cnt],-1,(0,0,255),2)
cv2.imshow('fan1_contour',fan1_contour)
cv2.waitKey(0)

# ===================== 5. 生成掩模 + 按位与抠图 =====================
# 创建纯黑背景(与灰度图同大小)
mask = np.zeros_like(fan4)
# 轮廓内部填充白色(生成掩模)
cv2.drawContours(mask,[max_circle_cnt],-1,255,thickness=-1)
cv_show('mask',mask)
cv2.waitKey(0)

# 单通道掩模转3通道,匹配彩色图像
mask_3channel = cv2.cvtColor(mask,cv2.COLOR_GRAY2BGR)
# 按位与:只保留掩模白色区域(扇子)
result = cv2.bitwise_and(fan3,mask_3channel)
cv_show('result',result)

# 保存最终结果
cv2.imwrite("shanzi.png",result)
print("已经保存:shanzi.png")
cv2.waitKey(0)

依次显示:原图→旋转图→边缘图→轮廓图→掩模图→最终抠图结果

自动生成 shanzi.png(纯扇子 + 黑背景)

结果如下:

二、SIFT 特征匹配 + 图像拼接

提取两张图片 SIFT 特征 → 暴力匹配 → 单应性矩阵 → 透视变换 → 图像无缝拼接

python 复制代码
import cv2
import numpy as np
import sys

# 图像显示函数
def cv_show(name,img):
    cv2.imshow(name,img)
    cv2.waitKey(0)

# 提取SIFT特征与描述子
def detectAndDescribe(image):
    gray = cv2.cvtColor(image,cv2.COLOR_BGR2GRAY)  # 转灰度
    sift = cv2.SIFT_create()                       # 创建SIFT
    (kps,des) = sift.detectAndCompute(gray,None)   # 检测特征点
    kps_float = np.float32([kp.pt for kp in kps]) # 坐标转浮点
    return(kps,kps_float,des)

# ===================== 读取待拼接图片 =====================
imageA=cv2.imread("A.jpg")
cv_show('imageA',imageA)
imageB=cv2.imread("B.jpg")
cv_show('imageB',imageB)

# ===================== 提取SIFT特征 =====================
(kpsA,kps_floatA,desA)=detectAndDescribe(imageA)
(kpsB,kps_floatB,desB)=detectAndDescribe(imageB)

# ===================== 暴力匹配特征点 =====================
matcher=cv2.BFMatcher()
rawMatches =matcher.knnMatch(desB,desA,2)

good=[]
matches=[]
for m in rawMatches:
    # 比例测试,筛选优质匹配
    if len(m) == 2 and m[0].distance < 0.65*m[1].distance:
        good.append(m)
        matches.append((m[0].queryIdx,m[0].trainIdx))

# 绘制匹配点
vis =cv2.drawMatchesKnn(imageB,kpsB,imageA,kpsA,good,None,flags=cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS)
cv_show("keypoint Matches",vis)

# ===================== 计算单应性矩阵 + 拼接 =====================
if len(matches)>4:
    ptsB = np.float32([kps_floatB[i] for (i,_) in matches])
    ptsA = np.float32([kps_floatA[i] for (_,i) in matches])
    # RANSAC 鲁棒估计单应性矩阵
    (H,mask) = cv2.findHomography(ptsB,ptsA,cv2.RANSAC,10)
else:
    print('图片未找到4个以上的匹配点')
    sys.exit()

# 透视变换
result = cv2.warpPerspective(imageB,H,(imageB.shape[1]+imageA.shape[1],imageB.shape[0]))
# 左图覆盖
result[0:imageA.shape[0],0:imageA.shape[1]]=imageA
cv_show('result',result)
cv2.imwrite('pingjie.jpg',result)

显示特征匹配连线

输出无缝拼接大图 pingjie.jpg

三、答题卡自动阅卷系统

图像预处理 → 轮廓检测 → 透视变换摆正 → 选项识别 → 自动判分 → 输出分数

python 复制代码
import numpy as np
import cv2

# 正确答案
ANSWER_KEY ={0:1,1:4,2:0,3:3,4:1}

# 四点坐标排序
def order_points(pts):
    rect = np.zeros((4, 2), dtype="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

# 四点透视变换
def four_point_transform(image, pts):
    rect = order_points(pts)
    (tl, tr, br, bl) = rect
    widthA = np.sqrt(((br[0] - bl[0]) ** 2) + ((br[1] - bl[1]) ** 2))
    widthB = np.sqrt(((tr[0] - tl[0]) ** 2) + ((tr[1] - tl[1]) ** 2))
    maxWidth = max(int(widthA), int(widthB))
    heightA = np.sqrt(((tr[0] - br[0]) ** 2) + ((tr[1] - br[1]) ** 2))
    heightB = np.sqrt(((tl[0] - bl[0]) ** 2) + ((tl[1] - bl[1]) ** 2))
    maxHeight = max(int(heightA), int(heightB))
    dst = np.array([[0, 0],[maxWidth - 1, 0],[maxWidth - 1, maxHeight - 1],[0, maxHeight - 1]], dtype="float32")
    M = cv2.getPerspectiveTransform(rect, dst)
    warped = cv2.warpPerspective(image, M, (maxWidth, maxHeight))
    return warped

# 轮廓排序
def sort_contours(cnts, method="left-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 = [cv2.boundingRect(c) for c in cnts]
    (cnts, boundingBoxes) = zip(*sorted(zip(cnts, boundingBoxes),key=lambda b: b[1][i], reverse=reverse))
    return (cnts, boundingBoxes)

# 显示图像
def cv_show(name, img):
    cv2.imshow(name, img)
    cv2.waitKey(0)

# ===================== 预处理 =====================
image =cv2.imread(r'./images/test_01.png')
contours_img =image.copy()
gray =cv2.cvtColor(image,cv2.COLOR_BGR2GRAY)
blurred = cv2.GaussianBlur(gray,(5,5),0)
edged=cv2.Canny(blurred,75,200)

# ===================== 轮廓检测 + 透视变换 =====================
cnts = cv2.findContours(edged.copy(),cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)[-2]
cv2.drawContours(contours_img,cnts,-1,(0,0,255))
cnts = sorted(cnts,key=cv2.contourArea,reverse=True)

docCnt =None
for c in cnts:
    peri =cv2.arcLength(c,True)
    approx = cv2.approxPolyDP(c,0.02*peri,True)
    if len(approx)==4:
        docCnt = approx
        break

warped_t=four_point_transform(image,docCnt.reshape(4,2))
warped_new=warped_t.copy()
warped =cv2.cvtColor(warped_t,cv2.COLOR_BGR2GRAY)

# ===================== 二值化 + 选项识别 =====================
thresh = cv2.threshold(warped,0,255,cv2.THRESH_BINARY_INV | cv2.THRESH_OTSU)[1]
cnts = cv2.findContours(thresh,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)[-2]

questionCnts=[]
for c in cnts:
    (x,y,w,h)=cv2.boundingRect(c)
    ar = w/float(h)
    if w>=20 and h>=20 and 0.9<=ar <=1.1:
        questionCnts.append(c)

# ===================== 自动判分 =====================
questionCnts = sort_contours(questionCnts,method="top-to-bottom")[0]
correct =0

for(q,i) in enumerate(np.arange(0,len(questionCnts),5)):
    cnts=sort_contours(questionCnts[i:i+5])[0]
    bubbled =None
    for(j,c) in enumerate(cnts):
        mask=np.zeros(thresh.shape,dtype="uint8")
        cv2.drawContours(mask,[c],-1,255,-1)
        thresh_mask_and = cv2.bitwise_and(thresh,thresh,mask=mask)
        total = cv2.countNonZero(thresh_mask_and)
        if bubbled is None or total>bubbled[0]:
            bubbled=(total,j)

    color =(0,0,255)
    k=ANSWER_KEY[q]
    if k == bubbled[1]:
        color =(0,255,0)
        correct +=1
    cv2.drawContours(warped_new,[cnts[k]],-1,color,3)

# 输出分数
score = (correct/5.0)*100
print("INFO score:{:.2f}%".format(score))
cv2.putText(warped_new,"{:.2f}%".format(score),(10,30),cv2.FONT_HERSHEY_SIMPLEX,0.9,(0,0,255),2)

cv2.imshow("Original",image)
cv2.imshow("Exam",warped_new)
cv2.waitKey(0)
cv2.destroyAllWindows()

自动摆正答题卡

识别涂黑选项

对比答案输出正确率

绿色对、红色错标注

运行结果:

相关推荐
章鱼丸-3 小时前
DAY 39 图像数据与显存
人工智能
汀沿河3 小时前
4 human in loop中间件
人工智能·中间件
006_3 小时前
springboot 全球多语言情感分析 NLP 实现词云关键词提取-简易版
人工智能·自然语言处理·easyui
深藏功yu名3 小时前
Day25:RAG检索+重排序保姆级入门!
人工智能·ai·pycharm·agent·rag·rerank
人工智能培训3 小时前
具身智能中:人机交互与协作挑战
人工智能·深度学习·神经网络·机器学习·大模型·具身智能
你们补药再卷啦3 小时前
Agent建设(3/4)笔记
人工智能
TMT星球3 小时前
梦饷科技联手澳洲高端护肤品牌澳诗茉开展超级品牌日 销售额突破400万
大数据·人工智能
黎阳之光3 小时前
黎阳之光:数智硬核技术赋能应急管理装备创新,筑牢安全防线
大数据·人工智能·科技·算法·安全
*JOKER3 小时前
[LLM量化] 深入理解大模型量化:GPTQ 原理解析
人工智能·深度学习·机器学习·gptq·大模型量化