[논문 리뷰 + 코드 구현] A Simple Detector with Frame Dynamics is a Strong Tracker

이정민·2026년 2월 10일

[논문 리뷰]

목록 보기
6/19
post-thumbnail


A Simple Detector with Frame Dynamics is a Strong Tracker

최근 러-우 전쟁 등에서 드론의 중요성 및 위험성이 대두되고 있다.
때문에 드론을 추적하는 것이 매우 중요한 Task로 이어지는데, 본 논문은 일반 객체 탐지기를 고성능 적외선 UAV 추적기로 변환하는 방법을 제안한다. 별도의 복잡한 추적 알고리즘을 사용하는 대신, YOLOv11 등의 입력에 Frame Difference이나 Optical Flow 정보를 결합하여 탐지기가 타겟의 움직임 특성을 직접 학습하도록 만들었다.

이러한 단순하고 효율적인 접근 방식을 통해 제 4회 Anti-UAV 챌린지에서 Track 1 우승 및 Track 2 준우승을 차지하며, 적외선 소형 드론 추적에서의 탁월한 성능을 입증했다.

Introduction

객체 추적(Object Tracking)은 객체 탐지(Object Detection)의 확장판으로, Video Sequence에서 특정 타겟의 위치를 지속적으로 파악하는 기술이다.

이 기술은 Anti-UAV 애플리케이션에 필수적이며, 이 논문은 특히나 적외선 시나리오에서의 단일 UAV 추적에 초점을 맞추고 있다.

기존의 객체 추적기들이 주로 개체 탐지기(샴 네트워크 및 트랜스포머 기반 등)에 기반을 두고 발전해왔으나, 다음과 같은 단점이 존재했다.

1. Crop 방식의 문제
       우리가 기존에 수행하던 방식은 템플릿 프레임에서 Target을 따로 Crop하여 사용하는데, 이는 Target이 매우 작거나 초기 위치를 알 수 없는 경우 부적절하다.
2. 시간적 정보 활용의 한계
       이전 프레임의 타겟 위치를 기반으로 현재 프레임을 잘라내는 방식은 배경 억제(Background suppression)가 효과적이지 않아 모델이 배경과 타겟을 구분하지 못하게 만들 수 있다.
3. Dynamic Motion Pattern 학습 부재
       단순히 위치만 참조할 뿐, 타겟의 동적인 움직임 패턴(Motion Patterns)을 학습하지 못한다.
4. 오류 누적
       이전 프레임의 False Positive가 다음 프레임으로 계속 전파되어 추적 실패로 이어질 위험이 크다.

때문에 저자들은 이러한 단점들을 보완하기 위해 세 가지 조건이 필요하다고 한다.

* 배경 억제 (Background Suppression)이란?
복잡한 화면 속에서 내가 찾고자 하는 타겟(UAV) 이외의 모든 정보(건물, 구름, 새 등)을 무시하고 지워버리는 능력이다.
특히 적외선 영상에서는 모든 물체가 온도에 따라 붉게 표시되기 때문에 폭발로 인한 건물이나, 새와 같은 물체들이 드론과 유사하게 보여 구별이 어렵다.
만일 배경 억제가 잘 되지 않으면 드론을 놓치고, 최악의 경우 오인 추적하게 되는데, 다음 프레임에서도 오인한 위치를 기준으로 누적하여 오류가 발생한다.

1) 강력한 Global Detection Capability, 2) Target Feature뿐 아니라 움직임 자체의 특성 학습, 3) 시간적 사전 정보 (Temporal Priors)의 효과적 활용

