【深度学习】OpenCV 实战:从图片中精确提取扇子区域

文章目录


一个完整的图像提取流程:读取图片 → 缩放 + 旋转 → Canny 边缘检测 → 查找轮廓 → 生成掩膜 → 按位与提取目标区域。

项目要求:

一张名为fan.jpg的图片,现要求使用 Python 结合 OpenCV 库编写代码实现以下功能:

(1)读取名为fan.jpg的图片,将尺寸设置为宽640,高480,然后逆时针旋转90度;

(2)使用Canny边缘检测提取(1)处理后的边缘;

(3)在提取边缘的基础上,查找轮廓并选取扇子的外轮廓,生成相应的掩模;

(4)将步骤 1 处理后的图像与步骤 3 生成的掩模执行按位与操作,提取扇子区域图像,最终将结果保存为shanzi.png文件。

样图:

fan.jpy

完整代码一览

c 复制代码
import cv2
import numpy as np

def cv_show(name, image):
    cv2.imshow(name, image)
    cv2.waitKey(0)

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

# ---------- 主程序 ----------
# (1) 读取图片,缩放并逆时针旋转90度
img = cv2.imread(r'./fan.jpg')
img = cv2.resize(img, (640, 480))
img_1 = np.rot90(img, k=1)   # k=1 表示逆时针旋转90度
cv2.imshow('yuan_tu', img)
cv2.imshow('fan_image', img_1)
cv2.waitKey(0)
cv2.destroyAllWindows()

# (2) Canny边缘检测
contours_img = img_1.copy()
gray = cv2.cvtColor(img_1, cv2.COLOR_BGR2GRAY)
blurred = cv2.GaussianBlur(gray, ksize=(5, 5), sigmaX=0)
cv_show('blurred', blurred)
img_canny = cv2.Canny(blurred, threshold1=75, threshold2=200)
cv_show('img_canny', img_canny)

# (3) 查找轮廓,选取扇子的外轮廓,生成掩膜
cnts = cv2.findContours(img_canny.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)[-2]
cv2.drawContours(contours_img, cnts, -1, (0, 0, 255), 3)
cv_show('contours_img', contours_img)

# 排序(其实这里只有一个最大的轮廓,但保留排序习惯)
questionCnts = []
for c in cnts:
    x, y, w, h = cv2.boundingRect(c)
    questionCnts.append(c)
questionCnts = sort_contours(questionCnts, method="top-to-bottom")[0]
fan_conts = questionCnts[0]   # 取第一个(最大的)轮廓

mask = np.zeros_like(gray)
cv2.drawContours(mask, [fan_conts], -1, 255, thickness=-1)   # thickness=-1 填充轮廓内部
cv_show("img_mask", mask)

# (4) 按位与操作,提取扇子区域
img_mask_and = cv2.bitwise_and(img_1, img_1, mask=mask)
cv_show('img_mask_and', img_mask_and)

# 保存结果
cv2.imwrite('shanzi.png', img_mask_and)

导入库与辅助函数

定义显示图像函数cv_show

c 复制代码
import cv2
import numpy as np

def cv_show(name, image):
    cv2.imshow(name, image)
    cv2.waitKey(0)

辅助轮廓排序函数 sort_contours

c 复制代码
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

主程序流程

读取图片,缩放并逆时针旋转90度

c 复制代码
img = cv2.imread(r'./fan.jpg')
img = cv2.resize(img, (640, 480))
img_1 = np.rot90(img, k=1)
cv2.imshow('yuan_tu', img)
cv2.imshow('fan_image', img_1)
cv2.waitKey(0)
cv2.destroyAllWindows()

运行结果:

Canny 边缘检测

c 复制代码
contours_img = img_1.copy()
gray = cv2.cvtColor(img_1, cv2.COLOR_BGR2GRAY)
blurred = cv2.GaussianBlur(gray, ksize=(5, 5), sigmaX=0)
cv_show('blurred', blurred)
img_canny = cv2.Canny(blurred, threshold1=75, threshold2=200)
cv_show('img_canny', img_canny)

先复制旋转后的图像,用于后面绘制轮廓,再转为灰度图,用 5×5 的核进行平滑,去除噪点,避免边缘检测时出现大量假边缘。

Canny 边缘检测:阈值 75 和 200 是经验值,可根据图片对比度调整。数值越小,检测出的边缘越丰富(可能包含噪声);越大则只保留最强边缘。

运行结果:

查找轮廓,筛选扇子外轮廓

c 复制代码
cnts = cv2.findContours(img_canny.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)[-2]
cv2.drawContours(contours_img, cnts, -1, (0, 0, 255), 3)
cv_show('contours_img', contours_img)

运行结果:

这里把所有轮廓放入列表,然后用排序函数(按从上到下排序)------实际上因为只有一个大轮廓(其他小噪声轮廓面积小),排序后第一个就是面积最大的扇子轮廓。

c 复制代码
questionCnts = []
for c in cnts:
    x, y, w, h = cv2.boundingRect(c)
    questionCnts.append(c)
questionCnts = sort_contours(questionCnts, method="top-to-bottom")[0]
fan_conts = questionCnts[0]

生成掩膜

c 复制代码
mask = np.zeros_like(gray)
cv2.drawContours(mask, [fan_conts], -1, 255, thickness=-1)
cv_show("img_mask", mask)

运行结果:

按位与操作提取扇子区域

对两张图像(这里都是 img_1)按位与,但通过 mask 参数指定只有白色区域才参与运算,黑色区域结果直接为 0。

这样,原图中只有扇子部分被保留,背景全黑,完美抠出扇子。

c 复制代码
img_mask_and = cv2.bitwise_and(img_1, img_1, mask=mask)
cv_show('img_mask_and', img_mask_and)

运行结果:

保存结果

c 复制代码
cv2.imwrite('shanzi.png', img_mask_and)