콘텐츠로 이동

객체 감지

개요

이 튜토리얼에서는 RBLN SDK C/C++ Runtime API를 사용하여 PyTorch YOLOv8 모델로 추론을 실행하는 방법을 배웁니다. 모델은 RBLN SDK Python API를 사용해 컴파일하며, 생성된 *.rbln 파일은 RBLN SDK C/C++ Runtime API로 추론에 사용됩니다.
이 접근 방식은 Python에서 모델 준비의 편의성과 C/C++의 성능 이점을 결합합니다. 튜토리얼에 사용된 전체 코드는 RBLN Model Zoo에서 확인할 수 있습니다.

환경 설정 및 설치 확인

시작하기 전에 시스템 환경이 올바르게 구성되어 있으며, 필요한 모든 필수 패키지가 설치되어 있는지 확인하십시오. 다음 항목이 포함됩니다:

Note

  • rebel-compiler를 사용하려면 RBLN Portal 계정이 필요합니다.
  • 위 명령은 Debian 계열 Linux(예: Ubuntu)에서 pip로 패키지를 설치하는 일반적인 절차를 전제로 합니다. OS나 환경이 다른 경우에는 설치 가이드에서 지원되는 설치 조합과 적용 가능한 명령을 확인하세요.

RBLN Python API를 사용한 컴파일

RBLN Python API는 컴파일과 추론을 모두 처리할 수 있는 완전한 기능을 제공하는 반면,
RBLN SDK C/C++ Runtime API는 오로지 추론 작업에 최적화되어 설계되었습니다.
이 튜토리얼에서는 모델 컴파일에 RBLN Python API를, 추론 실행에 RBLN SDK C/C++ Runtime API를 사용합니다.

모델 준비

ultralytics 라이브러리에서 YOLOv8m 모델을 임포트하고 포워드 패스를 실행하여 모델을 초기화합니다.

1
2
3
4
5
6
7
8
from ultralytics import YOLO  
import rebel  
import torch  

model_name = "yolov8m"  
yolo = YOLO(f"{model_name}.pt")  
model = yolo.model.eval()  
model(torch.zeros(1, 3, 640, 640))  

모델 컴파일

Torch 모델(torch.nn.Module)이 준비되면 rebel.compile_from_torch()로 컴파일합니다. compiled_model.save()로 컴파일된 모델을 저장합니다.

1
2
3
4
5
# Compile the model  
compiled_model = rebel.compile_from_torch(model, [ ("x", [1, 3, 640, 640], "float32") ])  

# Save the compiled model to local storage  
compiled_model.save(f"{model_name}.rbln")  

컴파일 실행

컴파일 코드는 compile.py에 포함되어 있습니다. 다음 명령으로 스크립트를 실행하여 .rbln 파일을 생성하세요:

$ python compile.py --model-name=yolov8m  

RBLN SDK C/C++ Runtime API를 사용한 추론

RBLN SDK C/C++ Runtime API를 사용하여 컴파일된 모델을 로드하고 추론을 실행한 후, 결과를 확인합니다.

CMake 빌드 스크립트 준비

예제 애플리케이션은 OpenCV로 이미지 전/후처리를 수행하고, argparse로 CLI 파라미터를 파싱합니다.
아래 CMake 스크립트는 외부 패키지 종속성과 링크 설정을 설명합니다.

# Define dependencies for external Package  
include(FetchContent)  
include(cmake/opencv.cmake)  
include(cmake/argparse.cmake)  

# Define the name of executable  
add_executable(object_detection main.cc)  

# Update link info for package dependencies: OpenCV  
find_package(OpenCV CONFIG REQUIRED)  
target_link_libraries(object_detection ${OpenCV_LIBS})  

# Update link info for dependencies: RBLN  
find_package(rbln CONFIG REQUIRED)  
target_link_libraries(object_detection rbln::rbln_runtime)  

# Update including dependencies: argparse  
target_include_directories(object_detection PRIVATE ${argparse_INCLUDE_DIRS})  

입력 데이터 준비

OpenCV API를 사용하여 입력 이미지를 전처리합니다. 아래 코드에서는 이미지를 읽고 전처리하는 과정을 보여줍니다.

1
2
3
4
5
6
7
8
9
std::string input_path = "${SAMPLE_PATH}/people4.jpg";  
cv::Mat image;  
try {  
    image = cv::imread(input_path);  
} catch (const cv::Exception &err) {  
    std::cerr << err.what() << std::endl;  
    std::exit(1);  
}  
cv::Mat blob = cv::dnn::blobFromImage(GetSquareImage(image, 640), 1./255., cv::Size(), cv::Scalar(), true, false, CV_32F);  

동기식 추론 실행

아래 코드는 동기식 추론 예시를 보여줍니다.

std::string model_path = "${SAMPLE_PATH}/yolov8m.rbln";  
RBLNModel *mod = rbln_create_model(model_path.c_str());  
RBLNRuntime *rt = rbln_create_runtime(mod, "default", 0, 0);  

