二值图像的轮廓操作

  • 寻找二值图像的轮廓
  • 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) #需要二值图

opencv 二值化 仿色扩散 opencv 二值化 轮廓提取_计算机视觉

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)

opencv 二值化 仿色扩散 opencv 二值化 轮廓提取_python_02

二值图像的连通区域

在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