Given a certain quality control requirement, we implemented an automated system that measures certain dimensions of a steel plate production line. The problem is sometimes the system is not as robust as needed and the chosen pixel by the system does not reflect what our human reasoning considers the true corner. The image is about 17 MPixels-
This simplified code snipet should represent our measuring process:
def contrast_stretch(image, multiplier=1.0):
min_val = np.min(image)
max_val = np.max(image)
stretched = (image - min_val) * (255 / (max_val - min_val) * multiplier)
stretched = np.clip(stretched, 0, 255).astype(np.uint8)
return stretched
def distance(pt1, pt2):
return math.sqrt((pt2[0] - pt1[0]) ** 2 + (pt2[1] - pt1[1]) ** 2)
def measure_diagonals(image, contours, px_to_mm):
refined_corners = []
for cnt in contours:
rect = cv2.minAreaRect(cnt)
box = cv2.boxPoints(rect)
box = np.int0(box)
corners = cv2.cornerSubPix(image, np.float32(box), (5, 5), (-1, -1), (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.1))
refined_corners.append(corners)
if len(refined_corners) >= 2:
d1 = distance(refined_corners[0][0], refined_corners[0][2])
d2 = distance(refined_corners[1][0], refined_corners[1][2])
m1 = d1 * px_to_mm
m2 = d2 * px_to_mm
diff = abs(m2 - m1)
return m1, m2, diff
return None, None, None
# Load calibration data
calibration_data = load_calibration_data(calibration_data_path)
mtx = calibration_data["mtx"]
dist = calibration_data["dist"]
# Load image
frame = cv2.imread(img_path)
# Undistort image
frame_undistorted = cv2.undistort(frame, mtx, dist, None, mtx)
# Convert to grayscale
gray = cv2.cvtColor(frame_undistorted, cv2.COLOR_BGR2GRAY)
# Apply bilateral filter
filtered_image = cv2.bilateralFilter(gray, 9, 125, 25)
# Enhance contrast
enhanced_image = contrast_stretch(filtered_image)
# Detect contours
_, edges = cv2.threshold(enhanced_image, 140, 255, cv2.THRESH_BINARY)
contours, _ = cv2.findContours(edges, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
# Measure diagonals
px_to_mm = 0.1 # Example conversion factor
m1, m2, diff = measure_diagonals(enhanced_image, contours, px_to_mm)
This is an example of a correctly chosen corner:
Threshold for ROI generation (white dot), then rounded subpixel(black dot)
This is an example of a wrongly chosen corner:
In red what we know is the true corner
I am aware we should improve our ilumination. We are measuring a big area (>4 meters) and it’s extremelly challenging to have an illumination system that generates a bright, homogeneous image, so we apply tons of software corrections such as bilateral filter, gain and contrast enhancers.
Iván Carbone is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.