ウォームアップ時のFPSを平均FPSから除外するように修正し,平均FPSを総フレーム数/経過時間で計算するように変更
This commit is contained in:
1 parent
6d6b6a0ede
commit
acd08951c8
4 files changed
+64
-18
No files matched your search
@@ -337,6 +337,7 @@ performance:
|
|||||||
skip_frames: 0
|
skip_frames: 0
|
||||||
fps_average_window: 30
|
fps_average_window: 30
|
||||||
warn_fps_threshold: 10.0
|
warn_fps_threshold: 10.0
|
||||||
|
warmup_frames: 5
|
||||||
debug:
|
debug:
|
||||||
enabled: false
|
enabled: false
|
||||||
show_raw_model_output: false
|
show_raw_model_output: false
|
||||||
|
|||||||
+4
-4
@@ -143,7 +143,7 @@ class Application:
|
|||||||
person_state_manager: PersonStateManager | None = None
|
person_state_manager: PersonStateManager | None = None
|
||||||
unified_builder: UnifiedEventBuilder | None = None
|
unified_builder: UnifiedEventBuilder | None = None
|
||||||
analysis_builder: AnalysisTableBuilder | None = None
|
analysis_builder: AnalysisTableBuilder | None = None
|
||||||
fps = FPSCounter(self.config.performance.fps_average_window)
|
fps = FPSCounter(self.config.performance.fps_average_window, self.config.performance.warmup_frames)
|
||||||
visualizer = Visualizer(self.config.display, self.config.performance.warn_fps_threshold, self.config.performance.fps_average_window)
|
visualizer = Visualizer(self.config.display, self.config.performance.warn_fps_threshold, self.config.performance.fps_average_window)
|
||||||
frame_number, processed_frames = 0, 0
|
frame_number, processed_frames = 0, 0
|
||||||
start = time.perf_counter()
|
start = time.perf_counter()
|
||||||
@@ -277,10 +277,10 @@ class Application:
|
|||||||
should_infer = (frame_number - 1) % (self.config.performance.skip_frames + 1) == 0
|
should_infer = (frame_number - 1) % (self.config.performance.skip_frames + 1) == 0
|
||||||
detections: list[Detection] = detector.detect(frame, region) if should_infer else []
|
detections: list[Detection] = detector.detect(frame, region) if should_infer else []
|
||||||
pre_info = FrameInfo(frame_number, datetime.now().astimezone().isoformat(timespec="milliseconds"),
|
pre_info = FrameInfo(frame_number, datetime.now().astimezone().isoformat(timespec="milliseconds"),
|
||||||
time.perf_counter() - start - paused_total, width, height, fps.average_fps)
|
time.perf_counter() - start - paused_total, width, height, fps.recent_average_fps)
|
||||||
tracked = tracker.update(detections, frame, pre_info) if tracker is not None else []
|
tracked = tracker.update(detections, frame, pre_info) if tracker is not None else []
|
||||||
fps.tick()
|
fps.tick()
|
||||||
measured = fps.average_fps
|
measured = fps.recent_average_fps
|
||||||
now = datetime.now().astimezone()
|
now = datetime.now().astimezone()
|
||||||
info = FrameInfo(frame_number, now.isoformat(timespec="milliseconds"), time.perf_counter() - start - paused_total, width, height, measured)
|
info = FrameInfo(frame_number, now.isoformat(timespec="milliseconds"), time.perf_counter() - start - paused_total, width, height, measured)
|
||||||
events = track_manager.update(tracked, info) if track_manager is not None else []
|
events = track_manager.update(tracked, info) if track_manager is not None else []
|
||||||
@@ -537,7 +537,7 @@ class Application:
|
|||||||
self._writer.release()
|
self._writer.release()
|
||||||
cv2.destroyAllWindows()
|
cv2.destroyAllWindows()
|
||||||
elapsed = time.perf_counter() - start
|
elapsed = time.perf_counter() - start
|
||||||
LOGGER.info("Run summary: frames=%d elapsed=%.2fs average=%.2f FPS", processed_frames, elapsed, processed_frames / elapsed if elapsed else 0.0)
|
LOGGER.info("Run summary: frames=%d elapsed=%.2fs average=%.2f FPS", processed_frames, elapsed, fps.average_fps)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _tracking_counters(manager: TrackManager | None) -> dict[str, int] | None:
|
def _tracking_counters(manager: TrackManager | None) -> dict[str, int] | None:
|
||||||
|
|||||||
@@ -435,6 +435,7 @@ class PerformanceSettings:
|
|||||||
skip_frames: int
|
skip_frames: int
|
||||||
fps_average_window: int
|
fps_average_window: int
|
||||||
warn_fps_threshold: float
|
warn_fps_threshold: float
|
||||||
|
warmup_frames: float
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True, slots=True)
|
@dataclass(frozen=True, slots=True)
|
||||||
@@ -549,6 +550,8 @@ def _validate(config: AppConfig) -> None:
|
|||||||
raise ConfigurationError("detector image_size/max_detections must be positive and class ID non-negative")
|
raise ConfigurationError("detector image_size/max_detections must be positive and class ID non-negative")
|
||||||
if config.performance.fps_average_window <= 0 or config.performance.skip_frames < 0:
|
if config.performance.fps_average_window <= 0 or config.performance.skip_frames < 0:
|
||||||
raise ConfigurationError("performance window must be positive and skip_frames non-negative")
|
raise ConfigurationError("performance window must be positive and skip_frames non-negative")
|
||||||
|
if config.performance.warmup_frames < 0:
|
||||||
|
raise ConfigurationError("performance warmup_frames must be non-negative")
|
||||||
if config.display.resize_scale <= 0:
|
if config.display.resize_scale <= 0:
|
||||||
raise ConfigurationError("display.resize_scale must be positive")
|
raise ConfigurationError("display.resize_scale must be positive")
|
||||||
if config.display.trajectory_max_points <= 0:
|
if config.display.trajectory_max_points <= 0:
|
||||||
|
|||||||
+56
-14
@@ -5,25 +5,56 @@ from collections import deque
|
|||||||
|
|
||||||
|
|
||||||
class FPSCounter:
|
class FPSCounter:
|
||||||
"""Calculate instantaneous and moving-average FPS."""
|
"""Calculate instantaneous, overall-average and recent-average FPS.
|
||||||
|
|
||||||
def __init__(self, average_window: int) -> None:
|
- current_fps: 直近1フレームの瞬間FPS(表示用)
|
||||||
|
- average_fps: ウォームアップ後の「総フレーム数 ÷ 経過時間」
|
||||||
|
- recent_average_fps: 直近 average_window フレームの「フレーム数 ÷ 経過時間」
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, average_window: int, warmup_frames: int) -> None:
|
||||||
if average_window <= 0:
|
if average_window <= 0:
|
||||||
raise ValueError("average_window must be positive")
|
raise ValueError("average_window must be positive")
|
||||||
self._samples: deque[float] = deque(maxlen=average_window)
|
if warmup_frames < 0:
|
||||||
|
raise ValueError("warmup_frames must be non-negative")
|
||||||
|
self._average_window = average_window
|
||||||
|
# 起動直後の数フレームは初期化処理で遅くなるため平均から除外する
|
||||||
|
self._warmup_frames = warmup_frames
|
||||||
|
# 計測の基準となる tick 番号(1回目の tick は基準時刻の記録のみ)
|
||||||
|
self._baseline_tick = max(warmup_frames, 1)
|
||||||
|
self._reset_state()
|
||||||
|
|
||||||
|
def _reset_state(self) -> None:
|
||||||
self._last_time: float | None = None
|
self._last_time: float | None = None
|
||||||
self._current_fps = 0.0
|
self._current_fps = 0.0
|
||||||
|
self._tick_count = 0
|
||||||
|
self._start_time: float | None = None
|
||||||
|
self._frames = 0 # 基準時刻以降に経過したフレーム間隔の数
|
||||||
|
# 窓内の平均用タイムスタンプ(N個の間隔に N+1 点必要)
|
||||||
|
self._timestamps: deque[float] = deque(maxlen=self._average_window + 1)
|
||||||
|
|
||||||
def tick(self) -> float:
|
def tick(self) -> float:
|
||||||
now = time.perf_counter()
|
now = time.perf_counter()
|
||||||
|
self._tick_count += 1
|
||||||
|
|
||||||
if self._last_time is None:
|
if self._last_time is None:
|
||||||
self._last_time = now
|
self._last_time = now
|
||||||
return 0.0
|
self._current_fps = 0.0
|
||||||
elapsed = now - self._last_time
|
else:
|
||||||
self._last_time = now
|
elapsed = now - self._last_time
|
||||||
self._current_fps = 1.0 / elapsed if elapsed > 0.0 else 0.0
|
self._last_time = now
|
||||||
if self._current_fps > 0.0:
|
self._current_fps = 1.0 / elapsed if elapsed > 0.0 else 0.0
|
||||||
self._samples.append(self._current_fps)
|
|
||||||
|
# ウォームアップ期間中は表示用の瞬間FPSのみ更新し、平均には含めない
|
||||||
|
if self._tick_count == self._baseline_tick:
|
||||||
|
self._start_time = now
|
||||||
|
self._frames = 0
|
||||||
|
self._timestamps.clear()
|
||||||
|
self._timestamps.append(now)
|
||||||
|
elif self._tick_count > self._baseline_tick:
|
||||||
|
self._frames += 1
|
||||||
|
self._timestamps.append(now)
|
||||||
|
|
||||||
return self._current_fps
|
return self._current_fps
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@@ -32,13 +63,24 @@ class FPSCounter:
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def average_fps(self) -> float:
|
def average_fps(self) -> float:
|
||||||
return sum(self._samples) / len(self._samples) if self._samples else 0.0
|
"""ウォームアップ後の総フレーム数 ÷ 経過時間。"""
|
||||||
|
if self._start_time is None or self._last_time is None or self._frames == 0:
|
||||||
|
return 0.0
|
||||||
|
elapsed = self._last_time - self._start_time
|
||||||
|
return self._frames / elapsed if elapsed > 0.0 else 0.0
|
||||||
|
|
||||||
|
@property
|
||||||
|
def recent_average_fps(self) -> float:
|
||||||
|
"""直近 average_window フレームのフレーム数 ÷ 経過時間。"""
|
||||||
|
if len(self._timestamps) < 2:
|
||||||
|
return 0.0
|
||||||
|
elapsed = self._timestamps[-1] - self._timestamps[0]
|
||||||
|
return (len(self._timestamps) - 1) / elapsed if elapsed > 0.0 else 0.0
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def sample_count(self) -> int:
|
def sample_count(self) -> int:
|
||||||
return len(self._samples)
|
"""平均に含まれているフレーム数(ウォームアップ除外後)。"""
|
||||||
|
return self._frames
|
||||||
|
|
||||||
def reset(self) -> None:
|
def reset(self) -> None:
|
||||||
self._samples.clear()
|
self._reset_state()
|
||||||
self._last_time = None
|
|
||||||
self._current_fps = 0.0
|
|
||||||
Reference in new issue
Block a user