如何检测类瓶圆柱物体是否与相机光轴垂直?
Got it, let's figure out how to check if a bottle-shaped cylinder is perpendicular to the camera's optical axis—since you don't need high precision, we can keep this straightforward and avoid overcomplicating things!
Your input has a huge advantage: a solid-color background paired with a single bottle-like cylinder. We can leverage the contour shape features of the object to make the call:
- When the object is perpendicular to the optical axis, it’ll appear as either a perfectly circular end face or a neat rectangular side profile from the camera’s view.
- When it’s not perpendicular (like in a slight top-down shot or tilted angle), the end face will stretch into an ellipse (with a big gap between major and minor axes), and the side profile will become a trapezoid or a rotated rectangle.
1. Preprocessing: Separate Object from Background
The solid background makes this step trivial:
- Convert the image to grayscale, then use binary thresholding (
cv2.threshold()) to split the object from the background. - Use morphological opening (
cv2.morphologyEx()) to wipe out tiny noise spots that might mess up contour detection.
2. Extract Contour Features & Make the Judgment
Grab the largest contour (that’s your target object), then pick a logic based on how the object is facing the camera:
Scenario 1: Object’s End Face is Facing the Camera
Fit a minimum enclosing ellipse to the contour, then calculate the ratio of its major axis to minor axis:
- If perpendicular, the ellipse is nearly a circle, so the ratio is close to 1.
- If not perpendicular, the ellipse gets stretched, and the ratio will be much larger than 1.
Scenario 2: Object’s Side Profile is Facing the Camera
Fit a minimum enclosing rectangle to the contour, then check its rotation angle:
- If perpendicular, the rectangle is "straight" (rotation angle is close to 0° or 90°).
- If not perpendicular, the rectangle will have a noticeable tilt, or the contour will approximate a trapezoid.
Here’s a simple implementation that works for both scenarios:
import cv2 import numpy as np def is_cylinder_perpendicular(img_path, ratio_threshold=0.15, angle_threshold=5): # Read the input image img = cv2.imread(img_path) if img is None: return False # Preprocess to isolate the object gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # Adjust THRESH_BINARY/THRESH_BINARY_INV based on your object/background colors _, binary = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY_INV) # Remove small noise kernel = np.ones((3, 3), np.uint8) binary = cv2.morphologyEx(binary, cv2.MORPH_OPEN, kernel) # Extract the main object contour contours, _ = cv2.findContours(binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) if not contours: return False main_contour = max(contours, key=cv2.contourArea) # Handle end face scenario (fit ellipse) if len(main_contour) >= 5: ellipse = cv2.fitEllipse(main_contour) major_axis, minor_axis = ellipse[1] # Ensure major axis is the longer one if major_axis < minor_axis: major_axis, minor_axis = minor_axis, major_axis axis_ratio = major_axis / minor_axis # Check if ratio is close to 1 return abs(axis_ratio - 1) < ratio_threshold else: # Handle side profile scenario (fit rectangle) rect = cv2.minAreaRect(main_contour) _, _, angle = rect # Normalize angle to 0-45° range if angle > 45: angle = 90 - angle # Check if rectangle is "straight" return abs(angle) < angle_threshold
- If you know your objects are mostly facing one way (end face/side profile), you can strip out the unused logic to simplify the code.
- Tweak
ratio_thresholdandangle_thresholdbased on your samples: since you don’t need high precision, feel free to set them a bit loose (e.g., ratio_threshold = 0.2). - If your background isn’t grayscale, use
cv2.inRange()with RGB color ranges for cleaner object segmentation.
内容的提问来源于stack exchange,提问作者chamoda

