2026-08-04 14:23:07 +08:00
# include "bmp_loader.h"
# include "result_json.h"
# include "yolo_runtime.h"
2026-10-25 17:46:07 +08:00
# include "mask_dilation.h"
2026-08-04 14:23:07 +08:00
# include <stdio.h>
# include <string.h>
2026-10-22 14:24:47 +08:00
# include <stdlib.h>
# include <limits.h>
2026-08-04 14:23:07 +08:00
static const char * value_after ( int argc , char * * argv , const char * option ) {
for ( int index = 1 ; index + 1 < argc ; + + index )
if ( strcmp ( argv [ index ] , option ) = = 0 ) return argv [ index + 1 ] ;
return NULL ;
}
int main ( int argc , char * * argv ) {
2026-10-25 17:46:07 +08:00
int dilation_radius = 0 ;
if ( parse_mask_dilation ( argc , argv , & dilation_radius ) ! = 0 ) { fprintf ( stderr , " Invalid --mask-dilation: integer 0..100 required \n " ) ; return 2 ; }
2026-08-04 14:23:07 +08:00
const char * config = value_after ( argc , argv , " --config " ) ;
const char * image_path = value_after ( argc , argv , " --image " ) ;
const char * json_path = value_after ( argc , argv , " --json " ) ;
const char * geometry_name = value_after ( argc , argv , " --seg-geometry " ) ;
if ( ! config | | ! image_path | | ! json_path ) {
2026-10-25 17:46:07 +08:00
fprintf ( stderr , " Usage: %s --config model.yaml --image sample.bmp --json result.json [--seg-geometry rect|circle|mean|fit] [--rgb-mask] [--mask-dilation 0..100] [--roi-lt L T | --roi-whlt W H L T] \n " , argv [ 0 ] ) ;
2026-08-04 14:23:07 +08:00
return 2 ;
}
2026-10-22 14:24:47 +08:00
if ( geometry_name & & strcmp ( geometry_name , " rect " ) & & strcmp ( geometry_name , " circle " ) & &
strcmp ( geometry_name , " mean " ) & & strcmp ( geometry_name , " fit " ) ) return 2 ;
2026-08-04 14:23:07 +08:00
if ( yolo_runtime_get_abi_version ( ) ! = YOLO_RUNTIME_ABI_VERSION ) return 3 ;
unsigned char * pixels = NULL ;
int width = 0 , height = 0 ;
if ( load_bmp_rgb ( image_path , & pixels , & width , & height ) ! = 0 ) return 4 ;
yolo_error_info_v1_t error = { sizeof ( error ) , YOLO_ERROR_INFO_VERSION } ;
yolo_runtime_t * runtime = NULL ;
if ( yolo_runtime_create ( config , & runtime , & error ) ! = YOLO_STATUS_OK | | ! runtime ) {
fprintf ( stderr , " Create failed: %s \n " , error . message ) ;
free_bmp_rgb ( pixels ) ;
return 5 ;
}
yolo_runtime_info_v1_t info = { sizeof ( info ) , YOLO_RUNTIME_INFO_VERSION } ;
if ( yolo_runtime_get_info ( runtime , & info ) ! = YOLO_STATUS_OK ) return 6 ;
2026-10-25 17:46:07 +08:00
if ( dilation_radius & & info . task ! = YOLO_TASK_SEG ) { yolo_runtime_destroy ( runtime ) ; free_bmp_rgb ( pixels ) ; return 2 ; }
2026-08-04 14:23:07 +08:00
yolo_image_view_v1_t image = { sizeof ( image ) , YOLO_IMAGE_VIEW_ABI_VERSION } ;
image . data = pixels ;
image . width = width ;
image . height = height ;
image . row_stride_bytes = width * 3 ;
image . pixel_format = YOLO_PIXEL_FORMAT_RGB888 ;
yolo_infer_options_v1_t options = { sizeof ( options ) , YOLO_INFER_OPTIONS_VERSION } ;
options . seg_geometry_mode = info . task = = YOLO_TASK_SEG
? ( geometry_name & & strcmp ( geometry_name , " circle " ) = = 0
2026-10-22 14:24:47 +08:00
? YOLO_SEG_GEOMETRY_MIN_ENCLOSING_CIRCLE
: ( geometry_name & & strcmp ( geometry_name , " mean " ) = = 0 ? YOLO_SEG_GEOMETRY_MEAN_DIAMETER_CIRCLE
: ( geometry_name & & strcmp ( geometry_name , " fit " ) = = 0 ? YOLO_SEG_GEOMETRY_ROBUST_FIT_CIRCLE : YOLO_SEG_GEOMETRY_MIN_AREA_RECT ) ) )
2026-08-04 14:23:07 +08:00
: YOLO_SEG_GEOMETRY_NONE ;
2026-10-25 17:46:07 +08:00
/* ROI 默认数据: W=800( 宽) 、H=736( 高) 、L=0( 左) 、T=0( 上) , 单位为原图像素。
默 认 初 始 化 填 写 结 构 体 大 小 、 版 本 并 清 零 保 留 字 段 , ABI 5 的 字 段 顺 序 不 变 。 */
yolo_roi_v1_t roi = yolo_roi_default ( ) ;
int roi_values [ 4 ] = { 0 } ;
const int roi_mode = parse_example_roi_args ( argc , argv , roi_values ) ;
int has_roi = roi_mode ;
if ( roi_mode = = 1 ) {
/* 只提供 L、T: 保留默认宽高 800× 736, 只修改左上角位置。 */
roi . x = roi_values [ 0 ] ; /* L: 例如 --roi-lt 217 738 中的 217。 */
roi . y = roi_values [ 1 ] ; /* T: 例如 --roi-lt 217 738 中的 738。 */
} else if ( roi_mode = = 2 ) {
/* 完整指定 W、H、L、T; 例如 --roi-whlt 771 609 217 738。 */
roi = yolo_roi_from_whlt ( roi_values [ 0 ] , roi_values [ 1 ] , roi_values [ 2 ] , roi_values [ 3 ] ) ;
} else if ( roi_mode = = 0 ) {
/* 未传 ROI 参数时读取模型 roi_info.txt; 不存在则全图推理,
不 会 因 为 默 认 初 始 化 而 自 动 裁 剪 为 800 × 736 。 */
has_roi = load_model_roi ( config , width , height , & roi ) ;
}
/* ROI 越界或宽高非正时由推理接口报错,不自动裁边。 */
if ( has_roi < 0 ) {
fprintf ( stderr , " Invalid ROI arguments, roi_info.txt or source image dimensions \n " ) ;
yolo_runtime_destroy ( runtime ) ; free_bmp_rgb ( pixels ) ;
return 7 ;
}
if ( has_roi ) printf ( " ROI W=%d H=%d L=%d T=%d \n " , roi . width , roi . height , roi . x , roi . y ) ;
2026-08-04 14:23:07 +08:00
yolo_result_set_t * results = NULL ;
error = ( yolo_error_info_v1_t ) { sizeof ( error ) , YOLO_ERROR_INFO_VERSION } ;
const yolo_status_t status =
2026-10-25 17:46:07 +08:00
yolo_runtime_infer_image_roi ( runtime , & image , has_roi ? & roi : NULL , & options , & results , & error ) ;
2026-08-04 14:23:07 +08:00
if ( status ! = YOLO_STATUS_OK | | ! results ) {
fprintf ( stderr , " Inference failed: %s \n " , error . message ) ;
yolo_runtime_destroy ( runtime ) ;
free_bmp_rgb ( pixels ) ;
return 7 ;
}
uint32_t count = 0 ;
yolo_result_set_get_count ( results , & count ) ;
printf ( " task=%d results=%u \n " , info . task , count ) ;
2026-10-25 17:46:07 +08:00
yolo_mask_view_v1_t masks [ 255 ] ;
unsigned char * owned_masks [ 255 ] = { 0 } ;
memset ( masks , 0 , sizeof ( masks ) ) ;
const yolo_roi_v1_t region = has_roi ? roi : yolo_roi_from_whlt ( width , height , 0 , 0 ) ;
2026-10-22 14:24:47 +08:00
int mask_status = 0 ;
/* Seg always demonstrates in-memory RGB IDs; --rgb-mask remains accepted. */
if ( info . task = = YOLO_TASK_SEG ) {
2026-10-25 17:46:07 +08:00
2026-10-22 14:24:47 +08:00
if ( count > 255 ) { fprintf ( stderr , " RGB supports at most 255 instances \n " ) ; mask_status = - 1 ; }
for ( uint32_t i = 0 ; i < count & & mask_status = = 0 ; + + i ) {
masks [ i ] . struct_size = sizeof ( masks [ i ] ) ; masks [ i ] . version = YOLO_MASK_VIEW_VERSION ;
/* Local 0/255 data is borrowed until results is released. */
if ( yolo_result_set_get_seg_mask ( results , i , & masks [ i ] ) ! = YOLO_STATUS_OK ) mask_status = - 1 ;
2026-10-25 17:46:07 +08:00
/* 独立调用公共库膨胀;输出由调用方持有,保留实例重叠。 */
if ( mask_status = = 0 & & dilate_example_mask ( & masks [ i ] , dilation_radius , & region , & owned_masks [ i ] ) ! = 0 ) mask_status = - 1 ;
2026-10-22 14:24:47 +08:00
}
const uint64_t stride = ( uint64_t ) width * 3 , bytes = stride * ( uint64_t ) height ;
unsigned char * rgb = NULL ;
if ( stride > INT_MAX | | bytes > SIZE_MAX ) mask_status = - 1 ;
if ( mask_status = = 0 ) {
rgb = ( unsigned char * ) malloc ( ( size_t ) bytes ) ;
if ( ! rgb ) mask_status = - 1 ;
}
if ( mask_status = = 0 ) {
error = ( yolo_error_info_v1_t ) { sizeof ( error ) , YOLO_ERROR_INFO_VERSION } ;
if ( yolo_masks_compose_rgb_ids ( masks , count , width , height , rgb , ( int32_t ) stride , bytes , & error ) ! = YOLO_STATUS_OK ) {
fprintf ( stderr , " RGB mask failed: %s \n " , error . message ) ; mask_status = - 1 ;
} else {
uint64_t foreground = 0 , instance_pixels [ 256 ] = { 0 } ;
for ( int y = 0 ; y < height ; + + y ) for ( int x = 0 ; x < width ; + + x ) {
const unsigned char * pixel = rgb + ( size_t ) y * ( size_t ) stride + ( size_t ) x * 3 ;
unsigned char ids [ 3 ] = { 0 } ; int n = 0 ;
/* Remove zero and duplicate channel values to decode membership. */
for ( int c = 0 ; c < 3 ; + + c ) {
unsigned char id = pixel [ c ] ; int duplicate = 0 ;
if ( ! id ) continue ;
for ( int j = 0 ; j < n ; + + j ) if ( ids [ j ] = = id ) duplicate = 1 ;
if ( ! duplicate ) { ids [ n + + ] = id ; + + instance_pixels [ id ] ; }
}
if ( n ) {
if ( ! foreground ) {
printf ( " RGB_PIXEL x=%d y=%d RGB=%u,%u,%u IDs= " , x , y , pixel [ 0 ] , pixel [ 1 ] , pixel [ 2 ] ) ;
for ( int j = 0 ; j < n ; + + j ) printf ( " %s%u " , j ? " , " : " " , ids [ j ] ) ;
printf ( " \n " ) ;
}
+ + foreground ;
}
}
for ( uint32_t i = 1 ; i < = count ; + + i ) printf ( " ID_PIXELS id=%u pixels=%llu \n " , i , ( unsigned long long ) instance_pixels [ i ] ) ;
printf ( " RGB_ID_MASK_OK width=%d height=%d instances=%u foreground=%llu bytes=%llu \n " ,
width , height , count , ( unsigned long long ) foreground , ( unsigned long long ) bytes ) ;
/* Process rgb here directly; saving a bitmap is optional. */
}
}
free ( rgb ) ; /* Caller owns RGB; result set separately owns binary masks. */
}
2026-10-25 17:46:07 +08:00
const int json_status = mask_status = = 0 ? write_result_json_masks ( json_path , config , results , info . task ,
info . task = = YOLO_TASK_SEG ? masks : NULL , dilation_radius ) : - 1 ;
for ( int i = 0 ; i < 255 ; + + i ) free ( owned_masks [ i ] ) ;
printf ( " MASK_DILATION radius=%d \n " , dilation_radius ) ;
2026-08-04 14:23:07 +08:00
yolo_result_set_release ( results ) ;
yolo_runtime_destroy ( runtime ) ;
free_bmp_rgb ( pixels ) ;
2026-10-22 14:24:47 +08:00
return json_status = = 0 & & mask_status = = 0 ? 0 : 8 ;
2026-08-04 14:23:07 +08:00
}