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

Gaussian Blur will be:

We apply GaussianBlur for reducing noise and outliers.
Convert the frame to the HSV scale.
hsv = cv2.cvtColor(blurred, cv2.COLOR_BGR2HSV)

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)

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)

erode and dilate are commonly used for preprocessing images.
erode removes pixels on object boundaries.
dilate connecting areas that are separated by spaces. source
-
- 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:

Now, if we combine all frames, the final will be:
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()