use std::ffi::CStr;
use std::os::raw::c_char;
use std::ptr;
use std::slice;
use ndarray::Array3;
use od_opencv::model_factory::Model;
use od_opencv::model_trait::ObjectDetector;
use od_opencv::BBox;
use od_opencv::face_pipeline::FacePipeline;
#[repr(C)]
#[derive(Debug, Clone, Copy)]
pub struct OdDetection {
pub bbox_x: i32,
pub bbox_y: i32,
pub bbox_w: i32,
pub bbox_h: i32,
pub class_id: i32,
pub confidence: f32,
}
#[repr(C)]
pub struct OdDetections {
pub data: *mut OdDetection,
pub len: i32,
}
#[repr(C)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum OdError {
Ok = 0,
InvalidArgument = 1,
ModelLoadFailed = 2,
DetectionFailed = 3,
ImageConvertFailed = 4,
}
enum ModelInner {
Ort(od_opencv::backend_ort::ModelUltralyticsOrt),
#[cfg(feature = "trt")]
Trt(od_opencv::backend_tensorrt::ModelUltralyticsRt),
#[cfg(feature = "rknn")]
Rknn(od_opencv::backend_rknn::ModelUltralyticsRknn),
}
pub struct ModelHandle {
inner: ModelInner,
}
impl ModelHandle {
fn detect(
&mut self,
img: &od_opencv::ImageBuffer,
conf: f32,
nms: f32,
) -> Result<(Vec<BBox>, Vec<usize>, Vec<f32>), OdError> {
match &mut self.inner {
ModelInner::Ort(m) => m.detect(img, conf, nms).map_err(|e| {
eprintln!("od_model_detect (ort): {e:?}");
OdError::DetectionFailed
}),
#[cfg(feature = "trt")]
ModelInner::Trt(m) => m.detect(img, conf, nms).map_err(|e| {
eprintln!("od_model_detect (trt): {e:?}");
OdError::DetectionFailed
}),
#[cfg(feature = "rknn")]
ModelInner::Rknn(m) => m.detect(img, conf, nms).map_err(|e| {
eprintln!("od_model_detect (rknn): {e:?}");
OdError::DetectionFailed
}),
}
}
}
unsafe fn parse_cstr(p: *const c_char) -> Option<&'static str> {
if p.is_null() {
return None;
}
unsafe { CStr::from_ptr(p) }.to_str().ok()
}
fn into_handle(inner: ModelInner) -> *mut ModelHandle {
Box::into_raw(Box::new(ModelHandle { inner }))
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn od_model_create(
model_path: *const c_char,
input_w: u32,
input_h: u32,
) -> *mut ModelHandle {
let Some(path) = (unsafe { parse_cstr(model_path) }) else {
return ptr::null_mut();
};
match Model::ort(path, (input_w, input_h)) {
Ok(model) => into_handle(ModelInner::Ort(model)),
Err(e) => {
eprintln!("od_model_create: {e:?}");
ptr::null_mut()
}
}
}
#[cfg(feature = "cuda")]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn od_model_create_cuda(
model_path: *const c_char,
input_w: u32,
input_h: u32,
) -> *mut ModelHandle {
let Some(path) = (unsafe { parse_cstr(model_path) }) else {
return ptr::null_mut();
};
match Model::ort_cuda(path, (input_w, input_h)) {
Ok(model) => into_handle(ModelInner::Ort(model)),
Err(e) => {
eprintln!("od_model_create_cuda: {e:?}");
ptr::null_mut()
}
}
}
#[cfg(feature = "tensorrt")]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn od_model_create_tensorrt(
model_path: *const c_char,
input_w: u32,
input_h: u32,
) -> *mut ModelHandle {
let Some(path) = (unsafe { parse_cstr(model_path) }) else {
return ptr::null_mut();
};
match Model::ort_tensorrt(path, (input_w, input_h)) {
Ok(model) => into_handle(ModelInner::Ort(model)),
Err(e) => {
eprintln!("od_model_create_tensorrt: {e:?}");
ptr::null_mut()
}
}
}
#[cfg(feature = "trt")]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn od_model_create_trt(
engine_path: *const c_char,
) -> *mut ModelHandle {
let Some(path) = (unsafe { parse_cstr(engine_path) }) else {
return ptr::null_mut();
};
match Model::tensorrt(path) {
Ok(model) => into_handle(ModelInner::Trt(model)),
Err(e) => {
eprintln!("od_model_create_trt: {e:?}");
ptr::null_mut()
}
}
}
#[cfg(feature = "rknn")]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn od_model_create_rknn(
model_path: *const c_char,
num_classes: u32,
) -> *mut ModelHandle {
let Some(path) = (unsafe { parse_cstr(model_path) }) else {
return ptr::null_mut();
};
match Model::rknn(path, num_classes as usize) {
Ok(model) => into_handle(ModelInner::Rknn(model)),
Err(e) => {
eprintln!("od_model_create_rknn: {e:?}");
ptr::null_mut()
}
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn od_model_free(handle: *mut ModelHandle) {
if !handle.is_null() {
drop(unsafe { Box::from_raw(handle) });
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn od_model_detect(
handle: *mut ModelHandle,
pixels_rgb: *const u8,
img_w: i32,
img_h: i32,
conf_threshold: f32,
nms_threshold: f32,
out: *mut OdDetections,
) -> OdError {
if handle.is_null() || pixels_rgb.is_null() || out.is_null() {
return OdError::InvalidArgument;
}
if img_w <= 0 || img_h <= 0 {
return OdError::InvalidArgument;
}
let model = unsafe { &mut *handle };
let h = img_h as usize;
let w = img_w as usize;
let n_bytes = h * w * 3;
let rgb_slice = unsafe { slice::from_raw_parts(pixels_rgb, n_bytes) };
let arr = match Array3::from_shape_vec((h, w, 3), rgb_slice.to_vec()) {
Ok(a) => a,
Err(_) => {
unsafe {
(*out).data = ptr::null_mut();
(*out).len = 0;
}
return OdError::ImageConvertFailed;
}
};
let img_buf = od_opencv::ImageBuffer::from_rgb(arr);
let (bboxes, class_ids, confidences) = match model.detect(&img_buf, conf_threshold, nms_threshold) {
Ok(result) => result,
Err(e) => {
unsafe {
(*out).data = ptr::null_mut();
(*out).len = 0;
}
return e;
}
};
let count = bboxes.len();
if count == 0 {
unsafe {
(*out).data = ptr::null_mut();
(*out).len = 0;
}
return OdError::Ok;
}
let mut results: Vec<OdDetection> = Vec::with_capacity(count);
for i in 0..count {
results.push(OdDetection {
bbox_x: bboxes[i].x,
bbox_y: bboxes[i].y,
bbox_w: bboxes[i].width,
bbox_h: bboxes[i].height,
class_id: class_ids[i] as i32,
confidence: confidences[i],
});
}
let mut results = results.into_boxed_slice();
unsafe {
(*out).data = results.as_mut_ptr();
(*out).len = count as i32;
}
std::mem::forget(results);
OdError::Ok
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn od_detections_free(detections: *mut OdDetections) {
if detections.is_null() {
return;
}
let d = unsafe { &mut *detections };
if !d.data.is_null() && d.len > 0 {
let _ = unsafe {
Vec::from_raw_parts(d.data, d.len as usize, d.len as usize)
};
d.data = ptr::null_mut();
d.len = 0;
}
}
#[repr(C)]
#[derive(Debug, Clone, Copy)]
pub struct FaceDetectionResult {
pub bbox_x: f32,
pub bbox_y: f32,
pub bbox_w: f32,
pub bbox_h: f32,
pub confidence: f32,
pub landmarks: [f32; 10],
pub embedding: [f32; 512],
}
#[repr(C)]
pub struct FaceDetectionResults {
pub data: *mut FaceDetectionResult,
pub len: i32,
}
pub struct FacePipelineHandle {
inner: FacePipeline,
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn face_pipeline_create(
detector_path: *const c_char,
recognizer_path: *const c_char,
) -> *mut FacePipelineHandle {
let Some(det_path) = (unsafe { parse_cstr(detector_path) }) else {
return ptr::null_mut();
};
let Some(rec_path) = (unsafe { parse_cstr(recognizer_path) }) else {
return ptr::null_mut();
};
match FacePipeline::new(det_path, rec_path) {
Ok(pipeline) => Box::into_raw(Box::new(FacePipelineHandle { inner: pipeline })),
Err(e) => {
eprintln!("face_pipeline_create: {e:?}");
ptr::null_mut()
}
}
}
#[cfg(feature = "cuda")]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn face_pipeline_create_cuda(
detector_path: *const c_char,
recognizer_path: *const c_char,
) -> *mut FacePipelineHandle {
let Some(det_path) = (unsafe { parse_cstr(detector_path) }) else {
return ptr::null_mut();
};
let Some(rec_path) = (unsafe { parse_cstr(recognizer_path) }) else {
return ptr::null_mut();
};
match FacePipeline::new_cuda(det_path, rec_path) {
Ok(pipeline) => Box::into_raw(Box::new(FacePipelineHandle { inner: pipeline })),
Err(e) => {
eprintln!("face_pipeline_create_cuda: {e:?}");
ptr::null_mut()
}
}
}
#[cfg(feature = "tensorrt")]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn face_pipeline_create_tensorrt(
detector_path: *const c_char,
recognizer_path: *const c_char,
) -> *mut FacePipelineHandle {
let Some(det_path) = (unsafe { parse_cstr(detector_path) }) else {
return ptr::null_mut();
};
let Some(rec_path) = (unsafe { parse_cstr(recognizer_path) }) else {
return ptr::null_mut();
};
match FacePipeline::new_tensorrt(det_path, rec_path) {
Ok(pipeline) => Box::into_raw(Box::new(FacePipelineHandle { inner: pipeline })),
Err(e) => {
eprintln!("face_pipeline_create_tensorrt: {e:?}");
ptr::null_mut()
}
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn face_pipeline_aligned_size(
handle: *const FacePipelineHandle,
) -> u32 {
if handle.is_null() {
return 0;
}
unsafe { &*handle }.inner.aligned_size()
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn face_pipeline_process(
handle: *mut FacePipelineHandle,
pixels_rgb: *const u8,
img_w: i32,
img_h: i32,
conf_threshold: f32,
nms_threshold: f32,
out: *mut FaceDetectionResults,
) -> OdError {
if handle.is_null() || pixels_rgb.is_null() || out.is_null() {
return OdError::InvalidArgument;
}
if img_w <= 0 || img_h <= 0 {
return OdError::InvalidArgument;
}
let pipeline = unsafe { &mut *handle };
let h = img_h as usize;
let w = img_w as usize;
let n_bytes = h * w * 3;
let rgb_slice = unsafe { slice::from_raw_parts(pixels_rgb, n_bytes) };
let arr = match Array3::from_shape_vec((h, w, 3), rgb_slice.to_vec()) {
Ok(a) => a,
Err(_) => {
unsafe {
(*out).data = ptr::null_mut();
(*out).len = 0;
}
return OdError::ImageConvertFailed;
}
};
let img_buf = od_opencv::ImageBuffer::from_rgb(arr);
let faces = match pipeline.inner.process(&img_buf, conf_threshold, nms_threshold) {
Ok(f) => f,
Err(e) => {
eprintln!("face_pipeline_process: {e:?}");
unsafe {
(*out).data = ptr::null_mut();
(*out).len = 0;
}
return OdError::DetectionFailed;
}
};
let count = faces.len();
if count == 0 {
unsafe {
(*out).data = ptr::null_mut();
(*out).len = 0;
}
return OdError::Ok;
}
let mut results: Vec<FaceDetectionResult> = Vec::with_capacity(count);
for face in &faces {
let mut landmarks = [0.0f32; 10];
for k in 0..5 {
landmarks[k * 2] = face.landmarks[k][0];
landmarks[k * 2 + 1] = face.landmarks[k][1];
}
results.push(FaceDetectionResult {
bbox_x: face.x,
bbox_y: face.y,
bbox_w: face.width,
bbox_h: face.height,
confidence: face.confidence,
landmarks,
embedding: face.embedding,
});
}
let mut results = results.into_boxed_slice();
unsafe {
(*out).data = results.as_mut_ptr();
(*out).len = count as i32;
}
std::mem::forget(results);
OdError::Ok
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn face_pipeline_embed(
handle: *mut FacePipelineHandle,
pixels_rgb: *const u8,
size: i32,
out_embedding: *mut f32,
) -> OdError {
if handle.is_null() || pixels_rgb.is_null() || out_embedding.is_null() {
return OdError::InvalidArgument;
}
if size <= 0 {
return OdError::InvalidArgument;
}
let pipeline = unsafe { &mut *handle };
let s = size as usize;
let n_bytes = s * s * 3;
let rgb_slice = unsafe { slice::from_raw_parts(pixels_rgb, n_bytes) };
let arr = match Array3::from_shape_vec((s, s, 3), rgb_slice.to_vec()) {
Ok(a) => a,
Err(_) => return OdError::ImageConvertFailed,
};
let img_buf = od_opencv::ImageBuffer::from_rgb(arr);
match pipeline.inner.embed(&img_buf) {
Ok(embedding) => {
unsafe {
ptr::copy_nonoverlapping(embedding.as_ptr(), out_embedding, 512);
}
OdError::Ok
}
Err(e) => {
eprintln!("face_pipeline_embed: {e:?}");
OdError::DetectionFailed
}
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn face_pipeline_destroy(handle: *mut FacePipelineHandle) {
if !handle.is_null() {
drop(unsafe { Box::from_raw(handle) });
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn face_pipeline_results_free(results: *mut FaceDetectionResults) {
if results.is_null() {
return;
}
let r = unsafe { &mut *results };
if !r.data.is_null() && r.len > 0 {
let _ = unsafe {
Vec::from_raw_parts(r.data, r.len as usize, r.len as usize)
};
r.data = ptr::null_mut();
r.len = 0;
}
}