// Set input data  
rbln_set_input(rt, 0, blob.data);  

// Run sync inference  
rbln_run(rt);  

// Get output results  
void *data = rbln_get_output(rt, 0);  

비동기식 추론 실행

아래 코드는 비동기식 추론 예시를 보여줍니다.

std::string model_path = "${SAMPLE_PATH}/yolov8m.rbln";  
RBLNModel *mod = rbln_create_model(model_path.c_str());  
RBLNRuntime *rt = rbln_create_async_runtime(mod, "default", 0, 0);  

// Input Buffer Memory Mapping for Async execution
std::vector<void*> inputs = {blob.data};

// Output Buffer Memory Mapping for Async execution
auto n_out = rbln_get_num_outputs(rt);
std::vector<cv::Mat> outputs(n_out);
std::vector<char *> output_ptrs(n_out);
for (auto idx = 0; idx < n_out; idx++) {
    const RBLNTensorLayout *layout = rbln_get_output_layout(rt, idx);
    outputs[idx] = cv::Mat{layout->ndim, layout->shape, CV_32F};
    output_ptrs[idx] = reinterpret_cast<char *>(outputs[idx].data);
}

// Run async inference  
int rid = rbln_async_run(rt, inputs.data(), output_ptrs.data());

// Wait inference done  
rbln_async_wait(rt, rid, 1000);  

후처리(Post Processing)

출력 데이터(float32 배열, shape=(1,84,8400))에 대해 NMS를 수행하고 바운딩 박스를 그립니다.
아래 코드는 처리 과정을 개략적으로 보여줍니다:

// Postprocessing for NMS  
const RBLNTensorLayout *layout = rbln_get_output_layout(rt, 0);  
cv::Mat logits{layout->ndim, layout->shape, CV_32F};  
memcpy(logits.data, data, rbln_get_layout_nbytes(layout));  

std::vector<cv::Rect> nms_boxes;  
std::vector<float> nms_confidences;  
std::vector<size_t> nms_class_ids;  
for (size_t i = 0; i < layout->shape[2]; i++) {  
    auto cx = logits.at<float>(0, 0, i);  
    auto cy = logits.at<float>(0, 1, i);  
    auto w = logits.at<float>(0, 2, i);  
    auto h = logits.at<float>(0, 3, i);  
    auto x = cx - w / 2;  
    auto y = cy - h / 2;  
    cv::Rect rect{static_cast<int>(x), static_cast<int>(y), static_cast<int>(w), static_cast<int>(h)};  
    float confidence = std::numeric_limits<float>::min();  
    int cls_id;  
    for (size_t j = 4; j < layout->shape[1]; j++) {  
        if (confidence < logits.at<float>(0, j, i)) {  
            confidence = logits.at<float>(0, j, i);  
            cls_id = j - 4;  
        }  
    }  
    nms_boxes.push_back(rect);  
    nms_confidences.push_back(confidence);  
    nms_class_ids.push_back(cls_id);  
}  
std::vector<int> nms_indices;  
cv::dnn::NMSBoxes(nms_boxes, nms_confidences, 0.25f, 0.45f, nms_indices);  
cv::Mat output_img = image.clone();  
for (size_t i = 0; i < nms_indices.size(); i++) {  
    auto idx = nms_indices[i];  
    auto class_id = nms_class_ids[idx];  
    auto scaled_box = ScaleBox(nms_boxes[idx], output_img.size(), 640);  
    cv::rectangle(output_img, scaled_box, cv::Scalar(255, 0, 0));  
    std::stringstream ss;  
    ss << COCO_CATEGORIES[class_id] << ": " << nms_confidences[idx];  
    cv::putText(output_img, ss.str(), scaled_box.tl() - cv::Point(0, 1), cv::FONT_HERSHEY_DUPLEX, 1, cv::Scalar(255, 0, 0));  
}  
cv::imwrite("result.jpg", output_img);  

리소스 해제

런타임과 모델을 해제합니다.

rbln_destroy_runtime(rt);  
rbln_destroy_model(mod);  

CMake 빌드 방법

예제 코드는 RBLN Model Zoo C++ 예제에 포함되어 있습니다.
아래 명령으로 컴파일하고 실행 파일을 생성하세요:

1
2
3
4
$ mkdir ${SAMPLE_PATH}/build  
$ cd ${SAMPLE_PATH}/build  
$ cmake ..  
$ make  

실행 파일 사용 방법

모든 단계가 완료된 후, 빌드 디렉토리에서 실행 파일을 확인할 수 있습니다.

1
2
3
4
5
# Synchronous execution  
$ ${SAMPLE_PATH}/build/object_detection -i ${SAMPLE_PATH}/people4.jpg  -m ${SAMPLE_PATH}/yolov8m.rbln  

# Asynchronous execution  
$ ${SAMPLE_PATH}/build/object_detection_async -i ${SAMPLE_PATH}/people4.jpg  -m ${SAMPLE_PATH}/yolov8m.rbln  

예시 출력: Image

참고