epfl-cs358 / 2024sp-robopong

1 stars 0 forks source link

[CV] Efficient ball tracking #6

Closed Amene-Gafsi closed 6 months ago

Amene-Gafsi commented 6 months ago

Develop and implement an efficient real-time ball tracking algorithm.

Amene-Gafsi commented 6 months ago

In order to track the real-time coordinates of a ball, I employed a color-based tracking system using the OpenCV library and Python. The algorithm identifies and tracks the center of the largest green-colored circle in each frame, storing the coordinates in a data structure.

Algorithm Overview:

  1. Video Capture: The algorithm starts by capturing video frames from a webcam or video file. If no video path is provided, it defaults to the webcam.

  2. Frame Processing: Each frame is resized to a consistent width to standardize the input and then blurred using a Gaussian blur. The Gaussian blur is crucial as it reduces high-frequency noise, smoothing out the image. This step enhances the detection process by allowing us to focus on significant structural objects—namely, the ball—without being distracted by minor imperfections in the image.

  3. Color Segmentation: I apply a color filter to isolate the green hues representing the ball. This is done by converting the frame to the HSV color space and creating a binary mask where only the pixels falling within the predefined green color range are white, and all others are black.

  4. Morphological Operations: To further clean the image, the mask undergoes erosion and dilation. Erosion removes small white noise and separates objects connected by thin lines, while dilation restores object size and improves the object's visibility.

  5. Contour Detection: The algorithm then finds contours in the mask. A contour is a curve joining all continuous points along the boundary of a white object in the mask. The largest contour is assumed to be the ball.

  6. Tracking the Ball: The minimum enclosing circle of the largest contour is computed to find the center and radius of the ball. The center coordinates are then recorded.

  7. Data Storage: The coordinates of the ball's center are stored in a deque, which is a data structure similar to a list but with faster append and pop operations at both ends. This feature makes it highly suitable for real-time tracking where the efficiency of data insertion and retrieval is paramount.

Adaptability to Real-World Conditions: To adapt this algorithm for a real-world application in tracking the ball in the pong game, the only adjustments that might be required are the ball radius and color range.

Code :

from collections import deque
from imutils.video import VideoStream
import numpy as np
import argparse
import cv2
import imutils
import time

# construct the argument parse and parse the arguments
ap = argparse.ArgumentParser()
ap.add_argument("-v", "--video",
    help="path to the (optional) video file")
ap.add_argument("-b", "--buffer", type=int, default=64,
    help="max buffer size")
args = vars(ap.parse_args())

# define the lower and upper boundaries of the "green"
# ball in the HSV color space, then initialize the
# list of tracked points
greenLower = (29, 86, 6)
greenUpper = (64, 255, 255)

pts = deque(maxlen=args["buffer"])

# if a video path was not supplied, grab the reference
# to the webcam
if not args.get("video", False):
    vs = VideoStream(src=0).start()

# otherwise, grab a reference to the video file
else:
    vs = cv2.VideoCapture(args["video"])
# allow the camera or video file to warm up
time.sleep(2.0)

# keep looping
while True:
    # grab the current frame
    frame = vs.read()

    # handle the frame from VideoCapture or VideoStream
    frame = frame[1] if args.get("video", False) else frame

    # if we are viewing a video and we did not grab a frame,
    # then we have reached the end of the video
    if frame is None:
        break

    # resize the frame, blur it, and convert it to the HSV
    # color space
    frame = imutils.resize(frame, width=600) #size of board
    blurred = cv2.GaussianBlur(frame, (11, 11), 0)
    hsv = cv2.cvtColor(blurred, cv2.COLOR_BGR2HSV)

    # construct a mask for the color "green", then perform
    # a series of dilations and erosions to remove any small
    # blobs left in the mask
    mask = cv2.inRange(hsv, greenLower, greenUpper)
    mask = cv2.erode(mask, None, iterations=2)
    mask = cv2.dilate(mask, None, iterations=2)

    # find contours in the mask and initialize the current
    # (x, y) center of the ball
    cnts = cv2.findContours(mask.copy(), cv2.RETR_EXTERNAL,
        cv2.CHAIN_APPROX_SIMPLE)
    cnts = imutils.grab_contours(cnts)
    center = None

    # only proceed if at least one contour was found
    if len(cnts) > 0:

        # find the largest contour in the mask, then use
        # it to compute the minimum enclosing circle and
        # centroid
        c = max(cnts, key=cv2.contourArea)
        ((x, y), radius) = cv2.minEnclosingCircle(c)
        M = cv2.moments(c)
        center = (int(M["m10"] / M["m00"]), int(M["m01"] / M["m00"]))

        # only proceed if the radius meets a minimum size
        if radius > 10:
            # print x and y coordinates of the ball
            print("x: ", x, "y: ", y)
            print("center : " ,center[0], center[1])

            # draw the circle and centroid on the frame,
            # then update the list of tracked points
            cv2.circle(frame, (int(x), int(y)), int(radius),
                (0, 255, 255), 2)
            cv2.circle(frame, center, 5, (0, 0, 255), -1)

    # update the points queue
    pts.appendleft(center)

    # show the frame to our screen
    cv2.imshow("Frame", frame)
    key = cv2.waitKey(1) & 0xFF

    # if the 'q' key is pressed, stop the loop
    if key == ord("q"):
        break

# if we are not using a video file, stop the camera video stream
if not args.get("video", False):
    vs.stop()

# otherwise, release the camera
else:
    vs.release()

# close all windows
cv2.destroyAllWindows()