Machine Vision
// HAMK Machine Vision Course · Python + OpenCV + RoboDK + Dobot MG400 · Group 11
Task A — RGB Channel Separation (OpenCV)
Original Image (BGR→RGB)
Red Channel
Green Channel
Blue Channel
Image shape: 480 × 640 × 3 ·
dtype: uint8 ·
Channel order: BGR (OpenCV) → RGB (matplotlib)
Pixel Intensity Histogram
Segmentation Pipeline — Step by Step
① Original
② Grayscale
③ CLAHE+Blur
④ Otsu Threshold
⑤ Morphology
⑥ Detected Objects
Detected Objects — connectedComponentsWithStats
Histogram (Grayscale)
Otsu threshold: — · Objects kept: — · Min area: 1244 px
Camera → Robot Coordinate Mapping (Homography)
Click anywhere on the image to map pixel coordinates → Dobot MG400 robot coordinates. 8-point calibration, cv2.findHomography().
📌 Click to get robot coordinates · 8 calibration tiles shown
Last Click
Pixel: —
Robot X: — mm
Robot Y: — mm
Calibration Data (8 pts)
| ID | Robot X | Robot Y | px u | px v |
Homography Matrix H
Mean error: 0.8 mm · Max error: 1.9 mm
Segmentation Pipeline — Python + OpenCV
import cv2
import numpy as np
import matplotlib.pyplot as plt
# ── 1. Load and convert ──────────────────────────────
img = cv2.imread('robodk_snapshot.png')
img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# ── 2. Contrast enhancement (CLAHE) + denoise ────────
clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8,8))
gray_clahe = clahe.apply(gray)
blurred = cv2.GaussianBlur(gray_clahe, (5,5), 0)
# ── 3. Otsu thresholding (objects darker than BG) ────
_, binary = cv2.threshold(
blurred, 0, 255,
cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU
)
# ── 4. Morphological cleanup ─────────────────────────
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5,5))
opened = cv2.morphologyEx(binary, cv2.MORPH_OPEN, kernel)
cleaned = cv2.morphologyEx(opened, cv2.MORPH_CLOSE, kernel)
# ── 5. Connected components ──────────────────────────
num_labels, labeled, stats, centroids = cv2.connectedComponentsWithStats(
cleaned, connectivity=4, ltype=cv2.CV_32S
)
MIN_AREA = 1244 # px — area filter threshold
kept = [(stats[i], centroids[i]) for i in range(1, num_labels)
if stats[i][cv2.CC_STAT_AREA] >= MIN_AREA]
print(f"Found {len(kept)} objects")
# ── 6. Annotate ──────────────────────────────────────
annotated = img_rgb.copy()
for (stat, (cx, cy)) in kept:
x, y, w, h = stat[:4]
cv2.rectangle(annotated, (x,y), (x+w,y+h), (0,255,0), 2)
cv2.circle(annotated, (int(cx),int(cy)), 5, (255,0,0), -1)
cv2.putText(annotated, f"({int(cx)},{int(cy)})",
(x+2, y-6), cv2.FONT_HERSHEY_SIMPLEX,
0.4, (0,255,0), 1)
Homography — pixel_to_robot(u, v, H)
import numpy as np
import cv2
# Calibration points (8 tiles at known robot positions)
img_pts = np.array([[604,717],[609,1235],[997,951],[721,468],
[476,482],[484,973],[847,586],[740,962]], dtype=np.float32)
robot_pts = np.array([[350,0],[350,100],[275,50],[325,-50],
[375,-75],[375,50],[300,-25],[325,50]], dtype=np.float32)
H, _ = cv2.findHomography(img_pts, robot_pts, method=0)
def pixel_to_robot(u, v, H):
p = np.array([u, v, 1.0], dtype=np.float32).reshape(3,1)
pr = H @ p
pr = pr / pr[2,0] # homogeneous → Euclidean
return pr[0,0], pr[1,0] # (X_mm, Y_mm)
# Mean calibration error: 0.8 mm — Max: 1.9 mm