차선인식

5-2 최적화 프로젝트

newnewnewnew 2026. 6. 22. 15:04

목적

  • 빌드된 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
  • 에러 해결
    • 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