当前位置: 首页 > news >正文

ImageToTensor函数的完整实现版本,專門用在 .NET MAUI + YOLOv8 ONNX 推理流程中

以下是ImageToTensor函数的完整实现版本,專門用在 .NET MAUI + YOLOv8 ONNX 推理流程中,將 SkiaSharp 的 SKBitmap 轉換成 ONNX Runtime 需要的DenseTensor(形狀通常為 [1, 3, 640, 640] 或你模型的輸入尺寸)。

這個函數實現了 YOLOv8 最常見的預處理步驟:

  • BGR → RGB(如果來源是 BGR)
  • 縮放到模型輸入尺寸(通常 640×640)
  • 歸一化到 [0,1](像素值 / 255)
  • 通道順序轉為 CHW(C=3, H=640, W=640)
  • 增加 batch 維度 [1, 3, H, W]

推薦實現(最常用版本)

usingMicrosoft.ML.OnnxRuntime.Tensors;usingSkiaSharp;/// <summary>/// 將 SKBitmap 轉換為 YOLOv8 ONNX 模型所需的輸入 Tensor/// 輸入形狀:[1, 3, height, width],通道順序 CHW,值域 [0,1]/// </summary>/// <param name="bitmap">原始或已 resize 的 SKBitmap(建議已 resize 到模型輸入尺寸)</param>/// <param name="targetWidth">模型期望的寬度(通常 640)</param>/// <param name="targetHeight">模型期望的高度(通常 640)</param>/// <returns>DenseTensor&lt;float&gt; 供 ONNX Runtime 使用</returns>privateDenseTensor<float>ImageToTensor(SKBitmapbitmap,inttargetWidth=640,inttargetHeight=640){// 如果輸入圖片尺寸不匹配,先 resize(保持比例 + 填充黑邊 或 直接強制拉伸)// 這裡採用最常見的強制 resize 到 640x640(YOLOv8 訓練時多為此方式)usingvarresized=bitmap.Resize(newSKImageInfo(targetWidth,targetHeight),SKFilterQuality.High);// 創建輸出 Tensor:[1, 3, H, W]vartensor=newDenseTensor<float>(new[]{1,3,targetHeight,targetWidth});// 遍歷每個像素,進行通道重排 + 歸一化for(inty=0;y<targetHeight;y++){for(intx=0;x<targetWidth;x++){// 獲取像素顏色(SkiaSharp 是 RGBA 格式)SKColorpixel=resized.GetPixel(x,y);// 轉換為 RGB(YOLOv8 期望 RGB 順序)floatr=pixel.Red/255f;floatg=pixel.Green/255f;floatb=pixel.Blue/255f;// CHW 佈局:先通道,再高度,再寬度// channel 0 = R, channel 1 = G, channel 2 = Btensor[0,0,y,x]=r;// Rtensor[0,1,y,x]=g;// Gtensor[0,2,y,x]=b;// B}}returntensor;}

更高效版本(使用 Span + 內存複製,推薦生產環境)

如果你在意性能(尤其是移動端),可以用 Span 避免逐像素 GetPixel/SetPixel 的開銷:

