Posts 여러 카메라의 검출을 하나로 묶기 — BEV 호모그래피와 특징 매칭
Post
Cancel

여러 카메라의 검출을 하나로 묶기 — BEV 호모그래피와 특징 매칭

같은 물체인지 어떻게 아는가

카메라 여러 대가 같은 지역을 본다. 각 카메라는 자기 화면에서 객체를 검출하고 자기 트래커로 ID를 붙인다. 그런데 카메라 A의 3번과 카메라 B의 7번이 같은 물체인지 아닌지는 아무도 모른다.

이걸 판정해서 전역 ID를 부여하는 것이 카메라 간 연관(association)이다. 파이프라인으로 쓰면 네 단계다.

1
2
3
4
0. 노드 구성      트랙 ID가 있는 검출만 후보로
1. 엣지 생성      쌍마다 "같은 물체일 가능성" 판정
2. 클러스터링     엣지를 묶어 클러스터로 (카메라당 하나 제약)
3. ID 배정        클러스터에 전역 ID 부여 (이력 투표)

핵심은 1단계다. 여기서 무엇을 근거로 쌍을 잇느냐가 전체 품질을 결정한다.

근거 세 가지

방법원리강점약점
월드 좌표 거리각 검출을 세계 좌표로 올린 뒤 거리 임계싸다측위 오차가 그대로 오연관
외형 특징(ReID)크롭 임베딩 유사도기하 불필요시점·조도 차이에 취약
특징점 매칭두 영상 간 대응점으로 판정기하적으로 강함비싸다

세 번째를 쓰기로 했는데, 그냥 쓰면 문제가 있다. 카메라 두 대가 서로 다른 방향에서 같은 지역을 보면 시점 차이가 커서 원본 영상끼리는 대응점이 잘 안 잡힌다. 특히 학습 기반 매칭기라도 시점 차가 60도를 넘으면 급격히 나빠진다.

지면에 정사영해서 시점 차이를 없앤다

두 카메라 영상을 각각 지면 평면으로 투영하면 둘 다 위에서 내려다본 같은 평면 영상이 된다. 시점 차이가 제거되므로 매칭이 훨씬 쉬워진다.

내부 파라미터 K와 자세 R, t만 있으면 지면 호모그래피는 계산으로 나온다. 특징점이 필요 없다.

1
H_prior = K · [r1  r2  (t + d·n)] 형태로 지면 평면 → 이미지 대응

이 사전 호모그래피로 두 영상을 각각 BEV 캔버스에 깔고, 겹치는 영역의 픽셀 수를 센다. 일정 수(예: 500픽셀) 이상 겹치면 그 BEV 영상끼리 특징 매칭을 돌린다. 겹치지 않으면 매칭 자체를 건너뛴다.

효과가 실제로 얼마나 큰가

BEV를 쓰는 경우와 원본으로 바로 매칭하는 경우를 A/B로 비교했다.

 BEV 사용원본 직접 매칭
서로 다른 지점 간 오매칭 쌍51741 (14배)
좌표 불일치 중앙값28.1m18.8m

여기서 함정이 하나 있다. 좌표 불일치만 보면 BEV를 끈 쪽이 좋아 보인다. 28.1m보다 18.8m가 작으니까. 그런데 그건 오매칭 쌍이 14배로 늘어나면서 분포 자체가 달라진 결과다. 엉뚱한 것끼리 묶으면 가까운 것끼리만 묶이는 편향이 생겨 불일치 중앙값이 오히려 내려간다.

지표 하나로 판단하면 정반대 결론이 나오는 전형적인 사례였다. 연관 품질은 최소한 오매칭 수와 좌표 정확도를 같이 봐야 한다.

계산량을 줄이는 두 가지 게이트

특징 매칭은 비싸다. 카메라가 N대면 쌍은 N(N-1)/2개고, 프레임마다 그만큼 돈다. 줄일 방법 두 개를 넣었다.

시야 겹침 게이트

이미지 네 모서리에서 광선을 쏴 지면과 교차시키면 그 카메라가 보는 지면 영역의 사각형이 나온다. 두 카메라의 사각형이 겹치지 않으면 같은 물체를 볼 수가 없으므로 매칭을 아예 건너뛴다.

효과가 극적이었다.

1
2
원본 매칭 소요:  392.9ms (전체의 100%)  →  2.1ms (2%)
처리 속도:       2.0 fps                →  16.8 fps

다만 두 가지 안전장치가 필요했다.

  • 자세 오차 여유: 사각형에 10m 정도 여유를 준다. 자세가 조금 틀리면 실제로는 겹치는 쌍이 안 겹치는 것으로 판정된다.
  • 판단 불가일 때는 통과: 지평선이 화면에 걸리면 광선이 지면과 안 만나 사각형이 정의되지 않는다. 이때는 게이트를 적용하지 않고 기존 동작을 유지한다. 근거가 없을 때 잘라내면 안 된다.

프레임 건너뛰기

카메라가 고정돼 있으면 호모그래피는 프레임마다 다시 계산할 필요가 없다. N프레임에 한 번만 새로 구하고 나머지는 이전 값을 재사용한다.

