Field Detection
Deteksi lapangan, garis, dan landmark untuk lokalisasi robot
Field Detection
Deteksi lapangan dan garis penting untuk lokalisasi robot di lapangan RoboCup.
Pada praktiknya, kita mulai dengan segmentation untuk memisahkan area lapangan dari objek lain. Mask lapangan ini membantu mengurangi false positive saat mendeteksi garis, gawang, atau landmark.
Field Segmentation
Green Field Detection
Purpose: Membuat mask area lapangan agar deteksi objek lain lebih bersih. Inputs: image BGR. Outputs: mask biner area hijau. Steps:
- Konversi image ke HSV.
- Threshold warna hijau.
- Bersihkan mask dengan morphology. Pitfalls: lighting ekstrem membuat warna hijau bergeser. Validation: mask menutup area lapangan, bukan penonton atau robot.
import cv2
import numpy as np
def detect_field(image):
"""Detect green field area."""
hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
# Green color range
lower_green = np.array([35, 50, 50])
upper_green = np.array([85, 255, 255])
mask = cv2.inRange(hsv, lower_green, upper_green)
# Clean up
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (15, 15))
mask = cv2.morphologyEx(mask, cv2.MORPH_CLOSE, kernel)
mask = cv2.morphologyEx(mask, cv2.MORPH_OPEN, kernel)
return maskLine Detection
Setelah field mask didapat, garis putih bisa dicari dengan threshold pada intensitas putih. Langkah ini biasanya menghasilkan banyak noise, sehingga edge detection (Canny) dan Hough Transform dipakai untuk mengekstrak garis yang valid.
White Line Extraction
Purpose: Menemukan garis putih di atas mask lapangan.
Inputs: image dan optional field_mask.
Outputs: garis hasil Hough dan white_mask.
Steps:
- Konversi ke grayscale.
- Terapkan field mask.
- Threshold putih dan Canny.
- HoughLinesP untuk segment garis. Pitfalls: threshold terlalu rendah memunculkan noise. Validation: garis lapangan muncul sebagai segmen pendek.
def detect_white_lines(image, field_mask=None):
"""Detect white lines on the field."""
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# Apply field mask if available
if field_mask is not None:
gray = cv2.bitwise_and(gray, gray, mask=field_mask)
# Threshold for white
_, white_mask = cv2.threshold(gray, 180, 255, cv2.THRESH_BINARY)
# Detect edges
edges = cv2.Canny(white_mask, 50, 150)
# Hough Line Transform
lines = cv2.HoughLinesP(edges, 1, np.pi/180, 50,
minLineLength=30, maxLineGap=10)
return lines, white_maskLine Classification
Purpose: Mengelompokkan garis berdasarkan orientasi.
Inputs: satu segment garis dan ukuran image.
Outputs: label horizontal, vertical, atau diagonal.
Steps:
- Hitung
dxdandy. - Tentukan angle dalam derajat.
- Klasifikasikan berdasarkan ambang angle.
Pitfalls: segment pendek memberi angle tidak stabil.
Validation: garis samping lapangan dominan
vertical.
def classify_line(line, image_width, image_height):
"""Classify line type (horizontal, vertical, diagonal)."""
x1, y1, x2, y2 = line
dx = abs(x2 - x1)
dy = abs(y2 - y1)
if dx == 0:
return 'vertical'
angle = np.arctan(dy / dx) * 180 / np.pi
if angle < 20:
return 'horizontal'
elif angle > 70:
return 'vertical'
else:
return 'diagonal'Landmark Detection
Landmark seperti gawang dan lingkaran tengah membantu robot menentukan posisi relatifnya di lapangan. Deteksi landmark biasanya lebih sulit karena bentuknya tipis/parsial dan bisa tertutup robot lain.
Goal Post Detection
Purpose: Mendeteksi tiang gawang sebagai kontur putih tinggi.
Inputs: image dan field_mask.
Outputs: daftar goal_posts dengan bbox dan center.
Steps:
- Threshold putih.
- Cari kontur.
- Filter berdasarkan rasio aspek dan tinggi. Pitfalls: robot putih bisa terdeteksi sebagai gawang. Validation: bbox sempit dan tinggi di lokasi gawang.
def detect_goal_posts(image, field_mask):
"""Detect goal posts (white vertical structures)."""
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# White threshold
_, white = cv2.threshold(gray, 200, 255, cv2.THRESH_BINARY)
# Find contours
contours, _ = cv2.findContours(white, cv2.RETR_EXTERNAL,
cv2.CHAIN_APPROX_SIMPLE)
goal_posts = []
for contour in contours:
x, y, w, h = cv2.boundingRect(contour)
# Goal posts are tall and narrow
aspect_ratio = h / w if w > 0 else 0
if aspect_ratio > 3 and h > 50:
goal_posts.append({
'bbox': (x, y, w, h),
'center': (x + w//2, y + h//2)
})
return goal_postsCenter Circle Detection
Purpose: Mendeteksi lingkaran tengah menggunakan Hough Circle.
Inputs: edge image dan field_mask.
Outputs: daftar lingkaran atau None.
Steps:
- Mask edge dengan field mask.
- Jalankan HoughCircles.
- Pilih hasil pertama yang valid. Pitfalls: noise pada edge membuat false circle. Validation: lingkaran muncul saat robot berada dekat tengah.
def detect_center_circle(edges, field_mask):
"""Detect center circle using Hough Circle."""
# Apply field mask
masked_edges = cv2.bitwise_and(edges, edges, mask=field_mask)
circles = cv2.HoughCircles(
masked_edges,
cv2.HOUGH_GRADIENT,
dp=1,
minDist=100,
param1=100,
param2=30,
minRadius=50,
maxRadius=200
)
if circles is not None:
circles = np.uint16(np.around(circles))
return circles[0]
return NoneComplete Field Analyzer
Purpose: Menggabungkan field, line, dan landmark menjadi satu pipeline. Inputs: image BGR. Outputs: dict hasil analisis dan debug overlay. Steps:
- Detect field mask.
- Detect lines dan goal posts.
- Kembalikan hasil untuk lokalisasi. Pitfalls: urutan langkah salah membuat mask tidak konsisten. Validation: overlay menampilkan garis dan gawang dengan stabil.
class FieldAnalyzer:
def __init__(self):
self.green_lower = np.array([35, 50, 50])
self.green_upper = np.array([85, 255, 255])
def analyze(self, image):
"""Complete field analysis."""
# Detect field
field_mask = detect_field(image)
# Detect lines
lines, line_mask = detect_white_lines(image, field_mask)
# Detect landmarks
goal_posts = detect_goal_posts(image, field_mask)
return {
'field_mask': field_mask,
'lines': lines,
'goal_posts': goal_posts,
}
def draw_analysis(self, image, analysis):
"""Draw analysis results on image."""
output = image.copy()
# Draw lines
if analysis['lines'] is not None:
for line in analysis['lines']:
x1, y1, x2, y2 = line[0]
cv2.line(output, (x1, y1), (x2, y2), (0, 0, 255), 2)
# Draw goal posts
for post in analysis['goal_posts']:
x, y, w, h = post['bbox']
cv2.rectangle(output, (x, y), (x+w, y+h), (255, 0, 0), 2)
return outputResources
Next Steps
- Machine Learning - CNN untuk deteksi robust
- Camera Calibration - Proyeksi 3D