有没有办法可以计算 OpenCV 中球的反弹?

1 c++ python opencv numpy computer-vision

我使用本指南制作了一个球跟踪程序:https : //www.pyimagesearch.com/2015/09/14/ball-tracking-with-opencv/

我想问一下,是否有一种方法可以告诉我某个球在特定时间内反弹了多少次。甚至任何我可以用来计算球在地面上的反弹的方法,因为我打算使用该程序来跟踪进行篮球运球训练的人。先感谢您 :)

我想做类似这样的事情:https ://youtu.be/OMXYvkryF1I at 2:26

如果有帮助,这是我的代码:

# import the necessary packages

from collections import deque 
#list like data structure will keep prev positions of ball 
#can make a trail of the ball from it 

import numpy as np
import argparse
import imutils 
#this is that guys list of Opencv stuff he uses - got resizing and all - can use pip to get it 
#$ pip install --upgrade imutils

import cv2
import time


# construct the argument parse and parse the arguments
ap = argparse.ArgumentParser()
ap.add_argument("-v", "--video",
    help="C:/Object_detection/models-master/research/object_detection/test_images/multi_angle.mp4") 
#can put path to video here. That is if it is there
#if not there the program will just use the webcam

ap.add_argument("-b", "--buffer", type=int, default=64,
    help="max buffer size")
# this tells max size of deque which is the list with points 

args = vars(ap.parse_args())


##Put lower & upper boundaries of colour 
#colourLow = (0, 135, 30)
#colourHigh = (19, 255, 255)


#Put lower & upper boundaries of colour 
colourLow = (0, 135, 30)
colourHigh = (19, 255, 255)
pts = deque(maxlen=args["buffer"]) #initialises our deque points

# if a video path was not supplied, grab the reference
# to the webcam
# item that tells if we using a video or webcam
if not args.get("video", False):
    cap = cv2.VideoCapture(0) #imutils.Video stream item works good with webcam 

# otherwise, grab a reference to the video file
else:
    cap = cv2.VideoCapture(args["video"]) #this is if the video is supplied


    
#Loop for video frame capturing
while True:
    #calls the read method in our capture module 
    ret, frame = cap.read()

    #if we were running a video from external source and no other frame was taken again for processing
    #it means we reached end of video so we break out of loop
    if frame is None:
        break

    frame = imutils.resize(frame, width=800) #smaller frames means faster processing so we resize
    blurred = cv2.GaussianBlur(frame, (11, 11), 0) #blur reduces picture noise to allow us to see stuff more clearly 
    hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV) # converting frame to HSV

    # we now masking to get the desired colour only
    # we do erosion, dilation and removal of blobs
    mask = cv2.inRange(hsv, colourLow, colourHigh) #locates our object in the frame
    mask = cv2.erode(mask, None, iterations=2) #erosion
    mask = cv2.dilate(mask, None, iterations=2) #removal of blobs

    # Will draw outline of ball and find (x, y) center of ball
    cnts = cv2.findContours(mask.copy(), cv2.RETR_EXTERNAL, 
        cv2.CHAIN_APPROX_SIMPLE)[-2]   #this makes sure contour will work on all opencv items 
    center = None #make the coords of the ball 0 at first 

    
    if len(cnts) > 0:     # only proceed if at least one contour was found
        # finds largest contour mask, then uses this to get minimum enclosing circle and 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"]))  #this & above line get centre coords
        

        # only proceed if the radius meets a minimum size
        if (radius > 30): 
            # 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 list of points
    pts.appendleft(center)

    # loop over set of points 
    for i in range(1, len(pts)):
        #if we don't have tracked points we should ignore them 
        if pts[i - 1] is None or pts[i] is None:
            continue

        ickk = int(np.sqrt(args["buffer"] / float(i + 1)) * 2.5)

        def drawline(img,pt1,pt2,color,thickness=ickk,style='dotted',gap=20):
            dist =((pt1[0]-pt2[0])**2+(pt1[1]-pt2[1])**2)**.5
            pts= []
            for i in  np.arange(0,dist,gap):
                r=i/dist
                x=int((pt1[0]*(1-r)+pt2[0]*r)+.5)
                y=int((pt1[1]*(1-r)+pt2[1]*r)+.5)
                p = (x,y)
                pts.append(p)

            if style=='dotted':
                for p in pts:
                    cv2.circle(img,p,thickness,color,-1)
            else:
                s=pts[0]
                e=pts[0]
                i=0
                for p in pts:
                    s=e
                    e=p
                    if i%2==1:
                        cv2.line(img,s,e,color,thickness)
                    i+=1

        #if we do we will draw point connecting line 
        #gotta define the thickness first
        thickness = int(np.sqrt(args["buffer"] / float(i + 1)) * 2.5)
        #cv2.line(frame, pts[i - 1], pts[i], (0, 0, 255), thickness)
        drawline(frame,pts[i - 1], pts[i],(0, 0, 255),thickness)

    # 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