문제는 카메라가 움직였을 때다. 그래서 재사용 전에 안전장치를 하나 건다. 캐시된 사전 호모그래피와 지금 계산한 사전 호모그래피로 네 모서리를 각각 변환해서 그 이동량이 임계를 넘으면 조기에 다시 계산한다.

한 값이 네 곳에서 쓰이고 있었다

이 시스템에서 가장 비싼 버그가 여기서 나왔다.

ground_z(지면 고도)는 위의 계산 여러 곳에 들어간다. 실제로 세어보니 네 곳이었다.

#위치상태
1검출 → 월드 좌표 변환z=0 하드코딩
2시야 겹침 사각형 계산z=0 하드코딩
3호모그래피 계산 호출부인자 미전달 → 기본값 0.0
4캐시 갱신 판정 안의 호모그래피 계산인자 미전달 → 기본값 0.0

1~3번을 고치고 4번을 놓쳤다. 그 결과 캐시 판정이 평면 고도 394로 만든 캐시평면 고도 0으로 만든 새 값을 비교하게 됐다. 모서리 이동량이 4,631픽셀로 나왔고 임계는 100이었다. 매 프레임 조기 재계산이 걸리면서 프레임 건너뛰기가 완전히 무력화됐다. 특징 매칭이 의도의 세 배로 돌고 있었다.

더 뼈아픈 것은 지표가 이미 그 사실을 말하고 있었다는 점이다. “새로 추론한 비율”이 여섯 줄 연속 100%로 찍혀 있었다. 건너뛰기 간격이 3이면 33%가 정상이다. 읽고도 지나쳤다.

여기서 얻은 규칙은 이렇다. 어떤 값을 쓰는 곳을 셀 때, 그 값으로 좌표를 만드는 곳만 세면 안 된다. 그 값을 전제로 만들어진 결과를 비교하는 곳도 소비처다.

관측 지표를 어떻게 설계할 것인가

이 시스템에서 한 줄로 찍는 상태 로그는 이렇게 생겼다.

1
2
bev_match 46.8ms(21.4fps 30%) | raw_match 12.0ms(5%) | track 5.1ms | world 1.2ms
| fresh=33% | overlap_pass=87/87(100%) | edges=261

각 값의 읽는 법을 정해두는 것이 실제로 중요했다.

지표정상이상할 때의 의미
overlap_pass안정적으로 높음0% 고정이면 평면이 틀렸다. 요동치면 부분적으로 틀렸다
fresh1/건너뛰기간격100%면 건너뛰기가 죽었다
edges장면에 비례0이면 게이트가 과하게 잘라내고 있다
구간별 ms맨 앞이 병목. 1000/ms가 그 구간의 fps 상한

특히 게이트가 잘라낸 쌍의 수를 반드시 노출해야 한다. 게이트가 느슨할 때는 아무것도 안 잘라내니 없어도 티가 안 나지만, 날카로워진 뒤에는 카메라 한 대의 고도 값이 어긋나면 모든 쌍이 잘려나간다. 그런데 그 상태에서 아무 경고도 안 나온다. 결과가 조용히 0이 될 뿐이다.

순서를 뒤집는 안을 검토했다가 접은 이유

지금 순서는 카메라별 추적 → 카메라 간 연관이다. 반대로 연관 → 융합 → 추적으로 바꾸면 한 카메라가 가려져도 융합된 객체가 유지된다는 장점이 있다. 검토해봤는데 접었다.

이유는 연관에는 트랙 ID가 필요 없지만 식별자 유지에는 전적으로 필요하다는 점이었다. 전역 ID 레지스트리의 키가 (카메라, 로컬 트랙 ID)이고, 프레임 간 투표와 나이 기반 타이브레이크가 전부 그 위에 서 있다. 트랙 ID를 없애면 매 프레임 모든 클러스터가 새 ID를 받는다.

그리고 기존 트래커를 그대로 옮겨 붙일 수도 없었다. 그 트래커의 칼만 상태가 픽셀 단위(tlwh 기반)이고 매칭 거리가 이미지 대각선으로 정규화돼 있다. 융합 결과는 미터 단위라 스케일이 무의미해진다. 월드 공간 트래커를 따로 만들어야 한다.

위험도 하나 더 있다. 지금은 잘못된 엣지가 그 프레임의 ID 배정에만 영향을 주고 카메라별 트랙은 무사하다. 순서를 뒤집으면 오연관이 융합 좌표를 오염시키고 그 위에서 칼만이 평활화하므로 오류가 시간축으로 전파된다.

순서를 뒤집는 것이 유리한 조건은 객체가 시야를 자주 드나들거나 픽셀 운동이 심하게 비선형일 때다. 카메라가 고정돼 같은 지점을 보고 검출 중심 이동이 중앙값 0.5픽셀인 조건에서는 해당하지 않았다. 얻는 것보다 잃는 것이 컸다.

This post is licensed under CC BY 4.0 by the author.

단안 카메라에서 픽셀을 세계 좌표로 — 공선조건과 지면 평면

비동기로 도착하는 검출 결과를 프레임 단위로 묶기