Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 54 additions & 0 deletions inference/cpp/tensorrt/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
cmake_minimum_required(VERSION 3.18)
project(rfdetr-tensorrt LANGUAGES CXX CUDA)

# Set C++ standard
set(CMAKE_CXX_STANDARD 17)
add_definitions(-DAPI_EXPORTS)

# Add source files
set(SOURCES
main.cpp
src/preprocess.cu
)

# Check for rfdetr.cpp
if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/src/RF_DETR.cpp")
list(APPEND SOURCES src/RF_DETR.cpp)
else()
message(WARNING "src/RF_DETR.cpp not found. Please check your sources.")
endif()

# Add headers (optional for IDE support)
set(HEADERS
src/RF_DETR.h
src/macros.h
src/logging.h
src/cuda_utils.h
src/preprocess.h
src/common.h
)

# Set paths for OpenCV and TensorRT
set(OpenCV_DIR "your OpenCV build directory path")
set(TENSORRT_DIR "your tensorrt path")

# Find OpenCV
find_package(OpenCV REQUIRED)
include_directories(${OpenCV_INCLUDE_DIRS})

# Include TensorRT
include_directories(${TENSORRT_DIR}/include)
link_directories(${TENSORRT_DIR}/lib)
set(TENSORRT_LIBS nvinfer nvinfer_plugin nvparsers nvonnxparser)

# Add include directories
include_directories(src/)

# Create executable
add_executable(${PROJECT_NAME} ${SOURCES} ${HEADERS})

# Link libraries
target_link_libraries(${PROJECT_NAME}
${OpenCV_LIBS}
${TENSORRT_LIBS}
)
77 changes: 77 additions & 0 deletions inference/cpp/tensorrt/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
# RF-DETR TensorRT CPP

