【问题标题】:Improve image segmentation to create a closed contour surrounding my object改进图像分割以创建围绕我的对象的闭合轮廓
【发布时间】:2019-02-25 09:46:22
【问题描述】:

这是我用英特尔 Realsense d435 rgb 相机拍摄的场景照片。

我希望能够识别图像中的金属物体并在其周围绘制闭合轮廓。这样我可以参考轮廓内的所有像素以供将来使用。

当前方法

  1. 目前,我正在裁剪场景图像,假装我正在运行一些对象识别软件,允许我在对象周围创建一个边界框。所以我裁剪了该部分并将其应用于空白图像。

  1. 我遵循 OpenCV 文档并使用形态变换和分水岭算法来分割图像。我最终提取了确定的前景图像并运行精明的边缘检测和轮廓检测。但是,他们返回的线路相当差。

2.5。目前,我只是使用确定的前景图像并获取所有黑色像素并将它们保存为我的对象,但是,我的确定前景图像中有这些巨大的白点没有被拾取。

如何改进我的图像分割以获得更好的图像轮廓,以便我可以抓取对象包围的所有像素(大部分)?

如果有帮助,我可以添加我的代码,但它非常大。

编辑: 我尝试了 SentDex 教程中的 GrabCut 算法,但是虽然它可以删除一些背景,但之后分水岭算法无法找到准确的前景表示。

左边的图像是在应用 GrabCut 之后,然后右边的 GrabCut 算法被传递给分水岭算法以找到确定的前景。

【问题讨论】:

  • 尝试使用grabCut。你的情况就好了
  • GrabCut 是一个很好的建议,但我的程序的应用程序不会涉及任何用户界面。边界框作为占位符静态放置,用于以后的对象识别。应用 GrabCut 让我的其余代码更难识别边缘。

标签: python opencv image-processing computer-vision image-segmentation


【解决方案1】:

通过从对 RGB 图像的颜色识别和分析切换到对相机深度信息的边缘检测,我能够获得更好的对象轮廓。

以下是我为找到更好的边缘图而采取的一般步骤。

  1. 将我的深度信息保存在 NxMx1 矩阵中。其中 N,M 值是我的图像分辨率的形状。对于 480,640 的图像,我有一个 (480,640,1) 的矩阵,其中每个像素 (i,j) 存储了该像素坐标对应的深度值。

  2. 使用 astropy 的卷积方法,使用 2D 高斯核对深度矩阵中的任何缺失数据进行平滑和填充。

  3. 求我的深度矩阵的梯度和梯度中每个像素对应的大小。

  4. 根据均匀深度过滤掉数据。均匀的深度意味着一个平坦的物体,所以我发现了我的幅度(来自深度梯度)的高斯分布,而那些填充在 X 标准偏差内的那些被设置为零。这减少了图像中的一些额外噪点。

  5. 然后我将幅度矩阵的值从 0 标准化为 1,因此我的矩阵可以被视为通道 1 图像矩阵。

因此,由于我的深度矩阵是 (480,640,1) 的形式,当我找到对应的梯度矩阵也是 (480,640,1) 时,我将值 (:,:,1) 从 0 缩放到1. 这样我以后可以将其表示为灰度或二值图像。

def gradient_demo(self, Depth_Mat):
    """
    Gradient display entire image
    """
    shape = (Depth_Mat.shape)
    bounds = ( (0,shape[0]), (0, shape[1]) )

    smooth_depth = self.convolve_smooth_Depth(Depth_Mat, bounds)
    gradient, magnitudes = self.depth_gradient(smooth_depth, bounds)
    magnitudes_prime = magnitudes.flatten()

    #hist, bin = np.histogram(magnitudes_prime, 50)  # histogram of entire image
    mean = np.mean(magnitudes_prime)
    variance = np.var(magnitudes_prime)
    sigma = np.sqrt(variance)

    # magnitudes_filtered = magnitudes[(magnitudes > mean - 2 * sigma) & (magnitudes < mean + 2 * sigma)]
    magnitudes[(magnitudes > mean - 1.5 * sigma) & (magnitudes < mean + 1.5 * sigma)] = 0

    magnitudes = 255*magnitudes/(np.max(magnitudes))
    magnitudes[magnitudes != 0] = 1

    plt.title('magnitude of gradients')
    plt.imshow(magnitudes, vmin=np.nanmin(magnitudes), vmax=np.amax(magnitudes), cmap = 'gray')
    plt.show()

    return  magnitudes.astype(np.uint8)
