我正在开发一个项目,涉及使用 OpenCV 检测地图上的圆形标记(图钉)。标记有时部分连接到街道,这使得使用标准轮廓过滤方法很难检测到它们。
我尝试了几种方法来改进检测,包括:
- 形态操作:我使用了 cv2.morphologyEx、cv2.erode和 cv2.dilate 具有不同的内核大小。然而,这些方法要么错过了标记,要么导致更多误报。
- 模板匹配:我尝试了 cv2.matchTemplate,但结果更糟,并且对于部分模糊的标记来说不可靠。
- 颜色分割:我实现了颜色分割,但我担心不同图像中街道地图颜色的变化,这可能会影响结果的一致性。
import cv2
import numpy as np
from matplotlib import pyplot as plt
# Read the image
image = cv2.imread('Capture.jpg')
# Convert to grayscale
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# Apply thresholding to create a binary image
ret, thresh = cv2.threshold(gray, 200, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)
kernel = np.ones((3,3), np.uint8)
eroded = cv2.erode(thresh, kernel, iterations=1)
dilated = cv2.dilate(eroded, kernel, iterations=1)
# dilated = cv2.morphologyEx(thresh, cv2.MORPH_HITMISS, np.ones((3,3), np.uint8))
# Find contours
contours, hierarchy = cv2.findContours(dilated, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
# Debug: Display all detected contours
image_with_contours = image.copy()
cv2.drawContours(image_with_contours, contours, -1, (0, 255, 0), 1)
plt.figure(figsize=(12, 6))
plt.imshow(cv2.cvtColor(image_with_contours, cv2.COLOR_BGR2RGB))
plt.title('All Contours')
plt.show()
# Function to check if a contour is likely a marker pin based on circularity
def is_marker_pin(contour):
area = cv2.contourArea(contour)
perimeter = cv2.arcLength(contour, True)
if perimeter == 0: # To avoid division by zero
return False
circularity = 4 * np.pi * (area / (perimeter * perimeter))
x, y, w, h = cv2.boundingRect(contour)
aspect_ratio = float(w) / h
return 50 < area < 2500 and 0.5 < aspect_ratio < 2.5 and 0.6 < circularity < 1.5
# Filter contours based on shape characteristics
marker_contours = [cnt for cnt in contours if is_marker_pin(cnt)]
# Calculate and print the centroid of each marker contour
centroids = []
for cnt in marker_contours:
M = cv2.moments(cnt)
if M['m00'] != 0:
cx = int(M['m10'] / M['m00'])
cy = int(M['m01'] / M['m00'])
centroids.append((cx, cy))
else:
centroids.append((0, 0))
# Debug: Display only marker contours and their centroids
image_with_marker_contours = image.copy()
cv2.drawContours(image_with_marker_contours, marker_contours, -1, (0, 255, 0), 1)
for (cx, cy) in centroids:
cv2.circle(image_with_marker_contours, (cx, cy), 5, (255, 0, 0), -1)
plt.figure(figsize=(12, 6))
plt.imshow(cv2.cvtColor(image_with_marker_contours, cv2.COLOR_BGR2RGB))
plt.title('Marker Contours with Centroids')
plt.show()
# Create a mask for the marker pins
mask = np.zeros_like(gray)
cv2.drawContours(mask, marker_contours, -1, (255), thickness=cv2.FILLED)
# Apply the mask to the original image
result = cv2.bitwise_and(image, image, mask=mask)
# Display the result
plt.figure(figsize=(12, 6))
plt.subplot(1, 2, 1)
plt.imshow(mask, cmap='gray')
plt.title('Mask')
plt.subplot(1, 2, 2)
plt.imshow(cv2.cvtColor(result, cv2.COLOR_BGR2RGB))
plt.title('Markers Isolated')
plt.show()
# Print the number of detected markers and their centroids
print(f"Number of detected markers: {len(marker_contours)}")
print("Centroids of the detected markers:")
for i, (cx, cy) in enumerate(centroids):
print(f"Marker {i + 1}: ({cx}, {cy})")
我当前的问题是:
- 部分连接到街道的标记没有被可靠地检测到.
如何修改我的方法或轮廓过滤标准以可靠地检测这些部分模糊的标记?我是否应该考虑任何其他技术或参数来提高检测精度?
任何建议或改进将不胜感激!
走在正确的轨道上,但需要对方法进行一些调整才能可靠地检测部分遮挡的圆形标记。形态操作和轮廓过滤的组合可以很好地解决这个问题,但关键在于微调和正确的技术组合。
以下是一些改进的方法,以及对现有代码的更改建议:
-
预处理:
-
自适应阈值化: 由于地图的不同部分可能具有不同的照明条件,因此请考虑使用自适应阈值化(如
cv2.adaptiveThreshold
)而不是全局cv2.threshold
。这将创建针对局部光照变化更稳健的二值图像。 -
形态学操作的顺序: 首先应用膨胀然后腐蚀(闭合操作)可以帮助关闭标记内的间隙,使其更像圆形。实验内核大小和迭代次数。
python kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5, 5)) closing = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, kernel, iterations=2)
-
轮廓过滤:
-
圆度检查: 的圆度检查是一个很好的功能。为了使其对轻微变形更稳健,请考虑放宽圆度阈值。
- 纵横比检查: 纵横比检查也有帮助。但是,由于标记可能是部分遮挡的,因此放宽纵横比条件以允许更大范围的值可能会有用。
- 最小面积: 过滤掉小于一定阈值的轮廓,以消除噪声和小的误报。的代码中已经包含此内容。
-
轮廓逼近: 可以使用
cv2.approxPolyDP
用更少的顶点逼近轮廓。这可以帮助平滑由于部分遮挡而导致的任何凹凸不平的边缘。 -
霍夫圆变换:
-
cv2.HoughCircles
: 除了基于轮廓的方法之外,还可以考虑使用霍夫圆变换 (cv2.HoughCircles
) 直接检测圆。此方法对部分遮挡具有鲁棒性。但是,需要根据图像大小和预期标记大小仔细调整参数(尤其是最小距离和半径范围)。 -
代码改进:
-
循环优化: 在
is_marker_pin
函数中,可以在计算圆度之前检查周长是否为零,以避免不必要的计算和潜在的零除错误。
修改后的代码:
import cv2
import numpy as np
# 读取图像
image = cv2.imread('Capture.jpg')
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# 自适应阈值化
thresh = cv2.adaptiveThreshold(gray, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY_INV, 11, 2)
# 形态学操作
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5, 5))
closing = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, kernel, iterations=2)
# 查找轮廓
contours, hierarchy = cv2.findContours(closing, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
marker_contours = []
for contour in contours:
area = cv2.contourArea(contour)
perimeter = cv2.arcLength(contour, True)
if perimeter > 0:
circularity = 4 * np.pi * (area / (perimeter * perimeter))
x, y, w, h = cv2.boundingRect(contour)
aspect_ratio = float(w) / h
if 50 < area < 2500 and 0.4 < aspect_ratio < 2.5 and 0.5 < circularity < 1.5:
marker_contours.append(contour)
# 绘制检测到的标记
image_with_markers = image.copy()
cv2.drawContours(image_with_markers, marker_contours, -1, (0, 255, 0), 2)
# 显示结果
cv2.imshow("Detected Markers", image_with_markers)
cv2.waitKey(0)
cv2.destroyAllWindows()
其他提示:
- 尝试不同的参数组合,以找到最适合特定图像的组合。
- 如果在不同环境中有多个图像,请考虑使用这些图像创建一个小型数据集,并使用它来微调参数和评估的方法。
- 对于更高级的技术,可以探索基于深度学习的目标检测模型,例如 YOLO 或 Mask R-CNN,这些模型可以经过训练以检测复杂背景下的对象,即使是部分遮挡的对象。
请记住,没有一种万能的解决方案。可能需要尝试不同的方法和参数组合,以找到最适合特定应用程序的方法。
标签:python,opencv,computer-vision,detection From: 78803325