在我们平时使用购物软件是会发现通常在搜索框右侧都会有一个相机的标志,这个标志是可以让用户通过图片来搜索自己需要购买的内容。拍照购物将用户拍摄的商品与商品图库的图像进行对比,找到最为相似的商品。但是由于用户拍摄的图像是任意的随机的,不可能与商家的商品图完全吻合,复杂的商品背景对拍照购物造成了很大的影响,商品与背景图像的分离技术成为了技术的关键。
本期我们来学习使用图像轮廓相关的技术来实现图像前景的提取。
完成本期内容,你可以:
- 了解图像轮廓的基本定义
- 了解图像轮廓检测与边缘检测的区别
- 学会提取图像的轮廓并绘制轮廓
若要运行案例代码,你需要有:
-
操作系统:Ubuntu 16 以上 或者 Windows10
-
工具软件:VScode 或者其他源码编辑器
-
硬件环境:无特殊要求
-
核心库:python 3.6.13, opencv-contrib-python 3.4.11.39,opencv-python 3.4.2.16
判断图形轮廓
OpenCV中提供了cv2.findContours()函数可以通过计算图像梯度来判断出图像的边缘。
函数原型:contours, hierarchy = cv2.findContours( image, mode, method)
contours为返回的轮廓。hierarchy为返回的图像的拓扑信息(轮廓层次)。
参数描述如下:
- contours:返回的轮廓。
- hierarchy:图像的拓扑信息(轮廓层次)。
- image:原始图像。
- mode:轮廓检索模式。
- method:轮廓的近似方法
mode参数 | 含义 |
---|---|
cv2.RETR_EXTERNAL | 只检测外轮廓 |
cv2.RETR_LIST | 检测所有轮廓,轮廓不建立等级 |
cv2.RETR_CCOMP | 检测所有轮廓,建立两个等级的轮廓 |
cv2.RETR_TREE | 检测所有轮廓,建立等级树轮廓 |
method参数 | 含义 |
---|---|
cv2.CHAIN_APPROX_NONE | 存储所有的轮廓点 |
cv2.CHAIN_APPROX_SIMPLE | 压缩存储 |
cv2.CHAIN_APPROX_TC89_L1 | 使用ten-Chinl chain 近似算法 |
cv2.CHAIN_APPROX_TC89_KCOS | 使用ten-Chinl chain 近似算法 |
绘制图像轮廓
OpenCV中提供了cv2.drawContours()函数来绘制图像轮廓。
函数原型:image=cv.drawContours(image, contours, contourIdx, color[, thickness[, lineType[, hierarchy[, maxLevel[, offset]]]]])
image为目标图像,绘制了边缘的原始图像。
参数描述如下:
- image:待绘制轮廓图像。
- contours:需要绘制的轮廓。
- contourIdx:需要绘制的边缘索引。
- color:绘制的颜色,用 BGR 格式表示。
- thickness:可选参数,表示绘制轮廓时所用的画笔粗细。
- lineType:可选,绘制的线型。
- hierarchy:对应函数cv2.findContours()所输出的层次信息。
- maxLevel:该参数控制绘制的轮廓层次深度。
- offset:偏移参数。
绘制多边形轮廓
OpenCV中提供了cv2.approxPolyDP()函数来绘制多边形轮廓。
函数原型:approxCurve = cv2.approxPolyDP( curve, epsilon, closed )
approxCurve为逼近多边形点集;
参数描述如下:
- curve:是轮廓。
- epsilon:精度,原始轮廓的边界点与逼近多边形边界之间的最大距离。
- closed:逻辑值。该值为真时,逼近多边形是封闭的;否则,逼近曲线是不封闭的。
具体步骤
使用图像轮廓相关技术提取图像前景。
步骤一:创建项目工具
创建项目名为提取图像前景
,项目根目录下新建code
文件夹储存代码,新建dataset
文件夹储存数据,项目结构如下:
提取图像前景 # 项目名称
├── code # 储存代码文件
├── dataset # 储存数据文件
注:如项目结构已存在,无需再创建。
步骤二:获取轮廓并绘制
- 导入所需模块:OpenCV、NumPy ;
- 读取
dataset
文件夹下的dandelion.jpg
图片; - 将原图像复制,用于绘制图像轮廓;
- 将图像转换为二值图像;
- 获取图像轮廓并绘制;
代码实现
# 导入OpenCV、numpy
import cv2
import numpy as np
o = cv2.imread('../dataset/dandelion.jpg')# 读取原图
img = o.copy() # 复制图像
gray = cv2.cvtColor(o,cv2.COLOR_BGR2GRAY) # 原图从彩图变成单通道灰度图像
ret, binary = cv2.threshold(gray,127,255,cv2.THRESH_BINARY) # 灰度图像转化为二值图像,阈值为127,最大值为255
# 获取二值化图像中的轮廓以及储存轮廓层次数据
contours, hierarchy = cv2.findContours(binary,
cv2.RETR_LIST,
cv2.CHAIN_APPROX_SIMPLE)
# 在复制得到的图像上绘制轮廓
cv2.drawContours(img, contours, -1, (0, 0, 255), 2)
步骤三:提取图像前景
- 创建一张与原始图像大小相同值为0的数组(黑色图像)作为掩码;
- 在掩码中绘制出实心轮廓,颜色为白色;
- 将原始图像与掩码图像进行与运算,获取前景;
代码实现
# 创建原始图像大小相同的值为0的数组(黑色图像)
mask=np.zeros(o.shape,np.uint8)
# 在mask中绘制出实心轮廓,颜色为白色
mask=cv2.drawContours(mask,contours,-1,(255,255,255),-1)
# 将mask和原始图像进行计算,获取前景
loc=cv2.bitwise_and(o,mask)
步骤四:结果展示
- 展示出绘制了图像轮廓的图像;
- 展示出掩码图像;
- 展示提取的前景图像;
代码实现
cv2.imshow("contours",img)
cv2.imshow("mask" ,mask)
cv2.imshow("foreground" ,foreground)
cv2.waitKey()
cv2.destroyAllWindows()
使用图像轮廓相关的技术来实现提取图像的前景,主要是因为绘制图像轮廓时可以绘制实心轮廓,在通过一张掩码图像来进行图像运算,根据之前讲过的图像运算的规律,可以将图像的前景提取出来。