From f0007d35c45e0813bc2b95b5ce7bf558376a3767 Mon Sep 17 00:00:00 2001 From: Maolin Li Date: Sat, 23 Sep 2023 18:26:02 +0200 Subject: [PATCH 1/7] create vidoes --- src/contest/capture.py | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/src/contest/capture.py b/src/contest/capture.py index 7869cd5..7f3d06a 100644 --- a/src/contest/capture.py +++ b/src/contest/capture.py @@ -61,6 +61,7 @@ from contest.game import Actions from contest.game import GameStateData, Game, Grid, Configuration from contest.util import nearestPoint, manhattanDistance +from contest.captureGraphicsDisplay import * #DIR_SCRIPT = sys.path[0] + "/src/contest/" import contest @@ -1018,7 +1019,7 @@ def load_agents(is_red, agent_file, cmd_line_args): return create_team_func(indices[0], indices[1], is_red, **args) -def replay_game(layout, agents, actions, display, length, red_team_name, blue_team_name, wait_end=True, delay=1): +def replay_game(layout, agents, actions, display, length, red_team_name, blue_team_name, replay_filename,wait_end=True, delay=1): rules = CaptureRules() game = rules.new_game(layout, agents, display, length, False, False) state = game.state @@ -1033,6 +1034,9 @@ def replay_game(layout, agents, actions, display, length, red_team_name, blue_te display.update(state.data) # Allow for game specific conditions (winning, losing, etc.) rules.process(state, game) + base_name = os.path.basename(replay_filename).replace(".replay", "") + saveFrame(base_name) + time.sleep(delay) game.game_over = True @@ -1066,10 +1070,20 @@ def replay_game(layout, agents, actions, display, length, red_team_name, blue_te input("PRESS ENTER TO CONTINUE") except: print("END") + + if SAVE_POSTSCRIPT: + # Convert all PostScript files to PNG + convert_all_ps_to_png(base_name) + # Create videos + output_file = os.path.join(PNG_DIR, f'{os.path.basename(replay_filename).replace(".replay", ".mp4")}') + create_video_from_pngs(base_name) + + #clear files + clear_directory(PS_DIR) + clear_directory(PNG_DIR) display.finish() - def run_games(layouts, agents, display, length, num_games, record, num_training, red_team_name, blue_team_name, contest_name="default", mute_agents=False, catch_exceptions=False, delay_step=0, match_id=0): rules = CaptureRules() From d3352456951d4e37e88e74c930828beadf3b09a7 Mon Sep 17 00:00:00 2001 From: Maolin Li Date: Mon, 25 Sep 2023 13:45:00 +0200 Subject: [PATCH 2/7] test push --- src/contest/capture.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/contest/capture.py b/src/contest/capture.py index 7f3d06a..c57d267 100644 --- a/src/contest/capture.py +++ b/src/contest/capture.py @@ -11,7 +11,6 @@ # Student side autograding was added by Brad Miller, Nick Hay, and # Pieter Abbeel (pabbeel@cs.berkeley.edu). - # capture.py # ---------- # Licensing Information: Please do not distribute or publish solutions to this From e70253c0abdbf3f1256aa2a5c03a25784f866193 Mon Sep 17 00:00:00 2001 From: Maolin Li Date: Mon, 25 Sep 2023 13:51:14 +0200 Subject: [PATCH 3/7] conver pngs to mp4 --- src/contest/captureGraphicsDisplay.py | 81 ++++++++++++++++++++++++--- 1 file changed, 72 insertions(+), 9 deletions(-) diff --git a/src/contest/captureGraphicsDisplay.py b/src/contest/captureGraphicsDisplay.py index 8f1bbfe..d5c0a9f 100644 --- a/src/contest/captureGraphicsDisplay.py +++ b/src/contest/captureGraphicsDisplay.py @@ -802,17 +802,80 @@ def add(x, y): # convert -delay 7 -loop 1 -compress lzw -layers optimize frame* out.gif # convert is part of imagemagick (freeware) -SAVE_POSTSCRIPT = False -POSTSCRIPT_OUTPUT_DIR = 'frames' +SAVE_POSTSCRIPT = True +PS_DIR = './frames' +PNG_DIR = './frames_png' FRAME_NUMBER = 0 import os +import subprocess +import shutil +def convert_ps_to_png(ps_file, png_file): + try: + cmd = ["convert", ps_file, png_file] + subprocess.run(cmd, check=True) + except subprocess.CalledProcessError as e: + print(f"Error converting {ps_file} to {png_file}: {e}") -def saveFrame(): - "Saves the current graphical output as a postscript file" - global SAVE_POSTSCRIPT, FRAME_NUMBER, POSTSCRIPT_OUTPUT_DIR - if not SAVE_POSTSCRIPT: return - if not os.path.exists(POSTSCRIPT_OUTPUT_DIR): os.mkdir(POSTSCRIPT_OUTPUT_DIR) - name = os.path.join(POSTSCRIPT_OUTPUT_DIR, 'frame_%08d.ps' % FRAME_NUMBER) +def convert_all_ps_to_png(base_name): + for i in range(FRAME_NUMBER): + ps_name = os.path.join(PS_DIR, f'{base_name}_frame_%08d.ps' % i) + png_name = os.path.join(PNG_DIR, f'{base_name}_frame_%08d.png' % i) + convert_ps_to_png(ps_name, png_name) + + +def convert_pngs_to_mp4(base_name): + """ + Convert PNG images in PNG_DIR with the given base_name to an MP4 video. + """ + output_file = os.path.join('./contest_video', f'{base_name}.mp4') + os.makedirs('./contest_video', exist_ok=True) + + try: + cmd = [ + 'ffmpeg', + '-framerate', '30', + '-i', os.path.join(PNG_DIR, f'{base_name}_frame_%08d.png'), + '-vf', 'pad=ceil(iw/2)*2:ceil(ih/2)*2', + '-c:v', 'libx264', + '-pix_fmt', 'yuv420p', + output_file + ] + subprocess.run(cmd, check=True) + except subprocess.CalledProcessError as e: + print(f"Error creating MP4 for {base_name}: {e}") + + + +def create_video_from_pngs(base_name, output_file): + convert_pngs_to_mp4(base_name, output_file) + +def clear_directory(directory): + """ + Clear all files and subdirectories in the given directory. + """ + for filename in os.listdir(directory): + file_path = os.path.join(directory, filename) + try: + if os.path.isfile(file_path) or os.path.islink(file_path): + os.unlink(file_path) + elif os.path.isdir(file_path): + shutil.rmtree(file_path) + except Exception as e: + print(f'Failed to delete {file_path}. Reason: {e}') + + +def saveFrame(base_name): + global SAVE_POSTSCRIPT, FRAME_NUMBER, PS_DIR + + if not SAVE_POSTSCRIPT: + return FRAME_NUMBER + + if not os.path.exists(PS_DIR): + os.mkdir(PS_DIR) + + ps_name = os.path.join(PS_DIR, f'{base_name}_frame_%08d.ps' % FRAME_NUMBER) FRAME_NUMBER += 1 - writePostscript(name) # writes the current canvas + writePostscript(ps_name) # 假设这个函数是用来保存 PostScript 文件的 + + return FRAME_NUMBER From 1d899583c5dbd2e5f566ab6ba67a319d6f3ce16f Mon Sep 17 00:00:00 2001 From: Maolin Li Date: Mon, 25 Sep 2023 14:04:03 +0200 Subject: [PATCH 4/7] modify note --- src/contest/captureGraphicsDisplay.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/contest/captureGraphicsDisplay.py b/src/contest/captureGraphicsDisplay.py index d5c0a9f..ebce807 100644 --- a/src/contest/captureGraphicsDisplay.py +++ b/src/contest/captureGraphicsDisplay.py @@ -876,6 +876,6 @@ def saveFrame(base_name): ps_name = os.path.join(PS_DIR, f'{base_name}_frame_%08d.ps' % FRAME_NUMBER) FRAME_NUMBER += 1 - writePostscript(ps_name) # 假设这个函数是用来保存 PostScript 文件的 + writePostscript(ps_name) return FRAME_NUMBER From 1c579a6b80dee0ef4103161f1e5568883592593a Mon Sep 17 00:00:00 2001 From: Maolin Li Date: Mon, 25 Sep 2023 14:09:06 +0200 Subject: [PATCH 5/7] del codes --- src/contest/capture.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/contest/capture.py b/src/contest/capture.py index c57d267..3c0e34a 100644 --- a/src/contest/capture.py +++ b/src/contest/capture.py @@ -1074,7 +1074,6 @@ def replay_game(layout, agents, actions, display, length, red_team_name, blue_te # Convert all PostScript files to PNG convert_all_ps_to_png(base_name) # Create videos - output_file = os.path.join(PNG_DIR, f'{os.path.basename(replay_filename).replace(".replay", ".mp4")}') create_video_from_pngs(base_name) #clear files From 5203a632c2d3d026056e2daf4939c904bf1dd050 Mon Sep 17 00:00:00 2001 From: Maolin Li Date: Mon, 25 Sep 2023 14:16:33 +0200 Subject: [PATCH 6/7] modify clear function --- src/contest/capture.py | 2 ++ src/contest/captureGraphicsDisplay.py | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/contest/capture.py b/src/contest/capture.py index 3c0e34a..ec95bfa 100644 --- a/src/contest/capture.py +++ b/src/contest/capture.py @@ -1019,6 +1019,8 @@ def load_agents(is_red, agent_file, cmd_line_args): def replay_game(layout, agents, actions, display, length, red_team_name, blue_team_name, replay_filename,wait_end=True, delay=1): + clear_directory(PS_DIR) + clear_directory(PNG_DIR) rules = CaptureRules() game = rules.new_game(layout, agents, display, length, False, False) state = game.state diff --git a/src/contest/captureGraphicsDisplay.py b/src/contest/captureGraphicsDisplay.py index ebce807..465f6b8 100644 --- a/src/contest/captureGraphicsDisplay.py +++ b/src/contest/captureGraphicsDisplay.py @@ -847,8 +847,8 @@ def convert_pngs_to_mp4(base_name): -def create_video_from_pngs(base_name, output_file): - convert_pngs_to_mp4(base_name, output_file) +def create_video_from_pngs(base_name): + convert_pngs_to_mp4(base_name) def clear_directory(directory): """ From 4c95c9090aca6f225bd117f497f4a4db86abd776 Mon Sep 17 00:00:00 2001 From: Maolin Li Date: Mon, 25 Sep 2023 16:46:16 +0200 Subject: [PATCH 7/7] generating video --- src/contest/capture.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/contest/capture.py b/src/contest/capture.py index ec95bfa..49090fa 100644 --- a/src/contest/capture.py +++ b/src/contest/capture.py @@ -866,6 +866,7 @@ def read_command(argv): recorded['red_team_name'] = parsed_options.red recorded['blue_team_name'] = parsed_options.blue recorded['wait_end'] = False + recorded['replay_filename'] = parsed_options.replay replay_game(**recorded) sys.exit(0) @@ -879,7 +880,7 @@ def read_command(argv): recorded['red_team_name'] = parsed_options.red recorded['blue_team_name'] = parsed_options.blue recorded['wait_end'] = False - + recorded['replay_filename'] = parsed_options.replay replay_game(**recorded) sys.exit(0)