Hello all.
Trying again to bother you a bit with your experience
I’m trying to detect the inner ring of my micro bearing to get a positionnal offset for my CNC.
In another post Mr. Crackwitz advised not to use hough transformation, but rather look for local maximas (Detecting balls in a bearing),
But here I’m trying to find the one and only circle that is the inner ring (a bit hidden by a cover though )
I have a better camera for the focus and I’d say (but only I) that my lightning is all right, but so far the detection is so-so.
In another post (still with Mr. Crackwitz), I’ve read apparently HoughCircles detection is not so good and a newbie trap (Circle Detection Issues - #4 by crackwitz) but what makes me afraid in this post in the “fixing the position”. As it’s to calculate an offset for my cnc, the position will not be the exat same, even if normally very small position variations (few mm max) will happen.
Any way, here is my orginal image :thinking :
Here is the code I’m trying so far :
#!/usr/bin/env python
#pylint:disable=no-member
# BEC - 2024
# Python GUI (Tkinter) for the "Bench assembly for the micro bearing of Rheon"
#-------------------------------------------------------------------------------
import cv2
import numpy as np
#-------------------------------------------------------------------------------
# Simulate win_info for standalone usage
class ConsoleLogger:
"""A simple logger to print messages to the console."""
@staticmethod
def add(message, level="info"):
levels = {"debug": "[DEBUG]", "info": "[INFO]", "warning": "[WARNING]", "error": "[ERROR]"}
print(f"{levels.get(level.lower(), '[DEBUG]')} {message}")
#-------------------------------------------------------------------------------
def detectInnerRing(image_path, win_info):
'''Detect the inner ring in image_path, return the processed image and offsets'''
offset_x, offset_y = None, None
image = cv2.imread(image_path)
if image is None:
win_info.add(f"Failed to read image at {image_path}", level="error")
return None, None, None
image_copy = cv2.imread(image_path)
# Conversion en niveaux de gris
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# Appliquer un flou léger pour réduire le bruit
blurred = cv2.GaussianBlur(gray, (9, 9), 0)
# Détection des contours
edges = cv2.Canny(blurred, 100, 180) # 50 et 150 sont les seuils min et max
# Détection des contours
circles = cv2.HoughCircles(edges, cv2.HOUGH_GRADIENT, dp=1, minDist=1000, param1=180, param2=90, minRadius=80, maxRadius=150)
# Si des cercles sont détectés
if circles is not None:
circles = np.round(circles[0, :]).astype("int")
for (x, y, r) in circles:
# Dessiner le cercle détecté
cv2.circle(image, (x, y), r, (0, 255, 0), 4)
# Ajouter un point au centre
cv2.circle(image, (x, y), 5, (0, 0, 255), -1)
win_info.add(f"Circle center detected at position: ({x}, {y})")
else:
win_info.add("No circles detected.", level="warning")
return None, None, None
detected_center_x, detected_center_y = circles[0][0], circles[0][1]
# Centre du crosshair
crosshair_center_x, crosshair_center_y = image.shape[1] // 2, image.shape[0] // 2
win_info.add(f"Image size: ({image.shape[1]}, {image.shape[0]})", level="debug")
win_info.add(f"Crosshair center position: ({crosshair_center_x}, {crosshair_center_y}", level="debug")
# Draw crosshairs on the image
height, width = image.shape[:2]
# Calculate center of the image
center_x, center_y = width // 2, height // 2
# Draw vertical line
cv2.line(image, (center_x, 0), (center_x, height), (0, 0, 255), 1)
# Draw horizontal line
cv2.line(image, (0, center_y), (width, center_y), (0, 0, 255), 1)
cv2.circle(image, (center_x, center_y), 111, (0, 0, 255), 1)
# Calcul de l'offset
offset_x = detected_center_x - crosshair_center_x
offset_y = detected_center_y - crosshair_center_y
win_info.add(f"Offset in X: {offset_x} pixels")
win_info.add(f"Offset in Y: {offset_y} pixels")
# Affichage de l'offset sur l'image
cv2.line(image, (crosshair_center_x, crosshair_center_y), (detected_center_x, detected_center_y), (255, 255, 0), 2)
# Affichage des valeurs de l'offset sur l'image. Positionner le texte près du centre détecté
text_position = (detected_center_x + 10, detected_center_y - 10)
# Préparer le texte à afficher
text_offset_x = f"Offset X: {offset_x} px"
text_offset_y = f"Offset Y: {offset_y} px"
# Afficher les offsets sur l'image
cv2.putText(image, text_offset_x, text_position, cv2.FONT_HERSHEY_SIMPLEX,
0.5, (0, 0, 0), 2)
cv2.putText(image, text_offset_y, (text_position[0], text_position[1] + 25),
cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 0), 2)
return image, offset_x, offset_y
#-------------------------------------------------------------------------------
# Standalone execution
if __name__ == "__main__":
# Path to the image for standalone testing
image_path = "/home/stephane/Documents/ABBA/Soft/Images/InnerRingImages/inner_ring_image.jpg"
# Use the console logger when running standalone
logger = ConsoleLogger()
# Run detection
processed_image, offset_x, offset_y = detectInnerRing(image_path, logger)
# Display intermediate and final images
if processed_image is not None:
# Reload images to show intermediate steps
image = cv2.imread(image_path)
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
blurred = cv2.GaussianBlur(gray, (9, 9), 0)
edges = cv2.Canny(blurred, 100, 180)
cv2.imshow("Image originale", image)
cv2.imshow("Image en niveaux de gris", gray)
cv2.imshow("Image floue", blurred)
cv2.imshow("Contours detectes", edges)
cv2.imshow("Detected Inner Ring", processed_image)
# Save the processed image
output_path = "/home/stephane/Documents/ABBA/Soft/Images/InnerRingImages/processed_inner_ring.jpg"
cv2.imwrite(output_path, processed_image)
logger.add(f"Processed image saved to {output_path}")
cv2.waitKey(0)
cv2.destroyAllWindows()
And here is a result I’m having most of the time :
And rarely, but just to keep me motivated :
Does anybody have a suggestion ?
The resolution and lightning seems alright (to me only ),
But am I missing something with this approach ?
Thanks a lot for your reading time and any advices,
Take care in all cases.