使用Python OpenCV进行圆检测

huangapple go评论73阅读模式
英文:

Circles detection with Python OpenCV

问题

以下是您提供的代码的翻译部分:

我目前正在面临一个我开发的程序的问题该程序旨在读取包含圆环的图像文件并识别圆环内外的两个圆此外它旨在类似于声纳一样扫描内部圆并检测其中的任何缺陷然而我遇到了一个问题即程序无法检测到圆环内部的圆我已尝试调整各种参数但这些修改都没有证明有效我非常感谢关于如何解决这个问题的任何指导或建议

这是我的代码

您提供的代码部分已经翻译完成。如果您需要更多帮助或有其他问题,请随时告诉我。

英文:

I am currently facing an issue with a program I have developed. The program is designed to read an image file containing an annulus and identify both the circle inside the annulus and the one outside. Furthermore, it is intended to scan the inner circle similar to a sonar and detect any imperfections within it. However, I am encountering a problem where the program fails to detect the circle inside the annulus. I have attempted adjusting various parameters, but none of these modifications have proven effective. I would greatly appreciate any guidance or suggestions on how to address this issue.[text]

This is my code:

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()

and the image i used is :Annulus Image

答案1

得分: 1

主要问题是外圈和内圈具有相同的中心坐标,但minDist参数限制了中心之间的最小距离。

minDist - 检测到的圆心之间的最小距离。如果参数值太小,除了一个真实的圆之外,可能还会误检测到多个相邻圆。如果参数值太大,可能会错过一些圆。

通常情况下,cv2.HoughCircles 不是设计用来检测具有相同中心坐标的多个圆的。

我们可以通过两次迭代来找到外圈和内圈:

  • 使用 cv2.HoughCircles 来找到外圈(忽略内圈)。
  • edges 中擦除外圈(在外圈上绘制黑色)。
  • 使用 cv2.HoughCircles 来找到内圈。

代码示例:

import cv2
import numpy as np

def detect_annulus(image):
    # 将图像转换为灰度
    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

    # 对图像进行去噪
    denoised = cv2.fastNlMeansDenoising(gray, None, 10, 10, 7)

    # 使用Canny边缘检测
    edges = cv2.Canny(denoised, 50, 100)

    # 使用Hough Circle Transform检测圆
    circles = cv2.HoughCircles(edges, cv2.HOUGH_GRADIENT, dp=1, minDist=50, param1=50, param2=25, minRadius=220, 
                               maxRadius=1000)

    # 检查是否检测到了圆
    if circles is not None:
        # 将圆参数转换为整数
        circles = np.round(circles[0, :]).astype(int)

        # 根据需要基于大小、宽高比或位置筛选圆

        # 按半径降序排序圆
        circles = sorted(circles, key=lambda x: x[2], reverse=True)

        # 提取环形圆(外圈)
        x, y, r_outer = circles[0]

        # 在图像上绘制圆
        cv2.circle(image, (x, y), r_outer, (0, 255, 0), 2)

        # 擦除外圈(大圆)
        cv2.circle(edges, (x, y), r_outer, 0, 5)

        # 使用Hough Circle Transform检测圆(检测内圈)
        circles = cv2.HoughCircles(edges, cv2.HOUGH_GRADIENT, dp=1, minDist=50, param1=50, param2=25, minRadius=220, 
                                maxRadius=300)        

        if circles is not None:
            circles = np.round(circles[0, :]).astype(int)

            # 按半径降序排序圆
            circles = sorted(circles, key=lambda x: x[2], reverse=True)

            # 提取内圈(小圆)
            x_inner, y_inner, r_inner = circles[0]

            cv2.circle(image, (x_inner, y_inner), r_inner, (0, 0, 255), 2)

            # 计算环形内的半径
            r_final = r_outer - r_inner
        else:
            print("未检测到内圆。")
            return None

        # 显示带有圆的图像
        cv2.imshow('Circles', image)
        cv2.waitKey(0)
        cv2.destroyAllWindows()

        return r_final
    else:
        print("未检测到圆。")
        return None

# 其他代码部分不翻译

输出:
使用Python OpenCV进行圆检测


注意:
总的来说,cv2.HoughCircles 不是一个用于找到圆的强大方法。
使用 cv2.threshold(使用 cv2.THRESH_BINARY_INV)然后 cv2.findContours(不使用 cv2.Canny)是更稳健的方法,用于找到圆的轮廓。
我们可以使用 cv2.minEnclosingCircle 来找到轮廓的中心和半径。
我们还可以使用两个阶段(首先找到外圈,然后是内圈)的方法。

英文:

The main issue is that the outer circle and the inner circle have the same center coordinate, but minDist parameter limits the minimum distance between the centers.

>minDist - Minimum distance between the centers of the detected circles. If the parameter is too small, multiple neighbor circles may be falsely detected in addition to a true one. If it is too large, some circles may be missed.

In general cv2.HoughCircles is not designed to detect multiple circles with the same center coordinate.

We may find the outer and inner circles in two iterations:

  • Use cv2.HoughCircles for finding the outer circle (ignore the inner circle).
  • Erase the outer circle from edges (draw black color over the outer circle).
  • Use cv2.HoughCircles for finding the inner circle.

Code sample:

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]
# 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)
# Erase the outer (large) circle.
cv2.circle(edges, (x, y), r_outer, 0, 5)
#cv2.imshow('edges', edges)
# Detect circles using Hough Circle Transform (detect the inner circle).
circles = cv2.HoughCircles(edges, cv2.HOUGH_GRADIENT, dp=1, minDist=50, param1=50, param2=25, minRadius=220, 
maxRadius=300)        
if circles is not None:
circles = np.round(circles[0, :]).astype(int)
# Sort circles by radius in descending order
circles = sorted(circles, key=lambda x: x[2], reverse=True)
# Extract the inside circle (smaller circle)
x_inner, y_inner, r_inner = circles[0]
cv2.circle(image, (x_inner, y_inner), r_inner, (0, 0, 255), 2)
# Calculate the radius inside the annulus
r_final = r_outer - r_inner
else:
print("No Inner circle detected.")
return None
# Display the image with circles
cv2.imshow('Circles', image)
cv2.waitKey(0)
cv2.destroyAllWindows()
#cv2.imwrite('circles_image.png', image)
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()

Output:
使用Python OpenCV进行圆检测


Note:
In general, cv2.HoughCircles is not a robust method for finding circles.
Using cv2.threshold (with cv2.THRESH_BINARY_INV), and then cv2.findContours (without cv2.Canny) is much more robust way for finding the contours of the circles.
We may use cv2.minEnclosingCircle for finding the center and the radius of the contours.
We may also use two stages (finding the outer circle than the inner circle).

huangapple
  • 本文由 发表于 2023年6月8日 17:11:39
  • 转载请务必保留本文链接:https://go.coder-hub.com/76430304.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定