使用OpenCV进行轮廓识别

3

我有一组图像中的对象。

检查样本输入图像这里

我想找到每个对象的轮廓。

我正在使用OpenCV2以下方法来识别轮廓。

gray = cv2.cvtColor(input_image, cv2.COLOR_BGR2GRAY)
gray = cv2.GaussianBlur(gray, (7, 7), 0)
edged = cv2.Canny(gray, 50, 100)
dilate= cv2.dilate(edged, None, iterations=1)
erode= cv2.erode(dilate, None, iterations=1)
cnts = cv2.findContours(erode, cv2.RETR_EXTERNAL,
        cv2.CHAIN_APPROX_SIMPLE)

这是我从上述代码获取的轮廓输出:查看输出图像 有没有更好的方法来识别图像中的对象?

你可以尝试使用超像素分割,选择具有与苹果“内部区域”相似的颜色直方图的超像素,并拒绝那些与苹果“外部区域”相似的超像素,这可以通过某些预处理和阈值处理大致检测出来。自动化的方式是使用GrabCut算法。为了获得更好的质量,请搜索用于对象语义分割的神经网络。 - Slowpoke
例如,在我早期的一个项目中,我编写了一个应用程序,可以通过手动选择/取消选择超像素来修复不正确的超像素分割,并保存结果二进制掩码。在我有一百个左右的地面真实例子(图像和二进制掩码)之后,我使用图像作为输入和掩码作为输出训练了pip2pix神经网络。 - Slowpoke
1个回答

8
你在代码片段中错过了一个简单的步骤,cv2.findContours() 最适合用于二值图像,但你只是将灰度图像传递给了 cv2.findContours。我按照以下步骤从背景中分割出苹果:
第一步:分割出主要包含灰度像素的背景。
你可以在这里使用 HSV 颜色域,其中饱和度的低值会将背景分割为:
img_hsv = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2HSV_FULL)

# Filter out low saturation values, which means gray-scale pixels(majorly in background)
bgd_mask = cv2.inRange(img_hsv, np.array([0, 0, 0]), np.array([255, 30, 255]))

enter image description here

步骤2:对于纯黑像素,饱和度值很突然,因此我们将极端的黑色和白色像素分割:

# Get a mask for pitch black pixel values
black_pixels_mask = cv2.inRange(img_bgr, np.array([0, 0, 0]), np.array([70, 70, 70]))

# Get the mask for extreme white pixels.
white_pixels_mask = cv2.inRange(img_bgr, np.array([230, 230, 230]), np.array([255, 255, 255]))

Black pixels mask White pixels mask

步骤三:合并这些掩模以获取用于cv2.findContours的最终掩模:

final_mask = cv2.max(bgd_mask, black_pixels_mask)
final_mask = cv2.min(final_mask, ~white_pixels_mask)
final_mask = ~final_mask

Merged mask

第四步:现在我们要填补空洞,我们对图像进行侵蚀和膨胀:

final_mask = cv2.erode(final_mask, np.ones((3, 3), dtype=np.uint8))
final_mask = cv2.dilate(final_mask, np.ones((5, 5), dtype=np.uint8))

enter image description here

步骤5:使用cv2.findContours()获取轮廓并根据面积过滤掉较小的轮廓:

# Now you can finally find contours.
im, contours, hierarchy = cv2.findContours(final_mask.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)

final_contours = []
for contour in contours:
    area = cv2.contourArea(contour)
    if area > 2000:
        final_contours.append(contour)

步骤6:显示最终轮廓

Final output

这是完整的代码片段:

import cv2
import numpy as np

img_bgr = cv2.imread("/home/anmol/Downloads/tWuTW.jpg")
img_hsv = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2HSV_FULL)

# Filter out low saturation values, which means gray-scale pixels(majorly in background)
bgd_mask = cv2.inRange(img_hsv, np.array([0, 0, 0]), np.array([255, 30, 255]))

# Get a mask for pitch black pixel values
black_pixels_mask = cv2.inRange(img_bgr, np.array([0, 0, 0]), np.array([70, 70, 70]))

# Get the mask for extreme white pixels.
white_pixels_mask = cv2.inRange(img_bgr, np.array([230, 230, 230]), np.array([255, 255, 255]))

final_mask = cv2.max(bgd_mask, black_pixels_mask)
final_mask = cv2.min(final_mask, ~white_pixels_mask)
final_mask = ~final_mask

final_mask = cv2.erode(final_mask, np.ones((3, 3), dtype=np.uint8))
final_mask = cv2.dilate(final_mask, np.ones((5, 5), dtype=np.uint8))

# Now you can finally find contours.
im, contours, hierarchy = cv2.findContours(final_mask.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)

final_contours = []
for contour in contours:
    area = cv2.contourArea(contour)
    if area > 2000:
        final_contours.append(contour)


for i in xrange(len(final_contours)):
    img_bgr = cv2.drawContours(img_bgr, final_contours, i, np.array([50, 250, 50]), 4)


debug_img = img_bgr
debug_img = cv2.resize(debug_img, None, fx=0.3, fy=0.3)
cv2.imwrite("./out.png", debug_img)

谢谢您的回答。我已经尝试了另一张图片,但是轮廓检测效果不好。您能帮忙解决吗?请参考以下输入和输出图片链接:https://imgur.com/a/jG4Wb4r - Rayees Ck
我刚刚为您提供了一个起点,显然您需要尝试许多不同的图像来获取此脚本的一些通用值,我仅针对给定的图像调整了这些值。您可以从这里获取概念,并根据自己的需求调整这些值。主要的收获是:由于您的背景颜色是黑色的,因此请先将其分割出来,而不是分割苹果。您还可以尝试其他颜色域,例如YCrCb、LAB等。 - ZdaR
谢谢你的建议。我会尝试其他的方法。 - Rayees Ck

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接