code:
import cv2
from filterpy.kalman import KalmanFilter
import numpy as np

kf = KalmanFilter(dim_x=4, dim_z=2)
kf.x = np.array([0, 0, 0, 0]) # initial state estimate
kf.P = np.eye(4) * 1000 # initial error covariance matrix
kf.F = np.array([[1, 0, 1, 0],
[0, 1, 0, 1],
[0, 0, 1, 0],
[0, 0, 0, 1]]) # state transition matrix
kf.H = np.array([[1, 0, 0, 0],
[0, 1, 0, 0]]) # measurement matrix
kf.R = np.diag([0.1, 0.1]) # measurement noise covariance matrix
kf.Q= np.diag([0.1, 0.1, 0.1, 0.1])
dt=1.0

cap = cv2.VideoCapture('1_1.mp4')
frame_num = 0

# optical flow parameters
lk_params = dict(winSize=(15,15),
maxLevel=4,
criteria=(cv2.TERM_CRITERIA_EPS | cv2.TERM_CRITERIA_COUNT, 10, 0.03),
flags=0)

prev_gray = None
prev_pts = None
motion_regions = []
ball_position = None
ball_speed = None

while True:
    ret, frame = cap.read()
    if ret is False:
         break

    frame_num += 1

# convert the frame to grayscale for optical flow calculation
    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)

# compute the feature points for the first frame
    if prev_gray is None or prev_pts is None:
        prev_gray = gray
        prev_pts = cv2.goodFeaturesToTrack(prev_gray, maxCorners=200, qualityLevel=0.01, minDistance=10, blockSize=3)
        continue

# apply optical flow to estimate the motion between the frames
    pts, status, _ = cv2.calcOpticalFlowPyrLK(prev_gray, gray, prev_pts, None, **lk_params)

    # extract the motion regions based on the optical flow magnitude
    motion_regions = []
    for p, s in zip(pts, status):
        if s == 1:
            x, y = p.ravel()
            motion_regions.append((int(x), int(y)))
            motion_regions = np.array(motion_regions)

    if len(motion_regions) > 0:
    # calculate the centroid of the motion regions to determine the position of the ball
        ball_position = np.mean(motion_regions, axis=0).astype(np.float32)

    if ball_speed is not None:
    # predict where the ball will be in the next frame based on its position and velocity
        ball_position += dt * ball_speed

    # update the Kalman filter with the ball position measurement
    kf.predict()
    kf.update(ball_position)

    # get the filtered ball position as the predicted next position
    next_position = kf.x[:2]

    # update the ball speed based on the Kalman filter output
    ball_speed = next_position - ball_position

    # check if the ball is outside of the frame or changes direction, indicating a bounce
    if next_position[0] < 0 or next_position[0] > frame.shape[1] or next_position[1] < 0 or next_position[1] > frame.shape[0] or np.linalg.norm(ball_speed) < 1e-3:
        print("Bounce detected at frame ", frame_num)

    # visualize the bounce detection as a red circle around the ball position
    cv2.circle(frame, (int(ball_position[0]), int(ball_position[1])), 10, (0, 0, 255), 4)

    # visualize the ball position and predicted next position as blue and green circles, respectively
    cv2.circle(frame, (int(ball_position[0]), int(ball_position[1])), 5, (255, 0, 0), 2)
    cv2.circle(frame, (int(next_position[0]), int(next_position[1])), 5, (0, 255, 0), 2)

    # update the previous frame and feature points for the next iteration
    prev_gray = gray
    prev_pts = cv2.goodFeaturesToTrack(prev_gray, maxCorners=200, qualityLevel=0.01, minDistance=10, blockSize=3)

    cv2.putText(frame, f'Frame: {frame_num}', (10, 50), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2)
    cv2.imshow('raw', frame)

    if cv2.waitKey(1) & 0xFF == ord('q'):
      break

    cap.release()
    cv2.destroyAllWindows()
error:
Traceback (most recent call last):
  File "/Users/surabhi/Documents/kalman/b2.py", line 57, in <module>
    motion_regions.append((int(x), int(y)))
    ^^^^^^^^^^^^^^^^^^^^^
AttributeError: 'numpy.ndarray' object has no attribute 'append'