尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

SAM模型部署实战:基于ONNX与OpenVINO的C++推理优化方案

SAM模型部署实战:基于ONNX与OpenVINO的C++推理优化方案 简介模型部署是将训练好的AI模型从研究框架迁移到生产环境的关键环节其核心目标是实现高效、稳定的推理。ONNX作为一种开放的模型格式解决了不同深度学习框架如PyTorch、TensorFlow间的互操作性问题使模型能够脱离原始训练环境。OpenVINO则是英特尔推出的高性能推理工具包通过图优化、层融合等技术在英特尔CPU、集成显卡等硬件上对模型进行深度优化显著提升推理速度并降低资源消耗。这一技术组合在计算机视觉、工业质检、实时视频分析等对延迟敏感的应用场景中价值尤为突出。本文以Meta的SAM图像分割模型为例详细阐述了如何通过ONNX实现模型格式统一并利用OpenVINO的C API进行性能调优最终构建出可集成于实际项目的独立推理库。1. 项目缘起为什么选择ONNX与OpenVINO部署SAM最近在做一个需要实时图像分割的项目最初尝试用PyTorch直接跑Meta的SAM模型效果确实惊艳但那个推理速度尤其是在CPU上简直让人“望图兴叹”。项目对延迟有硬性要求逼着我必须把模型从研究框架搬到生产环境。经过一番折腾和对比最终敲定了ONNX OpenVINO这条技术路线并用C实现了完整的部署流程。这条路走下来不仅性能提升显著模型也真正做到了“一次导出多处运行”。今天就把这套方案的核心思路、踩过的坑以及完整的C实现细节分享出来希望能帮到同样在模型部署路上摸索的朋友。简单来说ONNX解决了模型格式统一的问题让我们能把PyTorch、TensorFlow等框架训练的模型转换成一种中间表示脱离原始训练框架的束缚。而OpenVINO则是英特尔推出的高性能推理工具包它特别擅长在英特尔硬件从CPU到集成显卡上对模型进行深度优化榨干硬件的每一分性能。把SAM模型通过这条管道部署我们最终得到的是一个独立、高效、且易于集成的C推理库可以直接嵌入到各种图像处理或视频分析的应用中。2. 部署方案全景从PyTorch到C推理的完整链路部署一个像SAM这样的复杂模型绝不是简单的格式转换。它是一条环环相扣的链路任何一个环节出问题都会导致最终推理失败或性能不达标。下图清晰地展示了我们这次部署的核心步骤与工具链flowchart TD A[PyTorch SAM 预训练模型br.pth/.pt] -- B[ONNX 模型导出brtorch.onnx.export] B -- C{ONNX 模型检查与简化bronnx.checker, onnxsim} C -- D[优化后的 .onnx 模型] D -- E[OpenVINO 模型转换brmo.py] E -- F[优化后的 .xml 与 .bin 文件] F -- G[C 推理应用开发] G -- H[模型加载brCore::read_model] H -- I[编译与设备选择brCore::compile_model] I -- J[创建推理请求brCompiledModel::create_infer_request] J -- K[数据预处理] K -- L[执行推理] L -- M[后处理与结果解析] subgraph “性能调优可选” N[Auto-Device Plugin] O[Async API] P[动态量化 INT8] end I -.- N J -.- O E -.- P整个流程可以概括为三个阶段模型准备与导出从PyTorch的.pth模型开始利用torch.onnx.export将其转换为ONNX格式。这一步的关键在于正确设置输入输出的动态维度尤其是图像和提示点并使用onnxsim等工具对模型进行简化去除冗余算子。模型优化与转换使用OpenVINO的模型优化器mo.py将ONNX模型转换为OpenVINO特有的中间表示IR格式即.xml网络结构和.bin权重数据文件。优化器会执行一系列图优化如常数折叠、层融合为后续在目标设备上的高效推理打下基础。C推理应用开发这是本文的重点。我们将使用OpenVINO C API一步步实现模型的加载、编译、输入数据准备、推理执行以及结果解析最终封装成一个可调用的函数或类。接下来我们将深入每个阶段特别是C实现的细节。3. 前期准备模型导出与优化的关键细节在写C代码之前我们需要先把模型准备好。这一步在Python环境中完成但直接影响后续C推理的成败。3.1 PyTorch模型导出为ONNXSAM模型通常包含一个图像编码器Image Encoder和一个提示解码器Prompt-guided Mask Decoder。部署时一种高效的做法是将两者分开。图像编码器运行一次为一张图像生成图像嵌入Image Embedding。之后对于该图像上的不同提示点、框可以多次、快速地运行轻量的提示解码器。导出图像编码器时需要特别注意输入尺寸。SAM编码器通常接受固定尺寸的输入如1024x1024。我们需要在导出时明确动态维度以支持可能的批次处理batch processing。import torch import onnx from segment_anything import sam_model_registry # 1. 加载PyTorch模型 sam_checkpoint “sam_vit_b_01ec64.pth” model_type “vit_b” sam sam_model_registry[model_type](checkpointsam_checkpoint) image_encoder sam.image_encoder image_encoder.eval() # 2. 准备示例输入 dummy_input torch.randn(1, 3, 1024, 1024) # (batch, channel, height, width) # 3. 设置动态轴尤其重要 dynamic_axes { ‘input_image’: {0: ‘batch_size’}, # 批次维度动态 ‘image_embeddings’: {0: ‘batch_size’} # 输出对应批次维度也动态 } # 4. 导出ONNX onnx_encoder_path “sam_image_encoder.onnx” torch.onnx.export( image_encoder, dummy_input, onnx_encoder_path, input_names[‘input_image’], output_names[‘image_embeddings’], dynamic_axesdynamic_axes, opset_version14, # 使用较新的opset以获得更好支持 do_constant_foldingTrue )导出提示解码器时情况更复杂一些。它的输入包括图像嵌入、提示坐标、提示标签等。其中提示点的数量即空间维度必须是动态的因为每次推理的提示数量可能不同。# 假设我们已经有了解码器模型 ‘mask_decoder’ # 定义示例输入 image_embeddings torch.randn(1, 256, 64, 64) # 假设的嵌入维度 point_coords torch.randn(1, 2, 2) # (batch, 2个点, xy坐标) point_labels torch.randint(0, 2, (1, 2)) # (batch, 2个点)标签0为背景1为前景 # 动态轴设置重点在于提示点的数量维度 dynamic_axes { ‘image_embeddings’: {0: ‘batch_size’}, ‘point_coords’: {0: ‘batch_size’, 1: ‘num_points’}, # num_points 动态 ‘point_labels’: {0: ‘batch_size’, 1: ‘num_points’}, ‘masks’: {0: ‘batch_size’}, ‘iou_predictions’: {0: ‘batch_size’} } onnx_decoder_path “sam_mask_decoder.onnx” torch.onnx.export( mask_decoder, (image_embeddings, point_coords, point_labels), onnx_decoder_path, input_names[‘image_embeddings’, ‘point_coords’, ‘point_labels’], output_names[‘masks’, ‘iou_predictions’], dynamic_axesdynamic_axes, opset_version14 )注意在实际的SAM官方实现中提示解码器可能还包含一个可学习的“输出token”作为输入并且输入需要经过一层位置编码PE层。在导出时务必确保你的PyTorch前向传播逻辑与OpenVINO推理时的输入预处理完全一致。一个常见的坑是PyTorch模型里的一些预处理如坐标归一化、PE计算如果被写在模型的forward函数之外导出时就会被遗漏导致C端需要自己补上这部分计算。3.2 ONNX模型简化与OpenVINO转换导出的ONNX模型可能包含一些冗余算子或复杂结构直接转换可能效率不高或报错。# 1. 安装onnx-simplifier pip install onnx-simplifier # 2. 简化模型 python -m onnxsim sam_image_encoder.onnx sam_image_encoder_sim.onnx python -m onnxsim sam_mask_decoder.onnx sam_mask_decoder_sim.onnx简化完成后使用OpenVINO的模型优化器进行最终转换。这里有一个关键选择是否进行INT8量化。从热搜词“.onnx量化int8”可以看出这是大家关心的热点。INT8量化可以大幅提升推理速度并减少内存占用但会引入轻微的精度损失。对于SAM这类对分割边缘精度要求较高的模型需要谨慎评估。# 转换FP32模型保精度 mo --input_model sam_image_encoder_sim.onnx --output_dir ./openvino_models/ --model_name sam_encoder_fp32 mo --input_model sam_mask_decoder_sim.onnx --output_dir ./openvino_models/ --model_name sam_decoder_fp32 # 尝试INT8量化需校准数据集 # 首先需要准备一个代表数据集然后使用OpenVINO的Post-Training Optimization Tool (POT) # 这是一个更复杂的过程此处不展开但其命令行工具或Python API可以调用。转换后你会得到.xml和.bin文件这就是我们C程序将要加载的模型。4. C推理引擎核心实现详解环境准备你需要安装OpenVINO Runtime C库。推荐使用vcpkg或直接下载官方编译好的包并配置好CMake。这里假设你的开发环境是VSCode需要正确配置c_cpp_properties.json和tasks.json来包含OpenVINO的头文件和库路径这也是热搜词“vscode配置c/c环境”和“vscode 配置c”关注的问题。4.1 模型加载与编译构建推理管道首先我们创建一个管理类SAMEngine来封装编码器和解码器。// sam_engine.h #pragma once #include openvino/openvino.hpp #include opencv2/opencv.hpp #include vector #include memory class SAMEngine { public: SAMEngine(const std::string encoder_model_path, const std::string decoder_model_path); bool encode(const cv::Mat image, std::vectorfloat image_embedding); bool decode(const std::vectorfloat image_embedding, const std::vectorstd::pairfloat, float points, const std::vectorint point_labels, cv::Mat output_mask, float iou_score); private: ov::Core core_; std::shared_ptrov::CompiledModel compiled_encoder_; std::shared_ptrov::CompiledModel compiled_decoder_; ov::InferRequest encoder_request_; ov::InferRequest decoder_request_; // 模型固定的输入输出张量形状信息 ov::Shape encoder_input_shape_; ov::Shape encoder_output_shape_; ov::Shape decoder_embedding_shape_; // 图像嵌入的预期形状 const int target_size_ 1024; // SAM编码器输入尺寸 // 预处理和后处理辅助函数 cv::Mat preprocess_image(const cv::Mat src); void prepare_decoder_inputs(const std::vectorfloat embedding, const std::vectorstd::pairfloat, float points, const std::vectorint labels, ov::Tensor embedding_tensor, ov::Tensor coords_tensor, ov::Tensor labels_tensor); };在构造函数中我们加载并编译模型。设备选择是性能调优的第一步。OpenVINO支持CPU、GPU、AUTO等。对于没有独立显卡的机器CPU是默认选择。如果你有英特尔集成显卡如Iris Xe可以尝试GPU但需要驱动支持热搜词“怎么确定显卡是vulkan还是openvino”可能源于此OpenVINO的GPU插件后端可以是Vulkan或OpenCL通常自动选择。// sam_engine.cpp (部分) SAMEngine::SAMEngine(const std::string encoder_path, const std::string decoder_path) { // 1. 加载模型 auto encoder_model core_.read_model(encoder_path); auto decoder_model core_.read_model(decoder_path); // 2. 配置编译选项 ov::AnyMap encoder_config, decoder_config; // 可以设置一些性能相关的配置例如 // config[ov::hint::performance_mode.name()] ov::hint::PerformanceMode::THROUGHPUT; // config[ov::hint::num_requests.name()] 2; // 用于异步推理 // 3. 编译模型 // 使用 AUTO 插件让 OpenVINO 自动选择最佳设备 compiled_encoder_ std::make_sharedov::CompiledModel(core_.compile_model(encoder_model, “AUTO”, encoder_config)); compiled_decoder_ std::make_sharedov::CompiledModel(core_.compile_model(decoder_model, “AUTO”, decoder_config)); // 4. 创建推理请求 encoder_request_ compiled_encoder_-create_infer_request(); decoder_request_ compiled_decoder_-create_infer_request(); // 5. 获取并保存输入输出信息用于后续数据准备 auto encoder_input_port compiled_encoder_-input(); encoder_input_shape_ encoder_input_port.get_shape(); // 例如 [1, 3, 1024, 1024] auto encoder_output_port compiled_encoder_-output(); encoder_output_shape_ encoder_output_port.get_shape(); // 图像嵌入的形状 // 解码器期望的图像嵌入形状需要与编码器输出一致 auto decoder_embedding_input compiled_decoder_-input(“image_embeddings”); // 根据导出时的名字 decoder_embedding_shape_ decoder_embedding_input.get_shape(); }4.2 图像编码器推理预处理与数据填充编码器的输入是一张经过预处理的图像。SAM的预处理包括调整大小至1024x1024保持长宽比短边缩放长边居中填充像素值从[0,255]归一化到[0,1]再使用固定的均值方差进行标准化。cv::Mat SAMEngine::preprocess_image(const cv::Mat src) { cv::Mat processed; // 1. 转换BGR到RGB (如果模型期望RGB) cv::cvtColor(src, processed, cv::COLOR_BGR2RGB); // 2. 计算缩放和填充 int old_h processed.rows, old_w processed.cols; float scale target_size_ * 1.0 / std::min(old_h, old_w); int new_h std::round(old_h * scale); int new_w std::round(old_w * scale); cv::resize(processed, processed, cv::Size(new_w, new_h)); // 3. 创建1024x1024的画布并居中填充 cv::Mat canvas cv::Mat::zeros(target_size_, target_size_, CV_8UC3); int pad_top (target_size_ - new_h) / 2; int pad_left (target_size_ - new_w) / 2; processed.copyTo(canvas(cv::Rect(pad_left, pad_top, new_w, new_h))); // 4. 转换为float归一化并应用SAM的特定归一化参数 // SAM使用的均值和标准差通常为: mean[123.675, 116.28, 103.53], std[58.395, 57.12, 57.375] // 注意这里的归一化是 (x - mean) / std且输入是0-255范围 canvas.convertTo(canvas, CV_32FC3, 1.0); // 转为float std::vectorcv::Mat channels(3); cv::split(canvas, channels); float mean[] {123.675f, 116.28f, 103.53f}; float std[] {58.395f, 57.12f, 57.375f}; for (int i 0; i 3; i) { channels[i] (channels[i] - mean[i]) / std[i]; } cv::merge(channels, canvas); // 5. 调整维度顺序为 NCHW (1, 3, 1024, 1024) // OpenCV是HWCOpenVINO通常期望CHW。我们直接创建NCHW的Tensor更高效。 return canvas; // 此时是HWC, CV_32FC3 } bool SAMEngine::encode(const cv::Mat image, std::vectorfloat image_embedding) { cv::Mat processed preprocess_image(image); // 获取输入Tensor并填充数据 auto input_tensor encoder_request_.get_input_tensor(); // 方法1: 直接操作Tensor数据指针高效 float* input_data input_tensor.datafloat(); int C encoder_input_shape_[1]; int H encoder_input_shape_[2]; int W encoder_input_shape_[3]; // 将HWC的OpenCV Mat数据转换为NCHW格式并拷贝 // 这是一个内存重排操作需要小心处理 for (int c 0; c C; c) { for (int h 0; h H; h) { const float* row_ptr processed.ptrfloat(h); for (int w 0; w W; w) { // 假设processed是HWC通道顺序是RGB input_data[c * H * W h * W w] row_ptr[w * C c]; } } } // 执行推理 encoder_request_.infer(); // 获取输出图像嵌入 auto output_tensor encoder_request_.get_output_tensor(); const float* embedding_data output_tensor.dataconst float(); ov::Shape output_shape output_tensor.get_shape(); size_t total_elements ov::shape_size(output_shape); image_embedding.assign(embedding_data, embedding_data total_elements); // 保存原始图像尺寸和填充信息供后续解码器还原坐标用此处省略存储逻辑 return true; }踩坑记录数据布局Layout是最大的坑之一。PyTorch默认是NCHWOpenCV是HWC。在导出ONNX时如果输入已经是NCHW格式的Tensor那么C端也必须提供NCHW的数据。上述代码展示了如何从HWC手动转换到NCHW。一个更稳妥的做法是在导出ONNX时使用torch.onnx.export的input_names和dynamic_axes明确指定布局或者在OpenVINO转换时使用--layout参数进行指定。4.3 提示解码器推理动态输入的处理解码器的输入是动态的特别是提示点坐标。我们需要根据实际输入的点数来构造Tensor。void SAMEngine::prepare_decoder_inputs(const std::vectorfloat embedding, const std::vectorstd::pairfloat, float points, const std::vectorint labels, ov::Tensor embedding_tensor, ov::Tensor coords_tensor, ov::Tensor labels_tensor) { // 1. 填充图像嵌入 (形状固定) embedding_tensor ov::Tensor(ov::element::f32, decoder_embedding_shape_, embedding.data()); // 2. 准备点坐标和标签 size_t batch_size 1; // 假设批处理为1 size_t num_points points.size(); // 坐标Tensor形状: [batch, num_points, 2] ov::Shape coords_shape {batch_size, num_points, 2}; coords_tensor ov::Tensor(ov::element::f32, coords_shape); float* coords_data coords_tensor.datafloat(); // 标签Tensor形状: [batch, num_points] ov::Shape labels_shape {batch_size, num_points}; labels_tensor ov::Tensor(ov::element::i32, labels_shape); int32_t* labels_data labels_tensor.dataint32_t(); // 3. 填充数据 // 注意点坐标需要根据编码器预处理时的缩放和填充进行逆变换映射回1024x1024坐标系 // 这里假设我们已经保存了预处理时的缩放因子scale和填充偏移(pad_left, pad_top) // 原始图像坐标 (orig_x, orig_y) - 网络输入坐标 (net_x, net_y): // net_x (orig_x * scale) pad_left // net_y (orig_y * scale) pad_top // 然后需要归一化到 [0, 1] 或模型期望的范围内SAM通常期望坐标在[0,1023]或归一化到[0,1]需要查证原始代码 // 假设模型期望归一化到[0,1]除以1024 for (size_t i 0; i num_points; i) { float net_x (points[i].first * preprocess_scale_) preprocess_pad_left_; float net_y (points[i].second * preprocess_scale_) preprocess_pad_top_; coords_data[i * 2] net_x / target_size_; // x 坐标 coords_data[i * 2 1] net_y / target_size_; // y 坐标 labels_data[i] labels[i]; } } bool SAMEngine::decode(const std::vectorfloat image_embedding, const std::vectorstd::pairfloat, float points, const std::vectorint point_labels, cv::Mat output_mask, float iou_score) { // 1. 准备输入Tensor ov::Tensor embedding_tensor, coords_tensor, labels_tensor; prepare_decoder_inputs(image_embedding, points, point_labels, embedding_tensor, coords_tensor, labels_tensor); // 2. 设置解码器请求的输入 decoder_request_.set_input_tensor(“image_embeddings”, embedding_tensor); decoder_request_.set_input_tensor(“point_coords”, coords_tensor); decoder_request_.set_input_tensor(“point_labels”, labels_tensor); // 如果模型还有“output_token”等输入也需要相应设置 // 3. 执行推理 decoder_request_.infer(); // 4. 获取输出 auto masks_tensor decoder_request_.get_output_tensor(“masks”); // 掩码输出 auto iou_tensor decoder_request_.get_output_tensor(“iou_predictions”); // IOU分数输出 const float* masks_data masks_tensor.dataconst float(); const float* iou_data iou_tensor.dataconst float(); ov::Shape masks_shape masks_tensor.get_shape(); // 例如 [1, 1, 256, 256] // 5. 后处理将模型输出的低分辨率掩码(如256x256)上采样回原始图像尺寸 int out_h masks_shape[2]; int out_w masks_shape[3]; cv::Mat low_res_mask(out_h, out_w, CV_32FC1, const_castfloat*(masks_data)); // 上采样到1024x1024网络输入尺寸 cv::Mat high_res_mask; cv::resize(low_res_mask, high_res_mask, cv::Size(target_size_, target_size_), 0, 0, cv::INTER_LINEAR); // 根据编码器预处理时的填充裁剪出有效区域并缩放到原始图像尺寸 // ... (裁剪和缩放逻辑需要用到之前保存的预处理参数) // 最终将浮点掩码转换为二值掩码 (例如0 为前景) cv::Mat binary_mask; cv::threshold(high_res_mask, binary_mask, 0.0, 255.0, cv::THRESH_BINARY); binary_mask.convertTo(output_mask, CV_8UC1); iou_score iou_data[0]; // 取第一个也是唯一一个分数 return true; }5. 性能优化与高级特性探索基础流程跑通后我们可以进一步挖掘OpenVINO的潜力来提升性能。5.1 异步推理与多请求并行对于视频流处理同步推理infer()会阻塞线程。OpenVINO提供了异步API允许在推理进行时准备下一帧数据。// 异步推理示例 void start_async_inference(const cv::Mat frame) { // 1. 准备输入数据到input_tensor... // 2. 启动异步推理 infer_request_.start_async(); // 3. 此时可以去做其他事情比如准备下一帧 } void wait_and_process_result() { // 等待当前推理完成 infer_request_.wait(); // 获取并处理结果 auto output_tensor infer_request_.get_output_tensor(); // ... 处理逻辑 }更高级的用法是设置多个推理请求ov::hint::num_requests形成一个简单的流水线让数据准备、推理、后处理重叠进行最大化硬件利用率。5.2 使用Auto-Device Plugin进行负载均衡在构造函数中我们使用了“AUTO”设备。这意味着OpenVINO会自动将模型的不同层分配到可用的硬件设备如CPU和iGPU上执行以实现负载均衡。这对于拥有英特尔异构计算平台的系统非常有用。你可以通过配置更精细地控制ov::AnyMap config { {ov::device::priorities.name(), “GPU,CPU”}, // 优先尝试GPU失败回退CPU {ov::hint::performance_mode.name(), ov::hint::PerformanceMode::LATENCY}, // 或 THROUGHPUT {ov::hint::inference_precision.name(), ov::element::f16} // 如果硬件支持尝试FP16精度 }; compiled_model core.compile_model(model, “AUTO”, config);5.3 动态形状Dynamic Shape支持我们的解码器已经支持了动态的点数量num_points。OpenVINO对动态形状的支持越来越好。在编译模型时如果明确知道输入尺寸的范围可以设置上下限以帮助运行时优化内存分配。// 在编译前可以修改模型的输入维度如果导出时已是动态此步可选 auto decoder_input decoder_model-input(“point_coords”); ov::PartialShape partial_shape decoder_input.get_partial_shape(); // 设置动态维度批次为1点数在1到20之间坐标维度固定为2 partial_shape[0] 1; // batch固定 partial_shape[1] ov::Dimension(1, 20); // num_points 动态范围 partial_shape[2] 2; // xy固定 decoder_input.set_partial_shape(partial_shape); decoder_model-reshape({{“point_coords”, partial_shape}}); // 应用新形状6. 集成测试与常见问题排查将上述代码模块集成后编写一个简单的测试程序。// main.cpp #include “sam_engine.h” #include iostream int main() { try { SAMEngine engine(“./openvino_models/sam_encoder_fp32.xml”, “./openvino_models/sam_decoder_fp32.xml”); cv::Mat image cv::imread(“test.jpg”); if (image.empty()) { std::cerr “Failed to load image!” std::endl; return -1; } // 1. 编码 std::vectorfloat embedding; if (!engine.encode(image, embedding)) { std::cerr “Image encoding failed!” std::endl; return -1; } std::cout “Image encoded. Embedding size: ” embedding.size() std::endl; // 2. 定义提示点例如用户点击的前景点和背景点 std::vectorstd::pairfloat, float points {{100, 150}, {200, 300}}; // (x, y) std::vectorint labels {1, 0}; // 1: 前景, 0: 背景 // 3. 解码 cv::Mat mask; float iou; if (!engine.decode(embedding, points, labels, mask, iou)) { std::cerr “Mask decoding failed!” std::endl; return -1; } std::cout “Decoding successful. IoU: ” iou std::endl; // 4. 可视化或保存结果 cv::imwrite(“output_mask.png”, mask * 255); // 二值掩码放大到0-255 std::cout “Mask saved to output_mask.png” std::endl; } catch (const std::exception e) { std::cerr “Exception occurred: ” e.what() std::endl; return -1; } return 0; }编译与运行使用CMake链接OpenVINO和OpenCV库。确保你的CMakeLists.txt正确设置了包含目录和库文件路径。常见问题排查模型加载失败检查.xml和.bin文件路径是否正确检查OpenVINO版本是否匹配用OpenVINO的benchmark_app工具先测试模型是否能被正常加载和推理。推理结果不对或全零99%的问题出在数据预处理上。仔细对比C预处理和Python原模型推理前的预处理每一步包括颜色通道顺序RGB/BGR、归一化参数均值/标准差、数值范围0-1或0-255、坐标变换逻辑。建议将C预处理后的第一个样本数据保存下来在Python中用NumPy加载并打印与PyTorch推理前的输入数据进行逐元素对比。动态形状错误确保在导出ONNX时正确设置了dynamic_axes并且在C端构造Tensor时其形状与模型当前期望的形状完全一致。使用get_shape()或get_partial_shape()打印并确认输入输出的形状。性能不佳尝试使用benchmark_app评估模型在不同设备CPU、GPU上的性能。在代码中启用异步推理或调整ov::hint::performance_mode。对于CPU可以尝试设置线程数ov::inference_num_threads()。整个部署过程最磨人的部分往往就是数据对齐和预处理。一旦打通后面就是享受OpenVINO带来的稳定和性能红利了。这套方案不仅适用于SAM其核心思路——ONNX统一格式、OpenVINO优化加速、C实现核心推理——对于部署其他视觉AI模型同样具有很高的参考价值。本文还有配套的精品资源点击获取
返回列表