# cleanup the camera and close any open windows
cap.release()
cv2.destroyAllWindows()
Run Code Online (Sandbox Code Playgroud)

Ian*_*Chu 5

我设置了一个模拟来展示我在评论中所说的内容。基本上,每次相机拍照时(无论您的相机以何种 fps 运行),您都可以获得球的位置。使用该位置,您可以估计速度(位置变化除以时间)。如果该速度的方向突然发生变化,那么您可以将其视为反弹。

在此处输入图片说明

此代码的绝大部分用于设置模拟,出于您的目的可以安全地忽略。这是相关的代码块

    # check if it's time for a snapshot
    camera_timer += dt; # time since last snapshot
    if camera_timer > (1.0 / camera_fps):
        # estimate velocity
        est_vel[0] = (ball_pos[0] - prev_pos[0]) / camera_timer;
        est_vel[1] = (ball_pos[1] - prev_pos[1]) / camera_timer;

        # check if the sign of the velocity has changed
        if sign(est_vel[0]) != sign(prev_est_vel[0]) or sign(est_vel[1]) != sign(prev_est_vel[1]):
            # check for bounces from large change in velocity
            dvx = abs(est_vel[0] - prev_est_vel[0]);
            dvy = abs(est_vel[1] - prev_est_vel[1]);
            change_vel = math.sqrt(dvx*dvx + dvy*dvy);
            if change_vel > bounce_thresh:
                bounce_count += 1;

        # update previous state trackers
        prev_est_vel = est_vel[:];
        prev_pos = ball_pos[:];

        # reset camera timer
        camera_timer = 0;
        snap = True;
Run Code Online (Sandbox Code Playgroud)

如果您想自己玩模拟游戏,这就是全部内容

import cv2
import numpy as np
import time
import math

# get mouse click
click_pos = None;
click = False;
def mouseClick(event, x, y, flags, param):
    # hook to globals
    global click_pos;
    global click;

    # check for left mouseclick 
    if event == cv2.EVENT_LBUTTONDOWN:
        click = True;
        click_pos = (x,y);

# return sign of number
def sign(val):
    if val > 0:
        return 1;
    if val < 0:
        return -1;
    return 0;

# create blank image
res = (600,600,3);
bg = np.zeros(res, np.uint8);
display = np.zeros(res, np.uint8);

# set up click callback
cv2.namedWindow("Display");
cv2.setMouseCallback("Display", mouseClick);
click_force = 1000;

# font stuff
font = cv2.FONT_HERSHEY_SIMPLEX;
fontScale = 1;
fontColor = (255, 100, 0);
thickness = 2;

# make a ball
ball_radius = 20;
ball_pos = [300,300];
ball_vel = [0,0];

# set physics
drag = 0.98;
bounce_mult = 0.95;
grav = -9.8; # acceleration in pixels per second
time_scale = 5.0;

# register click animations
click_anims = [];
anim_dur = 0.25; # seconds
anim_radius = 20; # pixels

# track bounces
prev_pos = ball_pos[:];
est_vel = [0,0];
prev_est_vel = [0,0];
bounce_count = 0;
bounce_thresh = 10; # velocity must have a sudden change greater than this magnitude to count
camera_fps = 24; # we'll only take snapshots at this speed
camera_timer = 0; # time since last snapshot
snap = False;
pic_count = 0;

