LetterBox图像缩放

📅 2026/7/27 7:10:57 👁️ 阅读次数 📝 编程学习
LetterBox图像缩放

LetterBox图像缩放

一、技术背景

在深度学习目标检测中,不同尺寸的输入图像需要统一缩放到模型要求的固定尺寸。直接缩放会导致图像变形,影响检测精度。LetterBox(信箱框)技术通过保持原始宽高比进行缩放,并用灰色填充空白区域,既保证了图像几何特性不变形,又满足了模型输入尺寸要求。

YOLO系列模型采用LetterBox预处理,在缩放图像时保持原始宽高比,不足部分用灰色(114, 114, 114)填充,确保检测结果可以准确还原到原始图像坐标。

二、数学原理

2.1 缩放比例计算

设原始图像尺寸为(Horig,Worig)(H_{orig}, W_{orig})(Horig,Worig),目标尺寸为(Htarget,Wtarget)(H_{target}, W_{target})(Htarget,Wtarget),缩放比例rrr取高度和宽度缩放比例的最小值:

r=min⁡(HtargetHorig,WtargetWorig)r = \min\left(\frac{H_{target}}{H_{orig}}, \frac{W_{target}}{W_{orig}}\right)r=min(HorigHtarget,WorigWtarget)

2.2 新尺寸计算

根据缩放比例计算缩放后的图像尺寸:

Wnew=⌊Worig×r⌋W_{new} = \lfloor W_{orig} \times r \rfloorWnew=Worig×r
Hnew=⌊Horig×r⌋H_{new} = \lfloor H_{orig} \times r \rfloorHnew=Horig×r

2.3 填充量计算

计算需要在图像周围填充的灰色边框宽度:

dw=Wtarget−Wnewdw = W_{target} - W_{new}dw=WtargetWnew
dh=Htarget−Hnewdh = H_{target} - H_{new}dh=HtargetHnew

填充分布在图像两侧:

  • 左填充:left=⌊dw/2⌋left = \lfloor dw / 2 \rfloorleft=dw/2
  • 右填充:right=dw−leftright = dw - leftright=dwleft
  • 上填充:top=⌊dh/2⌋top = \lfloor dh / 2 \rfloortop=dh/2
  • 下填充:bottom=dh−topbottom = dh - topbottom=dhtop

2.4 坐标还原公式

将LetterBox后的坐标(x′,y′)(x', y')(x,y)还原到原始图像坐标(x,y)(x, y)(x,y)