privateDenseTensor<float>ImageToTensorFast(SKBitmapbitmap,inttargetWidth=640,inttargetHeight=640){usingvarresized=bitmap.Resize(newSKImageInfo(targetWidth,targetHeight),SKFilterQuality.Medium);// 鎖定像素內存usingvarpixmap=resized.PeekPixels();if(pixmap==null)thrownewInvalidOperationException("無法獲取像素內存");vartensor=newDenseTensor<float>(new[]{1,3,targetHeight,targetWidth});// 獲取原始像素數據(RGBA 格式,每像素 4 bytes)ReadOnlySpan<byte>pixels=pixmap.GetPixelSpan();intstride=pixmap.RowBytes;// 每行字节數(通常 width * 4)for(inty=0;y<targetHeight;y++){introwOffset=y*stride;for(intx=0;x<targetWidth;x++){intpixelOffset=rowOffset+x*4;floatr=pixels[pixelOffset+0]/255f;// R (假設 RGBA)floatg=pixels[pixelOffset+1]/255f;// Gfloatb=pixels[pixelOffset+2]/255f;// B// pixels[pixelOffset + 3] 是 Alpha,通常忽略tensor[0,0,y,x]=r;tensor[0,1,y,x]=g;tensor[0,2,y,x]=b;}}returntensor;}

使用方式(在 DetectAsync 中調用)

publicasyncTask<List<DetectionResult>>DetectAsync(byte[]imageData,intorigWidth,intorigHeight){usingvarskBmp=SKBitmap.Decode(imageData);if(skBmp==null)thrownewArgumentException("圖片解碼失敗");varinputTensor=ImageToTensorFast(skBmp,640,640);varinputs=new[]{NamedOnnxValue.CreateFromTensor("images",inputTensor)};usingvarresults=awaitTask.Run(()=>_session.Run(inputs));returnYoloV8PostProcess.ProcessOutput(results.First().AsTensor<float>(),origWidth,origHeight,0.4f,0.45f,_labels);}

常見變體與注意事項

需求修改方式
保持長寬比 + 黑邊填充先計算 scale,resize 後在 tensor 中填充 0(padding)
Letterbox(YOLO 官方推薦)計算 scale,resize 後把圖片貼到 640×640 中央,周圍填 114(灰色)
BGR 輸入模型把 R 和 B 通道交換順序(tensor[0,0,y,x] = b; tensor[0,2,y,x] = r)
輸入尺寸不是 640改 targetWidth/targetHeight 即可(需匹配導出的 ONNX 模型輸入形狀)
批量推理改 tensor 第一維為 batch size,同時處理多張圖

推薦 Letterbox + 灰色填充(最接近 Ultralytics 官方預處理)

如果你希望與 Python Ultralytics 的預處理完全一致,可以加入 letterbox 邏輯:

// 先計算縮放比例與填充floatscale=Math.Min((float)targetWidth/bitmap.Width,(float)targetHeight/bitmap.Height);intnewW=(int)(bitmap.Width*scale);intnewH=(int)(bitmap.Height*scale);usingvarscaled=bitmap.Resize(newSKImageInfo(newW,newH),SKFilterQuality.High);// 創建 640x640 畫布,背景灰色 (114,114,114)usingvarcanvasBmp=newSKBitmap(targetWidth,targetHeight);usingvarcanvas=newSKCanvas(canvasBmp);canvas.Clear(newSKColor(114,114,114));// 居中貼上縮放後的圖片intpadLeft=(targetWidth-newW)/2;intpadTop=(targetHeight-newH)/2;canvas.DrawBitmap(scaled,padLeft,padTop);// 再轉 Tensorvartensor=ImageToTensor(canvasBmp,targetWidth,targetHeight);

這樣處理後,檢測結果的座標需要反向映射回原始圖片尺寸(考慮 scale 和 padding)。

如果你需要這個反向映射的代碼,或是想處理 letterbox 的完整前後處理流程,可以再告訴我,我幫你補上完整版本。

http://www.jsqmd.com/news/470301/

相关文章:

  • 基于51单片机手机无线蓝牙APP遥控智能车系统论文
  • DeepSORT 参数调优指南(实用版,针对工业/安防/实时场景)
  • 使用surging 常见的几个问题
  • HTML粒子爱心代码
  • 指针与数组:高效访问的秘诀
  • 918. 环形子数组的最大和
  • JavaScript性能优化实战孟盎
  • 筑牢 AI Agent 关键业务落地的生命线:数据治理与 AI 治理的全体系解析与落地指南
  • 字节一面---客户端开发实习生
  • JavaScript性能优化实战郊蒲
  • 2026年数智项目管理品牌格局观察:平台化与业财融合趋势
  • 2026年四川达州渠县TOP1电器门店:品类超120种堪称全城最全?
  • 比赛吗,就应该有比赛的样子。规则不能够太容易了
  • 跟我学C++中级篇—悲观和乐观锁
  • OpenClaw 生态网站导航推荐
  • Python电商全维数据智能分析与随机森林销量预测系统 Django 可视化 机器学习 爬虫 大数据 大模型 agent 深度学习 计算机毕业设计源码(建议收藏)✅
  • Ruby 类案例
  • Windows实操
  • 嘎嘎降AI为什么能支持9大检测平台?多平台兼容的秘密
  • 双系统安装
  • 基于 PLC 的工业锅炉过程控制程序设计及其仿真
  • 基于离散韦格纳分布(DWVD)结合卷积神经网络(CNN)与长短期记忆网络(LSTM)的故障诊断研究附Matlab代码
  • 2026年口碑好的电动喷泵推进器厂家推荐:东莞冲浪板电动喷泵/卡丁船电动喷泵推进器行业内口碑厂家推荐 - 行业平台推荐
  • OpenClaw 入门教程 | OpenClaw教程 | 第1篇
  • 673. 最长递增子序列的个数(序列dp)
  • 华沙理工大学突破性发现:让音乐生成变得可控
  • 2026年质量好的藤椒花椒油品牌推荐:四川花椒油/四川藤椒花椒油/四川无添加花椒油厂家推荐参考 - 行业平台推荐
  • JAVA开发Drools规则引擎使用
  • xxxxxxxxx
  • 2026年质量好的桥架厂家推荐:镀锌桥架厂家选购参考汇总 - 品牌宣传支持者