如何在黑白图像中使用cv2.findcontours准确地找到物体?

4
这是一张完美的样本图片,包含了各种黑色物体。 白色背景下的黑色植物剪影 而这段代码应该可以找到所有的黑色植物。
import cv2 as cv
import numpy as np
img = cv.imread("12.jpg")
tresh_min= 200
tresh_max=255
gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
blurred = cv.GaussianBlur(gray, (5, 5), 0)
_, threshold = cv.threshold(blurred, tresh_min, tresh_max, 0)
(contours, _)= cv.findContours(threshold, cv.RETR_TREE, cv.CHAIN_APPROX_SIMPLE)

print(f'Number of countours: {len(contours)}')
mask = np.ones(img.shape[:2], dtype="uint8") * 255


# Draw the contours on the mask
cv.drawContours(mask, contours=contours, contourIdx=-1, color=(0, 255, 255), thickness=2)

但结果令人失望,因为这显示上述图像黑色物体轮廓包含了118个轮廓。请注意,我需要找到并取出14个物体。当轮廓实际上不正确时,如何切割每个植物?或者至少将非常接近的轮廓连接到每个更大的轮廓,以单独保存对象?谢谢。

1
你试过使用连通组件吗? - Elvis Oric
还没有,但谢谢提醒。 - redParrot
2个回答

4

我们可以使用 dilate 替代 GaussianBlur,使用 RETR_EXTERNAL 替代 RETR_TREE,并仅保留大轮廓。

  • Inverse the threshold:

     _, threshold = cv.threshold(gray, tresh_min, tresh_max, cv.THRESH_BINARY_INV)
    
  • Dilate with column kernel (assume the plants are tall and narrow):

     dilate_threshold = cv.dilate(threshold, np.ones((15, 1), np.uint8))
    
  • Loop over the contours list and keep only the ones with area above 1000.


代码示例:

import cv2 as cv
import numpy as np

img = cv.imread("12.jpg")
tresh_min= 200
tresh_max=255
gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
#blurred = cv.GaussianBlur(gray, (5, 5), 0)
_, threshold = cv.threshold(gray, tresh_min, tresh_max, cv.THRESH_BINARY_INV)
dilate_threshold = cv.dilate(threshold, np.ones((15, 1), np.uint8))
(contours, _)= cv.findContours(dilate_threshold, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE)  # Use RETR_EXTERNAL instead of RETR_TREE

print(f'Number of countours: {len(contours)}')
mask = np.ones(img.shape[:2], dtype="uint8") * 255


# Draw the contours on the mask
large_contours = []
for c in contours:
    area_tresh = 1000
    area = cv.contourArea(c)
    if area > area_tresh:
        cv.drawContours(mask, [c], contourIdx=-1, color=(0, 255, 255), thickness=1)
        large_contours.append(c)  # Append to list of "large contours".

print(f'Number of large countours: {len(large_contours)}')

# Show output for testing
cv.imshow('threshold', threshold)
cv.imshow('dilate_threshold', dilate_threshold)
cv.imshow('mask', mask)
cv.waitKey()
cv.destroyAllWindows()

输出 mask
enter image description here


2

这里有一个使用连通组件解决问题的想法。该解决方案是用C++编写的,但用Python编写应该不是大问题。

  auto original = cv::imread("image.jpg");

  cv::Mat gray;
  cv::cvtColor(original, gray, cv::COLOR_BGR2GRAY);
  cv::imshow("Gray", gray);

  cv::Mat thresh, dilatedImage, erodedImage;
  cv::threshold(gray, thresh, 253, 255, cv::THRESH_BINARY_INV);
  cv::imshow("Threshold", thresh);

  auto kernel = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(3, 3));
  cv::dilate(thresh, dilatedImage, kernel);
  cv::imshow("Dilated Image", dilatedImage);

  cv::Mat labels;
  auto numberOfComponentes = cv::connectedComponents(dilatedImage, labels);
  std::cout << "Number of components: " << numberOfComponentes << std::endl;
  std::cout << "Number of blobs: " << numberOfComponentes - 1 << std::endl;

  // Show all components separatly
  for (int i = 1; i < numberOfComponentes; ++i)
    cv::imshow(std::to_string(i), labels == i);

  cv::waitKey(0);
  cv::destroyAllWindows();

编辑:

此解决方案检测到所有组件。 由于每个组件都单独显示,因此有很多窗口。 输入图像描述


是的,让我用Python来检查一下。谢谢。 - redParrot

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