이를 위해 다음과 같은 방법들을 도입했다.

  • 프레임 역학(Frame Dynamics):입력 단계에서 프레임 차분(Frame Difference) 이나 광학 흐름(Optical Flow) 정보를 원본 이미지와 결합한다. 이를 통해 모델은 이전 프레임의 타겟 특징과 움직임 특성을 입력 레벨에서 학습하여 배경 Noise 속에서도 타겟을 더 잘 구별할 수 있다.
  • 궤적 제약 필터링 (TC-Filtering):추론 후처리 단계에서 시공간적 사전 정보를 활용하여, 예상되는 움직임 궤적을 벗어난 False Positives를 걸러낸다.

    * Frame Difference?
    Frame Difference는 정적인 객체 탐지기가 타겟의 움직임과 시간적 변화를 학습할 수 있도록 입력 데이터를 새롭게 구성하는 핵심 기법이다.

    현재 프레임(xtx_t)에 직전 프레임과의 차이와 전전 프레임과의 차이를 채널로 결합한다. 결과적으로 모델은 흑백의 1채널 적외선 이미지 대신, [현재 모습, 1단계 전 변화량, 2단계 전 변화량]을 담은 3채널 이미지를 입력받게 된다.

    즉, Frame Difference를 통해 복잡한 배경은 지워지고 움직이는 드론만 하이라이트 되는 효과를 얻을 수 있고, 잡음과 실제 타겟을 쉽게 구별할 수 있게 된다.


    * Optical Flow?

    변화량을 채널로..?
    컴퓨터 비전 분야에서 '채널(Channel)'은 색상뿐만 아니라 '정보의 종류'를 담는 그릇으로 폭넓게 사용된다.

    - 보통의 RGB이미지 : [빨강 밝기 행렬. 초록 밝기 행렬, 파랑 밝기 행렬]

    - 변화량을 넣으면 : [현재 모습 행렬, 1초 전 변화 행렬, 2초 전 변화 행렬]

    이렇게 입력 데이터의 Shape만 맞는다면, 그 안에 빨강색이 들어있든, 움직임 정보가 들어있든 상관없이 학습할 수 있다.


2.1 Object Detection

적외선 UAV 탐지는 낮은 신호 대 잡음비(SNR), 낮은 대비, 복잡하고 노이즈가 많은 배경 등으로 인해 매우 어렵다.

최근 이러한 분야에서 뛰어난 성능을 보인 모델들이 존재하는데, 대표적으로 Cascade R-CNNDINO, RepPoints 등이 있겠다.

대표적으로 Cascade R-CNN을 살펴보자면, IoU 임계값을 점진적으로 높이는 다단계 정제 전략을 사용하여 위치 정확도를 높이고 False Positives를 줄였다.

2.2 Object Tracking

객체 추적은 탐지의 연속선상에 있는 기술로, 최근 기술 발전의 흐름을 다룬다.
초기에는 TransTStark와 같은 연구들이 Feature Extraction이나 Fusion 영역을 강화했지만, 최근에는 MixFormer, SimTrack, OSTrack과 같이 템플릿과 검색 영역을 결합하여 상호작용하게 하는 one-stream 프레임워크로 발전했다.

* 기존 추적 모델(MixFormer, SimTrack 등)들은 Matching에 초점을 두어, 프레임을 두 가지 역할로 나누어 사용한다.
       1. 템플릿 프레임 (Template Frame): 추적 대상이 처음 등장한 프레임이다. 여기서 대상을 잘라내서 모델에게 어떻게 생겼는지 Reference를 준다.
       2. 검색 프레임 (Search Frame / Current Frame): 현재 대상을 찾아야 하는 프레임으로, 모델은 템플릿 프레임의 정보와 검색 프레임의 정보를 비교하여 대상을 찾는다.

* 논문에서 제시한 YOLO 기반
이 논문은 기존의 방식이 작은 타겟을 놓치거나 배경과 구분하지 못하는 단점이 있다고 하며 시간적 흐름(Dynamics)으로서의 프레임을 강조한다.

멀리 있는 '템플릿'을 가져오는 것이 아니라, 연속된 프레임 간의 변화 자체를 입력으로 사용한다.

  • 현재 프레임(xtx_t) : 지금 분석중인 이미지
  • 이전 프레임들(xt1,xt2x_{t-1}, x_{t-2}) : 바로 직전 순간의 이미지들
    이 두가지 프레임의 차이를 계산하여 입력 데이터로 사용한다.

    음.. 막상 쓰고나니 "두 프레임 차이를 계산" 하는거나, "과거 템플릿과 비교"하는거나, 거기서 거기 같은데, 프레임 차분 (Frame Difference) 방식이랑 템플릿 매칭 (Template Matching) 방식의 차이인데,

    요약하자면, "이전 프레임에서 똑같은 것 찾기" 랑, **"이전 프레임에서 움직임이 발생한 곳 찾기""의 차이인 것이다. 다시 말해, 이전까지의 프레임과 '비교' 가 아니라 '시간 흐름에 따른 변화'인 것이다.


3.Evolving a Detector into a Tracker

