Skip to content

Commit

Permalink
ffmpy + backend frontend integration
Browse files Browse the repository at this point in the history
  • Loading branch information
bzhang1945 committed Oct 27, 2023
1 parent 1d26a4a commit 6f33d27
Show file tree
Hide file tree
Showing 6 changed files with 46 additions and 62 deletions.
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@ gdown
flake8
yapf
isort==4.3.21
imageio

# Processing
scikit-learn
Expand All @@ -53,6 +52,7 @@ hydralit_components>=1.0.10

# Misc
pylint
ffmpy

# Additional Dependencies for Pose Estimation
json5
Expand Down
2 changes: 2 additions & 0 deletions src/modelrunner.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ def run(self):
with open('tmp/output.pickle', 'rb') as f:
self.output_dict = pickle.load(f)


def pose(self):
model = YOLO('src/pose_estimation/best.pt')
results = model(
Expand All @@ -64,6 +65,7 @@ def pose(self):
)
self.pose_estimator.estimate_pose(results = results)


def fetch_output(self) -> Tuple[str, str, str]:
"""
Converts the people and ball model output in self.output.dict into txt files.
Expand Down
41 changes: 6 additions & 35 deletions src/processing/render.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,11 @@
import random
import os
import numpy as np
import ffmpy
import subprocess
import sys

from state import GameState


# pass in homo matrix + +
# implement video reencoding
class VideoRender:
def __init__(self, homography):
self._TRUE_PATH = os.path.join("data", "true_map.png")
Expand All @@ -23,39 +20,13 @@ def reencode(self, input_path, output_path):
"""
Re-encodes a MPEG4 video file to H.264 format. Overrides existing output videos if present.
Deletes the unprocessed video when complete.
Ensures ffmpeg dependency is installed
"""

if sys.platform != "darwin":
print("Designed to install dependency for macOS")
else:
try:
# check if it is installed already
subprocess.run(
["brew", "list", "ffmpeg"],
check=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
)
except:
# install dependency
print("Installing ffmpeg")
try:
subprocess.run(
["brew", "install", "ffmpeg"],
check=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
)
except:
print("Error installing ffmpeg")
return

reencode_command = (
f"ffmpeg -y -i {input_path} -vcodec libx264 -c:a copy {output_path}"
ff = ffmpy.FFmpeg(
inputs={input_path: None},
outputs={output_path: "-vcodec libx264 -c:a copy -y"},
)
os.system(reencode_command)
# os.remove(input_path)
ff.run()


def render_video(self, state: GameState, filename: str, fps: int = 30):
"""
Expand Down
15 changes: 12 additions & 3 deletions src/processrunner.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,33 +28,40 @@ def __init__(
self.output_video_path_reenc = output_video_path_reenc
self.state: GameState = GameState()


def run_parse(self):
"Runs parse module over SORT (and pose later) outputs to update GameState"
parse.parse_sort_output(self.state, self.players_tracking)
parse.parse_sort_output(self.state, self.ball_tracking)


def run_possession(self):
self.state.filter_players(threshold=100)
self.state.recompute_possession_list(threshold=20, join_threshold=20)
self.state.recompute_pass_from_possession()


def run_team_detect(self):
team.split_team(self.state)


def run_shot_detect(self):
shot.shots(self.state, window=5)


def run_courtline_detect(self):
"""Runs courtline detection."""
c = court.Render(self.video_path)
self.homography = c.get_homography()


def run_video_render(self):
"""Runs video rendering and reencodes, stores to output_video_path_reenc."""
videoRender = render.VideoRender(self.homography)
videoRender.render_video(self.state, self.output_video_path)
videoRender.reencode(self.output_video_path, self.output_video_path_reenc)


def run(self):
"""
Runs all processing and statistics.
Expand All @@ -63,14 +70,16 @@ def run(self):
self.run_possession()
self.run_team_detect()
self.run_shot_detect()
print('G, T, S detect fine')
print('parsing, stat processing complete.')
self.run_courtline_detect()
print('courtline detect fine')
print('courtline detect complete.')
self.run_video_render()
print('video render fine')
print('video render complete.')


def get_results(self):
"""
Returns string of processed statistics.
"""
print(str(state.todict(self.state)))
return str(state.todict(self.state))
46 changes: 24 additions & 22 deletions src/state.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,27 @@ def todict(obj):
else:
return obj # Base case: return the original value

# def __repr__(self) -> str:
# result_dict = {
# "Rim coordinates": str(self.rim) if len(self.rim) > 0 else "None",
# "Backboard coordinates": str(self.backboard)
# if len(self.rim) > 0
# else "None",
# "Court lines coordinates": "None",
# "Number of frames": str(len(self.frames)),
# "Number of players": str(len(self.players)),
# "Number of passes": str(len(self.passes)),
# "Team 1": str(self.team1),
# "Team 2": str(self.team2),
# "Team 1 Score": str(self.score1),
# "Team 2 Score": str(self.score2),
# "Team 1 Possession": str(self.team1_pos),
# "Team 2 Possession": str(self.team2_pos),
# }
# for player in self.players:
# result_dict[player] = str(self.players[player])

# return str(result_dict)

# of importance for SORT object type enumerating
class ObjectType(Enum):
Expand Down Expand Up @@ -477,6 +498,7 @@ def filter_poss(self, lst: list, threshold: int = 20):
else:
i += 1 # next interval


def filter_players(self, threshold: int):
"removes all players which appear for less than [threshold] frames"
self.recompute_frame_count()
Expand All @@ -485,6 +507,7 @@ def filter_players(self, threshold: int):
if v.frames < threshold:
self.players.pop(k)


def recompute_pass_from_possession(self):
"Recompute passes naively from possession list"
self.passes: dict = {} # reset pass dictionary
Expand All @@ -499,6 +522,7 @@ def recompute_pass_from_possession(self):
p2 = self.possessions[i + 1].playerid
self.passes[p1][p2] += 1


## TODO Update for backend
def update_scores(self, madeshot_list):
"""
Expand Down Expand Up @@ -536,25 +560,3 @@ def update_scores(self, madeshot_list):
self.score1 += 2
else:
self.score2 += 2

# def __repr__(self) -> str:
# result_dict = {
# "Rim coordinates": str(self.rim) if len(self.rim) > 0 else "None",
# "Backboard coordinates": str(self.backboard)
# if len(self.rim) > 0
# else "None",
# "Court lines coordinates": "None",
# "Number of frames": str(len(self.frames)),
# "Number of players": str(len(self.players)),
# "Number of passes": str(len(self.passes)),
# "Team 1": str(self.team1),
# "Team 2": str(self.team2),
# "Team 1 Score": str(self.score1),
# "Team 2 Score": str(self.score2),
# "Team 1 Possession": str(self.team1_pos),
# "Team 2 Possession": str(self.team2_pos),
# }
# for player in self.players:
# result_dict[player] = str(self.players[player])

# return str(result_dict)
2 changes: 1 addition & 1 deletion src/view/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
if "state" not in st.session_state:
st.session_state.state = 0
st.session_state.logo = "src/view/static/basketball.png"
with open("data/short_new_1.mp4", "rb") as file:
with open("data/training_data.mp4", "rb") as file:
st.session_state.video_file = io.BytesIO(file.read())
st.session_state.processed_video = None
st.session_state.result_string = None
Expand Down

0 comments on commit 6f33d27

Please sign in to comment.