백엔드가 아직 없어 서비스 입력을 만들 수 없으므로, 에이전트만 로컬에서 돌려 미결 5번(팔 종목의 임팩트 정의가 릴리스보다 이르다)을 조사했다.
정정 (2026-08-31) 이 글에서 축구
contact_frame을 “정답”으로 적었으나 정확하지 않다. 저장된 사람 라벨이 아니라 공-발목 최근접에서 자동 도출한 관측 기반 참조이며 ±2프레임 불확실성을 가진다. 이후 조사에서 확인했다. 경위는 A-1을 되돌린다 참고.
결론부터 적으면 미결 5번의 전제가 틀렸다. 임팩트는 이른 것이 아니다. 임팩트 탐색 범위가 클립 전체라, 동작과 무관한 구간의 급변이 근소한 차이로 이기는 것이다. 팔 종목 문제도 아니다 — 문제없다고 적어 둔 축구에서도 같은 일이 일어나고 있었다.
먼저 정의를 확인했다
extension_peak은 이렇게 계산된다 (features.py의 segment_phases).
series = chain_series(kps, swing) # 어깨-팔꿈치-손목 각도 시계열
impact = _peak_frame(np.gradient(series), usable)
즉 신전 각속도가 최대인 프레임이다. 릴리스도 아니고 최대 신전도 아니다. 스무딩이 없고, 탐색 범위가 클립 전체의 전역 최댓값이며, 위상 게이트가 없다.
이름과 의미가 갈린다. 루브릭의 밴드(릴리스 팔 신전 150~172도)는 “릴리스 시 각도”를 가정하는데, 각속도가 최대인 순간의 관절각은 구조상 가동범위의 중간 이다. 채찍질 동작에서 속도 피크는 언제나 완전 신전보다 앞선다.
샘플링은 원인이 아니었다
production과 같은 조건(A, target_fps=15)과 원본 전 프레임(B, step=1)을 비교했다. production 코드는 고치지 않고 extract_keypoints의 target_fps만 바꿔 호출했다.
| A(production) | B(dense) | ||
|---|---|---|---|
| 야구 peak | 원본 48프레임 | 원본 49프레임 | 0.04초 차이 |
| 야구 peak 시 팔꿈치각 | 61.4도 | 102.4도 | 값만 41도 벌어진다 |
| 농구 peak | 원본 68프레임 | 원본 68프레임 | 완전히 동일 |
| 농구 peak 시 팔꿈치각 | 112.7도 | 112.7도 | 완전히 동일 |
프레임을 두 배로 늘려도 peak가 릴리스 쪽으로 이동하지 않는다. 샘플링이 바꾸는 것은 사건의 위치가 아니라 그 프레임에서 읽히는 값이다 — 야구에서 61.4도와 102.4도가 갈린 것은 미결 7번(프레임레이트에 따라 점수가 달라진다)과 같은 현상이지, 임팩트가 이르다는 근거는 아니다.
눈으로 보니 이야기가 달랐다
여기서부터가 이번 조사의 핵심이다. 선택된 프레임에 스켈레톤을 그려 확인했다.
야구 — extension_peak이 잡은 원본 49프레임은 와인드업이다. 양손을 가슴 앞에 모으고 다리를 든 자세다. 48~50프레임에서 팔꿈치각이 61 → 102 → 176 도로 급변하는데, 이건 투구 채찍질이 아니라 겹친 두 손 사이에서 왼팔 키포인트가 엉킨 것이다. 신뢰도는 어깨 0.94 / 팔꿈치 0.81 / 손목 0.89로 전부 게이트를 통과한다 — 신뢰도로는 안 잡히는 실패다.
그리고 클립 전체(92프레임)를 훑어보니 더 큰 문제가 있었다.
이 클립에는 릴리스가 들어 있지 않다. 마지막 프레임에서 팔이 아직 앞으로 나오는 중이고 영상이 끝난다.
루브릭의 validated_on에 “투구 구간 3.6초 절취”라고 적혀 있는데, 그 절취가 릴리스 앞에서 끊겼다.
농구 — extension_peak이 잡은 원본 68프레임은 카메라를 등지고 달리는 드리블 구간이다. 슛이 시작되기도 전이다. 실제 슛 동작은 82프레임(공을 잡음) → 85(들어올림) → 87~89(점프) → 91(림 앞)이고, 점프슛이 아니라 레이업이다. 공-손목 거리도 이걸 뒷받침한다: 87프레임에서 0.439로 최소였다가 88프레임부터 단조 증가해 103프레임에 3.415가 된다.
즉 bball_shot.mp4를 basketball/jump_shot 루브릭으로 채점하고 있었다. 레이업 루브릭은 따로 있고(distal_apex를 쓴다) 아직 draft다.
포즈 품질에서 하나 더 나왔다
농구 60~67프레임은 손목 신뢰도가 0.20~0.46이라 arm 임계 0.6에 걸려 unusable 이고, 그 구간 각도 계열이 28도와 175도 사이를 진동한다. 그런데 peak가 잡힌 68프레임의 속도값은 이렇게 계산된다.
gradient[68] = (series[69] - series[67]) / 2 = (155.5 - 56.3) / 2 = 49.6
series[67]은 unusable 프레임이다. _peak_frame은 후보를 usable로 제한하지만 np.gradient(series)는 원계열 전체로 계산되므로, 쓰레기 프레임이 이웃한 정상 프레임의 속도를 부풀린다.
축구로 양성 대조를 했다
팔 클립이 둘 다 부적격이었으므로, 임팩트가 실제로 들어 있는 클립에서 extension_peak이 그것을 잡는지 따로 확인했다. goldenset/soccerkicks_video의 킥 19건이다. 축구는 임팩트가 볼 접촉이라 공 궤적에서 관측으로 도출할 수 있다 — 스윙 발목과 공의 거리가 최소인 프레임을 접촉 참조로 삼는다.
19건 중 production이 실제로 채점하는 것은 16건이다. 1건은 공 미검출이고, 2건(19_freekick1·21_freekick)은 segment_phases의 경계 검사에 걸려 InsufficientQuality로 반려된다 — 잘못 채점되는 게 아니라 아예 채점되지 않는다. np.gradient가 양끝에서 한쪽 차분을 쓰는 탓에 0번 프레임이 전역 최대가 되는 경우가 있는데, 그 안전망이 이미 막고 있다.
| peak − contact 분포 (16건) | −11 −9 −6 −6 −2 −1 0 0 0 1 1 4 5 5 8 15 |
| 중앙값 | 0.0 |
| |차이| ≤ 1프레임 | 6/16 |
| |차이| 중앙값 | 4.5프레임 (실효 12~15fps에서 약 0.35초) |
한쪽으로 치우쳐 있지 않다. 미결 5번은 “임팩트가 릴리스보다 이르다”는 단방향 편향을 전제하는데, 임팩트가 실제로 들어 있는 종목에서는 이르기도 하고 늦기도 한다. 편향이 아니라 산포다.
그리고 이건 축구가 문제없다고 적어 둔 종목이라는 점이 중요하다. 미결 5번은 “축구 인스텝 슈팅은 발이 공에 닿는 순간이 곧 신전 각속도 피크라 문제가 없다”고 적고 있는데, 재 보니 절반 이상이 2프레임 넘게 어긋난다. 문제가 없었던 게 아니라 재 본 적이 없었다.
그 산포는 누구 것인가
접촉 추정에도 오차가 있으므로, 그것부터 분리했다. 이 클립들은 전부 페널티· 프리킥이라 공이 정지해 있다가 차이는 순간 움직인다. 그래서 서로 독립인 접촉 근거를 둘 만들 수 있다.
- P1 스윙 발목과 공의 거리가 최소인 프레임
- P2 정지해 있던 공이 움직이기 시작하기 직전 프레임
둘이 ±1 안에서 일치한 것은 18건 중 9건뿐이다. 나머지 절반에서는 접촉 프레임 자체가 불확실하고, 위 산포에는 그 오차가 섞여 있다.
그런데 둘이 일치한 클립만 봐도 임팩트는 여전히 빗나간다. 반려 1건을 뺀 8건에서 |차이| 중앙 3.0, ≤1프레임 4/8 (−11 −6 −6 0 0 0 1 5). 산포는 접촉 추정 탓만이 아니다.
그래서 접촉이 확실한 클립에서 각속도의 국소 최댓값을 전부 세고 접촉 ±2에 있는 것의 순위를 매겼다.
| 접촉 ±2에 국소 피크가 있는 클립 | 9/9 전부 |
| 그 피크가 전역 1위 | 5/9 |
| 전역 상위 3위 안 | 9/9 전부 |
| 진 경우 승자와의 배율 | 1.37 · 1.41 · 1.51배 (한 건만 8.95배) |
신호가 없어서 못 잡는 게 아니다. 접촉 지점의 피크는 언제나 존재하고 언제나 상위권이며, 지는 경우도 대개 1.4~1.5배 차이로 아슬아슬하게 진다. 범인은 클립 전체를 후보로 두는 전역 argmax다.
나머지 후보는 전부 떨어졌다.
- 평활 부재 — 아니다. 3탭 평활이 산포를 절반으로 줄이는 것처럼 보였는데, 그 개선은 거의 전부
19_freekick1한 건에서 나왔고 그 클립은 production이 이미 반려한다. 반려 건을 빼면 |차이| 중앙이 3.0 → 3.0으로 그대로다 - 스윙 다리 오판별 — 아니다. 반대쪽 다리로 계산하면 ≤1프레임이 0/8로 더 나빠진다(중앙 −2.0). 다리는 제대로 고르고 있다
- 경계 아티팩트 — 이미 막혀 있다. 위의 반려 2건이 그것이다
그래서 무엇이 바뀌나
미결 5번은 지금까지 세 가지 선택지를 놓고 있었다 — 임팩트 사건을 하나 더 만들거나, 밴드를 신전 중간 기준으로 다시 잡거나, 팔 종목의 프레임레이트를 올리거나. 셋 다 “릴리스를 제대로 잡았는데 각도가 안 맞는다”를 전제한다.
이번 조사가 보여주는 것은 그 전제가 성립하지 않는다는 것이다. 문서에 근거로 적혀 있던 61도와 112.7도는 릴리스 각도가 아니다 — 하나는 와인드업에서 엉킨 각도이고, 하나는 달리는 중의 각도다. 그리고 임팩트가 실제로 들어 있는 축구에서 재 보면 어긋남에 방향이 없다.
즉 질문이 바뀐다. “임팩트가 왜 이른가”가 아니라 “임팩트 프레임이 왜 실제 사건에 고정되지 않는가” 다. 그리고 그 답은 축구 분해에서 나왔다.
임팩트 탐색 범위가 클립 전체다. 접촉 지점의 신호는 언제나 있고 언제나 상위권인데, 동작과 무관한 구간의 비슷한 크기 급변이 근소한 차이로 이긴다.
이건 팔 종목에서 본 것과 같은 실패다. 야구는 와인드업의 키포인트 엉킴이, 농구는 주행 중 팔 스윙이 전역 argmax에서 이겼다. 축구에서는 경쟁자가 약해 1.4배 차이로 갈렸을 뿐이다. 종목 문제가 아니라 탐색 범위 문제다.
여기에 두 가지가 겹친다.
- 입력 데이터 타당성 — 야구 클립엔 릴리스가 없고, 농구 클립은 다른 동작이다. 이건 feature와 무관한 별개 문제다
- 포즈 품질 — 속도 계산 입력에 unusable 프레임이 섞인다(농구 f67). 다만 축구에서는 평활·다리 판별 어느 쪽도 산포를 줄이지 못했으므로, 이것이 산포의 주원인은 아니다
- 샘플링은 기각
무릎각을 peak에서 읽느냐 접촉에서 읽느냐에 따라 등급이 갈리는 클립이 절반을 넘는다. 접촉 추정 오차가 섞여 있어 정확한 건수를 주장하지는 않지만, 임팩트 프레임을 몇 프레임 잘못 잡으면 등급이 바뀐다는 것 자체는 분명하다.
그래도 여기서 코드를 고치지 않는다. “탐색을 좁히자”가 자연스러운 다음 수인데, 무엇으로 좁힐지(공 소유 구간인지, 동작 위상인지)가 정해지지 않았고 그건 feature 변경이다. 원인을 확정하는 것과 고치는 것은 다른 단계다.
다음
릴리스가 실제로 들어 있는 팔 종목 클립이 로컬에 없다. 판정 기준을 관측 가능한 사건으로 잡고(공이 손목 근처에 있다가 떠나고, 그 이탈이 클립 경계에서 충분히 안쪽일 것) 보유 자산을 훑은 결과다.
| 자산 | 결과 |
|---|---|
| UCF101 농구 134클립 (320×240) | 공이 MIN_TOOL_CONFIDENCE=0.8을 한 번도 못 넘는다 (돌린 5건 전부 0프레임) |
| vru_sample (1920×1080) | 품질·공 검출은 통과하는데 공이 선택된 선수의 손목 근처에 온 적이 없다 — 다인원 장면에서 면적 최대 selector가 슈터가 아닌 사람을 잡은 것으로 보인다 |
| pitchermotion (60fps 3,324클립) | 포즈만 있고 영상이 없다. 이번에 결정적이었던 시각 검증을 못 한다 |
| hmdb51 · sportsmot | 해상도·장면 구성 부적합 |
외부 확보 경로는 하나 있다 — eval/phaseA/fetch.sh의 Kinetics + yt-dlp 파이프라인이 그대로 동작한다(1080p 이하, 구간 절취). Kinetics-400에 shooting basketball 같은 클래스가 있으므로 같은 방식으로 받을 수 있지만, 로컬 어노테이션은 hitting baseball 서브셋뿐이라 클래스 목록을 따로 구해야 하고, 진행 여부는 팀이 정할 일이다.
다만 클립 확보가 급한 일은 아니게 됐다. 축구 분해로 원인이 탐색 범위라는 것이 나왔으므로, 클립을 더 받아도 그 범위를 좁히지 않으면 같은 문제를 다시 만난다. 순서는 탐색 범위를 무엇으로 좁힐지 정하는 것이 먼저다.
정할 것은 두 가지다.
- 무엇으로 좁힐 것인가 — 공 소유 구간인가, 동작 위상인가, 아니면 사람이 지정한 구간인가. 공 궤적을 쓰면 도구가 안 잡히는 영상에서 임팩트를 못 찾게 되므로, 도구 미검출 처리 원칙(그 항목만 빼고 채점)과 어떻게 맞물릴지 함께 봐야 한다
- 좁힌 뒤에 무엇으로 검증할 것인가 — 축구는 볼 접촉이라는 참조를 공짜로 얻을 수 있으므로 위 16건이 그대로 회귀 기준이 된다. 팔 종목은 여전히 릴리스 포함 클립이 필요하다
축구 클립의 포즈·공궤적은 soccer_dump.npz로 떠 두었다. 이후 비교는 GPU 없이 CPU로만 돌아가므로 변형을 여러 개 재 보는 비용이 거의 없다.
이번 단계에서 루브릭·feature·scoring·selector는 한 줄도 고치지 않았다. 원인을 확정하는 것과 고치는 것은 다른 단계다. 미결 5번 페이지 갱신은 다음 단계에서 한다 — 지금 문구(“임팩트가 릴리스보다 이르다”)는 틀렸고, “임팩트 탐색 범위가 클립 전체라 실제 사건에 고정되지 않는다”로 바뀌어야 한다.
서비스 입력은 여전히 0이다. 이번 실험은 전부 개발 영상이고 관측 기록은 SUPERSUB_METRICS_SINK로 분리해 서비스 sink에 남기지 않았다.