목적
- 빌드된 nms가 잘 동작되는지 확인
과정
- import 잘 되는지 확인
- 정상 동작 되는지 확인
1. import 태스트 코드 작성
#!/usr/bin/env python3
"""Check whether the official CLRNet CUDA NMS extension can be imported."""
import sys
from pathlib import Path
WORKSPACE_DIR = Path(__file__).resolve().parents[2]
CLRNET_DIR = WORKSPACE_DIR / "clrnet"
def main() -> int:
sys.path.insert(0, str(CLRNET_DIR))
try:
from clrnet.ops.nms import nms
except ImportError as exc:
print("[FAIL] from clrnet.ops.nms import nms")
print(f"error: {exc}")
return 1
print("[OK] from clrnet.ops.nms import nms")
print(f"nms: {nms}")
return 0
if __name__ == "__main__":
raise SystemExit(main())
- 에러 발생
- [FAIL] from clrnet.ops.nms import nms
error: libc10.so: cannot open shared object file: No such file or directory
- [FAIL] from clrnet.ops.nms import nms
- 에러 해결
- bashrc에 LD_LIBRARY_PATH 를 등록
- bashrc 파일을 열고 변경한 다음 저장하고 source ~/.bashrc를 터미널에 입력하면 됨
export LD_LIBRARY_PATH=/home/{내 경로}/.local/lib/python3.10/site-packages/torch/lib:$LD_LIBRARY_PATH
- nms 정의
- CLRNet의 lane proposal 중복 제거 함수
- 점수 높은 것만 남기고, 중복되는 후보는 제거하는 함수
- nms 입력
- boxes => [N, 77], N개의 lane proposal
- score_tensor =>각 lane proposal의 confidence score 값
- overlap => 두 lane을 중복으로 볼 거리 threshold값 (lane offset 차이 기준)
- top_k => 최대 몇 개 lane을 남길지
- nms 출력
- keep => 살아남은 lane proposal
- num_to_keep => 실제로 살아남은 lane proposal 개수
- parent_object_index => 각 lane이 어느 그룹에 속하는지
- 테스트 정의
- 작은 단위 테스트
- 아래 기능이 가능한지 확인하는 가상의 lane proposal을 만들어 테스트를 수행
- nms import 가능 여부
- 중복 lane 제거
- 중복 없는 lane 유지
- score 높은 lane 우선 유지
- top_k 제한 적용
- threshold 경계 동작
#!/usr/bin/env python3
"""Run functional tests for the official CLRNet CUDA NMS extension."""
import sys
from pathlib import Path
import torch
WORKSPACE_DIR = Path(__file__).resolve().parents[2]
CLRNET_DIR = WORKSPACE_DIR / "clrnet"
PROP_SIZE = 77
def make_lane(offset_value: float) -> torch.Tensor:
lane = torch.zeros(PROP_SIZE, dtype=torch.float32)
lane[2] = 0.0
lane[4] = 72.0
lane[5:] = offset_value
return lane
def run_nms_case(nms, name: str, offsets, scores, overlap, top_k, expected_keep) -> bool:
boxes = torch.stack([make_lane(offset) for offset in offsets]).cuda()
score_tensor = torch.tensor(scores, dtype=torch.float32, device="cuda")
keep, num_to_keep, parent_object_index = nms(boxes, score_tensor, overlap, top_k)
kept = keep[: int(num_to_keep.item())].cpu().tolist()
if kept != expected_keep:
print(f"[FAIL] {name}")
print(f"offsets: {offsets}")
print(f"scores: {scores}")
print(f"overlap: {overlap}")
print(f"top_k: {top_k}")
print(f"expected keep: {expected_keep}")
print(f"actual keep: {kept}")
print(f"parent index: {parent_object_index.cpu().tolist()}")
return False
print(f"[OK] {name}: keep={kept}")
return True
def main() -> int:
sys.path.insert(0, str(CLRNET_DIR))
if not torch.cuda.is_available():
print("[FAIL] CUDA is not available")
return 1
try:
from clrnet.ops.nms import nms
except ImportError as exc:
print("[FAIL] import nms")
print(f"error: {exc}")
return 1
print("[OK] import nms")
cases = [
[
"duplicate suppression",
[10.0, 10.0, 40.0],
[0.9, 0.8, 0.7],
1.0,
10,
[0, 2],
],
[
"no duplicate",
[10.0, 20.0, 40.0],
[0.9, 0.8, 0.7],
1.0,
10,
[0, 1, 2],
],
[
"score ordering",
[10.0, 10.0, 40.0],
[0.2, 0.9, 0.7],
1.0,
10,
[1, 2],
],
[
"top_k limit",
[10.0, 20.0, 40.0],
[0.9, 0.8, 0.7],
1.0,
1,
[0],
],
[
"threshold boundary",
[10.0, 10.5, 11.0],
[0.9, 0.8, 0.7],
1.0,
10,
[0, 2],
],
]
for case in cases:
if not run_nms_case(nms, *case):
return 1
torch.cuda.synchronize()
print("[OK] all nms tests passed")
return 0
if __name__ == "__main__":
raise SystemExit(main())
결과
[OK] import nms
[OK] duplicate suppression: keep=[0, 2]
[OK] no duplicate: keep=[0, 1, 2]
[OK] score ordering: keep=[1, 2]
[OK] top_k limit: keep=[0]
[OK] threshold boundary: keep=[0, 2]
[OK] all nms tests passed
'차선인식' 카테고리의 다른 글
| nms.cpp 코드 분석 (0) | 2026.06.30 |
|---|---|
| CUDA 프로그래밍 (CLRNet의 nms 파일 흐름 분석) (0) | 2026.06.30 |
| 5-1. 최적화 프로젝트 (0) | 2026.06.22 |
| 4-2 CLRNet 논문 요약 (0) | 2026.06.18 |
| 4-1 CLRNet 논문 요약 (0) | 2026.06.18 |