Detect semicircle in OpenCV

Learn detect semicircle in opencv with practical examples, diagrams, and best practices. Covers opencv, geometry, hough-transform development techniques with visual explanations.

Detecting Semicircles in OpenCV: A Comprehensive Guide

Hero image for Detect semicircle in OpenCV

Learn how to effectively detect semicircular shapes in images using OpenCV, leveraging techniques like edge detection, Hough Transform, and contour analysis.

Detecting specific geometric shapes like semicircles in images is a common task in computer vision, with applications ranging from industrial inspection to autonomous navigation. Unlike full circles, semicircles present a unique challenge due to their open-ended nature and the need to identify both the curved arc and the connecting line segment (diameter). This article will guide you through a robust approach using OpenCV, combining edge detection, Hough Transform for lines, and contour analysis to accurately identify semicircles.

Understanding the Challenge of Semicircle Detection

Semicircles are essentially half-circles, characterized by a curved arc and a straight line segment forming its diameter. The primary difficulty in detecting them lies in distinguishing them from full circles, other curved shapes, or even just random arcs. Traditional circle detection methods like the Hough Circle Transform are optimized for complete circles and may struggle with partial arcs. Therefore, a multi-stage approach is often required, breaking down the problem into detecting the arc and then verifying the presence of its diameter.

flowchart TD
    A[Input Image] --> B{Pre-processing: Grayscale, Blur}
    B --> C[Edge Detection: Canny]
    C --> D{Hough Line Transform}
    C --> E{Contour Detection}
    D --> F{Filter Lines: Length, Orientation}
    E --> G{Filter Contours: Arc Length, Area}
    F & G --> H{Combine & Verify: Arc-Line Proximity, Angle}
    H --> I[Detected Semicircles]

High-level workflow for semicircle detection in OpenCV.

Step-by-Step Detection Methodology

Our approach involves several key steps to isolate and identify semicircles. We'll start with image pre-processing, move to edge detection, then use the Hough Line Transform to find potential diameters, and finally, analyze contours to find the curved arcs. The crucial part is then combining these findings to confirm a semicircle.

1. 1. Pre-processing and Edge Detection

Convert the input image to grayscale and apply a Gaussian blur to reduce noise. Then, use the Canny edge detector to find prominent edges. These edges will form the basis for both line and arc detection.

2. 2. Hough Line Transform for Diameter Detection

Apply the Probabilistic Hough Line Transform (cv2.HoughLinesP) on the Canny edge map. This will detect straight line segments. Filter these lines based on length and orientation to identify potential diameters of semicircles. A diameter should be a relatively long, straight line.

3. 3. Contour Detection for Arc Identification

Find contours in the Canny edge map using cv2.findContours. Iterate through these contours and filter them based on properties like arc length and area. We are looking for contours that resemble arcs, so they should have a certain curvature and not be too small or too large.

4. 4. Combining Lines and Arcs to Form Semicircles

This is the most critical step. For each potential arc contour, try to find a corresponding line segment (diameter) from the Hough lines. A valid pair would have the endpoints of the line segment lying close to the endpoints of the arc, and the line segment should roughly bisect the arc. You might also check the angle between the line and the tangents at the arc's endpoints.

import cv2
import numpy as np

def detect_semicircles(image_path):
    img = cv2.imread(image_path)
    if img is None:
        print("Error: Could not load image.")
        return

    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    blurred = cv2.GaussianBlur(gray, (5, 5), 0)
    edges = cv2.Canny(blurred, 50, 150, apertureSize=3)

    # 1. Hough Line Transform for potential diameters
    lines = cv2.HoughLinesP(edges, 1, np.pi / 180, threshold=50, minLineLength=50, maxLineGap=10)

    # 2. Contour Detection for potential arcs
    contours, _ = cv2.findContours(edges.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

    detected_semicircles = []

    if lines is not None:
        for line in lines:
            x1, y1, x2, y2 = line[0]
            line_center = np.array([(x1 + x2) / 2, (y1 + y2) / 2])
            line_length = np.sqrt((x2 - x1)**2 + (y2 - y1)**2)

            if line_length < 50: # Filter out very short lines
                continue

            for contour in contours:
                # Approximate contour to simplify and check for arc-like shape
                perimeter = cv2.arcLength(contour, True)
                approx = cv2.approxPolyDP(contour, 0.02 * perimeter, True)

                # Filter contours that are too small or too large, or not arc-like
                if len(approx) < 5 or len(approx) > 50: # Heuristic for arc-like shapes
                    continue

                # Check if the arc's endpoints are close to the line's endpoints
                # This is a simplified check; a more robust approach would involve geometry
                # For demonstration, we'll check if the line's center is near the arc's 'center'
                M = cv2.moments(contour)
                if M["m00"] == 0: continue
                cx = int(M["m10"] / M["m00"])
                cy = int(M["m01"] / M["m00"])
                arc_center = np.array([cx, cy])

                distance = np.linalg.norm(line_center - arc_center)

                # A more robust check would involve: 
                # 1. Finding the two furthest points on the arc (potential endpoints)
                # 2. Checking if these points are close to (x1,y1) and (x2,y2)
                # 3. Verifying the arc's curvature and orientation relative to the line

                # Simple distance check for demonstration
                if distance < line_length / 2: # If arc center is close to line center
                    # Further checks needed: arc orientation, endpoints alignment
                    detected_semicircles.append((line, contour))
                    # Draw for visualization
                    cv2.line(img, (x1, y1), (x2, y2), (0, 255, 0), 2)
                    cv2.drawContours(img, [contour], -1, (0, 0, 255), 2)

    cv2.imshow("Detected Semicircles", img)
    cv2.waitKey(0)
    cv2.destroyAllWindows()

# Example usage:
detect_semicircles("path/to/your/image.jpg")

Python code demonstrating the basic structure for semicircle detection.

Refining Semicircle Verification

The most challenging aspect is accurately verifying that a detected arc and a detected line segment indeed form a semicircle. This involves several geometric checks:

  1. Endpoint Proximity: The two endpoints of the detected arc should be very close to the two endpoints of the detected line segment.
  2. Midpoint Alignment: The midpoint of the line segment should be close to the center of the circle from which the arc originates.
  3. Radius Consistency: The distance from the line's midpoint to any point on the arc should be approximately constant (the radius).
  4. Arc Length: The arc length should be roughly half the circumference of a circle with the detected radius.
  5. Orientation: The line segment should be perpendicular to the radius connecting the circle's center to the arc's midpoint.
Hero image for Detect semicircle in OpenCV

Key geometric properties for verifying a semicircle.

Implementing these geometric checks requires careful calculation of distances, angles, and potentially fitting a circle to the arc points to determine its center and radius. This iterative refinement process is key to achieving high accuracy in semicircle detection.