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

文章目录

  • 完整代码一览
  • 导入库与辅助函数
    • 定义显示图像函数cv_show
    • 辅助轮廓排序函数 sort_contours
  • 主程序流程
    • 读取图片,缩放并逆时针旋转90度
    • Canny 边缘检测
    • 查找轮廓,筛选扇子外轮廓
    • 生成掩膜
    • 按位与操作提取扇子区域
    • 保存结果

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

项目要求:
一张名为fan.jpg的图片,现要求使用 Python 结合 OpenCV 库编写代码实现以下功能:
(1)读取名为fan.jpg的图片,将尺寸设置为宽640,高480,然后逆时针旋转90度;
(2)使用Canny边缘检测提取(1)处理后的边缘;
(3)在提取边缘的基础上,查找轮廓并选取扇子的外轮廓,生成相应的掩模;
(4)将步骤 1 处理后的图像与步骤 3 生成的掩模执行按位与操作,提取扇子区域图像,最终将结果保存为shanzi.png文件。
样图:
fan.jpy

完整代码一览

import cv2 import numpy as np defcv_show(name,image):cv2.imshow(name,image)cv2.waitKey(0)defsort_contours(cnts,method='left-to-right'):reverse=False i=0ifmethod=='right-to-left'or method=='bottom-to-top':reverse=Trueifmethod=='top-to-bottom'or method=='bottom-to-top':i=1boundingBoxes=[cv2.boundingRect(c)forc in cnts](cnts,boundingBoxes)=zip(*sorted(zip(cnts,boundingBoxes),key=lambda b:b[1][i],reverse=reverse))returncnts,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=[]forc 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

import cv2 import numpy as np defcv_show(name,image):cv2.imshow(name,image)cv2.waitKey(0)

辅助轮廓排序函数 sort_contours

defsort_contours(cnts,method='left-to-right'):reverse=False i=0ifmethod=='right-to-left'or method=='bottom-to-top':reverse=Trueifmethod=='top-to-bottom'or method=='bottom-to-top':i=1boundingBoxes=[cv2.boundingRect(c)forc in cnts](cnts,boundingBoxes)=zip(*sorted(zip(cnts,boundingBoxes),key=lambda b:b[1][i],reverse=reverse))returncnts,boundingBoxes

主程序流程

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

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 边缘检测

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 是经验值,可根据图片对比度调整。数值越小,检测出的边缘越丰富(可能包含噪声);越大则只保留最强边缘。

运行结果:

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

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=[]forc 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)cv_show("img_mask",mask)

运行结果:

按位与操作提取扇子区域

对两张图像(这里都是 img_1)按位与,但通过 mask 参数指定只有白色区域才参与运算,黑色区域结果直接为 0。
这样,原图中只有扇子部分被保留,背景全黑,完美抠出扇子。

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)