golf ball tracking in Python/Opencv with different color balls

Viewed 4255

Just looking for some suggestions on how to approach the problem of golf ball tracking with different color balls.

I've explored using absdiff() to get the difference between frames to track the balls but it also picks up the player and club movement. Also, using HSV to pick up specific color balls but I want to be able to pick up most colors (white, yellow, orange, blue). Thanks.

1 Answers
    1. Do preprocess each frame

    • Apply GaussianBlur

      gaussian_blurr = cv2.GaussianBlur(frame, (22, 22), 0)
      

      You can change the parameters, the above parameters are just examples.

      Assume below is your original frame:

      enter image description here

      Gaussian Blur will be:

      enter image description here

      We apply GaussianBlur for reducing noise and outliers.

    • Convert the frame to the HSV scale.

      hsv = cv2.cvtColor(blurred, cv2.COLOR_BGR2HSV)
      

      enter image description here

      Converting hsv enable us to detect the ball in the current frame.

    • Apply inRange method:

      greenLower = (29, 86, 6)
      greenUpper = (64, 255, 255)
      
      mask = cv2.inRange(hsv, greenLower, greenUpper)
      

      enter image description here

      By defining our upper and lower boundaries, we locate the ball in the frame by declaring, 29 < Hue values < 64, 86 < Saturation < 255, 6 < Value < 255.

    • Apply erode and dilate:

      mask = cv2.erode(mask, None, iterations=2)
      mask = cv2.dilate(mask, None, iterations=2)
      

      enter image description here

    erode and dilate are commonly used for preprocessing images.

    erode removes pixels on object boundaries.

    dilate connecting areas that are separated by spaces. source

    1. Find the center of the ball

  • Find Contours

    cnts = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    

    Contours are used in shape detection and recognition. The output will be an array of ball location. We need the largest contours in the mask to find the center.

    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"]))
    

    minClosingCircle will cover the object with minimum area.

    moments will return the weighted area. source

    Updated: If you want to see the centroid of the ball add the following:


    cv2.circle(frame, center, 5, (0, 0, 255), -1)
    

    Result:

    enter image description here

  • Now, if we combine all frames, the final will be:


  • enter image description here

  • Final with the centroid of the ball:


  • enter image description here

Now, for different kinds of balls, you need to declare upper and lower bounds, as we declared greenUpper and greenLower. Then apply the steps starting from 1.

Full Code:

import cv2
import imutils
import time

greenLower = (29, 86, 6)
greenUpper = (64, 255, 255)

vs = cv2.VideoCapture("input.mp4")
time.sleep(2.0)

while True:
    _, frame = vs.read()

    if frame is None:
        break

    blurred = cv2.GaussianBlur(frame, (11, 11), 0)
    width, height = frame.shape[:2]
    hsv = cv2.cvtColor(blurred, cv2.COLOR_BGR2HSV)
    mask = cv2.inRange(hsv, greenLower, greenUpper)
    mask = cv2.erode(mask, None, iterations=2)
    mask = cv2.dilate(mask, None, iterations=2)
    cnts = cv2.findContours(mask.copy(), cv2.RETR_EXTERNAL,
                            cv2.CHAIN_APPROX_SIMPLE)
    cnts = imutils.grab_contours(cnts)
    center = None

    if len(cnts) > 0:
        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"]))

        # To see the centroid clearly
        if radius > 10:
            cv2.circle(frame, (int(x), int(y)), int(radius), (0, 255, 255), 5)
            cv2.imwrite("circled_frame.png", cv2.resize(frame, (int(height / 2), int(width / 2))))
            cv2.circle(frame, center, 5, (0, 0, 255), -1)

    cv2.imshow("Frame", frame)
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break

vs.release()
cv2.destroyAllWindows()
Related