![C++](https://img.shields.io/badge/language-C++-blue.svg)
![OpenCV](https://img.shields.io/badge/OpenCV-4.5.4-brightgreen.svg)
![CMake](https://img.shields.io/badge/CMake-3.12-blue.svg)
![CUDA](https://img.shields.io/badge/CUDA-12.4-green.svg)
![TensorRT](https://img.shields.io/badge/TensorRT-10.8.0-orange.svg)
[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/drive/1JL-UY4xjkNA5206zQKkrg74IGEqdSYrp?usp=sharing)

## Overview

This project is a high-performance **C++ implementation** for real-time object detection using **RF DETR**. It leverages **TensorRT** for optimized inference and **CUDA** for accelerated processing, enabling efficient detection on both images and videos. Designed for maximum speed and accuracy, this implementation ensures seamless integration with RF-DETR model, making it suitable for deployment in research, production, and real-time applications.

**Google Colab Support**: To make GPU-based inference more accessible, a **fully configured Google Colab notebook** is provided. This notebook allows users to **run the entire project from start to finish** using a **Google Colab T4 GPU**, including compiling and executing C++ code directly in Colab. This is especially helpful for those who **struggle with local GPU availability**.

## **Note:** The Google Colab notebook is not meant for performance testing, as the performance will be poor. It is intended for learning how to integrate C++, TensorRT, and CUDA.


## Features

- **TensorRT Integration**: Optimized deep learning inference using **NVIDIA TensorRT**, ensuring high-speed execution on **GPU**.
- **Efficient Memory Management**: Uses **CUDA buffers** and **TensorRT engine caching** for improved performance.
- **Real-Time Inference**: Supports **image** and **video** processing, allowing smooth detection across frames.
- **Custom Preprocessing & Postprocessing**: Handles **image normalization, tensor conversion, and result decoding** directly in CUDA for minimal overhead.
- **High-Performance Video Processing**: Efficiently processes video streams using OpenCV while maintaining low-latency inference with TensorRT.
- **Google Colab Support**: A **ready-to-use Google Colab Notebook** is provided to **run the project in Google Colab**, enabling easy setup and execution without requiring a local GPU.

## Requirements

Before building the project, ensure that the following dependencies are installed on your system:

- **C++ Compiler**: Compatible with **C++17** or higher.
- **CMake**: Version **3.12** or higher.
- **CUDA**: Version **12.4** .
- **TensorRT**: Tested with **TensorRT 10.8.0** for high-performance inference.
- **OpenCV**: Version **4.5.4** or higher for image and video processing.

## Installation And Usage

### 1- Generate ONNX models
Generate the onnx version of the RF-DETR model, You can use the same way defined in the official repo [RF-DETR](https://github.com/roboflow/rf-detr).

### 2- Clone Repository
Clone the repository to your local machine and ensure that you are in the TensorRT directory.

### 3- Build the C++ Code
**Ensure that OpenCV and TensorRT are installed.**

```bash
mkdir build && cd build
cmake ..
cmake --build . --config Release
```

### 4- Create a TensorRT Engine

Convert the ONNX model to a TensorRT engine:

```bash
./rfdetr-tensorrt rfdetr.onnx ""
```

### 5- Run Inference on an Image

Perform object detection on an image:

```bash
./rfdetr-tensorrt rfdetr.engine "zidane.jpg"
```

### 6- Run Inference on a Video

Perform object detection on a video:

```bash
./rfdetr-tensorrt rfdetr.engine "road.mp4"
```
179 changes: 179 additions & 0 deletions inference/cpp/tensorrt/main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
#ifdef _WIN32
#include <windows.h>
#else
#include <sys/stat.h>
#include <unistd.h>
#endif

#include <iostream>
#include <string>
#include "RF_DETR.h"


bool IsPathExist(const string& path) {
#ifdef _WIN32
DWORD fileAttributes = GetFileAttributesA(path.c_str());
return (fileAttributes != INVALID_FILE_ATTRIBUTES);
#else
return (access(path.c_str(), F_OK) == 0);
#endif
}

bool IsFile(const string& path) {
if (!IsPathExist(path)) {
printf("%s:%d %s not exist\n", __FILE__, __LINE__, path.c_str());
return false;
}

#ifdef _WIN32
DWORD fileAttributes = GetFileAttributesA(path.c_str());
return ((fileAttributes != INVALID_FILE_ATTRIBUTES) && ((fileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0));
#else
struct stat buffer;
return (stat(path.c_str(), &buffer) == 0 && S_ISREG(buffer.st_mode));
#endif
}

/**
* @brief Setting up Tensorrt logger
*/
class Logger : public nvinfer1::ILogger {
void log(Severity severity, const char* msg) noexcept override {
// Only output logs with severity greater than warning
if (severity <= Severity::kWARNING)
std::cout << msg << std::endl;
}
}logger;

int main(int argc, char** argv){

const string engine_file_path{ argv[1] };
const string path{ argv[2] };
vector<string> imagePathList;
bool isVideo{ false };

assert(argc == 3);
if (IsFile(path)){
string suffix = path.substr(path.find_last_of('.') + 1);
if (suffix == "jpg" || suffix == "jpeg" || suffix == "png"){
imagePathList.push_back(path);
}
else if (suffix == "mp4" || suffix == "avi" || suffix == "m4v" || suffix == "mpeg" || suffix == "mov" || suffix == "mkv" || suffix == "webm"){
isVideo = true;
}
else {
printf("suffix %s is wrong !!!\n", suffix.c_str());
abort();
}
}
else if (IsPathExist(path)){
glob(path + "/*.jpg", imagePathList);
}

RF_DETR model(engine_file_path, logger);

if (engine_file_path.find(".onnx") != std::string::npos){
return 0;
}

if (isVideo) {
cout << "Opening video: " << path << endl;
cv::VideoCapture cap(path);

if (!cap.isOpened()) {
cerr << "Error: Cannot open video file!" << endl;
return 0 ;
}

// Get frame width, height, and FPS
int frameWidth = static_cast<int>(cap.get(cv::CAP_PROP_FRAME_WIDTH));
int frameHeight = static_cast<int>(cap.get(cv::CAP_PROP_FRAME_HEIGHT));
int fps = static_cast<int>(cap.get(cv::CAP_PROP_FPS));

// Define the codec and create VideoWriter object
cv::VideoWriter videoWriter("output.mp4",
cv::VideoWriter::fourcc('m', 'p', '4', 'v'),
fps,
cv::Size(frameWidth, frameHeight));

if (!videoWriter.isOpened()) {
cerr << "Error: Cannot open VideoWriter!" << endl;
return 0 ;
}

while (true) {
cv::Mat image;
cap >> image;

if (image.empty()) {
break;
}

int imageWidth = image.cols; // Width of the image
int imageHeight = image.rows; // Height of the image


vector<Detection> objects;

model.preprocess(image);

auto start = std::chrono::system_clock::now();
model.infer();
auto end = std::chrono::system_clock::now();

model.postprocess(objects, imageWidth, imageHeight);
model.draw(image, objects);

auto tc = (double)std::chrono::duration_cast<std::chrono::microseconds>(end - start).count() / 1000.;
printf("Cost %2.4lf ms\n", tc);

// Write processed frame to output video
videoWriter.write(image);

if (cv::waitKey(1) == 27) { // Press 'ESC' to exit early
break;
}
}

// Release resources
cap.release();
videoWriter.release();
cv::destroyAllWindows();
}


else {
// path to folder saves images
for (const auto& imagePath : imagePathList){
// open image
Mat image = imread(imagePath);
if (image.empty()){
cerr << "Error reading image: " << imagePath << endl;
continue;
}

int imageWidth = image.cols;
int imageHeight = image.rows;

vector<Detection> objects;
model.preprocess(image);

auto start = std::chrono::system_clock::now();
model.infer();
auto end = std::chrono::system_clock::now();

model.postprocess(objects, imageWidth, imageHeight);
model.draw(image, objects);

auto tc = (double)std::chrono::duration_cast<std::chrono::microseconds>(end - start).count() / 1000.;
printf("cost %2.4lf ms\n", tc);

model.draw(image, objects);

imshow("Result", image);

waitKey(0);
}
}
return 0;
}
Loading