ウォームアップ時の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
|
||||
fps_average_window: 30
|
||||
warn_fps_threshold: 10.0
|
||||
warmup_frames: 5
|
||||
debug:
|
||||
enabled: false
|
||||
show_raw_model_output: false
|
||||
|
||||
+4
-4
@@ -143,7 +143,7 @@ class Application:
|
||||
person_state_manager: PersonStateManager | None = None
|
||||
unified_builder: UnifiedEventBuilder | 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)
|
||||
frame_number, processed_frames = 0, 0
|
||||
start = time.perf_counter()
|
||||
@@ -277,10 +277,10 @@ class Application:
|
||||
should_infer = (frame_number - 1) % (self.config.performance.skip_frames + 1) == 0
|
||||
detections: list[Detection] = detector.detect(frame, region) if should_infer else []
|
||||
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 []
|
||||
fps.tick()
|
||||
measured = fps.average_fps
|
||||
measured = fps.recent_average_fps
|
||||
now = datetime.now().astimezone()
|
||||
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 []
|
||||
@@ -537,7 +537,7 @@ class Application:
|
||||
self._writer.release()
|
||||
cv2.destroyAllWindows()
|
||||
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
|
||||
def _tracking_counters(manager: TrackManager | None) -> dict[str, int] | None:
|
||||
|
||||
@@ -435,6 +435,7 @@ class PerformanceSettings:
|
||||
skip_frames: int
|
||||
fps_average_window: int
|
||||
warn_fps_threshold: float
|
||||
warmup_frames: float
|
||||
|
||||
|
||||
@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")
|
||||
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")
|
||||
if config.performance.warmup_frames < 0:
|
||||
raise ConfigurationError("performance warmup_frames must be non-negative")
|
||||
if config.display.resize_scale <= 0:
|
||||
raise ConfigurationError("display.resize_scale must be positive")
|
||||
if config.display.trajectory_max_points <= 0:
|
||||
|
||||
+56
-14
@@ -5,25 +5,56 @@ from collections import deque
|
||||
|
||||
|
||||
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:
|
||||
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._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:
|
||||
now = time.perf_counter()
|
||||
self._tick_count += 1
|
||||
|
||||
if self._last_time is None:
|
||||
self._last_time = now
|
||||
return 0.0
|
||||
elapsed = now - self._last_time
|
||||
self._last_time = now
|
||||
self._current_fps = 1.0 / elapsed if elapsed > 0.0 else 0.0
|
||||
if self._current_fps > 0.0:
|
||||
self._samples.append(self._current_fps)
|
||||
self._current_fps = 0.0
|
||||
else:
|
||||
elapsed = now - self._last_time
|
||||
self._last_time = now
|
||||
self._current_fps = 1.0 / elapsed if elapsed > 0.0 else 0.0
|
||||
|
||||
# ウォームアップ期間中は表示用の瞬間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
|
||||
|
||||
@property
|
||||
@@ -32,13 +63,24 @@ class FPSCounter:
|
||||
|
||||
@property
|
||||
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
|
||||
def sample_count(self) -> int:
|
||||
return len(self._samples)
|
||||
"""平均に含まれているフレーム数(ウォームアップ除外後)。"""
|
||||
return self._frames
|
||||
|
||||
def reset(self) -> None:
|
||||
self._samples.clear()
|
||||
self._last_time = None
|
||||
self._current_fps = 0.0
|
||||
self._reset_state()
|
||||
Reference in new issue
Block a user