Python · OpenCV · RoboDK · HAMK ← Portfolio

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)
IDRobot XRobot Ypx upx 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