【问题标题】:How to go from contour CHAIN_APPROX_SIMPLE to contour CHAIN_APPROX_NONE如何从等高线 CHAIN_APPROX_SIMPLE 到等高线 CHAIN_APPROX_NONE
【发布时间】:2019-11-17 19:30:00
【问题描述】:

使用cv2.findContours() 可以创建“稀疏”(CHAIN_APPROX_SIMPLE)或“完整”(CHAIN_APPROX_NONE)的轮廓。如何将“稀疏”轮廓转换为“完整”轮廓?

我没有轮廓的源图像(虽然形状是已知的),只有轮廓,它们是“稀疏的”(CHAIN_APPROX_SIMPLE)。从这个“稀疏”表示中,我想解析“完整”(CHAIN_APPROX_NONE)表示,以便我可以使用它来访问其他图像的轮廓强度。

我的临时解决方案(参见代码 sn-p)是使用 cv2.drawContours(),它从“稀疏”轮廓表示中绘制所有轮廓像素。结果是一个图像,我可以从结果图像中提取索引,例如使用np.argwhere()

但是,考虑到cv2.drawContours() 在绘制结果图像之前可能已经在内部拥有这些索引,这个额外的步骤似乎有点不必要。我想我想要的是不带绘图部分的cv2.drawContours() 的变体,或者输出“完整”轮廓表示而不是图像的选项。

我的临时解决方案的另一个问题是它不保留原始轮廓中点的顺序。我想知道cv2.drawContours() 是否能够在将结果展平为图像之前在内部重新创建完整、有效的轮廓?

这个功能是否可以从 opencv 中的其他函数获得,也许是cv2.drawContours() 内部使用的更基本的函数?

import numpy as np
import cv2

# shape (Y,X)
shape = np.array((5, 5))

# sparse contour (X,Y)
sparse_contours = [np.array(
    [[[1, 0]],
     [[1, 4]],
     [[3, 4]],
     [[3, 0]]], dtype=np.int32)]

def full_contour_from_contour(shape, contour):
    # switch shape from y,x to x,y
    shp = shape[[1,0]]
    arr = np.zeros(shp, dtype=np.int32)
    cv2.drawContours(arr, [contour], 0, 1, 1)
    idx = np.argwhere(arr==1)
    # reorder Y,X -> X,Y
    idx = idx[:, [1, 0]]
    # reshape to contour layout
    rows, cols = idx.shape
    idx = idx.reshape(rows, 1, cols)
    return idx.astype(np.int32)

full_contour = full_contour_from_contour(shape, sparse_contour)

# output
# these are correct pixels, with pixels in sparse contour also
# part of the full contour. However, the full contour is not 
# necessarily correct or even valid due to 
# lost information regarding point sequence along the contour)

[[[1 0]]

 [[2 0]]

 [[3 0]]

 [[1 1]]

 [[3 1]]

 [[1 2]]

 [[3 2]]

 [[1 3]]

 [[3 3]]

 [[1 4]]

 [[2 4]]

 [[3 4]]]






【问题讨论】:

    标签: python numpy opencv contour


    【解决方案1】:

    查看文档时:https://docs.opencv.org/2.4/modules/imgproc/doc/structural_analysis_and_shape_descriptors.html?highlight=findcontours#findcontours 它指出它们之间的区别在于 CHAIN_APPROX_NONE 存储每个像素,而 CHAIN_APPROX_SIMPLE 仅存储形成轮廓的线的端点。因此,您可以简单地构造连接轮廓中每对连续顶点的线,以获得完整表示的近似值。属于线的每个像素也属于轮廓。

    【讨论】:

    • 谢谢@Sushi!它确实有助于更好地理解 CHAIN_APPROX_NONE 和 CHAIN_APPROX_SIMPLE。我还根据您的建议发布了code snippet
    【解决方案2】:

    suggestion一致,这里有一个代码sn-p,似乎可以解决我的问题。

    def full_from_sparse(contour):
        horizontal = np.array([1, 0], 'int')
        vertical = np.array([0, 1], 'int')
        diagonal = np.array([1, 1], 'int')
        def _get_points(p0, p1):
            # find all points on line connecting p0 and p1,
            # including p0, excluding p1
            # line must be horizontal, vertical or diagonal
            diff = p1-p0
            if np.max(np.abs(diff)) <= 1:
                # p0 and p1 are neighbor points
                # or duplicate points, i.e.g no in-between points
                return [p0]
            if diff[0] == 0:
                # vertical
                fac = diff[1]
                inc = vertical
            elif diff[1] == 0:
                # horizontal
                fac = diff[0]
                inc = horizontal
            elif diff[0] == diff[1]:
                # diagonal
                fac = diff[0]
                inc = diagonal
            else:
                raise Exception("points not connected", p0, p1)
            return [p0 + _fac*inc for _fac in range(0, fac, np.sign(fac))]
    
        full = []
        points = contour[:, 0, :]
        for i in range(len(points)-1):
            _points = _get_points(points[i], points[i+1])
            full.extend(_points)
    
        # add points from last segment, endpoint to startpoint
        _points = _get_points(points[-1], points[0])
        full.extend(_points)
    
        # reshape as contour
        full = np.array(full, dtype='int')
        rows, cols = full.shape
        return full.reshape(rows, 1, cols)
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-12-13
      • 2022-08-19
      • 1970-01-01
      • 2012-07-18
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多