You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于Python OpenCV的圆环内圈检测失败问题求助

圆环内圈检测问题及解决建议

问题描述

我开发了一个程序,用于读取含圆环的图像,识别圆环的外圈和内圈,并以类似声呐的方式扫描内圈检测缺陷。但目前程序无法检测到圆环的内圈,已尝试调整多个参数但均无效,恳请提供解决建议。

测试图像:圆环测试图像

现有代码

import cv2
import numpy as np

def detect_annulus(image):
    # Convert to grayscale
    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

    # Denoise the image
    denoised = cv2.fastNlMeansDenoising(gray, None, 10, 10, 7)

    # Perform Canny edge detection
    edges = cv2.Canny(denoised, 50, 100)

    # Detect circles using Hough Circle Transform
    circles = cv2.HoughCircles(edges, cv2.HOUGH_GRADIENT, dp=1, minDist=50, param1=50, param2=25, minRadius=220,
                               maxRadius=1000)

    # Check if circles are found
    if circles is not None:
        # Convert the circle parameters to integers
        circles = np.round(circles[0, :]).astype(int)

        # Filter circles based on size, aspect ratio, or position if needed

        # Sort circles by radius in descending order
        circles = sorted(circles, key=lambda x: x[2], reverse=True)

        # Extract the annulus circle (outer circle)
        x, y, r_outer = circles[0]

        # Extract the inside circle (smaller circle)
        x_inner, y_inner, r_inner = circles[1]

        # Calculate the radius inside the annulus
        r_final = r_outer - r_inner

        # Draw the circles on the image
        cv2.circle(image, (x, y), r_outer, (0, 255, 0), 2)
        cv2.circle(image, (x_inner, y_inner), r_inner, (0, 0, 255), 2)

        # Display the image with circles
        cv2.imshow('Circles', image)
        cv2.waitKey(0)
        cv2.destroyAllWindows()

        return r_final
    else:
        print("No circles detected.")
        return None

def scan_circle_for_imperfections(image, center_x, center_y, radius, step_size):
    # Initialize variables to store imperfections
    imperfections = []

    # Convert to grayscale
    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

    # Calculate the number of iterations based on step size
    num_iterations = int(360 / step_size)

    # Initialize variables to store the largest and smallest r_final values
    largest_r_final = None
    smallest_r_final = None

    # Iterate over angles around the circle
    for i in range(num_iterations):
        # Compute the current angle
        angle = i * step_size

        # Compute the coordinates of the point on the circle
        x = int(center_x + radius * np.cos(np.radians(angle)))
        y = int(center_y + radius * np.sin(np.radians(angle)))

        # Get the pixel intensity at the point
        intensity = gray[y, x]

        # Calculate the r_final value at the current point
        r_final = intensity / 255.0 * radius

        # Update the largest and smallest r_final values
        if largest_r_final is None or r_final > largest_r_final:
            largest_r_final = r_final
        if smallest_r_final is None or r_final < smallest_r_final:
            smallest_r_final = r_final

    # Calculate the threshold for detecting imperfections
    threshold = 0.5  # Adjust this value based on your requirements

    # Iterate over angles again to find imperfections
    for i in range(num_iterations):
        # Compute the current angle
        angle = i * step_size

        # Compute the coordinates of the point on the circle
        x = int(center_x + radius * np.cos(np.radians(angle)))
        y = int(center_y + radius * np.sin(np.radians(angle)))

        # Get the pixel intensity at the point
        intensity = gray[y, x]

        # Calculate the r_final value at the current point
        r_final = intensity / 255.0 * radius

        # Check if the r_final value is significantly larger than others
        if r_final - smallest_r_final > threshold * (largest_r_final - smallest_r_final):
            imperfections.append((x, y))

    return imperfections

def main():
    # Load the input image
    image = cv2.imread('Images/1.jpg')

    # Detect the annulus and calculate the inside and outside radii
    r_final = detect_annulus(image)

    if r_final is not None:
        # Find the center coordinates of the annulus
        center_x = int(image.shape[1] / 2)
        center_y = int(image.shape[0] / 2)

        # Define the scanning radius from the center
        scan_radius = int(r_final * 1.2)

        # Define the step size for scanning
        step_size = 5

        # Scan the circle for imperfections
        imperfections = scan_circle_for_imperfections(image, center_x, center_y, scan_radius, step_size)

        # Print the imperfections
        if imperfections:
            print(f"Imperfections found: {len(imperfections)}")
            for imperfection in imperfections:
                print(f"Coordinate: {imperfection}")
        else:
            print("No imperfections found.")
    else:
        print("Failed to detect the annulus.")


if __name__ == '__main__':
    main()

解决建议

1. 优化HoughCircles参数适配内圈

测试图像中内圈边缘对比度弱于外圈,当前参数对弱边缘小半径圆不友好,建议调整:

  • param2:降低至15-20,该值是圆心累加器阈值,越小越容易检测弱边缘圆
  • minRadius/maxRadius:缩小范围匹配内圈尺寸,比如设minRadius=100,maxRadius=200
  • minDist:内外圈圆心基本重合,调小至10,允许近距离圆心的圆被检测

调整后的调用示例:

circles = cv2.HoughCircles(edges, cv2.HOUGH_GRADIENT, dp=1, minDist=10, param1=50, param2=18, minRadius=100, maxRadius=200)

2. 改进预处理强化内圈边缘

  • 替换降噪方式:fastNlMeansDenoising易模糊内圈边缘,改用cv2.GaussianBlur(gray, (5,5), 0),降噪同时保留更多边缘
  • 调整Canny阈值:降低下限至30,保留弱边缘:
edges = cv2.Canny(denoised, 30, 80)

3. 增加圆心一致性校验

圆环内外圈圆心应重合,检测到多个圆后过滤偏差过大的圆:

# 过滤与外圈圆心偏差≤5像素的圆
outer_center = (x, y)
valid_inner_circles = []
for circle in circles:
    cx, cy, cr = circle
    distance = np.sqrt((cx - outer_center[0])**2 + (cy - outer_center[1])**2)
    if distance < 5:
        valid_inner_circles.append(circle)
# 从有效圆中选半径第二大的作为内圈
if len(valid_inner_circles) >=2:
    valid_inner_circles = sorted(valid_inner_circles, key=lambda x: x[2], reverse=True)
    x_inner, y_inner, r_inner = valid_inner_circles[1]
else:
    print("No valid inner circle found")
    return None

4. 轮廓检测替代方案

若Hough变换仍无效,改用轮廓提取:

  1. 二值化分离圆环区域
  2. 提取轮廓并筛选近似圆形的轮廓
  3. 按面积大小区分内外圈

示例代码片段:

# 二值化处理
_, binary = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY_INV)
# 提取轮廓
contours, _ = cv2.findContours(binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
# 筛选圆形轮廓
circular_contours = []
for cnt in contours:
    perimeter = cv2.arcLength(cnt, True)
    area = cv2.contourArea(cnt)
    if perimeter ==0:
        continue
    circularity = 4 * np.pi * area / (perimeter **2)
    # 圆形度0.8-1.2视为圆形
    if 0.8 < circularity < 1.2:
        circular_contours.append(cnt)
# 按面积排序,大的为外圈,小的为内圈
circular_contours.sort(key=lambda x: cv2.contourArea(x), reverse=True)
if len(circular_contours)>=2:
    (x_outer, y_outer), r_outer = cv2.minEnclosingCircle(circular_contours[0])
    (x_inner, y_inner), r_inner = cv2.minEnclosingCircle(circular_contours[1])

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.19 22:32:08