논문의 저자들은 YOLOv11-customized, Cascade R-CNN, DION, RepPoints, PAA등을 모델의 후보로 선정했다.
이 모델들은 단일 프레임 내의 객체 탐지는 뛰어나지만, 연속적인 추적을 위해서는 시간적 정보가 부족한 상태이다.

3.2. Empowering detector with frame dynamics

부족한 시간적 정보를 이겨내기 위해 입력 단계에서 시간적 정보를 주입하는 것이 가장 중요하다. 위에서 말했듯, 적외선 정보는 배경과 Target을 구별하기 매우 어려운데, 이를 해결하기 위해 프레임 역학 (Frame Dynamics)가 정보를 생성하여 원본 이미지와 결합하여 모델에 입력한다.

이에 두 가지 방식이 제안되었다.

  • 프레임 차분 (Frame Difference)
    - 현재 프레임(xtx_t)에 직전 프레임과의 차이전전 프레임과의 차이를 채널로 결합한다.
    - 수식 : xfd=cat(xt,xtxt1,xtxt2x_{fd} = cat(x_t, x_t - x_{t-1}, x_t - x_{t-2})
    이를 통해 타겟의 단기적인 움직임과 특징 변화를 포착할 수 있다.

  • 광학 흐름 (Optical FLow)
    - Farneback 알고리즘을 사용하여 현재 프레임과 이전 프레임 사이의 광학 흐름을 계산한다.
    - 현재 프레임(xtx_t)에 수평(uu) 및 수직(vv) 흐름 성분을 결합한다.
    xof=cat(xt,f(xt,xt1)v,f(xt,xt1)u)x_{of} = cat(x_t, f(x_t, x_{t-1})_v, f(x_t, x_{t-1})_u)
    이를 통해 전역적 움직임 필드를 추정하여 배경 대비 타겟의 독특한 움직임을 학습한다.

위 두 과정을 통해 2차원 적외선 이미지는 시간 차원이 포함된 3차원 데이터로 변환되어, 탐지기가 추적기처럼 작동하게 된다.

3.3 Refining tracking with TC-Filtering

추론 후처리 단계에서는 Trajectory-Constrained Filtering (TC-Filtering) 방식을 사용하여 모델이 탐지한 Bounding Box 중에서 False Positive를 제거한다.

물체의 움직임은 물리적 연속성을 가져야하기 때문에, 현재 프레임의 타겟 위치는 이전 프레임들의 위치를 기반으로 예측 가능한 범위 내에 있어야 한다.

말이 어려워서 그렇지, 이전 두 프레임(t-2, t-1)의 타겟 중심점을 이용하여 순간 속도를 계산하고, 이를 기반으로 현재 프레임(tt)의 위치(CtC_t)를 예측한다는 것이다.

TC-Filtering은 3 단계 수식을 통해 작동한다.

Step 1 : 순간 속도 계산 (Velocity Estimation)

먼저 전전 프레임(t2t-2)와 직전 프레임(t1t-1) 사이의 타겟 중심점 변화를 통해 타겟의 이동 속도(Vt1V_{t-1})을 계산한다.


Vt1=[VxVy]=Ct1Ct2ΔtV_{t-1} = \begin{bmatrix}V_x\\V_y\\ \end{bmatrix} =\frac{C_{t-1} - C_{t-2}}{\Delta t}

[VxVy]\begin{bmatrix}V_x\\V_y\\ \end{bmatrix} : 타겟의 속도 벡터로, 수평 방향(xx)와 수직 방향(yy)를 의미한다.

CC는 중심 좌표, Δt\Delta t는 프레임 간 시간 간격
가로 세로 각각 얼마만큼 이동했는지를 시간으로 나누어 속도 벡터를 만든 것이다.

Step 2 : 현재 위치 예측 (Position Prediction)

계산된 속도가 일정하게 유지된다 가정하고, 현재 프레임(tt)에서 타겟이 있을 것이라 예상되는 위치(CtC_t)를 계산한다.


CtCt1+Vt1Δt=2Ct1Ct2C_t \equiv C_{t-1} + V_{t-1} \cdot \Delta t = 2C_{t-1} - C_{t-2}

$x$좌표와 $y$좌표 각각에 대해, 직전 위치의 2배에서 전전 위치를 빼면 다음 위치가 예측 된다는 것


즉, 직전 위치에서 그 전 움직임만큼 더 이동했을 것이라고 예측한다.

방금 움직인 만큼 똑같이 한 번 더 움직일 것이라는 등속 운동의 원리이다.

왜 2×\times직전위치 - 전전위치 가 될까?
우리가 걷고 있다고 가정해보자.
1. 전전 위치(t2t-2) : 출발점
2. 직전 위치(t1t-1) : 현재 내 디딤발 위치

그럼, 다음 발(tt)은 어디에 내딛게 될까?
물리적으로 갑자기 방향을 틀지 않는다면
방금 걸어온 보폭 만큼 앞으로 한 발 더 디딜 것이다.

  • 보폭 : (지금 위치) - (아까 위치) = Ct1Ct2C_{t-1} - C_{t-2}
  • 다음 위치 : (지금 위치) + (보폭)
  • 결과 : Ct1+(Ct1Ct2)C_{t-1} + (C_{t-1} - C_{t-2})
    이 식을 정리하면, Ct1C_{t-1}이 두 개 이므로,
    2Ct1Ct22C_{t-1} - C_{t-2} 가 된다.

Step 3 : 제약 윈도우 설정 및 필터링

예측된 위치 (CtC_t)를 중심으로 반지름이 dmaxd_{max}동적 제약 윈도우 (Motion Constraint Window)를 설정한다. 그리고 모델이 현재 프레임에서 탐지한 후보 박스들을 확인한다. (*동적 제약 윈도우는 BB가 아니라 원형 )

  • 통과 : 후보 박스의 중심이 이 윈도우 안에 있으면 진짜 타겟으로 인정한다
  • 제거 : 후보 박스의 중심이 이 윈도우 밖에 있으면 False Positive로 간주하여 버린다.

Frame Dynamics이 입력 단계에서 정보량을 늘려 탐지 능력을 높였다면, TC-Filtering은 출력 단계에서 논리적인 필터를 씌워 정확도를 확정 짓는 역할을 한다.

탐지기가 배경의 구름이나 새를 드론으로 착각하더라도, 그 위치가 예상 경로에서 벗어난다면, 과감하게 버려, 추적의 Robustness가 크게 향상된다.


4. Experiments

4.1 Implementation Details

  • Datasets : The 4th Anti-UAV Challenge dataset
  • 훈련 세트(223개 비디오), 테스트 세트(Track 1, Track 2)로 구성된다.
  • 적외선 영상이며, 비디오 당 최대 1,500 프레임, 640×512640 \times 512 또는 512×512512 \times 512.
  • 저자들은 원본 데이터의 약 5%에 라벨링 오류가 있음을 확인하고, 수정하여 약 1.5 AP의 성능 향상

평가 지표 (Metrics)

  • AP(Average Precision) : 탐지 정확도를 평가하는 핵심 지표
  • AOA (Average Overlap Accuracy) : 긴 시퀸스에서의 추적 일관성을 평가한다. 3rd Anti-UAV Challenge 부터 도입된 '놓친 트랙에 대한 패널티'가 적용되어 더 엄격한 견고성을 측정한다.
    (아마 Track 3이겠죠..?)

Implementation Details

단일 모델이 아닌 YOLOv11MMDetection 프레임워크를 모두 사용하여 앙상블 전략을 취했다. 각 프레임워크의 설정이 매우 디테일하다..

YOLOv11 구현

  • 모델 구조 변경:
    - 기본 YOLOv11x를 기본 Backbone으로 사용한다.
    • AFPN (Asymptotic Feature Pyramid Network) : 멀티 스케일 특징 융합을 강화하기 위해 디코더를 수정하였다.
    • p2 Head : 작은 물체를 더 잘 잡기 위해 p2 탐지 헤드를 추가했다.
    • C3K2 모듈 : 특징 표현력을 높이기 위해 추가했다.

전처리 특이사항

  • HSV 증강 끄기 : 프레임 차분이나 광학 흐름을 사용할 때는 시간적 정보가 왜곡되는 것을 막기 위해 색상 변환(HSV Augmentation)을 비활성화 했다.
  • Resizing : 640 픽셀로 변환

훈련 파라미터

  • Optimizer : SGD
  • Epochs : 300
  • Mosaic Augmentation : 마지막 5 에포크에서는 꺼서(Disable) 안정성을 높인다.

MMDetection 설정 (앙상블용 모델)

  • 모델 선정 : 실험을 통해 가장 성능이 우수한 Cascade R-CNN, DINO, RepPoints, PAA 4가지를 선정했다.

설정 차이점:

- 고해상도 입력 : YOLO(640)과는 달리 1280×10241280 \times 1024 해상도로 훈련하여 미세한 특징을 포착했다.
- Optimizer : SGD 대신 AdamW + Scale 전략을 사용하여 수렴 속도와 성능을 높였다.

보조 추적기(LoRAT)

  • 추적이 끊기거나 불안정할 때를 대비해 LoRAT 모델을 훈련해두었다.
  • 샘플링 수 1800, 2000 에포크로 훈련하였으며, 더 큰 모델을 써도 성능 향상이 없어 Base 버전을 사용했다고 한다.

추론 및 후처리 전략 (Inference & Post-processing)

단순 탐지 결과만 내놓는 것이 아닌, 정교한 WBF(Weighted Box Fusion)TC-Filtering을 거친다.

앙상블 (Ensemble)

YOLO 모델들과 MMDetection 모델들의 결과를 합친다.

  • WBF 설정 : IoU 임계값 0.5, 신뢰도 임계값 0.3, 가중치 0.1로 설정하여 박스들을 융합한다.

TC-Filtering 적용

  • 앙상블 된 결과 중, 이전 프레임의 움직임 궤적(Trajectory)에서 크게 벗어난 박스는 가짜(FP)로 간주하여 제거한다.

백업 전략 (Fail-safe)

  • 만약 TC-Filtering 결과 남은 박스가 하나도 없다면, 탐지기가 타겟을 놓친 것이니 미리 준비한 LoRAT 추적기의 예측값을 최종 결과로 사용한다.
    (첫 프레임 정보가 없는 Track 2에서는 사용하지 않는다.)

정량적 결과 (Quantiative Results)

제 4회 Anti-UAV Challenge에서 Track 1 우승(AOA 73,23), Track 2 준우승 (AOA 57.12)을 차지하였다.

Frame Dynamics의 효과

단순 원본 이미지(RAW)만 사용했을 때보다 프레임 차분(FD)나 광학 흐름(OF)를 결합했을 때 AP와 AOA 점수가 일관되게 상승했다.


논문의 저자들은 모델의 성능에 영향을 미친 요소들을 전부 분석해보았다.

Ablation Study

훈련 데이터를 전부 쓰는 것이 좋은가?

모든 프레임을 다 쓰는 것보다 5 프레임마다 하나씩(Stride 5) 건너뛰며 훈련하는 것이 성능이 더 좋았다. (AP 66.5 % \rightarrow 67.1%)

비디오 데이터는 인접 프레임 간 중복이 매우 심하기 때문에, 이를 전부 학습해버리면 모델이 미세한 변화에 과적합하기 때문이다.

추론 시 임계값은?

보통 탐지 모델은 높은 신뢰도 (0.5 이상)을 요구하지만, 적외선 소형 타겟은 특징이 흐릿하기 때문에 임계값을 0.2로 낮춰 재현율(Recall)을 높이는 것이 중요했다. 박스 수를 5개로 줄인 것은 불필요한 노이즈를 컷하는데에 도움이 되었다.

Optimizer

SGD보다 AdamW + Scale + SyncBN 조합이 더 우수했다.
SyncBN(Synchronized Batch Normalization)은 배치 크기가 작을 때 정규화 편향을 줄여주어, 미세한 소형 타겟 학습에 효과적이었지만, 혼합 정밀도 훈련(AMP)는 오히려 수치적 불안정을 초래하여 성능이 떨어졌다고 한다.


이 작은걸 어떻게 찾아냈을까.......
여기서 P2 Detection Head의 중요성이 나온다.

  • 기존 YOLO의 헤드 구성인 P345는 AP가 48~49%인 반면, 작은 물체를 탐지하기 위해 P2 헤드를 추가하여 P2345 구성을 사용하자, AP가 53.4%로 올라갔다.

또한, 스케일링 팩터를 조절하는 것이 중요한데,

  • 해상도를 키우는 경우 연산량이 증가한다.
  • 하지만, 논문의 저자들은 해상도를 원본(640×512640 \times 512)로 유지하더라도, 스케일(Scale)을 0.7로 설정하였을 때 56.4%라는 성능을 달성했다.

즉, 소형 객체의 크기가 P2헤드의 수용 영역과 잘 맞아 떨어지도록 스케일링 팩터를 조절하는 것이 해상도를 무리하게 키우는 것보다 더 효과적임을 증명한다.


코드 구현 (with baseline)

로직

Optical Flow로는 Lucas-Kanade를 사용한다.

  • GMC
  • Optical Flow

<환경 변수>

  • track_active
  • gmc_initialized
  • prev_gray
  • p0
  1. glob.glob을 이용해서 모든 비디오에 대한 검사 수행

  2. 각 비디오 내 모든 프레임에 대한 검사 수행

  3. Ground Truth 불러오기

  4. num_frames(검사를 수행할 프레임의 수)의 결정 기준

    • len(gt_rects) > 1
  5. curr_gray에 현재의 프레임을 저장

  6. track_active가 False인 경우 : Detector(YOLO) 사용해야 함

  7. [else] track_active가 True : 추적 중인 대상이 있음 -> Tracker (SiamFC) 사용
    해당 로직에서는 Tracker (SiamFC)를 통해서 Update된 Bounding Box를 받아옴. <->
    만약 받아온 pred_bbox의 길이가 1이라는 것은 추적에 실패한 것 -> 환경 변수 초기화

  8. (6, 7)번을 통해 설정된 환경 변수에 따라 prev_gray에 curr_gray를 복사. 이후 p0로 Features 추출해서 저장해놓기 + gmc=True로 초기화

  9. 8 이후에 다음 Frame으로 넘어가서 curr_gray에 현재(넘어간) 프레임을 저장.

  10. 이 때는 8에서 초기화된 환경 변수에 따라 elif track_active and gmc_initialized로 넘어감 :

def iou(bbox1, bbox2):
    bbox1 = [float(x) for x in bbox1]
    bbox2 = [float(x) for x in bbox2]

    (x0_1, y0_1, w1_1, h1_1) = bbox1
    (x0_2, y0_2, w1_2, h1_2) = bbox2
    x1_1 = x0_1 + w1_1
    x1_2 = x0_2 + w1_2
    y1_1 = y0_1 + h1_1
    y1_2 = y0_2 + h1_2

    overlap_x0 = max(x0_1, x0_2)
    overlap_y0 = max(y0_1, y0_2)
    overlap_x1 = min(x1_1, x1_2)
    overlap_y1 = min(y1_1, y1_2)

    if overlap_x1 - overlap_x0 <= 0 or overlap_y1 - overlap_y0 <=0:
        return 0

    size_1 = (x1_1 - x0_1) * (y1_1 - y0_1)
    size_2 = (x1_2 - x0_2) * (y1_2 - y0_2)

    size_intersection = (overlap_x1 - overlap_x0) * (overlap_y1 - overlap_y0)
    size_union = size_1 + size_2 - size_intersection

    return size_intersection / size_union
def not_exist(pred):
    return (len(pred) == 1 and pred[0] == 0) or (len(pred) == 0)
def eval(out_res, label_res):
    measure_per_frame = []
    penalty_measure = []
    for _pred, _gt, _exist in zip(out_res, label_res['gt_rect'], label_res['exist']):
        # Target 존재 x : _exist==False | Target 존재 O : _exist == True
        measure_per_frame.append(not_exist(_pred) if not _exist else iou(_pred, _gt) if len(_pred) > 1 else 0)
        if _exist:
            if (len(_pred) > 1 and iou(_pred, _gt) > 1e-5):
                penalty_measure.append(0)
            else:
                penalty_measure.append(1)
    if len(measure_per_frame) == 0:
        measure_per_frame_mean = 0
    else:
        measure_per_frame_mean = np.mean(measure_per_frame)

    if len(penalty_measure) == 0:
        penalty_measure_mean = 0
    else:
        penalty_measure_mean = np.mean(penalty_measure)

    return measure_per_frame_mean - 0.2 * (penalty_measure_mean ** 0.3)
import torch 
def main(mode='IR', visualization=False):
    assert mode in ['IR', 'RGB'], 'Only Support IR or RGB to evaluate'

    global drive_path

    device = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu')
    print(f"현재 사용 장치 : {device}")
    net_path = os.path.join(drive_path, "Baseline_code", 'model.pth')
    tracker = TrackerSiamFC(net_path = net_path)
    yolo_model = tracker.initialize_yolo()
    yolo_model.to(device)
    tracker.net.to(device)

    video_paths = glob.glob(os.path.join(os.path.join('/content/data/track1_test'), "*"))
    video_num = len(video_paths)
    output_dir = os.path.join('results', tracker.name)
    if not os.path.exists(output_dir):
        os.makedirs(output_dir)
    overall_performance = []

    for video_id, video_path in enumerate(video_paths, start=1):
        video_name = os.path.basename(video_path) # 파일 경로에서 마지막 구성 요소만 추출 \\ 제일 마지막꺼
        video_file = os.path.join(video_path, "%s.mp4"%mode)
        frame_files = sorted(
            [f for f in os.listdir(video_path) if f.endswith(('.jpg', '.jpeg', 'png', '.bmp'))])
        
        # .txt로 굳이 변환하지 않고 json에서 읽어옴
        res_file = os.path.join(video_path, 'IR_label.json')
        with open(res_file, 'r') as f:
            label_res = json.load(f)
        if 'exist' not in label_res:
            label_res['exist'] = [1] * len(label_res['gt_rect'])

        output_file = os.path.join(output_dir, '%s.txt'% video_name)
        if os.path.exists(output_file):
            with open(output_file, 'r') as file:
                content = file.read()
                data = json.loads(content)
                out_res = data['res']
        else:
            frame_id = 0
            out_res = []
            pred_bbox = [0] # no prediction

            for frame_file in frame_files:
                frame_path = os.path.join(video_path, frame_file)
                frame = cv2.imread(frame_path)

                im_vis = frame.copy()
                if len(pred_bbox) == 1:
                    pred_bbox, im_vis = tracker.init(frame, yolo_model)

                    if isinstance(pred_bbox, (np.ndarray, list)):
                        pred_bbox = list(map(float, pred_bbox))
                    out_res.append(pred_bbox)

                    cv2.putText(im_vis, str(frame_id), (40, 40), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 255), 2)

                    if len(pred_bbox) == 1:
                        cv2.putText(im_vis, 'Fail to detect the UAV', (100, 40), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255), 2)
                    else:
                        pred_bbox = list(map(int, pred_bbox))
                        cv2.rectangle(im_vis, (pred_bbox[0], pred_bbox[1]), (pred_bbox[0] + pred_bbox[2], pred_bbox[1] + pred_bbox[3]), (0, 0, 255), 3)
                      
                else:
                    pred_bbox = tracker.update(frame) # tracking
                    pred_bbox = list(map(int, pred_bbox))
                    out_res.append(pred_bbox)
                    cv2.rectangle(im_vis, (pred_bbox[0], pred_bbox[1]), (pred_bbox[0] + pred_bbox[2], pred_bbox[1] + pred_bbox[3]), (0, 0, 255), 3)
                    cv2.putText(im_vis, str(frame_id), (40, 40), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 255), 2)

                if visualization:
                    cv2.imshow(video_name, im_vis)
                    cv2.waitKey(1)
                frame_id += 1
            if visualization:
                cv2.destroyAllWindows()
            ouptut_file = os.path.join(output_dir, "%s.txt" % video_name)

            clean_out_res = []
            for bbox in out_res:
                if(len(bbox) > 1):
                    clean_out_res.append([float(x) for x in bbox])
                else:
                    clean_out_res.append(bbox)
            
            with open(output_file, 'w') as f:
                json.dump({'res' : clean_out_res} ,f)
        mixed_measure = eval(out_res, label_res)
        overall_performance.append(mixed_measure)
        print("[%03d/%03d] %20s %5s Fixed Measure : %.03f" % (video_id, video_num, video_name, mode, mixed_measure))
    print("[Overall] %5s Mixed Measure: %.03f\n" % (mode, np.mean(overall_performance)))

if __name__ == '__main__':
    main(mode='IR', visualization=False)

NMS

        # NMS
        with dt[2]:
            pred = non_max_suppression(pred, self.cfg.conf_thres, self.cfg.iou_thres, self.cfg.classes,
                                       self.cfg.agnostic_nms, max_det=self.cfg.max_det)

TC-Filtering

# TC-Filtering

Results (TC-Filtering 이전)

Results (TC-Filtering 이후)


참고하면 좋은 논문

Farneback : Two-Frame Motion Estimation Based on Polynomial Expansion
WBS : Weighted boxes fusion: Ensembling boxes from different object detection models

https://github.com/Jungminii-1114

0개의 댓글