상태: 초안 (2026-10-16), 일부 실측. 공 모델(BlurBall)의 입력 크기별 지연은 집 랩의 8GB급 노트북용 GPU 한 장에서 1회용 파드로 쟀습니다(예열 20회 뒤 50회 × 5묶음의 중앙값). 건너뛰기·ROI 는 노트북 CPU 의 합성 궤적 흉내입니다(
code/05_skip_frames.py, 1초 안팎). 실제 탁구 영상에서 건너뛰기를 한 정확도는 미실측입니다. 시리즈 「비전 모델 읽기 3」 5편. 광류의 원리는 비전 모델 읽기 2 의 5편, 공 추적 모델의 학습은 같은 시리즈 9편에 있습니다.
목차
- 1. 직관: 이웃 프레임은 거의 같다
- 2. 검출은 가끔, 사이는 추적
- 3. 키프레임: 특징을 옮겨 쓰기
- 4. 해상도: 입력을 줄이면
- 5. ROI: 예측한 자리 둘레만
- 6. 한계
- 7. 우리 프로젝트와의 연결
- 참고 문헌
1. 직관: 이웃 프레임은 거의 같다
30fps 영상에서 이웃한 두 프레임은 33ms 차이입니다. 그사이 배경은 그대로이고, 사람은 몇 화소 움직입니다. 그런데 매 프레임 무거운 모델을 처음부터 돌리면 거의 같은 계산을 30번 되풀이합니다. 이 글의 방법들은 모두 이 중복을 덜어 냅니다.
| 무엇을 덜어 내나 | 방법 | 대표 |
|---|---|---|
| 시간(프레임 수) | 검출은 가끔, 사이는 추적 | SORT, MediaPipe, BlazePose |
| 시간(특징) | 키프레임에서만 특징을 뽑고 옮겨 쓰기 | Deep Feature Flow |
| 공간(해상도) | 장면마다 입력 크기 고르기 | AdaScale |
| 공간(영역) | 예측한 자리 둘레만 보기 | ROI 자르기 |
2. 검출은 가끔, 사이는 추적
Bewley 외의 SORT(2016)는 매 프레임 검출 결과를 받아, 물체마다 등속 칼만 필터로 다음 위치를 예측하고, 예측 상자와 새 검출 상자를 IoU 로 짝짓습니다(헝가리안 알고리즘). 추적 부분은 2016년 무렵 CPU 코어 하나에서 260Hz 로 돌았습니다(4.3절, 검출 시간은 빠진 것으로 읽힙니다). 같은 추적기에 검출기만 바꿔도 MOT 정확도(MOTA)가 15.1 에서 34.0 으로 올랐습니다(표 1). 추적의 질은 결국 검출이 정하고, 추적기 자체는 거의 공짜라는 뜻입니다.
이 「거의 공짜」 를 이용하면 검출을 가끔만 할 수 있습니다. MediaPipe 의 자세 추적은 검출기를 첫 프레임과 추적이 사람을 놓쳤을 때만 돌리고, 나머지 프레임은 이전 프레임의 관절 위치로 사람 둘레를 잘라 관절 모델만 돌립니다(공식 문서). BlazePose(2020)는 이렇게 해서 2017년 휴대폰의 CPU 코어 하나에서 Lite 모델로 약 30 FPS 를 냈습니다(표 1).
칼만 필터의 예측 단계는 한 줄입니다.
상태 x = [위치, 속도], 다음 예측 x' = F·x, F = [[1, Δt], [0, 1]] (등속 가정)
검출이 오면 예측과 검출을 불확실성 비율로 섞어 고친다(갱신 단계)
직접 해 보기: 탁구공에서 건너뛰면
등속 가정은 공이 방향을 바꾸는 순간 깨집니다. 탁구공은 0.5초에 한 번꼴로 탁구대에 튀거나 라켓에 맞습니다. 512×288 화면에 중력·바운드·타구가 있는 합성 랠리(60fps, 1,161프레임, 바운드 29·타구 40)를 만들고, 검출기를 k 프레임마다 한 번 돌리고(위치 잡음 σ=1px) 사이를 마지막 두 검출의 등속 예측으로 메웠습니다.
| k(검출 간격) | 검출 비용 | 위치 오차 중앙 · p95 | 바운드·타구 앞뒤 3프레임의 중앙 · p95 | 10px 넘게 빗나간 프레임 |
|---|---|---|---|---|
| 1 | 100% | 1.2 · 2.5px | 1.1 · 2.3px | 0.0% |
| 2 | 50% | 1.5 · 5.4px | 1.7 · 20.1px | 3.8% |
| 3 | 33% | 1.9 · 29.3px | 2.2 · 46.8px | 9.6% |
| 4 | 25% | 2.4 · 48.7px | 3.3 · 75.7px | 15.7% |
| 6 | 17% | 5.8 · 92.5px | 7.2 · 83.6px | 34.0% |
중앙값만 보면 3프레임마다 검출해도 2px 남짓으로 괜찮아 보입니다. 하지만 꼬리(p95)는 바운드·타구 앞뒤에서 46.8px 로, 공 지름(512 폭에서 약 5px)의 아홉 배가 넘습니다. 오차가 하필 우리가 알고 싶은 사건(바운드) 둘레에 몰립니다. 사람 자세처럼 부드럽게 움직이는 대상에서 잘 통하는 「검출은 가끔」 을, 점수 판정이 바운드에 달린 탁구공에 그대로 쓰기 어려운 까닭입니다.
3. 키프레임: 특징을 옮겨 쓰기
Zhu 외의 Deep Feature Flow(2017)는 건너뛸 단위를 「검출 결과」 가 아니라 「깊은 특징」 으로 잡습니다. 무거운 특징 망(ResNet-101)은 키프레임에서만 돌리고, 사이 프레임은 가벼운 광류 망이 구한 흐름을 따라 키프레임의 특징을 옮긴(warp) 뒤 가벼운 머리만 돌립니다. 끝까지 함께 학습합니다.
| 과제(논문 표 4) | 키프레임 간격 | 매 프레임 | Deep Feature Flow |
|---|---|---|---|
| ImageNet VID 영상 검출 | 10 | mAP 73.9, 4.05 fps | mAP 73.1, 20.25 fps(5.0배) |
| Cityscapes 분할 | 5 | mIoU 71.1, 1.52 fps | mIoU 69.2, 5.60 fps(3.7배) |
(2013년 데이터센터 GPU 한 장.) 정확도를 1–2점 내주고 3.7–5배 빨라졌습니다. 더 가벼운 광류 망을 쓰면 약 10배(41.26 fps)까지 갔지만 mAP 가 69.5 로 떨어졌습니다(5.2절). 광류는 「한 프레임 동안 조금씩 움직인다」 를 가정하므로, 한 프레임에 지름의 몇 배를 날아가는 공에는 맞지 않습니다(아래 4절의 표).
4. 해상도: 입력을 줄이면
계산은 화소 수, 곧 가로×세로에 비례합니다. 폭을 절반으로 줄이면 계산은 4분의 1 입니다. 대신 작은 물체가 더 작아집니다. 탁구대 길이 2.74m 가 화면 폭의 70% 를 차지한다고 두고 공(40mm)의 크기와 한 프레임 동안의 이동을 계산했습니다(공 속도 10m/s).
| 화면 폭 | 공 지름 | 한 프레임 이동(60fps / 30fps) | 화소 수(512×288 대비) |
|---|---|---|---|
| 1920 | 19.6px | 81.8 / 163.5px | 14.1배 |
| 1280 | 13.1px | 54.5 / 109.0px | 6.3배 |
| 768 | 7.8px | 32.7 / 65.4px | 2.3배 |
| 512 | 5.2px | 21.8 / 43.6px | 1 |
| 256 | 2.6px | 10.9 / 21.8px | 0.25배 |
어느 해상도에서든 공은 한 프레임에 자기 지름의 4.2배(60fps)–8.3배(30fps) 를 움직입니다. 그래서 공 모델은 프레임 하나가 아니라 여러 장을 함께 보고, 번진 자국까지 단서로 씁니다(같은 시리즈 2의 9편).
우리 실측: 공 모델을 입력 크기 넷으로
TT Score 의 공 모델(BlurBall, 3프레임 창, 파라미터 149만 개)을 실험용 GPU 에서 입력 크기만 바꿔 쟀습니다. 모델은 완전 합성곱이라 가중치를 그대로 두고 입력 크기만 바꿀 수 있습니다(학습은 512×288 에서 했으므로 다른 크기의 정확도는 미실측입니다).
| 입력 | FP32 | FP16 | 512×288 대비 화소 | 512×288 대비 시간(FP32) |
|---|---|---|---|---|
| 256×144 | 4.07ms | 4.69ms | 0.25배 | 0.27배 |
| 512×288 | 15.09ms | 9.77ms | 1 | 1 |
| 768×432 | 41.90ms | 24.10ms | 2.25배 | 2.78배 |
| 1024×576 | 82.24ms | 49.30ms | 4배 | 5.45배 |
(배치 1, 창 하나, FP16 은 autocast. 예열 20회 뒤 250회의 중앙값이고 다섯 묶음의 중앙값끼리 0.1ms 안에서 같았습니다.)
두 가지가 보입니다.
- 큰 쪽은 화소보다 더 빨리 느려졌습니다. 1024×576 은 화소가 4배인데 시간은 5.45배입니다. 이 모델은 해상도를 끝까지 유지하며 채널 수십 개짜리 특징 지도를 들고 다니므로, 입력이 커지면 중간 텐서가 GPU 캐시에 들어가지 않게 된 것으로 보입니다(원인은 추정, 프로파일링 미실측).
- 작은 쪽은 FP16 이 오히려 느렸습니다(4.07 대 4.69ms). 계산이 너무 적어서 GPU 가 일을 기다리는 시간(커널 부르기, 형식 바꾸기)이 시간 대부분이 되면 정밀도를 줄여도 소용이 없습니다. 3편과 6편에서 다시 봅니다.
30fps 예산(33ms)에 추론만 넣는다면 768×432 의 FP16(24ms)까지 들어갑니다. 하지만 1편에서 본 것처럼 예산에는 전처리·전송·후처리·다른 세션 몫이 함께 들어가야 하므로, 512×288(FP16 9.8ms)이 여유를 남기는 크기입니다. 이 값은 공 모델이 학습된 크기이기도 합니다.
장면마다 크기를 고르기: AdaScale
Chin 외의 AdaScale(2019)은 검출기 특징 위에 작은 회귀기를 달아, 다음 프레임에 쓸 입력 크기(짧은 변 600·480·360·240)를 예측합니다. 큰 물체만 있는 장면은 작게 넣어도 되고, 오히려 오검출이 줄어 정확도가 오르기도 합니다. ImageNet VID 에서 1.6배 빨라지면서 mAP 가 74.2 → 75.5 로 올랐습니다(R-FCN, 2017년 소비자용 상위 GPU, 75 → 47ms). 회귀기 자체는 약 2ms 입니다. 탁구에서는 공이 늘 작으므로 이 생각을 그대로 쓰기는 어렵지만, 「이번 장면은 공이 안 보이는 쉬는 시간이니 작게·드물게」 같은 형태로는 쓸 수 있습니다(미실측).
5. ROI: 예측한 자리 둘레만
추적이 다음 위치를 예측해 주면 그 둘레의 작은 창만 모델에 넣을 수 있습니다. 화면 전체 대신 96×96 창이면 화소는 6% 입니다. 대신 예측이 빗나가 공이 창 밖에 있으면 놓치고, 그때는 전체 화면을 다시 봐야 합니다. 2절의 합성 랠리에서 매 프레임 검출(σ=1px)하며 직전 두 추정으로 창 위치를 예측했습니다.
| 창 | 화소 몫 | 공이 창 밖(검출 실패 0%) | 공이 창 밖(검출 실패 10%) | 그중 바운드·타구 앞뒤(실패 10%) |
|---|---|---|---|---|
| 32×32 | 0.7% | 3.71% | 4.66% | 11.95% |
| 64×64 | 2.8% | 2.50% | 2.85% | 7.36% |
| 96×96 | 6.2% | 0.35% | 0.78% | 1.84% |
| 128×128 | 11.1% | 0.35% | 0.60% | 1.61% |
창 밖으로 나가는 순간은 역시 방향이 바뀌는 순간에 몰립니다. 이 흉내에는 가림·서브 시작(공이 손에서 갑자기 나타남)·카메라 흔들림이 없으므로 실제로는 더 자주 빗나갈 것입니다(미실측). 또 BlurBall 처럼 여러 프레임을 함께 보는 모델은 창을 자르면 앞뒤 프레임의 같은 자리도 잘라야 하므로, 창이 공을 따라 움직이면 「같은 자리의 변화」 라는 단서가 흐려집니다.
6. 한계
- 2·5절은 합성 궤적입니다. 실제 영상의 가림, 번짐, 공이 화면 밖으로 나갔다 들어오는 경우는 들어 있지 않습니다.
- 4절의 입력 크기별 측정은 시간만 쟀습니다. 학습과 다른 크기에서 공 모델이 얼마나 정확한지는 재지 않았습니다.
- 논문 수치는 각자의 GPU·데이터에서 나온 값이라 배수끼리 견줄 수 없습니다.
7. 우리 프로젝트와의 연결
- TT Score 는 공 모델을 매 프레임 돌립니다. 2절의 표처럼 건너뛰면 바운드 둘레에서 틀리기 때문입니다. 대신 덜 비싼 쪽을 「가끔」 으로 돌렸습니다. 탁구대 찾기는 경기 시작 때 한 번(EfficientTAM, 4편), 그 뒤로는 매 프레임 광류로 따라가고 크게 흔들릴 때만 다시 찾습니다. 광류·컷 판단을 매 프레임 해도 프레임당 약 1.7ms 였습니다(TT Score 성능 문서의 실측, 조용한 노드).
- 입력은 512×288 입니다. 폰이 카메라 화면을 이 크기로 줄여 보냅니다. 4절의 표처럼 이보다 크면 예산을 금방 넘고, 작으면 공이 3px 아래로 작아집니다. 세로로 든 폰은 이 크기에 맞추면 화면 폭이 162px 로 줄어 공이 거의 사라졌습니다(성능 문서의 조건별 시험에서 공 보임 5%). 촬영 가이드가 가로를 권하는 까닭입니다.
- ROI 는 아직 쓰지 않습니다. 공을 놓친 뒤 다시 찾는 비용과, 창을 자르면 여러 프레임 단서가 흐려지는 문제를 먼저 재야 합니다(미실측).
다음 글은 여러 사람이 한 GPU 를 함께 쓸 때, 묶어 돌리기가 지연과 처리량을 어떻게 바꾸는지입니다.
티티 한마디. 공은 한 프레임에 제 몸의 네 배를 날아가고, 꼭 튀는 순간에 방향을 바꿔요. 그래서 저는 눈을 한 번도 감지 않아요 — 대신 탁구대는 가끔만 봐요!
참고 문헌
- Bewley, A. et al. (2016). Simple Online and Realtime Tracking. ICIP 2016. https://arxiv.org/abs/1602.00763
- Bazarevsky, V. et al. (2020). BlazePose: On-device Real-time Body Pose tracking. CVPR Workshop. https://arxiv.org/abs/2006.10204
- MediaPipe Pose 문서(레거시 솔루션). https://github.com/google-ai-edge/mediapipe/blob/master/docs/solutions/pose.md
- Zhu, X. et al. (2017). Deep Feature Flow for Video Recognition. CVPR 2017. https://arxiv.org/abs/1611.07715
- Chin, T.-W., Ding, R., Marculescu, D. (2019). AdaScale: Towards Real-time Video Object Detection Using Adaptive Scaling. SysML 2019. https://arxiv.org/abs/1902.02910
- 우리 측정: 이 시리즈의 실측(원자료는 저장소 밖 연구 기록), TT Score 성능 문서 2.12·2.14절
- 예제 코드와 출력: 이 폴더의
code/05_skip_frames.py,code/05_skip_frames.out.txt