Save To CSV: Saves all frame metrics stored in the StatsManager to a CSV file. Arguments: csv_file: A file handle opened in write mode (e.g. open('...', 'w')) or a path as str. force_save: If True, writes metrics out even if an update is not required. Raises
(
self,
csv_file: StrPath | ty.TextIO,
force_save=True,
)
| 162 | return self._metrics_updated |
| 163 | |
| 164 | def save_to_csv( |
| 165 | self, |
| 166 | csv_file: StrPath | ty.TextIO, |
| 167 | force_save=True, |
| 168 | ) -> None: |
| 169 | """Save To CSV: Saves all frame metrics stored in the StatsManager to a CSV file. |
| 170 | |
| 171 | Arguments: |
| 172 | csv_file: A file handle opened in write mode (e.g. open('...', 'w')) or a path as str. |
| 173 | force_save: If True, writes metrics out even if an update is not required. |
| 174 | |
| 175 | Raises: |
| 176 | OSError: If `path` cannot be opened or a write failure occurs. |
| 177 | """ |
| 178 | if not (force_save or self.is_save_required()): |
| 179 | logger.info("No metrics to write.") |
| 180 | return |
| 181 | |
| 182 | # If we get a path instead of an open file handle, recursively call ourselves |
| 183 | # again but with file handle instead of path. |
| 184 | if isinstance(csv_file, (str, bytes, Path, os.PathLike)): |
| 185 | with open(csv_file, "w") as file: |
| 186 | self.save_to_csv(csv_file=file, force_save=force_save) |
| 187 | return |
| 188 | # csv_file is now narrowed to ty.TextIO (the path branch returned above). |
| 189 | |
| 190 | csv_writer = csv.writer(csv_file, lineterminator="\n") |
| 191 | metric_keys = sorted(list(self._metric_keys)) |
| 192 | csv_writer.writerow([COLUMN_NAME_FRAME_NUMBER, COLUMN_NAME_TIMECODE, *metric_keys]) |
| 193 | frame_keys = sorted(self._frame_metrics.keys()) |
| 194 | logger.info("Writing %d frames to CSV...", len(frame_keys)) |
| 195 | for frame_key in frame_keys: |
| 196 | # `frame_key` may be a bare `int` if the deprecated `load_from_csv` populated the dict. |
| 197 | # Skip such rows since we cannot recover a timecode without a base framerate. |
| 198 | if not isinstance(frame_key, FrameTimecode): |
| 199 | continue |
| 200 | csv_writer.writerow( |
| 201 | [frame_key.frame_num + 1, frame_key.get_timecode()] |
| 202 | + [str(metric) for metric in self.get_metrics(frame_key, metric_keys)] |
| 203 | ) |
| 204 | |
| 205 | @staticmethod |
| 206 | def valid_header(row: list[str]) -> bool: |