如何在 cv2.findcontours 上获得准确结果以在 BW/black-white 图像中查找对象?
How to Accurate result on cv2.findcontours to find objects in BW/black-white image?
这是一张完美的示例图像,其中包含一系列黑色物体。
这段代码假设找到所有黑色植物
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 个对象。
当轮廓实际上不正确时如何切割每株植物。或者至少将非常接近的那些加入到每个更大的轮廓中,以分别保存对象?
谢谢
我们可以用dilate
代替GaussianBlur
,用RETR_EXTERNAL
代替RETR_TREE
,只保留大轮廓。
反转threshold
:
_, threshold = cv.threshold(gray, tresh_min, tresh_max, cv.THRESH_BINARY_INV)
用柱核膨胀(假设植物又高又窄):
dilate_threshold = cv.dilate(threshold, np.ones((15, 1), np.uint8))
遍历等高线列表,只保留面积大于 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
:
下面是如何使用连接组件解决此问题的想法。
解决方案是用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();
编辑:
使用此解决方案检测到的所有组件。
有很多windows,因为每个组件都是单独显示的。
这是一张完美的示例图像,其中包含一系列黑色物体。
这段代码假设找到所有黑色植物
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 个对象。 当轮廓实际上不正确时如何切割每株植物。或者至少将非常接近的那些加入到每个更大的轮廓中,以分别保存对象? 谢谢
我们可以用dilate
代替GaussianBlur
,用RETR_EXTERNAL
代替RETR_TREE
,只保留大轮廓。
反转
threshold
:_, threshold = cv.threshold(gray, tresh_min, tresh_max, cv.THRESH_BINARY_INV)
用柱核膨胀(假设植物又高又窄):
dilate_threshold = cv.dilate(threshold, np.ones((15, 1), np.uint8))
遍历等高线列表,只保留面积大于 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
:
下面是如何使用连接组件解决此问题的想法。 解决方案是用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();
编辑:
使用此解决方案检测到的所有组件。
有很多windows,因为每个组件都是单独显示的。