YOLO CPU 前处理优化:5 种 HWC→NCHW 转换方法全网最详对比(速度测试+工程级代码)

张开发
2026/5/11 21:30:54 15 分钟阅读
YOLO CPU 前处理优化:5 种 HWC→NCHW 转换方法全网最详对比(速度测试+工程级代码)
在 YOLO 模型的 CPU 部署中图像格式转换HWC → NCHW是前处理阶段最关键的一步。OpenCV 读取图像默认是HWC高×宽×通道格式而模型输入要求NCHW批次×通道×高×宽格式转换效率直接决定前处理速度。本文纯 CPU 实现、无 GPU 依赖、无复杂语法提供5 种完全独立的 HWC→NCHW 转换方法附带完整可运行代码 真实速度对比帮你在工程中直接选用最优方案一、背景知识1. 格式区别HWCOpenCV 默认像素交织存储 →BGR BGR BGR ...NCHW模型输入通道连续存储 →BBBB... GGGG... RRRR...2. YOLOv5 标准 CPU 前处理流程读取图像BGR缩放尺寸到 640×640BGR → RGB归一化/255.0HWC → NCHW本文核心二、测试环境系统Linux x86_64OpenCV4.2.0兼容老版本输入尺寸640×640YOLOv5 标准输入测试方式每种方法运行 100 次取平均耗时编译C11三、5 种 HWC→NCHW 实现方法完整可运行所有方法独立函数、直接调用、无耦合。方法1三层循环遍历教学版、最容易理解使用atVec3f逐像素访问代码直观但速度最慢。voidhwc_to_nchw_loop3(constMatfloat_rgb,float*nchw){intHfloat_rgb.rows;intWfloat_rgb.cols;for(intc0;c3;c){for(inth0;hH;h){for(intw0;wW;w){nchw[c*H*Wh*Ww]float_rgb.atVec3f(h,w)[c];}}}}方法2单循环指针扁平化手写循环最快将图像视为一维数组单循环同时写 3 个通道效率极高。voidhwc_to_nchw_flat_ptr(constMatfloat_rgb,float*nchw){intHfloat_rgb.rows;intWfloat_rgb.cols;intareaH*W;constfloat*src(constfloat*)float_rgb.data;for(inti0;iarea;i){nchw[0*areai]src[i*30];nchw[1*areai]src[i*31];nchw[2*areai]src[i*32];}}方法3按行指针遍历OpenCV 官方推荐风格逐行获取数据指针缓存友好工业代码常用风格。voidhwc_to_nchw_row_ptr(constMatfloat_rgb,float*nchw){intHfloat_rgb.rows;intWfloat_rgb.cols;intareaH*W;float*c0nchw0*area;float*c1nchw1*area;float*c2nchw2*area;for(inty0;yH;y){constfloat*rowfloat_rgb.ptrfloat(y);for(intx0;xW;x){c0[y*Wx]row[x*30];c1[y*Wx]row[x*31];c2[y*Wx]row[x*32];}}}方法4split 直接写入目标内存速度冠军、工程首选利用 OpenCV 内置split直接将通道拆分到 NCHW 内存底层优化、极快。voidhwc_to_nchw_split(constMatfloat_rgb,float*nchw){intHfloat_rgb.rows;intWfloat_rgb.cols;intareaH*W;Match0(H,W,CV_32F,nchw0*area);Match1(H,W,CV_32F,nchw1*area);Match2(H,W,CV_32F,nchw2*area);vectorMatmats;mats.push_back(ch0);mats.push_back(ch1);mats.push_back(ch2);split(float_rgb,mats);}方法5split memcpy 拆分拷贝稳定通用先 split 拆分再用memcpy拷贝兼容性极强、不易出错。voidhwc_to_nchw_split_copy(constMatfloat_rgb,float*nchw){intHfloat_rgb.rows;intWfloat_rgb.cols;intareaH*W;vectorMatchannels;split(float_rgb,channels);memcpy(nchw0*area,channels[0].data,area*sizeof(float));memcpy(nchw1*area,channels[1].data,area*sizeof(float));memcpy(nchw2*area,channels[2].data,area*sizeof(float));}四、完整主程序带速度测试#includeiostream#includevector#includeopencv2/opencv.hpp#includechronousingnamespacestd;usingnamespacecv;// 把上面 5 个函数粘贴在这里 ……voidpreprocess(constMatbgr,Matfloat_rgb,inttarget_w,inttarget_h){Mat resized;resize(bgr,resized,Size(target_w,target_h));Mat rgb;cvtColor(resized,rgb,COLOR_BGR2RGB);rgb.convertTo(float_rgb,CV_32F,1.0f/255.0f);}intmain(){Mat bgrimread(test.jpg);constintWIDTH640,HEIGHT640;vectorfloatbuffer(3*HEIGHT*WIDTH);Mat float_rgb;cout\n 5种HWC-NCHW方法速度对比 \nendl;// 方法1preprocess(bgr,float_rgb,WIDTH,HEIGHT);autot1chrono::high_resolution_clock::now();for(inti0;i100;i)hwc_to_nchw_loop3(float_rgb,buffer.data());autot2chrono::high_resolution_clock::now();cout方法1 loop3: chrono::durationfloat,milli(t2-t1).count()/100 ms\n;// 方法2preprocess(bgr,float_rgb,WIDTH,HEIGHT);autot3chrono::high_resolution_clock::now();for(inti0;i100;i)hwc_to_nchw_flat_ptr(float_rgb,buffer.data());autot4chrono::high_resolution_clock::now();cout方法2 flat_ptr: chrono::durationfloat,milli(t4-t3).count()/100 ms\n;// 方法3preprocess(bgr,float_rgb,WIDTH,HEIGHT);autot5chrono::high_resolution_clock::now();for(inti0;i100;i)hwc_to_nchw_row_ptr(float_rgb,buffer.data());autot6chrono::high_resolution_clock::now();cout方法3 row_ptr: chrono::durationfloat,milli(t6-t5).count()/100 ms\n;// 方法4preprocess(bgr,float_rgb,WIDTH,HEIGHT);autot7chrono::high_resolution_clock::now();for(inti0;i100;i)hwc_to_nchw_split(float_rgb,buffer.data());autot8chrono::high_resolution_clock::now();cout方法4 split: chrono::durationfloat,milli(t8-t7).count()/100 ms\n;// 方法5preprocess(bgr,float_rgb,WIDTH,HEIGHT);autot11chrono::high_resolution_clock::now();for(inti0;i100;i)hwc_to_nchw_split_copy(float_rgb,buffer.data());autot12chrono::high_resolution_clock::now();cout方法5 split_copy: chrono::durationfloat,milli(t12-t11).count()/100 ms\n;return0;}五、速度对比结果真实测试 5种HWC-NCHW方法速度对比 方法1 loop3: 0.57 ms 方法2 flat_ptr: 0.48 ms 方法3 row_ptr: 0.47 ms 方法4 split: 0.17 ms ✅ 最快 方法5 split_copy: 0.21 ms六、结论与工程建议速度排名split方法4 split_copy方法5 row_ptr方法3 flat_ptr方法2 loop3方法1最佳方案推荐追求极致速度→方法4 split首选追求兼容性/稳定性→方法5 split_copy教学/理解原理→ 方法1 / 方法2工业部署正式使用→方法4 split速度比手写循环快 2~3 倍七、适用场景YOLOv5 / YOLOv8 / YOLOv9 前处理TensorRT / ONNX 模型 CPU 前处理C 部署、嵌入式部署、纯 CPU 环境

更多文章