Run image extraction on `video` using the current parameters. Thread-safe. Arguments: video: The video to process. scene_list: The scenes detected in the video. output_dir: Directory to write files to. show_progress: If `true` and tqdm is avai
(
self,
video: VideoStream,
scene_list: SceneList,
output_dir: StrPath | None = None,
show_progress=False,
)
| 161 | self._imwrite_param: list[int] = imwrite_param if imwrite_param is not None else [] |
| 162 | |
| 163 | def run( |
| 164 | self, |
| 165 | video: VideoStream, |
| 166 | scene_list: SceneList, |
| 167 | output_dir: StrPath | None = None, |
| 168 | show_progress=False, |
| 169 | ) -> dict[int, list[str]]: |
| 170 | """Run image extraction on `video` using the current parameters. Thread-safe. |
| 171 | |
| 172 | Arguments: |
| 173 | video: The video to process. |
| 174 | scene_list: The scenes detected in the video. |
| 175 | output_dir: Directory to write files to. |
| 176 | show_progress: If `true` and tqdm is available, shows a progress bar. |
| 177 | """ |
| 178 | # Setup flags and init progress bar if available. |
| 179 | completed = True |
| 180 | logger.info( |
| 181 | f"Saving {self._num_images} images per scene [format={self._image_extension}]" |
| 182 | f" {output_dir if output_dir else ''} " |
| 183 | ) |
| 184 | progress_bar = None |
| 185 | if show_progress: |
| 186 | progress_bar = tqdm( |
| 187 | total=len(scene_list) * self._num_images, unit="images", dynamic_ncols=True |
| 188 | ) |
| 189 | |
| 190 | timecode_list = self.generate_timecode_list(scene_list) |
| 191 | image_filenames = {i: [] for i in range(len(timecode_list))} |
| 192 | |
| 193 | filename_template = Template(self._image_name_template) |
| 194 | logger.debug("Writing images with template %s", filename_template.template) |
| 195 | scene_num_format = "%0" |
| 196 | scene_num_format += str(max(3, math.floor(math.log(len(scene_list), 10)) + 1)) + "d" |
| 197 | image_num_format = "%0" |
| 198 | image_num_format += str(math.floor(math.log(self._num_images, 10)) + 2) + "d" |
| 199 | |
| 200 | def format_filename(scene_number: int, image_number: int, image_timecode: FrameTimecode): |
| 201 | return "{}.{}".format( |
| 202 | filename_template.safe_substitute( |
| 203 | VIDEO_NAME=video.name, |
| 204 | SCENE_NUMBER=scene_num_format % (scene_number + 1), |
| 205 | IMAGE_NUMBER=image_num_format % (image_number + 1), |
| 206 | FRAME_NUMBER=image_timecode.frame_num, |
| 207 | TIMESTAMP_MS=int(image_timecode.seconds * 1000), |
| 208 | TIMECODE=image_timecode.get_timecode().replace(":", ";"), |
| 209 | ), |
| 210 | self._image_extension, |
| 211 | ) |
| 212 | |
| 213 | MAX_QUEUED_ENCODE_FRAMES = 4 |
| 214 | MAX_QUEUED_SAVE_IMAGES = 4 |
| 215 | encode_queue = queue.Queue(MAX_QUEUED_ENCODE_FRAMES) |
| 216 | save_queue = queue.Queue(MAX_QUEUED_SAVE_IMAGES) |
| 217 | error_queue = queue.Queue(2) # Queue size must be the same as the # of worker threads! |
| 218 | |
| 219 | def check_error_queue(): |
| 220 | try: |