BASCORRO
LearningComputer Vision

Field Detection

Deteksi lapangan, garis, dan landmark untuk lokalisasi robot

Computer Vision Fundamentals
0 dari 11 halaman selesai
In Progress
Scroll sampai 80% untuk menandai halaman selesai.

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:

  1. Konversi image ke HSV.
  2. Threshold warna hijau.
  3. 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 mask

Line 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:

  1. Konversi ke grayscale.
  2. Terapkan field mask.
  3. Threshold putih dan Canny.
  4. 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_mask

Line Classification

Purpose: Mengelompokkan garis berdasarkan orientasi. Inputs: satu segment garis dan ukuran image. Outputs: label horizontal, vertical, atau diagonal. Steps:

  1. Hitung dx dan dy.
  2. Tentukan angle dalam derajat.
  3. 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:

  1. Threshold putih.
  2. Cari kontur.
  3. 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_posts

Center Circle Detection

Purpose: Mendeteksi lingkaran tengah menggunakan Hough Circle. Inputs: edge image dan field_mask. Outputs: daftar lingkaran atau None. Steps:

  1. Mask edge dengan field mask.
  2. Jalankan HoughCircles.
  3. 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 None

Complete Field Analyzer

Purpose: Menggabungkan field, line, dan landmark menjadi satu pipeline. Inputs: image BGR. Outputs: dict hasil analisis dan debug overlay. Steps:

  1. Detect field mask.
  2. Detect lines dan goal posts.
  3. 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 output

Resources


Next Steps

On this page