x=x′−padxrx = \frac{x' - pad_x}{r}x=rxpadx
y=y′−padyry = \frac{y' - pad_y}{r}y=rypady

其中padxpad_xpadxpadypad_ypady为水平、垂直方向的填充偏移量。

三、代码实现

3.1 LetterBox缩放函数

// 文件路径: e:\SEM\Yolo11_section\Utils.cs// 缩放并填充到要求输入大小publicstaticvoidSegLetterBox(Matimage,outMatoutImage,OpenCvSharp.SizenewShape,Scalarcolor,boolautoPad=true,boolscaleFill=false,boolscaleUp=true,intstride=32){// 计算缩放比例:取高度和宽度缩放比例的最小值floatr=Math.Min((float)newShape.Height/image.Rows,(float)newShape.Width/image.Cols);// 如果不允许放大,则限制比例不超过1.0if(!scaleUp)r=Math.Min(r,1.0f);// 计算缩放后的新尺寸intnewW=(int)Math.Round(image.Cols*r);intnewH=(int)Math.Round(image.Rows*r);// 计算需要填充的空白区域intdw=newShape.Width-newW;intdh=newShape.Height-newH;// 自动填充:确保填充量是stride的整数倍if(autoPad){dw%=stride;dh%=stride;}elseif(scaleFill){// 直接拉伸填充整个目标区域newW=newShape.Width;newH=newShape.Height;dw=dh=0;}// 使用线性插值缩放图像varresized=newMat();Cv2.Resize(image,resized,newOpenCvSharp.Size(newW,newH),0,0,InterpolationFlags.Linear);// 计算上下左右填充量(均匀分布)inttop=dh/2;intbottom=dh-top;intleft=dw/2;intright=dw-left;// 使用指定颜色填充边框outImage=newMat();Cv2.CopyMakeBorder(resized,outImage,top,bottom,left,right,BorderTypes.Constant,color);}

3.2 坐标还原函数

// 文件路径: e:\SEM\Yolo11_section\Utils.cs// 将模型输出的坐标转换为原始图像的坐标publicstaticSegBoundingBoxScaleCoords(OpenCvSharp.SizeletterboxShape,SegBoundingBoxbox,OpenCvSharp.SizeorigShape,boolclip=true){// 计算缩放增益floatgain=Math.Min((float)letterboxShape.Height/origShape.Height,(float)letterboxShape.Width/origShape.Width);// 计算填充偏移量intpadX=(int)Math.Round((letterboxShape.Width-origShape.Width*gain)/2.0);intpadY=(int)Math.Round((letterboxShape.Height-origShape.Height*gain)/2.0);// 坐标还原公式intx=(int)Math.Round((box.X-padX)/gain);inty=(int)Math.Round((box.Y-padY)/gain);intw=(int)Math.Round(box.Width/gain);inth=(int)Math.Round(box.Height/gain);// 可选:裁剪到原始图像边界if(clip){x=Clamp(x,0,origShape.Width);y=Clamp(y,0,origShape.Height);w=Clamp(w,0,origShape.Width-x);h=Clamp(h,0,origShape.Height-y);}returnnewSegBoundingBox(x,y,w,h,box.ClassId,box.Score,box.MaskCoefficients);}

3.3 预处理调用

// 文件路径: e:\SEM\Yolo11_section\Utils.cspublicstaticMatPreprocess(Matimage,OpenCvSharp.SizeinputSize,boolisDynamic,reffloat[]inputTensor,reflong[]inputShape){MatletterboxImage;// 调用LetterBox,使用灰色(114,114,114)填充SegLetterBox(image,outletterboxImage,inputSize,newScalar(114,114,114),isDynamic,false,true,32);inputShape[2]=letterboxImage.Rows;inputShape[3]=letterboxImage.Cols;// 归一化到[0,1]letterboxImage.ConvertTo(letterboxImage,MatType.CV_32FC3,1.0/255.0);// ... 后续转换为CHW格式张量returnletterboxImage;}

四、参数调优

4.1 填充颜色选择

颜色值适用场景特点
(114, 114, 114)YOLO标准与图像内容区分明显
(0, 0, 0)深色背景适合暗色图像
(128, 128, 128)中性灰通用性较好
(255, 255, 255)白色背景适合浅色图像

4.2 stride参数影响

stride参数确保输出尺寸是stride的整数倍,这对某些需要下采样的网络结构很重要:

  • stride=32:适用于YOLO系列,因为最大下采样倍数为32
  • stride=16:适用于较浅层网络
  • stride=1:不进行约束

4.3 插值方法选择

// 不同插值方法InterpolationFlags.Linear// 线性插值(默认,速度快)InterpolationFlags.Area// 区域插值(缩小时质量好)InterpolationFlags.Cubic// 三次插值(放大时质量好)InterpolationFlags.Lanczos4// Lanczos插值(质量最高)

五、常见问题

5.1 坐标还原偏差

问题描述:检测框还原后位置不准确,出现偏移。

解决方案

  1. 确保缩放增益计算正确,使用Math.Min取最小比例
  2. 填充偏移使用Round四舍五入
  3. 检查clip参数是否正确裁剪边界

5.2 填充区域检测问题

问题描述:模型在灰色填充区域产生误检测。

解决方案

  1. 使用与训练数据一致的填充颜色(114, 114, 114)
  2. 在后处理阶段过滤掉完全位于填充区域的检测框
  3. 训练时对数据进行LetterBox增强

5.3 动态输入尺寸处理

问题描述:模型支持动态输入尺寸时,如何处理?

解决方案

// 动态模式下stride约束生效if(autoPad){dw%=stride;// 确保尺寸是stride的整数倍dh%=stride;}

5.4 高宽比极端情况

问题描述:图像高宽比极端时,填充区域过大。

解决方案

  1. 使用scaleFill=true放弃宽高比约束(会变形)
  2. 调整输入尺寸使其与图像宽高比接近
  3. 分块处理极端尺寸图像

5.5 内存管理

问题描述:LetterBox操作产生大量临时Mat对象。

解决方案

// 及时释放临时对象varresized=newMat();Cv2.Resize(image,resized,newOpenCvSharp.Size(newW,newH),0,0,InterpolationFlags.Linear);// ... 使用resizedresized.Dispose();// 手动释放