def convolve_smooth_Depth(self, raw_depth_mtx, bounds):
    """
    Iterate over subimage and fill in any np.nan values with averages depth values
    :param image: 
    :param bounds: ((ylow,yhigh), (xlow, xhigh)) -> (y,x)
    :return: Smooted depth values for a given square
    """
    ylow, yhigh = bounds[0][0], bounds[0][1]
    xlow, xhigh = bounds[1][0], bounds[1][1]

    kernel = Gaussian2DKernel(1)    #generate kernel 9x9 with stdev of 1
    # astropy's convolution replaces the NaN pixels with a kernel-weighted interpolation from their neighbors
    astropy_conv = convolve(raw_depth_mtx[ylow:yhigh, xlow:xhigh], kernel, boundary='extend')
    # extended boundary assumes original data is extended using a constant extrapolation beyond the boundary
    smoothedSQ = (np.around(astropy_conv, decimals= 3))

    return smoothedSQ

def depth_gradient(self, smooth_depth, bounds):
    """

    :param smooth_depth: 
    :param shape: Tuple with y_range and x_range of the image. 
            shape = ((0,480), (0,640)) (y,x) -> (480,640)
            y_range = shape[0]
            x_range = shape[1]
    :return: 
    """
    #shape defines the image array shape. Rows and Cols for an array

    ylow, yhigh = bounds[0][0], bounds[0][1]
    xlow, xhigh = bounds[1][0], bounds[1][1]
    gradient = np.gradient(smooth_depth)
    x,y = range(xlow, xhigh), range(ylow, yhigh)
    xi, yi = np.meshgrid(x, y)
    magnitudes = np.sqrt(gradient[0] ** 2 + gradient[1] ** 2)

    return gradient, magnitudes

使用此方法/代码,我能够获得以下图像。仅供参考,我稍微改变了场景。

我在这里问了另一个相关问题:How to identify contours associated with my objects and find their geometric centroid

这显示了如何在图像中找到我的对象的轮廓和质心。

【讨论】:

    【解决方案2】:

    在找到轮廓之前先扩张然后侵蚀怎么样:

    element = cv2.getStructuringElement(shape=cv2.MORPH_RECT, ksize=(21, 21))
    
    dilate = cv2.dilate(gray,element,1)
    cv2.imshow("dilate",dilate)
    erode = cv2.erode(dilate,element,1)
    
    #use erode as a mask to extract the object from the original image
    erode = cv2.bitwise_not(erode)
    
    erode = cv2.cvtColor(erode, cv2.COLOR_GRAY2BGR)
    
    res = cv2.add(original,erode)
    

    我只是展示如何应用蒙版,因为我没有你使用的图像和物体识别软件。

    【讨论】:

    • 我试图在我的预设边界框中获取围绕对象的轮廓,而不是边界框的轮廓。我有兴趣将前景对象与背景分开。
    • 我已经编辑了答案。您可以简单地使用侵蚀图像作为掩码来分离前景
    • 我想你误解了我试图用蓝色手柄识别图像中的金属物体。我目前的结果是分水岭算法的结果,它实现了侵蚀和膨胀。
    猜你喜欢
    • 1970-01-01
    • 2016-11-07
    • 1970-01-01
    • 2017-11-25
    • 1970-01-01
    • 2017-08-15
    • 1970-01-01
    • 1970-01-01
    • 2021-06-19
    相关资源
    最近更新 更多