15장. 비디오 처리와 실시간 영상처리
실시간 처리와 처리 시간
회전 초밥집의 요리사는 접시가 앞에 올 때마다 초밥을 올립니다. 접시가 3초마다 온다면 초밥 하나를 3초 안에 만들어야 합니다. 4초가 걸리면 빈 접시가 그냥 지나가고, 줄이 길어질수록 놓치는 접시가 늘어납니다. 실시간 영상처리가 바로 이것입니다. 카메라가 10fps 로 프레임을 보내면, 한 장의 처리는 100ms 안에 끝나야 합니다.
막대는 FPS 마다 한 프레임에 쓸 수 있는 시간, 곧 프레임 간격이고, 다홍 막대가 이 장의 동영상과 같은 10fps 입니다. 10fps 면 100ms 이지만 30fps 면 33.3ms, 60fps 면 16.7ms 로 줄어듭니다. 여기서 접시가 오는 간격이 프레임 간격이고, 초밥을 만드는 시간이 프레임 하나의 처리 시간이며, 그냥 지나간 빈 접시가 건너뛴 프레임입니다.
처리 시간 재기
time.perf_counter() 를 처리 앞뒤에서 부르고 그 차이를 구하면 걸린 시간(초)이 나옵니다. 1000 을 곱하면 ms 입니다. 한 장의 평균 처리 시간이 정해지면 1초에 처리할 수 있는 장 수는 1000 ÷ 평균 ms 입니다.
| 한 장 처리 시간 | 1초에 처리할 수 있는 장 수 | 10fps(100ms) | 30fps(33.3ms) |
|---|---|---|---|
| 5ms | 1000 ÷ 5 = 200장 | 제때 끝남 | 제때 끝남 |
| 40ms | 1000 ÷ 40 = 25장 | 제때 끝남 | 늦음 |
| 150ms | 1000 ÷ 150 ≈ 6.7장 | 늦음 | 늦음 |
import time
import cv2
import numpy as np
frame = np.zeros((240, 320, 3), np.uint8) # 320×240 빈 프레임 한 장
start = time.perf_counter() # 시작 시각(초)
for _ in range(20): # 너무 짧아 20번 되풀이해 잰다
cv2.GaussianBlur(frame, (5, 5), 0)
elapsed_ms = (time.perf_counter() - start) * 1000 / 20
print("한 장 평균:", round(elapsed_ms, 3), "ms") # 결과: 컴퓨터마다 다르다
for fps in (10, 30):
budget = 1000 / fps # 프레임 간격(ms)
print(fps, "fps → 한 장에", round(budget, 1), "ms, 제때 끝나나:", elapsed_ms <= budget)처리가 프레임 간격보다 오래 걸릴 때 쓰는 길은 그림의 세 가지입니다. 크기를 줄이면 칸 수가 줄어 처리가 빨라지고(가로세로를 절반으로 줄이면 칸 수는 4분의 1), 건너뛰면 처리하는 장 수가 줄고, 가벼운 처리로 바꾸면 한 장의 일이 줄어듭니다. 실습 5에서 첫째 길의 효과를 숫자로 잽니다.
주의할 점
- 처리 시간은 컴퓨터의 빠르기, 그때 돌고 있는 다른 프로그램에 따라 달라집니다. 이 교안의 숫자는 교안을 만든 PC 에서 잰 것이므로, 숫자 자체보다 "몇 배 빨라졌나"를 봅니다.
- 첫 프레임은 준비 작업 때문에 더 오래 걸리기도 합니다. 여러 장의 평균으로 잽니다.
- 동영상 파일은 처리가 늦어도 다음 장이 기다려 줍니다. 웹캠은 기다려 주지 않으므로, 늦으면 화면이 밀리거나 프레임이 빠집니다. 실습 5는 파일로 이 상황을 흉내 냅니다.