상태: 초안 (2026-10-13), 실측 반영. 이 글의 정확도와 시간은 필자가 직접 잰 값이고, 잰 날짜와 조건을 표마다 붙였습니다. 논문 수치는 「논문」이라고 따로 표시했고, 재지 못한 것은 「미실측」으로 적었습니다. 연구가 진행 중이라 숫자는 바뀔 수 있습니다. 작성 규칙: 실측 데이터가 기본이고, 못 잰 것은 「미실측」으로 표시합니다.
목차
- 1. TT Score 가 하려는 일
- 2. 네 층으로 나눈 지도
- 3. 시간 예산: 16.7ms 와 1.5초
- 4. 층 사이에 오가는 데이터
- 5. 지금 어디까지 왔나: 2026-10-13 기준선
- 6. 앞으로의 방향
- 7. 시리즈 지도
- 참고
1. TT Score 가 하려는 일
탁구대 옆에 휴대폰을 삼각대로 세워 두면, 화면의 점수판이 랠리마다 점수를 스스로 올립니다. 판정이 애매하면 「확인 필요」를 띄우고, 사람이 한 번 눌러 고칩니다. 고친 기록은 다음 학습 자료가 됩니다.
말로는 간단하지만, 점수 하나를 올리려면 기계가 다음을 모두 알아야 합니다.
- 공이 화면 어디에 있는가. 지름 4cm, 빠르면 시속 100km 가 넘는 공이 프레임마다 어디 있는지.
- 탁구대가 화면 어디에 있는가. 공이 테이블에 닿았는지, 어느 쪽 반에 닿았는지를 알려면 테이블의 네 모서리가 필요합니다.
- 무슨 일이 일어났는가. 공의 궤적에서 「바운드」, 「네트」, 「아웃」, 「랠리 끝」 같은 사건(이벤트)을 뽑아야 합니다.
- 그래서 누가 이겼는가. 사건의 순서를 탁구 규칙에 비추어 승자와 이유를 정해야 합니다.
이 시리즈는 이 네 가지 질문에 답하는 모델과 규칙을 하나씩 열어 봅니다. 이 글은 그 전체 지도입니다.
2. 네 층으로 나눈 지도
| 층 | 하는 일 | 쓰는 것 | 어디서, 얼마나 |
|---|---|---|---|
| 1층 인식 | 프레임에서 공 좌표, 탁구대 모서리를 찾는다 | 공: BlurBall. 탁구대: 사람의 탭, SAM 2.1. 보조: 자세(RTMPose, MediaPipe), 소리(실험) | 서버 GPU, 공 찾기 창 하나 10.5ms |
| 2층 이벤트 | 공 궤적에서 바운드·네트·아웃·끝을 뽑는다 | 손으로 쓴 규칙(events.py) |
CPU, 표본 하나에 0.1–0.4ms |
| 3층 심판 | 이벤트 순서로 랠리 승자와 이유를 정한다 | 탁구 규칙(referee.py, rules.py) |
같은 CPU 프로세스 |
| 4층 느린 확인 | 애매한 랠리만 사람이(앞으로는 영상-언어 모델도) 다시 본다 | 사람의 한 번 누르기 | 비동기, 점수를 막지 않는다 |
왜 층을 나누는가
가장 큰 이유는 판정 규칙이 모델보다 먼저 맞아야 하기 때문입니다. 공 좌표가 아무리 정확해도 「서브는 자기 쪽에 한 번, 상대 쪽에 한 번 떨어져야 한다」 같은 규칙을 잘못 옮기면 점수가 틀립니다. 그래서 2층과 3층은 모델과 떼어 순수 파이썬 함수로 두었습니다. 영상 없이 합성 궤적과 이벤트만으로 모든 규칙을 시험할 수 있고, 공 모델을 다른 것으로 바꿔 끼워도 심판은 그대로입니다.
두 번째 이유는 고장 난 곳을 가르기 위해서입니다. 끝에서 끝까지 하나의 큰 모델이 영상을 받아 점수를 낸다면, 틀렸을 때 공을 못 찾은 것인지 규칙을 잘못 적용한 것인지 알 수 없습니다. 층이 나뉘어 있으면 같은 채점을 「모델이 찾은 공」, 「정답 공」, 「정답 바운드」 세 가지 입력으로 돌려 오차가 어느 층에서 나는지 잴 수 있습니다. 5편에서 이 방법으로 「남은 오차의 대부분은 공 찾기가 아니라 이벤트 추출에서 난다」는 것을 보입니다.
세 번째 이유는 라이선스와 비용입니다. 층마다 가장 작은 도구를 고를 수 있습니다. 공 찾기는 149만 파라미터짜리 모델로 충분하고, 심판은 GPU 가 필요 없습니다.
각 층에서 무엇을 골랐고 무엇을 뺐나
| 자리 | 고른 것 | 고른 이유 | 뺀 것과 이유 |
|---|---|---|---|
| 공 | BlurBall(코드 MIT, 149만 파라미터) | 번진 공의 가운데에 라벨을 다는 방식이라 60fps 의 긴 번짐에 맞고, 비교 모델 다섯 가운데 단독 성능이 가장 좋았다(2편) | TrackNetV2·MonoTrack·WASB 단독: 재현율이 0.71–0.96 으로 낮았다. 함께 돌리면 어두운 영상에서 이득이 있지만 GPU 시간이 두 배다 |
| 탁구대 | 사람의 탭 네 번, 실험에서 SAM 2.1 tiny(Apache-2.0, 3,890만 파라미터) | 카메라가 고정이라 경기 시작 때 한 번이면 된다. 사람의 탭이 그대로 프롬프트가 된다(3편) | YOLO 계열 분할 모델: AGPL 조건. SAM 3: 별도 라이선스, 가중치 접근 승인 필요, 8억 4,800만 파라미터 |
| 자세·라켓 | RTMPose(Apache-2.0), MediaPipe Pose(코드 Apache-2.0) | 코칭 화면에서 이미 쓴다. 점수 판정에는 「확인 필요」 신호로만(4편) | 자세로 바운드를 지우면 승자 정확도가 1.1–5.5%p 떨어졌다 |
| 소리 | 엔진에 넣지 않음 | 깨끗한 녹음에서는 테이블 소리를 99% 가르지만, 실제 훈련장 녹음에서는 효과가 확인되지 않았다(6편) | — |
| 느린 확인 | 지금은 사람 | 점수를 바꾸는 권한은 사람에게 둔다 | 영상-언어 모델을 판정에: 기본 1초에 한 장을 보므로 바운드 순간이 입력에 없다(8편) |
3. 시간 예산: 16.7ms 와 1.5초
실시간 점수판에는 시간 제한이 두 개 있습니다.
- 프레임 예산. 60fps 영상은 16.7ms 마다 새 프레임이 옵니다. 그 안에 처리하지 못하면 프레임이 밀립니다. 30fps 면 33.3ms 입니다.
- 판정 예산. 랠리가 끝난 뒤 점수가 바뀔 때까지 기다릴 수 있는 시간입니다. 목표를 1.5초 안으로 잡았습니다.
프레임 하나에 실제로 드는 시간
| 부분 | 시간(예열 뒤 중앙값) | 조건 |
|---|---|---|
| 공 찾기, 실시간(새 프레임마다 최근 3장 창 하나) | 10.5ms(fp32), 8.0ms(fp16) | 2026-10-12, 서버 GPU(24GB급)가 다른 일 없이 쉬던 때, 예열 30회 뒤 200회 × 3번의 중앙값 |
| 공 찾기, 녹화본(겹치지 않는 3장 창, 8창 묶음) | 프레임당 3.42ms(1창), 2.81ms(8창 묶음) | 2026-10-11, 같은 GPU. 창 하나가 3프레임을 한꺼번에 처리하므로 프레임당 값은 창 시간의 약 3분의 1 |
| 공 찾기, 같은 카드를 다른 작업과 나눌 때 | 창 하나 40–46ms | 2026-10-11, 음성 합성 1개와 분할 실험 2–3개가 함께 돌던 때 |
| 이벤트 + 심판(최근 2.5초를 표본마다 다시 돌림) | 표본당 0.118ms(중앙), 0.422ms(95%) | 2026-10-13, 노트북 CPU, 표본 32만 개 |
두 줄의 공 찾기 시간이 세 배 차이 나는 이유가 실시간 설계의 핵심입니다. BlurBall 은 프레임 3장을 받아 히트맵 3장을 냅니다. 녹화본은 창을 겹치지 않게 3장씩 밀면 되지만, 실시간은 새 프레임이 올 때마다 「방금 온 프레임을 포함한 최근 3장」으로 창을 새로 돌리고 마지막 히트맵만 씁니다. 지연을 한 프레임 간격 안에 두는 대신 GPU 일을 세 배로 하는 선택입니다. 가운데 히트맵을 쓰면 앞뒤 프레임을 모두 보지만 한 프레임을 더 기다려야 하는데, 두 방식의 공 찾기 차이는 실측에서 1%p 안이었습니다(표본 300여 프레임).
정리하면, 빈 카드에서는 세션 하나가 60fps 예산의 약 63%(10.5 / 16.7ms)를 씁니다. 같은 카드를 다른 일과 나누면 창 하나가 40ms 를 넘어 60fps 는커녕 30fps 도 지키지 못합니다. 그래서 작업자는 밀린 창을 줄 세우지 않고 버립니다. 다음에 돌릴 창 자리를 하나만 두고, GPU 가 따라오지 못하면 새 창이 그 자리를 덮습니다. 줄을 세우면 지연이 끝없이 늘어나기 때문입니다. 2026-10-11 측정에서 처리 속도가 30fps 에 못 미친 만큼(보낸 프레임의 22–28%)이 버려졌고, 지연은 그만큼 늘지 않았습니다.
지금의 실시간 데모는 브라우저가 512×288 로 줄인 JPEG(품질 0.7, 실측 12–18KB)을 30fps 간격으로 보냅니다. 원본을 보내면 회선이 10배 넘게 들고 서버가 줄이는 일만 늘어나기 때문입니다. 개발 환경에서 브라우저가 보낸 뒤 답을 받기까지 중앙 65ms, 90% 84ms 였습니다. 실제 인터넷을 거치는 운영 경로는 홉이 달라 미실측입니다.
판정까지 걸리는 시간
이벤트 층은 「마지막 바운드 뒤 1.5초 동안 새 바운드가 없으면 랠리 끝」이라는 규칙을 씁니다. 1.5초는 실제 경기 12편에서 랠리 안 바운드 간격의 98%(1,087개 중 1,066개)가 들어오는 값입니다. 다만 이것은 가장 늦는 길입니다. 공이 테이블 밖으로 떨어지면 「아웃」이, 공이 화면에서 사라지면 「공 없음」 끝이 먼저 옵니다.
2026-10-13 에 실제 배포 경로(아래 4절)로 잰 판정 지연은 다음과 같습니다. 판정 지연은 판정을 낸 이벤트가 나온 시각에서 사람이 단 랠리 종료 라벨 시각을 뺀 값입니다.
| 범위 | 판정 지연 중앙 | 90% | 1.5초 안 |
|---|---|---|---|
| 실제 경기 12편, 275랠리 | 0.16초 | 1.23초 | 98.9% |
| 그중 시험 7경기, 57랠리 | 0.39초 | 1.32초 | 100% |
계산 시간과 네트워크 시간은 빠진, 알고리즘이 기다리는 시간만입니다.
4. 층 사이에 오가는 데이터
층을 나누면 층 사이의 「계약」이 중요해집니다. 실제로 오가는 값은 이렇습니다.
| 경계 | 모양 | 비고 |
|---|---|---|
| 브라우저 → 1층 | 512×288 JPEG, 순번, 찍은 시각(ms) | 120KB 넘으면 버린다 |
| 1층 → 2층 | 프레임마다 (t, x, y, conf) |
공이 없으면 x·y 가 비어 있다. 번짐 길이·각도도 함께 나오지만 지금 엔진은 쓰지 않는다 |
| 탁구대 → 2·3층 | 네 모서리 → 화면에서 테이블 평면으로 가는 변환 | 경기 시작 때 한 번 |
| 2층 → 3층 | Event(t, kind, side, conf, vx, vx_in) |
kind 는 bounce·net·out·end. vx 는 바운드 직후 테이블 길이 방향 속도(테이블 길이/초) |
| 3층 → 점수판 | PointCall(winner_side, reason, confidence, t) |
winner_side 가 비면 렛. 신뢰도 0.6 아래면 확인 필요 |
두 가지가 눈에 띕니다. 첫째, 2층은 공 좌표만 받습니다. 영상 화소는 1층을 넘어가지 않습니다. 둘째, 바운드 이벤트에 「가로 속도」가 실려 있습니다. 심판은 이 값으로 「서브처럼 네트 쪽으로 가는 공」과 「상대에게 공을 넘겨주는 공」을 가릅니다. 이 한 숫자가 실제 경기에서 가짜 점수를 크게 줄였습니다(5편).
5. 지금 어디까지 왔나: 2026-10-13 기준선
공개 데이터셋 OpenTTGames 의 실제 경기 12편(학습 5편과 시험 7편)과, 그 위에 랠리 종료 원인을 붙인 Extended OpenTTGames 라벨로 쟀습니다. 서버를 알 수 있는 랠리 275개가 대상이고, 주 지표는 랠리 승자 정확도입니다.
| 경로 | 승자 맞음 | 확인 필요 없이 틀림 | 확인 필요 | 판정 없음 |
|---|---|---|---|---|
| 녹화본 채점(랠리를 다 본 뒤 판정) | 91.3% (251/275) | 13 | 108 | 3 |
| 배포된 실시간 경로(최근 2.5초를 표본마다 다시 돌림) | 90.2% (248/275) | 15 | 129 | 2 |
| 시험 7경기만, 배포 경로 | 87.7% (50/57) | 6 | 26 | 1 |
조건: 공 좌표는 BlurBall 사전학습 가중치 그대로(미세조정 없음), 탁구대 모서리는 데이터셋의 분할 마스크에서 구한 것, 엔진은 2026-10-13 판. 노트북 CPU 에서 엔진만 다시 돌렸습니다.
이 숫자를 읽을 때 지켜야 할 단서가 넷 있습니다.
- 같은 엔진이라도 경로에 따라 다르다. 녹화본 채점과 실시간 경로가 깨끗한 영상에서 3랠리 차이 났고, 어둡거나 흐린 조건에서는 두 경로의 순위가 뒤집혔습니다. 그래서 이후의 모든 실험은 두 경로를 함께 적습니다.
- 표본 안의 점수다. 규칙 엔진은 이 12편을 보며 다듬어졌습니다. 시험 7경기도 마찬가지라, 이 정확도는 처음 보는 영상에서의 성능보다 낙관적입니다.
- 한 체육관, 한 각도다. OpenTTGames 는 한 경기장에서 옆면 고정 카메라, 120fps 로 찍었습니다(60fps 로 줄여 씀). 휴대폰, 동네 탁구장 조명, 다른 각도의 성능은 미실측입니다. BlurBall 학습 영상 26편의 목록이 공개되지 않아 OpenTTGames 와 겹치는지도 모릅니다.
- 모서리가 바뀌면 숫자가 바뀐다. 2026-10-12 에 SAM 2.1 로 구한 모서리를 쓴 녹화본 채점은 93.1%(256/275)였습니다. 그 모서리 파일이 남아 있지 않아 기준선은 데이터셋 마스크 모서리로 다시 잡았습니다. 모서리 품질이 점수에 얼마나 걸리는지는 3편과 5편에서 다룹니다.
6. 앞으로의 방향
남은 오차의 대부분은 공 찾기가 아니라 이벤트 추출에서 납니다(5편). 그래서 다음 단계의 관심은 「공을 더 잘 찾는 모델」보다 「바운드와 타구를 가르고, 규칙상 불가능한 순서를 걸러 내는 신호」에 있습니다. 2026-10-13 의 모델 조사가 권한 구조는 세 줄입니다. 아래는 조사와 설계이고, 효과는 아직 재지 않았습니다(진행 중).
- 인식. 신호마다 따로 확률과 「건강」(0–1, 이 신호를 지금 믿을 만한가)을 낸다. 공은 BlurBall 을 상시로, 가림·어두움일 때만 둘째 공 모델을 켠다. 라켓·자세, 소리(onset + 표면 분류, 동기화와 건강 판정이 먼저), 탁구대 평면을 더한다.
- 이벤트 → 심판. 지금의 규칙 기반 판단을, 탁구 랠리 순서를 허용 전이로 넣은 제약 Viterbi 디코딩으로 바꾼다. 가장 좋은 경로와 두 번째 경로의 점수 차가 작으면 「확인 필요」(5편 5절).
- 느린 확인. 확인 필요 랠리만 클립으로 영상-언어 모델에 보내 교차 확인하고, 심판과 다르면 표시만 한다(8편).
같은 조사가 꼽은 후보 다섯은 규칙 문법을 넣은 제약 Viterbi, 정밀 이벤트 위치 찾기 모델(AdaSpot 류), 소리 표면 분류와 소리–영상 동기화, 라켓 5점(RacketVision), 가림 대응 공 추적(TOTNet)입니다. 하나씩 같은 기준선(5절)과 같은 두 경로로 재서 이 시리즈에 덧붙이겠습니다.
7. 시리즈 지도
| 편 | 다루는 것 |
|---|---|
| 2편 공 찾기 | TrackNet → WASB → BlurBall. 히트맵 회귀, 여러 프레임 입력, 번짐 가운데 라벨, HRNet |
| 3편 탁구대 찾기 | SAM 2.1, EfficientTAM, SAM 3. 프롬프트 분할, 메모리 어텐션, 마스크에서 네 모서리와 호모그래피 |
| 4편 자세와 라켓 | RTMPose(SimCC), MediaPipe, RacketVision 의 라켓 5점 |
| 5편 이벤트와 심판 | 궤적에서 바운드·타구를 뽑는 규칙, 심판 엔진, 정확도가 66.9% 에서 올라온 길, 3D 극값 규칙과 제약 Viterbi |
| 6편 소리 | onset 검출과 표면 분류. 실제 녹음에서 효과가 없었다는 부정 결과 |
| 7편 데이터셋 | OpenTTGames, Extended, T3Set, TT Sounds, RacketVision, LATTE-MV 를 직접 세어 본 표와 함정 |
| 8편 영상-언어 모델 | 왜 판정을 맡기지 않는가 |
티티 한마디. 공을 잘 찾는 것과 점수를 잘 매기는 것은 다른 문제예요. 이 시리즈는 그 사이의 틈을 숫자로 따라가 봅니다.
참고
- 이 시리즈의 코드 경로는 TT Score 엔진 폴더 기준입니다.
ttscore/는 판정 엔진(순수 파이썬),live/는 실시간 작업자,batch/는 녹화 영상 작업자,experiments/는 실험 코드입니다. - 측정 원자료와 방법: 저장소
docs/tt/PERFORMANCE.md2.1·2.9.8, 실시간 데모 설계 문서 9절, 실험 원장 E00(2026-10-13). - OpenTTGames: https://lab.osai.ai/ (CC BY-NC-SA 4.0)
- Extended OpenTTGames: https://arxiv.org/abs/2512.19327, https://github.com/moamal01/table_tennis_data (CC BY-NC-SA 4.0)
- BlurBall: https://arxiv.org/abs/2509.18387, https://github.com/cogsys-tuebingen/BlurBall (코드 MIT)