二值图像的轮廓操作
- 寻找二值图像的轮廓
- 1、skimage
- 2、cv2
- 根据轮廓点画轮廓
- 根据轮廓点得到二值mask
- 最大内切圆和最小外接圆
- 点集的最小外接多边形
- 点集的最小外凸多边形
- 最小凸多边形填充
- 二值图像的连通区域
- 连通区域边界线
寻找二值图像的轮廓
1、skimage
import numpy as np
from skimage import measure
aa = np.array([[0,0,0,0],[0,1,1,0],[0,1,1,0],[1,1,1,0]])
contours = measure.find_contours(padded_binary_mask, 0.5)
contours
#[array([[2.5, 0. ],
[2. , 0.5],
[1. , 0.5],
[0.5, 1. ],
[0.5, 2. ],
[1. , 2.5],
[2. , 2.5],
[3. , 2.5]])]
得到 轮廓点的列表,每组轮廓点size为(n,2),格式 (y,x), 每个轮廓点在前景点边界外扩0.5像素点
2、cv2
import numpy as np
import cv2
contours,hierarchy = cv2.findContours(np.uint8(aa),cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)
#([array([[[1, 1]],
[[1, 2]],
[[0, 3]],
[[2, 3]],
[[2, 1]]], dtype=int32)], array([[[-1, -1, -1, -1]]], dtype=int32))
opencv2,返回两个值,contours是轮廓点列表,每组轮廓点size为(n,1,2),格式为(x,y), 每个轮廓点和前景点边界重合
opencv3,返回三个值,img(处理的原图), contours,hierarchy
第一个参数是寻找轮廓的图像;
第二个参数表示轮廓的检索模式,有四种(本文介绍的都是新的cv2接口):
cv2.RETR_EXTERNAL表示只检测外轮廓
cv2.RETR_LIST检测的轮廓不建立等级关系
cv2.RETR_CCOMP建立两个等级的轮廓,上面的一层为外边界,里面的一层为内孔的边界信息。如果内孔内还有一个连通物体,这个物体的边界也在顶层。
cv2.RETR_TREE建立一个等级树结构的轮廓。
第三个参数method为轮廓的近似办法
cv2.CHAIN_APPROX_NONE存储所有的轮廓点,相邻的两个点的像素位置差不超过1,即max(abs(x1-x2),abs(y2-y1))==1
cv2.CHAIN_APPROX_SIMPLE压缩水平方向,垂直方向,对角线方向的元素,只保留该方向的终点坐标,例如一个矩形轮廓只需4个点来保存轮廓信息
cv2.CHAIN_APPROX_TC89_L1,CV_CHAIN_APPROX_TC89_KCOS使用teh-Chinl chain 近似算法
根据轮廓点画轮廓
mask = np.uint8(np.zeros(mask_shape))
cv2.polylines(mask, np.int32([points]), 255, 1) #最后一个参数是线的粗细
根据轮廓点得到二值mask
mask = np.uint8(np.zeros(mask_shape))
cv2.fillPoly(mask, np.int32([points]), 255)
#多组轮廓点 如果重叠,则重叠部分消失
img = np.zeros((1080, 1920, 3), np.uint8)
area1 = np.array([[250, 200], [300, 100], [750, 800], [100, 1000]])
area2 = np.array([[1000, 200], [1500, 200], [1500, 400], [1000, 400]])
cv2.fillPoly(img, [area1, area2], (255, 255, 255))
最大内切圆和最小外接圆
# 获取轮廓
contours, _ = cv2.findContours(mask_gray, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
#最大内切圆
# Calculate the distances to the contour
raw_dist = np.empty(mask_gray.shape, dtype=np.float32)
for i in range(mask_gray.shape[0]):
for j in range(mask_gray.shape[1]):
raw_dist[i,j] = cv2.pointPolygonTest(contours[0], (j,i), True)
# 获取在轮廓内部的一个点,该点到所有轮廓距离的最小值,比其他内部点到所有轮廓距离的最小值都大
minVal, maxVal, _, maxDistPt = cv2.minMaxLoc(raw_dist)
# maxDistPt:最大内切圆圆心
# maxVal:最大内切圆半径
result = cv2.cvtColor(mask_gray,cv2.COLOR_GRAY2BGR)
cv2.circle(result,maxDistPt, np.int(maxVal),(0,255,0), 2, cv2.LINE_8, 0)
#最小外接圆
center,radius = cv2.minEnclosingCircle(contours[0])
cv2.circle(result,(int(center[0]),int(center[1])),int(radius),(0,0,255),2) #圆心和半径
cv2.imshow('circle.jpg', result)
点集的最小外接多边形
mask_0 = np.zeros(shape, np.uint8)
cv2.fillPoly(mask_0, ([point]), 255) #mask_0左边脸加右边脸的掩码
#(point:size=(n,2),类型np.int32 每个点的xy坐标)
若多个轮廓之间没有重叠,可以直接将每个轮廓的point添加到list中,一个命令生成mask
若有重叠,则使用for循环,每个轮廓单独生成mask
点集的最小外凸多边形
cv2.convexHull寻找轮廓点的凸包点,然后填充
mask_0 = np.zeros(shape, np.uint8)
cv2.fillConvexPoly(mask_0, cv2.convexHull(point), 255) #mask_0左边脸加右边脸的掩码
最小凸多边形填充
找到包含整张图像的所有前景的最小凸多边形,并填充成前景
from skimage import morphology
chull = morphology.convex_hull_image(img) #需要二值图
convex_hull_image()是将图片中的所有目标看作一个整体,因此计算出来只有一个最小凸多边形。如果图中有多个目标物体,每一个物体需要计算一个最小凸多边形,则需要使用convex_hull_object()
from skimage import morphology,feature
#检测canny边缘,得到二值图片
edgs=feature.canny(img, sigma=3, low_threshold=10, high_threshold=50)
chull = morphology.convex_hull_object(edgs)
二值图像的连通区域
在skimage包中,使用measure子模块下的label函数即可实现连通区域标记。
参数input表示需要处理的二值图像,connectivity表示判定连通的模式(1代表4连通,2代表8连通),默认为8连通,输出labels为一个从0开始的标记数组。
import numpy as np
from skimage.measure import label
>>> x = np.eye(3).astype(int)
>>> print(x)
[[1 0 0]
[0 1 0]
[0 0 1]]
>>> print(label(x, connectivity = 1))
[[1 0 0]
[0 2 0]
[0 0 3]]
>>> print(label(x, connectivity = 2))
[[1 0 0]
[0 1 0]
[0 0 1]]
>>> print(label(x, background = -1))
[[1 2 2]
[2 1 2]
[2 2 1]]
>>> x = np.array([[1, 0, 0],
...
连通区域边界线
from skimage.measure import find_contours
def draw_edges(mask_3, mask, color = (255,255,255)):
'''
mask_3: img_h,img_w,3
mask: img_h,img_w mask为mask_3的二值图像
'''
#补边,防止超边界
padded_mask = np.zeros(
(mask.shape[0] + 2, mask.shape[1] + 2), dtype=np.uint8)
padded_mask[1:-1, 1:-1] = mask
#查找轮廓。参数1:一个二值数组图像;参数2:在图像中查找轮廓的级别值
contours = find_contours(padded_mask, 0.5) #得到点集合,n个(y,x)
for ind, verts in enumerate(contours): #遍历每个连通区域的外边界
Pts = np.zeros((len(verts),1,2),dtype=np.int32) #len(verts)个轮廓
# Subtract the padding and flip (y, x) to (x, y)
Pts[:,0,:] = np.fliplr(verts) - 1
cv2.polylines(mask_3,[Pts],True,color,3)
return mask_3