You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

使用OpenCV Python分离图像中线条与圆形的技术求助

Hey there! Let's work through this problem of separating circles from connected lines in your images— I’ve run into similar tricky edge cases before, so here’s a practical, Python-first approach using OpenCV that should resolve your issues:

Step-by-Step Method to Separate Circles from Connected Lines

The core strategy is to first identify circle regions, break their connections to lines, then refine the detection to isolate only the circles. Here's how to implement it:

1. Preprocess the Image

First, we clean up the image to simplify contour detection. We convert to grayscale, reduce noise with blur, and create a binary mask (adjust the threshold flag if your image has light objects on dark background).

import cv2
import numpy as np

# Load your input image
img = cv2.imread("your_image.png")
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# Preprocess: Blur to reduce noise + binary threshold
blurred = cv2.GaussianBlur(gray, (5, 5), 0)
_, binary_img = cv2.threshold(blurred, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)

2. Detect Initial Circle Candidates (Even if Imperfect)

You mentioned HoughCircles doesn't catch all circles— that's okay! We'll use it to mark potential circle regions as a starting point, then fill in the gaps later.

# Detect circles (tune these parameters to match your image's circle size/density)
circles = cv2.HoughCircles(
    gray,
    cv2.HOUGH_GRADIENT,
    dp=1.2,          # Inverse ratio of accumulator resolution
    minDist=20,      # Minimum distance between circle centers
    param1=50,       # Upper threshold for Canny edge detector
    param2=30,       # Accumulator threshold (lower = more circles)
    minRadius=10,    # Smallest circle radius to detect
    maxRadius=50     # Largest circle radius to detect
)

# Create a mask to mark detected circles
circle_mask = np.zeros_like(binary_img)
if circles is not None:
    circles = np.uint16(np.around(circles))
    for circle in circles[0, :]:
        # Draw filled circles on the mask to preserve them
        cv2.circle(circle_mask, (circle[0], circle[1]), circle[2], 255, -1)

3. Break Line-Circle Connections with Morphology

This is the key step to fix your connected elements issue. We'll use erosion with a thin rectangular kernel to cut thin lines, then combine with the circle mask to keep circles intact, then dilate to restore circle shape.

# Create a thin rectangular kernel to target line connections
line_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3, 1))

# Erode to break thin line connections to circles
eroded = cv2.erode(binary_img, line_kernel, iterations=2)

# Combine eroded image with circle mask to keep all detected circles
combined_mask = cv2.bitwise_or(eroded, circle_mask)

# Dilate slightly with an elliptical kernel to restore circle edges
restored_mask = cv2.dilate(combined_mask, cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3, 3)), iterations=1)

4. Refine Contour Detection to Isolate Circles

Now that lines are separated, we can detect contours and filter them to keep only circular shapes using circularity (a value close to 1 means a perfect circle).

# Find external contours in the cleaned mask
contours, _ = cv2.findContours(restored_mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

# Filter contours to keep only circles
final_circles = []
for cnt in contours:
    area = cv2.contourArea(cnt)
    perimeter = cv2.arcLength(cnt, True)
    if perimeter == 0:
        continue  # Skip invalid contours
    # Calculate circularity: 4π(area/perimeter²)
    circularity = 4 * np.pi * (area / (perimeter ** 2))
    # Adjust this threshold based on your circle's shape (0.8-1.2 works for most cases)
    if 0.8 < circularity < 1.2:
        final_circles.append(cnt)

# Draw the separated circles on the original image for visualization
result = img.copy()
cv2.drawContours(result, final_circles, -1, (0, 255, 0), 2)

# Display results
cv2.imshow("Original Image", img)
cv2.imshow("Separated Circles", result)
cv2.waitKey(0)
cv2.destroyAllWindows()

5. Fixing Example 2's Merged Output

If your output still has fused circles and lines, try these tweaks:

  • Tune HoughCircles parameters: Lower param2 to detect more circles, or adjust minRadius/maxRadius to match your circles' actual size.
  • Adjust erosion iterations: Increase iterations if lines are thick and still connected to circles.
  • Adaptive thresholding: Replace the Otsu threshold with cv2.adaptiveThreshold if your image has uneven lighting.
  • Watershed algorithm (for complex cases): If connections are really stubborn, use the watershed algorithm to segment regions:
    # Distance transform to find sure foreground
    dist_transform = cv2.distanceTransform(binary_img, cv2.DIST_L2, 5)
    _, sure_fg = cv2.threshold(dist_transform, 0.7 * dist_transform.max(), 255, 0)
    sure_fg = np.uint8(sure_fg)
    unknown = cv2.subtract(binary_img, sure_fg)
    
    # Label markers for watershed
    _, markers = cv2.connectedComponents(sure_fg)
    markers += 1
    markers[unknown == 255] = 0
    
    # Apply watershed
    markers = cv2.watershed(img, markers)
    img[markers == -1] = [255, 0, 0]  # Mark boundaries in red
    
    # Then filter contours from the segmented image as before
    

内容的提问来源于stack exchange,提问作者Strange

火山引擎 最新活动