# loop
done = False;
prev_time = time.time();
while not done:
    # refresh display
    display = np.copy(bg);

    # update timestep
    now_time = time.time();
    dt = now_time - prev_time;
    dt *= time_scale;
    prev_time = now_time;

    # update physics
    # position
    ball_pos[0] += ball_vel[0] * dt;
    ball_pos[1] += ball_vel[1] * dt;

    # velocity
    ball_vel[1] -= grav * dt;
    drag_mult = (1 - ((1 - drag) * dt));
    ball_vel[0] *= drag_mult;
    ball_vel[1] *= drag_mult;

    # check for mouse click
    if click:
        # register animation
        click = False;
        click_anims.append([time.time(), click_pos[:]]);

        # get dist
        dx = ball_pos[0] - click_pos[0];
        dy = ball_pos[1] - click_pos[1];
        dist = math.sqrt(dx*dx + dy*dy);

        # clamp dist
        if dist < 1:
            dist = 1;

        # get force attenuation
        # force = click_force / (dist*dist); # too much
        force = click_force / dist; 

        # get angle and get axial force
        angle = math.atan2(dy, dx);
        xforce = math.cos(angle) * force;
        yforce = math.sin(angle) * force;

        # apply force
        ball_vel[0] += xforce;
        ball_vel[1] += yforce;


    # check for bounce
    # left
    if ball_pos[0] - ball_radius < 0:
        ball_pos[0] = 0 + ball_radius;
        ball_vel[0] *= -bounce_mult;

    # right
    if ball_pos[0] + ball_radius > res[0]:
        ball_pos[0] = res[0] - ball_radius;
        ball_vel[0] *= -bounce_mult;

    # up # +y-axis is down in OpenCV
    if ball_pos[1] - ball_radius < 0:
        ball_pos[1] = 0 + ball_radius;
        ball_vel[1] *= -bounce_mult;

    # down
    if ball_pos[1] + ball_radius > res[1]:
        ball_pos[1] = res[1] - ball_radius;
        ball_vel[1] *= -bounce_mult;

    # check if it's time for a snapshot
    camera_timer += dt; # time since last snapshot
    if camera_timer > (1.0 / camera_fps):
        # estimate velocity
        est_vel[0] = (ball_pos[0] - prev_pos[0]) / camera_timer;
        est_vel[1] = (ball_pos[1] - prev_pos[1]) / camera_timer;

        # check if the sign of the velocity has changed
        if sign(est_vel[0]) != sign(prev_est_vel[0]) or sign(est_vel[1]) != sign(prev_est_vel[1]):
            # check for bounces from large change in velocity
            dvx = abs(est_vel[0] - prev_est_vel[0]);
            dvy = abs(est_vel[1] - prev_est_vel[1]);
            change_vel = math.sqrt(dvx*dvx + dvy*dvy);
            if change_vel > bounce_thresh:
                bounce_count += 1;

        # update previous state trackers
        prev_est_vel = est_vel[:];
        prev_pos = ball_pos[:];

        # reset camera timer
        camera_timer = 0;
        snap = True;

    # draw bounce text
    cv2.putText(display, "Bounces: " + str(bounce_count), (15,40), font,
                fontScale, fontColor, thickness, cv2.LINE_AA);

    # draw ball
    x, y = ball_pos;
    cv2.circle(display, (int(x), int(y)), ball_radius, (220,150,0), -1);

    # draw click animations
    for a in range(len(click_anims)-1, -1, -1):
        # get lifetime
        life = now_time - click_anims[a][0];
        if life > anim_dur:
            del click_anims[a];
        else:
            # draw
            mult = life / anim_dur;
            radius = int(anim_radius * mult);
            if radius > 0:
                val = 255 - int(255 * mult);
                color = [val, val, val];
                cv2.circle(display, click_anims[a][1], radius, color, 2);

    # show
    cv2.imshow("Display", display);
    key = cv2.waitKey(1);

    # # if snapshot, save a picture
    # if snap:
    #   snap = False;
    #   cv2.imwrite("bouncy/" + str(pic_count).zfill(5) + ".png", display);
    #   pic_count += 1;

    # check keypresses
    done = key == ord('q');
Run Code Online (Sandbox Code Playgroud)