Skip to content

Getting started

1. 환경 설정


소스 코드를 준비합니다.

git clone https://github.com/SAIGE-AI/3d-inspection.git -b 1.0.0
cd 3d-inspection

Python 3.11.9로 conda 환경을 생성합니다.

conda create -n 3d_inspection python=3.11.9 -y
conda activate 3d_inspection

아래의 명령어로 dependencies를 설치합니다.

pip install -r requirements/develop.txt

SaigeToolkit은 사내 패키지 색인에서 받으므로 설치는 사내망에서 진행합니다. 허용하는 SaigeToolkit 버전 범위는 requirements/common.txt에 있습니다.

제품은 Windows에서 구동합니다. 온보딩과 스캔 식별(identify_scan)은 렌더링에 GL context가 필요하므로, 원격 셸이나 Windows 서비스가 아닌 로그온된 데스크톱 세션에서 실행합니다. 온보딩과 추론의 연산 device 기본값은 cuda:0입니다.

2. 리소스 다운로드


필요한 모델 weight 번들을 다운로드합니다.

python -c "from core.util.weight_bundle import check_and_download_weights; check_and_download_weights()"

번들은 core/resource/deploy에 저장되며, 이미 있는 번들은 다시 다운로드하지 않습니다.

3. Python API 테스트


API 테스트로 소스 코드와 환경이 올바르게 설정되었는지 확인합니다.

python -m pytest tests/1_integration_test -v

마지막 줄에 failed 없이 passed가 출력되면 정상입니다. 테스트는 합성 부품을 온보딩한 뒤 이미지 식별과 스캔 검사까지 두 handler의 public 메서드를 모두 호출합니다.

4. 라이브러리 빌드 & API 테스트


원하는 경로에 3D Inspection 라이브러리를 빌드합니다. 예제에서는 deploy 폴더를 사용합니다. 지정한 폴더가 이미 있으면 삭제한 뒤 빌드합니다.

python setup.py build_ext --build-lib deploy

빌드 결과에는 모델 weight 번들과 THIRD_PARTY_NOTICES.md, licenses 폴더가 함께 포함됩니다.

빌드된 바이너리 라이브러리로 API 테스트를 진행합니다.

python tests/1_integration_test/test_api.py --module_path deploy

마지막 줄에 failed 없이 passed가 출력되면 정상입니다.

5. 빌드된 라이브러리 사용


빌드 폴더를 module 검색 경로에 추가한 뒤 core를 import해 필요한 기능을 사용합니다.

import sys

sys.path.insert(0, "deploy")

from core import InferenceHandler, OnboardHandler

...

각 기능의 설명은 API reference에 있습니다.

6. 데모 코드


API로 CAD 부품을 온보딩하고 이미지로 부품을 식별하는 예제입니다.

import cv2
import numpy as np

from core import InferenceHandler, OnboardHandler
from core.data.catalog.catalog_data import CatalogData, CatalogEntry

err, msg, onboarder = OnboardHandler.build({"device": "cuda:0"})
parts = []
# part_id는 호출자가 부여하는 int입니다.
for part_id, path in {1024: "/cad/bracket.stp"}.items():
    err, msg, record = onboarder.onboard(path)
    if err < 0:
        continue  # 실패한 부품만 제외하고 다음 부품을 진행합니다.
    parts.append(CatalogEntry(part_id=part_id, record=record))

err, msg, handler = InferenceHandler.build({"device": "cuda:0"})
err, msg, _ = handler.set_catalog(CatalogData(catalog_id="v0001", parts=parts))
err, msg, _ = handler.warmup()
# cv2.imread는 Windows에서 비 ASCII 문자가 있는 경로의 파일을 읽지 못하므로 파일 내용을 읽어 decode합니다.
image = cv2.imdecode(np.fromfile("front.jpg", dtype=np.uint8), cv2.IMREAD_COLOR)
# 이미지는 RGB uint8 [H, W, 3] 배열로 전달합니다.
images = [cv2.cvtColor(image, cv2.COLOR_BGR2RGB)]
# candidate마다 part_id, score, matched_view_indices가 있습니다.
err, msg, result = handler.identify_images(images)

3D 스캔을 검사하는 예제입니다. catalog는 위 예제와 같이 조립한 CatalogData이고, parts는 그 CatalogEntry 목록입니다.

err, msg, handler = InferenceHandler.build({"device": "cuda:0"})
err, msg, _ = handler.set_catalog(catalog)

err, msg, scan = handler.prepare_scan("scan.stl")
# 바닥 평면 (nx, ny, nz, d)이며 단위는 mm입니다. 바닥을 검출하지 못하면 None입니다.
print(scan.floor_plane)
err, msg, ident = handler.identify_scan(scan)
# score 내림차순으로 정렬된 part_id 목록입니다.
print([candidate.part_id for candidate in ident.candidates])

# top-1 candidate를 검사합니다. 호출자가 이미 아는 부품의 geometry를 전달해도 됩니다.
geometry = {part.part_id: part.record.geometry for part in parts}
err, msg, result = handler.inspect_scan(scan, geometry[ident.candidates[0].part_id])
# 4x4 부품 → 스캔 pose이며 단위는 mm입니다.
print(result.registration.transform)
# 차순위 pose 후보 점수에서 채택 pose 점수를 뺀 값입니다.
print(result.registration.runner_up_margin)
# 스캔 삼각형별 부호 있는 표면 편차이며 단위는 mm입니다.
print(result.deviation.distances_mm)

호출 순서와 반환 값 규약은 API reference에 있습니다.