Big Project · AI's EYE 13
영상 추적: 파일 경계와 장면 전환 사이의 오해
여러 영상 파일을 차례로 분석한다면 파일이 바뀔 때마다 ByteTrack을 초기화하는 것이 안전해 보인다. 왜 그것이 오히려 같은 장면의 사람 ID를 끊는지 이해하기 어렵다.
근거 · 프로젝트 문서 · 코드 · 테스트
1장: 파일 경계: 트래커 ID가 끊기는 이유
솔라는 모니터 위를 미끄러지는 타임라인 바를 손가락으로 꾹 누른 채 멈췄다. 화면 속에는 카페 로비를 오가는 사람들의 모습이 담긴 영상이 재생되고 있었다. 한 남자가 화면 왼쪽에서 나타나 카운터를 향해 걸어갔다. 시스템은 정확하게 남자를 인식하고, 네모난 경계 상자 위에 ‘ID: 17’이라는 작은 표식을 달아주었다. 남자는 몇 걸음 더 걸어 화면 중앙에 이르렀다. 바로 그 순간이었다.
“어?”
솔라의 입에서 짧은 소리가 튀어나왔다. 방금까지 ‘ID: 17’이었던 표식이 순식간에 ‘ID: 23’으로 바뀌어 있었다. 남자는 같은 옷을 입고 같은 걸음을 이어가고 있었지만, 시스템에게 그는 더 이상 17번이 아니었다. 마치 두 개의 다른 존재가 바통 터치를 한 것처럼.
“언니, 이것 좀 봐. 트래커가 사람을 놓치는 것 같아.”
솔라는 옆자리에서 조용히 코드를 보던 루나를 불렀다. 루나는 의자를 끌어 솔라의 모니터 앞으로 다가왔다. 화면 아래에는 현재 재생 중인 파일 이름이 떠 있었다. cafe_lobby_cam01_part002.mp4.
“어떤데?”
“이 사람. 분명히 계속 같은 사람인데, ID가 바뀌었어. 여기서 17번이었다가… 갑자기 23번이 됐어.”
솔라는 타임라인 바를 다시 뒤로 돌렸다. ‘ID: 17’이 선명하게 보이는 cafe_lobby_cam01_part001.mp4의 마지막 몇 초. 그리고 플레이어를 다시 앞으로 감자, 파일 이름이 part002.mp4로 바뀌는 동시에 남자의 ID도 23번으로 갱신되었다.
루나는 말없이 그 장면을 두어 번 반복해서 보더니, 솔라에게 물었다.
“ID가 바뀌는 바로 그 순간, 영상은 어때? 남자의 움직임이나 배경에 큰 변화가 있어?”
“아니, 전혀. 봐봐.” 솔라는 프레임 단위로 영상을 끊어 보여주었다. “part001의 마지막 프레임이랑 part002의 첫 프레임, 거의 똑같아. 그냥 자연스럽게 이어지는 장면이야. 그런데 ID만…”
말을 잇던 솔라의 눈이 문득 파일 이름에 고정되었다. part001, part002. 마치 드라마 1부와 2부처럼, 영상이 여러 개의 파일로 나뉘어 저장된 형태였다.
“아…”
짧은 탄식과 함께 솔라는 무언가 깨달은 듯한 표정을 지었다.
“설마… 파일이 바뀔 때마다 트래커를 그냥 새로 시작하는 거야? 그래서 ID가 초기화되는 거고?”
“그렇게 볼 수 있겠네.”
루나의 차분한 대답에 솔라는 허탈한 웃음을 터뜨렸다. “말도 안 돼. 그럼 이건 트래커의 버그가 아니었네. 그냥 규칙을 너무 단순하게 만든 거잖아. 파일 하나 처리 끝나면 트래커 끄고, 다음 파일에서 새로 켜는 식으로.”
솔라는 이제 ID가 아니라, 화면 하단의 파일 이름이 바뀌는 경계선을 뚫어져라 쳐다봤다. 이전까지는 그저 데이터를 담은 그릇이라고만 생각했던 ‘파일’이, 이제는 전혀 다른 의미로 보였다. 그것은 추적의 흐름을 멋대로 끊어버리는 보이지 않는 벽이었다.
“물리적인 파일의 경계랑 실제 영상 속 장면의 경계가 항상 같지는 않다는 거구나.”
솔라는 스스로 결론을 내리며 중얼거렸다. 연속된 장면이 기술적인 이유로 여러 파일에 나뉘어 저장될 수 있다는 당연한 사실을, ID가 끊어지는 현상을 보고 나서야 비로소 온몸으로 이해하게 된 것이다. 눈앞의 영상 속에서 17번 남자는 23번 남자가 되어 유유히 카운터로 사라졌다. 시스템의 기록 속에서 그 둘은 영원히 다른 사람으로 남을 터였다.
문제를 정확히 인지하자, 솔라의 머릿속에는 곧바로 단순하고 명쾌해 보이는 해결책이 떠올랐다.
“그럼… 간단하잖아. 파일이 바뀔 때마다 트래커를 초기화하는 규칙을 그냥 없애버리면 되는 거 아닐까? 장면이 이어지든 말든, 그냥 계속 추적하도록 두는 거지.”
솔라는 자신의 생각에 만족하며 루나를 바라보았다. 하지만 루나는 즉시 답하는 대신, 마우스 커서를 다른 영상 폴더 위로 가져가고 있었다. 그곳에는 전혀 다른 시간, 다른 장소에서 촬영된 영상 파일들이 날짜별로 정리되어 있었다. 그 모습은 마치 솔라의 ‘간단한’ 해결책이 또 다른 문제를 낳을 수 있다고 말하는 듯했다.
2장: 장면 전환: ID 재사용의 숨겨진 위험
루나의 손길에 따라 마우스 커서가 멈춘 곳은 이전과 다른 영상 폴더였다. 방금 전까지 솔라가 씨름하던 cafe_lobby_cam01_part001.mp4, part002.mp4 같은 파일들이 아니었다. 대신 화면에는 corridor_20240510_1400.mp4와 lobby_20240510_1800.mp4 라는, 시간과 장소가 명확히 구분된 두 개의 파일이 나란히 놓여 있었다. 솔라가 제안했던 ‘파일이 바뀌어도 트래커를 초기화하지 않는’ 규칙을 시험해 보기에 더없이 좋은 재료처럼 보였다.
루나는 두 파일을 드래그해서 하나의 재생 목록으로 만들었다. 마치 하나의 긴 영상처럼 이어 붙인 것이다. 그리고는 솔라가 제안한 대로, 파일 경계에서 트래커의 상태를 초기화하는 로직을 비활성화한 시뮬레이션을 실행했다. 솔라는 자신의 해결책이 완벽하게 작동하는 모습을 기대하며 화면에 집중했다.
첫 번째 영상, corridor_20240510_1400.mp4가 재생되었다. 복도를 지나가는 몇몇 사람들에게 ‘ID: 1’, ‘ID: 2’와 같은 표식이 붙었다가 사라졌다. 영상이 끝날 무렵, 마지막으로 ‘ID: 7’을 부여받은 한 직원이 복도 끝으로 사라졌다. 여기까지는 아무런 문제가 없었다. 잠시 후, 플레이어는 자동으로 다음 파일인 lobby_20240510_1800.mp4를 재생하기 시작했다.
화면이 바뀌었다. 오후 두 시의 텅 빈 복도와는 전혀 다른, 저녁 여섯 시의 북적이는 로비 풍경이 펼쳐졌다. 새로운 사람들이 화면에 나타났다. 그때였다. 로비 문을 열고 들어온 첫 번째 사람의 머리 위로 경계 상자가 그려지더니, ‘ID: 7’이라는 표식이 나타났다.
“어?”
솔라는 자신도 모르게 소리를 냈다. 분명 오후 두 시 복도 영상의 마지막에 사라졌던 7번이었다. 그런데 네 시간 뒤, 전혀 다른 장소인 로비에 나타난 다른 사람에게 같은 ID가 붙어버린 것이다.
“말도 안 돼! 저 사람들은 전혀 다른 사람이잖아! 옷도, 시간도, 장소도 다른데 어떻게 같은 ID를 줄 수 있어?”
솔라는 당황했다. 파일 경계에서 ID가 끊어지는 문제를 해결하려다, 이제는 엉뚱한 사람에게 과거의 ID를 붙여주는 더 심각한 오류를 만들어 낸 셈이었다. 연속성을 지키려던 시도가 정체성의 혼란을 낳은 것이다.
루나는 말없이 화면을 잠시 멈추고는, 솔라 옆에 있던 디지털 메모장을 열었다. 그리고 간결하게 두 가지 상황을 정리하기 시작했다.
| 상황 | 원인 | 문제 |
|---|---|---|
| 1. 같은 장면, 다른 파일 | 파일 경계에서 강제 초기화 | ID 단절 (같은 사람이 새 ID 받음) |
| 2. 다른 장면, 이어진 파일 | 장면 전환 시 초기화 누락 | ID 재사용 (다른 사람이 옛 ID 받음) |
솔라는 모니터 속 표를 멍하니 바라보았다. 머릿속에 뒤엉켜 있던 문제들이 한눈에 들어왔다. 첫 번째 줄은 방금 전 part001과 part002 영상에서 겪었던 문제였다. 그리고 두 번째 줄은 자신이 만든 ‘간단한 해결책’이 초래한 끔찍한 결과였다. 두 문제는 정반대의 원인에서 비롯된, 동전의 양면과도 같았다.
“아…”
솔라는 깊은 한숨을 내쉬었다. “무조건 초기화하는 것도 문제고, 무조건 초기화하지 않는 것도 문제였구나. 진짜 중요한 건 파일이 바뀌었느냐가 아니라… 실제 ‘장면’이 바뀌었느냐 아니냐를 구분하는 거였어.”
그녀의 시선은 이제 ‘ID 단절’과 ‘ID 재사용’이라는 두 개의 상반된 오류 사이를 오갔다. 하나를 피하면 다른 하나의 함정에 빠지는 구조였다. 이 두 가지 오작동을 명확히 비교하고 나니, 문제의 핵심이 보였다. 트래커를 언제 ‘리셋’하고 언제 ‘유지’할지 결정하는 섬세한 규칙이 필요했다. 파일 이름 같은 기술적 경계가 아니라, 영상의 내용 자체에 기반한 논리적 경계 말이다.
솔라는 고개를 들어 루나를 보았다. 이전의 자신감 넘치던 표정은 사라지고, 대신 더 근본적인 질문을 담은 진지한 눈빛이 그 자리를 채우고 있었다.
“그럼 언니, ‘장면이 바뀌었다’는 건 대체 어떻게 알 수 있는 거야? 그냥 배경이 좀 다르다고 해서 장면 전환이라고 할 수 있나? 그걸 기계가 정확하게 판단할 방법이 있어야 하잖아.”
3장: 시각적 단서: MAD로 장면 전환 감지하기
솔라의 마지막 질문이 공중에 채 가시기도 전에, 루나는 이미 화면에 새로운 창을 띄워놓고 있었다. 거기에는 네 개의 작은 이미지가 2x2 격자로 배열되어 있었다. 솔라가 방금 전까지 씨름하던 두 가지 문제 상황의 결정적 순간들이었다.
왼쪽 열에는 카페 로비 영상의 part001 마지막 프레임과 part002 첫 프레임이 위아래로 놓여 있었다. 두 이미지는 거의 똑같아서, 틀린 그림 찾기 문제처럼 보일 정도였다. 오른쪽 열에는 달랐다. 위쪽에는 텅 빈 복도 영상(corridor_...)의 마지막 모습이, 아래쪽에는 북적이는 로비 영상(lobby_...)의 첫 장면이 자리했다. 이 두 이미지는 배경, 조명, 사람까지 모든 것이 달라서 한눈에 봐도 다른 시공간임을 알 수 있었다.
루나는 아무 말 없이 마우스로 두 이미지 쌍 사이를 가리켰다. 하나는 거의 변화가 없는 ‘연속된’ 장면, 다른 하나는 모든 것이 바뀐 ‘단절된’ 장면. 솔라가 던진 “기계가 장면 전환을 어떻게 아는가?”라는 질문에 대한 대답은 바로 이 시각적 차이 속에 숨어있는 듯했다.
“네 말대로, ‘장면이 바뀌었다’는 걸 판단할 객관적인 기준이 필요해.”
루나는 마침내 입을 열며 왼쪽의 카페 로비 이미지 중 하나를 확대했다. 화면이 픽셀 단위로 깨져 보일 정도로 커지자, 루나는 이미지의 작은 사각형 영역을 선택했다.
“사람의 눈에는 이게 그냥 ‘비슷한 이미지’로 보이지만, 기계에게는 결국 숫자의 배열일 뿐이야. 예를 들어, 이 픽셀의 밝기가 150이라면, 다음 프레임의 같은 위치에 있는 픽셀은 152 정도겠지. 거의 차이가 없어.”
루나는 곧바로 오른쪽, 복도와 로비 이미지에서도 같은 위치의 영역을 확대했다.
“하지만 이 경우는 어떨까? 복도 벽을 찍던 픽셀의 밝기가 80이었다면, 로비의 환한 창문을 찍게 된 다음 프레임의 픽셀은 230 정도로 크게 변할 거야.”
솔라는 루나의 설명을 들으며 고개를 끄덕였다. 머릿속에서 막연하게 ‘다르다’고 생각했던 감각이 픽셀 밝기라는 구체적인 숫자로 번역되는 순간이었다.
“그럼… 그 픽셀 값 차이를 전부 계산하면 되겠네! 두 프레임 사이의 모든 픽셀 값 차이를 구해서 평균을 내는 거야. 장면이 이어지면 평균값이 작을 거고, 장면이 완전히 바뀌면 엄청 커지겠지!”
“바로 그거야. 그걸 ‘평균 절대 편차(Mean Absolute Difference, MAD)’라고 불러.”
루나는 작은 계산기를 화면에 띄워, 방금 예시로 든 픽셀 값들을 가지고 직접 계산하는 시늉을 했다. 먼저 모든 색상 정보를 흑백(grayscale)으로 단순화한 뒤, 각 픽셀의 밝기 차이를 계산해 평균을 내는 과정이었다.
| 프레임 쌍 | 이전 픽셀 밝기 | 현재 픽셀 밝기 | 차이(절대값) |
|---|---|---|---|
| 카페 로비 (연속) | 150 | 152 | 2 |
| 복도 → 로비 (전환) | 80 | 230 | 150 |
“실제로는 이미지 전체 픽셀로 계산해야겠지만, 원리는 같아. 이렇게 계산한 MAD 값이 특정 기준, 즉 임계값(threshold)을 넘어서면 ‘아, 이건 큰 장면 전환이다’라고 판단하고 트래커를 초기화하는 거지.”
솔라의 눈이 반짝였다. 막연했던 ‘장면 전환 감지’라는 문제가, MAD와 임계값이라는 두 개의 간단한 도구로 풀리는 명쾌한 공학 문제로 바뀌었다. 주관적인 인상이 아니라, 측정 가능한 숫자로 기계에게 판단의 근거를 만들어주는 방법이었다.
“알겠다! 그럼 이제 됐네. 파일이 바뀔 때마다 프레임 간 MAD를 계산해서, 값이 크면 트래커를 리셋하고, 작으면 그냥 유지하면 되는 거구나. ID 단절 문제랑 ID 재사용 문제를 동시에 해결할 수 있겠어!”
자신감에 찬 솔라의 목소리에, 루나는 긍정의 의미로 희미하게 미소 지었다. 하지만 동시에 화면 한편에 있던 작은 메모장을 클릭해 내용을 보여주었다. 그곳에는 솔라가 미처 생각지 못한 다른 조건들이 나열되어 있었다.
Reset 조건: 1. 큰 장면 전환(MAD 임계값 초과) 2. 카메라 ID 변경 3. 영상 누락 구간 발생 4. 비정상적인 시간 간격
솔라는 목록을 잠시 응시했다. MAD는 분명 강력한 시각적 단서였지만, 그것이 전부가 아니었다. 카메라 자체가 바뀌거나, 영상 데이터가 중간에 몇 분씩 통째로 사라지는 경우처럼 시각 정보만으로는 해결할 수 없는 문제들이 있었다.
“MAD만으로는 부족한가 보네. 생각보다 고려할 게 더 있구나.”
루나는 다시 한번 프로젝트 문서의 한 구절을 하이라이트했다.
...이러한 리셋 조건이 발생할 때마다 'tracking epoch'를 갱신하고, 'camera_id + tracking epoch + ByteTrack ID'를 조합하여 최종 공개 ID를 생성한다.
솔라의 시선이 ‘tracking epoch’라는 낯선 단어에 머물렀다. MAD로 시각적 전환을 감지하는 법은 알았지만, 그것을 포함한 여러 조건을 종합해서 ‘추적의 한 시대(epoch)’를 구분하고, 더 복잡한 형태의 ID를 만들어낸다는 개념은 또 다른 거대한 질문을 던지고 있었다. 시각적 단서를 넘어, 이제는 논리적 단서들을 엮어낼 시간이 온 것이다.
4장: 논리적 연결: tracking epoch와 통합 ID
솔라는 화면 한구석에 떠 있는 메모장의 내용을 뚫어져라 보고 있었다. MAD라는 강력한 시각적 단서를 발견한 흥분도 잠시, 그녀의 머릿속은 새롭게 등장한 목록과 공식으로 다시 복잡해져 있었다.
Reset 조건: 1. 큰 장면 전환(MAD) 2. 카메라 ID 변경 3. 영상 누락 구간 4. 비정상적 시간 간격
최종 공개 ID = camera_id + tracking epoch + ByteTrack ID
‘더하기…?’ 솔라는 펜으로 허공에 더하기 기호를 그려 보았다. 마치 수학 공식 같기도 하고, 암호문 같기도 했다. MAD로 큰 장면 전환을 감지하는 것은 이제 알겠다. 하지만 카메라가 바뀌거나, 영상이 중간에 몇 분씩 통째로 사라지는 건 어떻게 이 공식에 통합된다는 말일까. 그리고 무엇보다, ‘tracking epoch’라는 낯선 단어의 정체가 가장 궁금했다. ‘추적 시대’라니. 거창한 이름과는 달리, 그 역할이 전혀 짐작되지 않았다.
그때 루나가 솔라의 모니터에 새로운 창을 띄웠다. 이전의 영상 플레이어나 이미지 뷰어가 아니었다. 마치 시스템 로그처럼, 시간 순서에 따라 이벤트가 기록된 간단한 텍스트 테이블이었다. 루나는 이 테이블에 두 개의 빈 열, Epoch와 공개 ID를 추가했다.
| Timestamp | Camera | Event | Tracker ID | Epoch | 공개 ID |
|---|---|---|---|---|---|
| 14:01:05.100 | Cam01 | 등장 | 1 | ? | ? |
| 14:01:05.800 | Cam01 | 등장 | 2 | ? | ? |
| 14:01:12.300 | Cam01 | 큰 전환(MAD>T) | - | ? | ? |
| 14:01:12.400 | Cam01 | 등장 | 1 | ? | ? |
| 14:05:20.900 | Cam01 | 퇴장 | 1 | ? | ? |
| 18:30:01.100 | Cam01 | 비정상 시간 간격 | - | ? | ? |
| 18:30:01.200 | Cam01 | 등장 | 1 | ? | ? |
| 18:55:03.400 | Cam02 | 카메라 변경 | - | ? | ? |
| 18:55:03.500 | Cam02 | 등장 | 1 | ? | ? |
“솔라, 네가 한번 이 빈칸을 채워봐.”
루나의 목소리는 차분했다. “규칙은 간단해. tracking epoch는 0부터 시작해. 그리고 아까 봤던 리셋 조건, 즉 저기 Event 열에 표시된 이벤트가 발생할 때마다 숫자를 1씩 올리는 거야. 트래커는 epoch가 바뀔 때마다 초기화되고, Tracker ID는 다시 1부터 시작해. 최종 ‘공개 ID’는 카메라ID-Epoch-TrackerID 형식으로 만들면 돼.”
솔라는 잠시 망설이다가, 첫 번째 줄부터 펜을 움직이기 시작했다.
첫 번째 줄, 14시 1분 5초. 첫 등장이니 Epoch는 0. Tracker ID는 1. 공개 ID는 Cam01-0-1. 간단했다. 다음 줄도 마찬가지. Epoch는 그대로 0, Tracker ID는 2. 공개 ID는 Cam01-0-2.
| Timestamp | Camera | Event | Tracker ID | Epoch | 공개 ID |
|---|---|---|---|---|---|
| 14:01:05.100 | Cam01 | 등장 | 1 | 0 | Cam01-0-1 |
| 14:01:05.800 | Cam01 | 등장 | 2 | 0 | Cam01-0-2 |
세 번째 줄에서 이벤트가 발생했다. 큰 전환(MAD>T). MAD 값이 임계값을 넘었다는 의미다. 규칙에 따라 솔라는 Epoch 열에 1을 적었다. 그리고 이어진 네 번째 줄. 트래커가 초기화되었으니 Tracker ID는 다시 1부터 시작했다. 공개 ID는 자연스럽게 Cam01-1-1이 되었다.
“아…!” 솔라는 짧은 탄성을 내뱉었다. 이전의 Cam01-0-1과는 완전히 다른, 새로운 ID가 만들어졌다. tracking epoch가 마치 차단벽처럼 ID의 연속성을 끊고 새로운 시작을 알리는 역할을 한 것이다.
솔라는 이내 자신감을 얻어 나머지 칸을 채워나갔다. 14시 5분에 사람이 퇴장하고, 다음 기록은 4시간 뒤인 18시 30분이었다. 비정상 시간 간격 이벤트. 솔라는 망설임 없이 Epoch를 2로 올렸다. 새로 등장한 사람은 Cam01-2-1이라는 ID를 부여받았다. 마지막으로 카메라가 Cam02로 바뀌는 순간, 그녀는 Epoch를 3으로 올리고 Cam02-3-1이라는 최종 ID를 적어 넣었다.
| Timestamp | Camera | Event | Tracker ID | Epoch | 공개 ID |
|---|---|---|---|---|---|
| … | … | … | … | … | … |
| 14:01:12.300 | Cam01 | 큰 전환(MAD>T) | - | 1 | - |
| 14:01:12.400 | Cam01 | 등장 | 1 | 1 | Cam01-1-1 |
| … | … | … | … | … | … |
| 18:30:01.100 | Cam01 | 비정상 시간 간격 | - | 2 | - |
| 18:30:01.200 | Cam01 | 등장 | 1 | 2 | Cam01-2-1 |
| … | … | … | … | … | … |
| 18:55:03.400 | Cam02 | 카메라 변경 | - | 3 | - |
| 18:55:03.500 | Cam02 | 등장 | 1 | 3 | Cam02-3-1 |
모든 칸이 채워진 테이블을 보며 솔라는 무릎을 쳤다. 이제야 모든 조각이 맞춰지는 기분이었다.
“알겠어! tracking epoch는 그 자체가 추적 ID가 아니었구나. 이건 그냥 ‘추적의 장(Chapter)’을 구분하는 번호였어! MAD, 시간 간격, 카메라 변경 같은 큰 사건이 터질 때마다 다음 장으로 넘어가는 거고. 트래커는 각 장 안에서 1번, 2번, 3번… 하고 자유롭게 ID를 붙이지만, ‘카메라-장 번호-ID’를 합치면 절대 겹치지 않는 고유한 이름이 만들어지는 거구나!”
파일 경계라는 물리적 구분도 아니고, MAD라는 시각적 구분 하나만도 아니었다. 시각 정보(MAD)와 시간(간격), 공간(카메라) 같은 논리적인 단서들을 모두 엮어, ‘추적의 연속성’이라는 하나의 맥락을 정의하는 정교한 규칙. 그것이 바로 tracking epoch의 정체였다. ID 단절과 ID 재사용이라는 두 개의 함정을 모두 피하기 위해 설계된 통합적인 논리 회로였다.
솔라는 자신이 완성한 테이블의 마지막 줄, Cam02-3-1을 가리켰다. 이 ID 하나에 카메라가 바뀌었고, 그전에 두 번의 큰 맥락 전환이 있었다는 역사가 모두 담겨 있었다.
이제 솔라는 이 논리적 연결고리가 얼마나 우아하고 강력한지 이해할 수 있었다. 하지만 동시에 새로운 질문이 고개를 들었다. 이상적으로 보이는 이 설계도가, 현실의 지저분한 데이터 앞에서도 과연 제 역할을 해냈을까?
“언니, 이 방법… 그래서 실제로 써보니까 어땠어? ID가 엉키는 문제가 정말로 해결됐어? 얼마나 좋아졌는데?”
5장: 검증과 한계: 연속성 판단 로직의 실제 효과
솔라의 질문이 채 끝나기도 전에, 루나는 이미 새로운 창을 열어 화면 중앙에 배치했다. 그곳에는 두 개의 그래프가 나란히 놓여 있었다. 왼쪽 그래프는 ‘일반 경계’라는 제목 아래 완만한 언덕 모양의 분포를 보였고, 오른쪽 그래프는 ‘큰 전환’이라는 제목 아래 훨씬 더 높고 오른쪽에 치우친 분포를 보이고 있었다. 가로축에는 ‘프레임 간 MAD 값’이라고 적혀 있었다.
그 옆에는 간결한 표가 하나 더 있었다. ‘Held-out 구간 검증 결과’라는 제목 아래, 단 두 줄의 항목만 적혀 있었다. 루나는 아무 말 없이 표의 첫 번째 줄을 하이라이트했다.
| 검증 항목 | 결과 |
|---|---|
| 장면 전환 후, 이전 공개 ID 재사용 여부 | 0건 |
| IDF1 Score 개선 폭 | +0.012 |
솔라는 “0건”이라는 글자를 보고 눈을 동그랗게 떴다. 자신이 제안했던 ‘무조건 초기화하지 않는’ 방식이 낳았던 끔찍한 오류. 전혀 다른 시간과 장소에 나타난 사람에게 과거의 ID를 잘못 붙여주던 바로 그 문제가 완벽하게 해결되었다는 의미였다. 복도에 있던 7번 직원의 ID가 4시간 뒤 로비에 나타난 방문객에게 붙을 일은 이제 영원히 없었다.
“와… 대박. 진짜로 잡았네. ‘ID 재사용’ 문제가 완전히 사라진 거잖아! 그럼 성공이네!”
솔라는 흥분을 감추지 못했다. 파일 경계에서 ID가 끊어지는 문제와, 장면 전환을 무시해 ID가 엉뚱하게 재사용되는 문제. 이 두 개의 큰 함정을 모두 피하는 논리 회로를 설계했고, 그 핵심적인 목표가 달성되었음을 눈으로 확인한 순간이었다. 그녀는 이 tracking epoch 방식이 완벽한 해결책이라고 생각했다.
하지만 루나는 솔라의 환호에 바로 동조하지 않았다. 대신, 하이라이트를 표의 두 번째 줄로 옮겼다. IDF1 Score 개선 폭: +0.012.
“어… 이건 뭐지? IDF1…?”
솔라의 목소리가 한 톤 낮아졌다. “0건”이라는 압도적인 성공 지표 옆에 놓인 ‘+0.012’라는 숫자는 어딘지 초라해 보였다.
“추적 성능을 종합적으로 평가하는 지표 중 하나야.” 루나가 차분하게 설명했다. “얼마나 정확하게, 그리고 얼마나 꾸준히 같은 대상을 놓치지 않고 따라가는지를 숫자로 나타낸 거지. 1에 가까울수록 완벽해.”
“그런데… 개선 폭이 고작 0.012라고? 거의 변화가 없는 거나 마찬가지잖아. ID 재사용이라는 큰 오류를 잡았는데, 왜 전체 점수는 거의 그대로인 거야? 뭔가 잘못된 거 아니야?”
솔라의 얼굴에 혼란이 가득 찼다. 완벽한 승리라고 생각했던 순간, 예상치 못한 성적표가 눈앞에 나타난 기분이었다. 하나는 100점짜리 결과인데, 다른 하나는 겨우 1점짜리 가산점에 불과했다. 이 두 개의 지표는 서로 모순되는 것처럼 보였다.
루나는 잠시 생각할 시간을 주려는 듯, 말없이 기다렸다. 솔라는 모니터의 두 숫자, ‘0’과 ‘+0.012’ 사이를 몇 번이고 오가며 스스로에게 질문을 던졌다. ID 재사용 0건이 정말로 큰 성과라면, 왜 전체 점수에는 반영이 안 됐을까? 반대로 전체 점수가 거의 그대로라면, 우리가 지금까지 한 일이 별 의미가 없었다는 뜻일까?
그때, 솔라의 머릿속에 이전에 놓쳤던 장면들이 스쳐 지나갔다. tracking epoch가 해결해 주지 못하는 사소한 오류들. 사람이 기둥 뒤에 잠시 숨었다 나타났을 때 ID가 바뀌는 경우, 너무 멀리 있어 작게 보이는 사람을 잠시 놓치는 경우, 비슷한 옷을 입은 두 사람이 스쳐 지나갈 때 ID가 서로 바뀌는 경우…
“아…”
솔라는 마침내 깨달음을 얻은 듯 낮은 탄성을 내뱉었다.
“알겠다. tracking epoch는… 모든 추적 문제를 해결하는 만병통치약이 아니었구나. 이건 오직 ‘연속된 장면이라고 착각해서는 안 되는 명백한 단절’이 있을 때, ID의 대역사가 다음 시대로 넘어가지 않도록 막아주는 거대한 ‘방화벽’ 같은 거였어. 카페 영상이 복도 영상으로 바뀌는 것처럼 말이야.”
그녀는 고개를 들어 루나를 보았다.
“그러니까 ‘ID 재사용 0건’은 이 방화벽이 완벽하게 작동했다는 증거인 거고. 하지만 방화벽 안, 즉 하나의 epoch 안에서 일어나는 자잘한 실수들—사람을 잠시 놓치거나 다른 사람이랑 헷갈리는—은 여전히 그대로인 거야. IDF1 점수는 그런 자잘한 실수들까지 전부 포함해서 계산하니까, 방화벽 하나 잘 세웠다고 해서 점수가 극적으로 오르지는 않았던 거고.”
솔라는 이제 두 개의 상반된 결과를 하나의 이야기로 엮어낼 수 있었다. 이것은 실패가 아니었다. 오히려 문제의 본질을 정확히 파악하고, 그에 맞는 가장 효과적인 수술을 집도한 것에 가까웠다. 시스템 전체의 체력을 비약적으로 올리지는 못했지만, 가장 치명적일 수 있는 ‘정체성 혼란’이라는 암세포를 확실하게 도려낸 것이다.
이것이 바로 ID 연속성 검증 지표를 올바르게 해석하는 방법이었다. 눈에 보이는 숫자 너머, 그 숫자가 측정하는 대상의 본질과 한계를 이해하는 것.
솔라는 이제야 비로소 자신이 처음 가졌던 질문, “저장 파일의 경계와 현실 장면의 경계를 어떻게 구분하는가”에 대한 완전한 답을 얻었다고 느꼈다. 그리고 그 답은 단순한 기술이나 알고리즘이 아니었다. 그것은 문제를 정의하고, 측정하고, 한계를 인정하며, 그럼에도 가장 중요한 것을 지켜내는 설계의 철학에 가까웠다.
솔라는 자신의 노트북을 열어 새로운 메모 파일을 생성했다. 그리고 방금까지의 깨달음을 바탕으로, 미래의 자신과 동료들을 위한 작은 가이드라인을 적어 내려가기 시작했다.
[영상 추적 ID 연속성 판단 로직 설계 가이드라인 (초안)]
- 목표 정의: ID 단절을 막을 것인가, ID 오남용을 막을 것인가? 두 문제의 우선순위를 정한다. (우리 프로젝트의 경우, 장면 전환 후 ID 재사용 방지가 최우선이었음.)
- 리셋 조건 선정: 추적 연속성을 끊어야 할 ‘사건’을 명확히 정의한다.
- 시각적 단서: MAD 임계값 (장비/환경에 따라 튜닝 필요)
- 시간적 단서: 프레임 간 비정상적 시간 공백 (e.g., 1분 이상)
- 공간적/논리적 단서: 카메라 ID 변경
- 검증:
- 핵심 목표 달성 여부 확인: (e.g., 장면 전환 후 ID 재사용 발생 건수 = 0?)
- 전체 성능 지표 변화 확인: (e.g., IDF1) -> 극적인 개선이 없더라도 실망하지 말 것. 우리가 해결하려던 문제가 무엇이었는지 기억할 것.
한 줄 한 줄 써 내려가며, 솔라는 더 이상 루나에게 정답을 묻지 않았다. 스스로 문제를 분석하고, 기준을 세우고, 그 결과를 해석하며 앞으로 나아갈 길을 만들고 있었다. 화면 속 커서가 마지막 문장에서 깜빡이는 것을 보며, 솔라는 옅은 미소를 지었다. 이제 그녀는 어떤 영상이 주어지더라도 길을 잃지 않을 자신만의 지도를 갖게 된 것이다.