From f6a2a6a9aa8df56ee4fff7001599182bf35d96c3 Mon Sep 17 00:00:00 2001 From: Tao Peng Date: Tue, 24 Jun 2025 13:11:05 -0700 Subject: [PATCH 1/5] fix: sample video quality --- mapillary_tools/ffmpeg.py | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/mapillary_tools/ffmpeg.py b/mapillary_tools/ffmpeg.py index 0125150ae..234cafe14 100644 --- a/mapillary_tools/ffmpeg.py +++ b/mapillary_tools/ffmpeg.py @@ -170,11 +170,9 @@ def extract_frames( if stream_idx is not None: stream_selector = ["-map", f"0:{stream_idx}"] ouput_template = f"{sample_prefix}_{stream_idx}_%06d{FRAME_EXT}" - stream_specifier = f"{stream_idx}" else: stream_selector = [] ouput_template = f"{sample_prefix}_{NA_STREAM_IDX}_%06d{FRAME_EXT}" - stream_specifier = "v" cmd: list[str] = [ # global options should be specified first @@ -186,7 +184,7 @@ def extract_frames( # filter videos *["-vf", f"fps=1/{sample_interval}"], # video quality level (or the alias -q:v) - *[f"-qscale:{stream_specifier}", "2"], + *["-qscale:v", "2"], # -q:v=1 is the best quality but larger image sizes # see https://stackoverflow.com/a/10234065 # *["-qscale:v", "1", "-qmin", "1"], @@ -228,11 +226,9 @@ def extract_specified_frames( if stream_idx is not None: stream_selector = ["-map", f"0:{stream_idx}"] ouput_template = f"{sample_prefix}_{stream_idx}_%06d{FRAME_EXT}" - stream_specifier = f"{stream_idx}" else: stream_selector = [] ouput_template = f"{sample_prefix}_{NA_STREAM_IDX}_%06d{FRAME_EXT}" - stream_specifier = "v" # Write the select filter to a temp file because: # The select filter could be large and @@ -270,7 +266,7 @@ def extract_specified_frames( # but fps_mode is not avaliable on some older versions ;( # *[f"-fps_mode:{stream_specifier}", "passthrough"], # Set the number of video frames to output - *[f"-frames:{stream_specifier}", str(len(frame_indices))], + *["-frames:v", str(len(frame_indices))], # Disabled because it doesn't always name the sample images as expected # For example "select(n\,1)" we expected the first sample to be IMG_001.JPG # but it could be IMG_005.JPG @@ -279,7 +275,7 @@ def extract_specified_frames( # *["-frame_pts", "1"], ], # video quality level (or the alias -q:v) - *[f"-qscale:{stream_specifier}", "2"], + *["-qscale:v", "2"], # -q:v=1 is the best quality but larger image sizes # see https://stackoverflow.com/a/10234065 # *["-qscale:v", "1", "-qmin", "1"], From bd8070aacc8495bbab33329d24ef6d17643ee0ad Mon Sep 17 00:00:00 2001 From: Tao Peng Date: Tue, 24 Jun 2025 13:27:31 -0700 Subject: [PATCH 2/5] add comment --- mapillary_tools/ffmpeg.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mapillary_tools/ffmpeg.py b/mapillary_tools/ffmpeg.py index 234cafe14..6d1e3b0ec 100644 --- a/mapillary_tools/ffmpeg.py +++ b/mapillary_tools/ffmpeg.py @@ -265,7 +265,7 @@ def extract_specified_frames( # vsync is deprecated by fps_mode, # but fps_mode is not avaliable on some older versions ;( # *[f"-fps_mode:{stream_specifier}", "passthrough"], - # Set the number of video frames to output + # Set the number of video frames to output (this is an optimization to let ffmpeg stop early) *["-frames:v", str(len(frame_indices))], # Disabled because it doesn't always name the sample images as expected # For example "select(n\,1)" we expected the first sample to be IMG_001.JPG From d2a3c4b57f2af053a51150451d1d47b01e38b10f Mon Sep 17 00:00:00 2001 From: Tao Peng Date: Tue, 24 Jun 2025 13:30:45 -0700 Subject: [PATCH 3/5] fix typo --- mapillary_tools/ffmpeg.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/mapillary_tools/ffmpeg.py b/mapillary_tools/ffmpeg.py index 6d1e3b0ec..f00db1934 100644 --- a/mapillary_tools/ffmpeg.py +++ b/mapillary_tools/ffmpeg.py @@ -169,10 +169,10 @@ def extract_frames( sample_prefix = sample_dir.joinpath(video_path.stem) if stream_idx is not None: stream_selector = ["-map", f"0:{stream_idx}"] - ouput_template = f"{sample_prefix}_{stream_idx}_%06d{FRAME_EXT}" + output_template = f"{sample_prefix}_{stream_idx}_%06d{FRAME_EXT}" else: stream_selector = [] - ouput_template = f"{sample_prefix}_{NA_STREAM_IDX}_%06d{FRAME_EXT}" + output_template = f"{sample_prefix}_{NA_STREAM_IDX}_%06d{FRAME_EXT}" cmd: list[str] = [ # global options should be specified first @@ -189,7 +189,7 @@ def extract_frames( # see https://stackoverflow.com/a/10234065 # *["-qscale:v", "1", "-qmin", "1"], # output - ouput_template, + output_template, ] self._run_ffmpeg(cmd) @@ -225,10 +225,10 @@ def extract_specified_frames( sample_prefix = sample_dir.joinpath(video_path.stem) if stream_idx is not None: stream_selector = ["-map", f"0:{stream_idx}"] - ouput_template = f"{sample_prefix}_{stream_idx}_%06d{FRAME_EXT}" + output_template = f"{sample_prefix}_{stream_idx}_%06d{FRAME_EXT}" else: stream_selector = [] - ouput_template = f"{sample_prefix}_{NA_STREAM_IDX}_%06d{FRAME_EXT}" + output_template = f"{sample_prefix}_{NA_STREAM_IDX}_%06d{FRAME_EXT}" # Write the select filter to a temp file because: # The select filter could be large and @@ -280,7 +280,7 @@ def extract_specified_frames( # see https://stackoverflow.com/a/10234065 # *["-qscale:v", "1", "-qmin", "1"], # output - ouput_template, + output_template, ] self._run_ffmpeg(cmd) finally: From 9c595ec0c2d675e646080f6ee3bd755f6a94d305 Mon Sep 17 00:00:00 2001 From: Tao Peng Date: Tue, 24 Jun 2025 14:28:53 -0700 Subject: [PATCH 4/5] generate_binary_search --- mapillary_tools/ffmpeg.py | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/mapillary_tools/ffmpeg.py b/mapillary_tools/ffmpeg.py index f00db1934..0f6e56a3b 100644 --- a/mapillary_tools/ffmpeg.py +++ b/mapillary_tools/ffmpeg.py @@ -194,7 +194,22 @@ def extract_frames( self._run_ffmpeg(cmd) - def generate_binary_search(self, sorted_frame_indices: list[int]) -> str: + @classmethod + def generate_binary_search(cls, sorted_frame_indices: T.Sequence[int]) -> str: + """ + >>> FFMPEG.generate_binary_search([]) + '0' + + >>> FFMPEG.generate_binary_search([1]) + 'eq(n\\\\,1)' + + >>> FFMPEG.generate_binary_search([1, 2]) + 'if(lt(n\\\\,2)\\\\,eq(n\\\\,1)\\\\,eq(n\\\\,2))' + + >>> FFMPEG.generate_binary_search([1, 2, 3]) + 'if(lt(n\\\\,2)\\\\,eq(n\\\\,1)\\\\,if(lt(n\\\\,3)\\\\,eq(n\\\\,2)\\\\,eq(n\\\\,3)))' + """ + length = len(sorted_frame_indices) if length == 0: @@ -203,8 +218,11 @@ def generate_binary_search(self, sorted_frame_indices: list[int]) -> str: if length == 1: return f"eq(n\\,{sorted_frame_indices[0]})" - middle = length // 2 - return f"if(lt(n\\,{sorted_frame_indices[middle]})\\,{self.generate_binary_search(sorted_frame_indices[:middle])}\\,{self.generate_binary_search(sorted_frame_indices[middle:])})" + middle_idx = length // 2 + left = cls.generate_binary_search(sorted_frame_indices[:middle_idx]) + right = cls.generate_binary_search(sorted_frame_indices[middle_idx:]) + + return f"if(lt(n\\,{sorted_frame_indices[middle_idx]})\\,{left}\\,{right})" def extract_specified_frames( self, From 6083f62792962ee14a4fdaa7c2663d1dc3d18f14 Mon Sep 17 00:00:00 2001 From: Tao Peng Date: Tue, 24 Jun 2025 14:42:54 -0700 Subject: [PATCH 5/5] Revert "generate_binary_search" This reverts commit 9c595ec0c2d675e646080f6ee3bd755f6a94d305. --- mapillary_tools/ffmpeg.py | 24 +++--------------------- 1 file changed, 3 insertions(+), 21 deletions(-) diff --git a/mapillary_tools/ffmpeg.py b/mapillary_tools/ffmpeg.py index 0f6e56a3b..f00db1934 100644 --- a/mapillary_tools/ffmpeg.py +++ b/mapillary_tools/ffmpeg.py @@ -194,22 +194,7 @@ def extract_frames( self._run_ffmpeg(cmd) - @classmethod - def generate_binary_search(cls, sorted_frame_indices: T.Sequence[int]) -> str: - """ - >>> FFMPEG.generate_binary_search([]) - '0' - - >>> FFMPEG.generate_binary_search([1]) - 'eq(n\\\\,1)' - - >>> FFMPEG.generate_binary_search([1, 2]) - 'if(lt(n\\\\,2)\\\\,eq(n\\\\,1)\\\\,eq(n\\\\,2))' - - >>> FFMPEG.generate_binary_search([1, 2, 3]) - 'if(lt(n\\\\,2)\\\\,eq(n\\\\,1)\\\\,if(lt(n\\\\,3)\\\\,eq(n\\\\,2)\\\\,eq(n\\\\,3)))' - """ - + def generate_binary_search(self, sorted_frame_indices: list[int]) -> str: length = len(sorted_frame_indices) if length == 0: @@ -218,11 +203,8 @@ def generate_binary_search(cls, sorted_frame_indices: T.Sequence[int]) -> str: if length == 1: return f"eq(n\\,{sorted_frame_indices[0]})" - middle_idx = length // 2 - left = cls.generate_binary_search(sorted_frame_indices[:middle_idx]) - right = cls.generate_binary_search(sorted_frame_indices[middle_idx:]) - - return f"if(lt(n\\,{sorted_frame_indices[middle_idx]})\\,{left}\\,{right})" + middle = length // 2 + return f"if(lt(n\\,{sorted_frame_indices[middle]})\\,{self.generate_binary_search(sorted_frame_indices[:middle])}\\,{self.generate_binary_search(sorted_frame_indices[middle:])})" def extract_specified_frames( self,