图像分块与滑窗处理
一、技术背景
图像分块(Image Patching)是将大图像分割为小块进行处理的技术。在SEM图像分析和深度学习推理中,图像分块用于:
- 处理超大尺寸图像
- 满足模型输入尺寸限制
- 实现局部精细分析
滑窗(Sliding Window)策略通过重叠分块来避免边缘信息丢失,是目标检测和语义分割中的常用技术。
二、数学原理
2.1 分块参数
分块操作的关键参数:
- 块尺寸(Patch Size):每个块的宽度和高度
- 步长(Stride):相邻块的移动距离
- 重叠率(Overlap):相邻块的重叠比例
关系式:
O
v
e
r
l
a
p
=
1
−
S
t
r
i
d
e
P
a
t
c
h
S
i
z
e
Overlap = 1 - \frac{Stride}{PatchSize}
Overlap=1−PatchSizeStride
2.2 分块数量计算
对于尺寸为 W × H W \times H W×H 的图像,分块数量:
N x = ⌈ W − P a t c h S i z e S t r i d e ⌉ + 1 N_x = \left\lceil \frac{W - PatchSize}{Stride} \right\rceil + 1 Nx=⌈StrideW−PatchSize⌉+1
N y = ⌈ H − P a t c h S i z e S t r i d e ⌉ + 1 N_y = \left\lceil \frac{H - PatchSize}{Stride} \right\rceil + 1 Ny=⌈StrideH−PatchSize⌉+1
总块数: N = N x × N y N = N_x \times N_y N=Nx×Ny
2.3 边缘对齐
当图像尺寸不是块尺寸的整数倍时,最后一块需要对齐到图像边界:
x l a s t = W − P a t c h S i z e x_{last} = W - PatchSize xlast=W−PatchSize
y l a s t = H − P a t c h S i z e y_{last} = H - PatchSize ylast=H−PatchSize
2.4 滑窗的必要性
滑窗重叠的作用:
- 避免边缘截断:目标可能被分割到不同块中
- 完整检测目标:重叠区域提供多次检测机会
- 提高检测精度:目标在块中心时检测效果更好
三、代码实现
3.1 PatchHelper中的分块实现
文件路径: e:\SEM\Methods\PatchHelper.cs
using OpenCvSharp;
using System.Collections.Generic;
public class Patch
{
public Rect Region; // 裁剪区域(在原图中的位置)
public Mat Image; // 裁剪后的图像
}
public static class PatchHelper
{
public static List<Patch> CropToPatches(Mat img, int patchW, int patchH, int strideW, int strideH)
{
List<Patch> patches = new List<Patch>();
int imgW = img.Cols;
int imgH = img.Rows;
int x = 0, y = 0;
while (y < imgH)
{
// 边缘对齐:确保不超出图像高度
if (y + patchH > imgH)
y = imgH - patchH;
x = 0; // 重置x坐标到0
while (x < imgW)
{
// 边缘对齐:确保不超出图像宽度
if (x + patchW > imgW)
{
x = imgW - patchW;
}
var roi = new Rect(x, y, patchW, patchH);
patches.Add(new Patch
{
Region = roi,
Image = new Mat(img, roi).Clone() // 裁剪图像
});
// 到达右边界,退出当前行
if (x == imgW - patchW)
break;
x += strideW; // 水平移动
}
// 到达下边界,退出
if (y == imgH - patchH)
break;
y += strideH; // 垂直移动
}
return patches;
}
}
3.2 典型参数配置
SEM项目中的典型配置:
- 块尺寸:512 × 512 像素
- 步长:384 像素
- 重叠率: ( 512 − 384 ) / 512 = 25 % (512 - 384) / 512 = 25\% (512−384)/512=25%
// 示例调用
Mat largeImage = new Mat("large_sem_image.png");
List<Patch> patches = PatchHelper.CropToPatches(
largeImage,
patchW: 512,
patchH: 512,
strideW: 384,
strideH: 384);
Console.WriteLine($"总块数: {patches.Count}");
3.3 分块处理流程
// 完整的分块处理流程
Mat largeImage = new Mat("input.png");
List<Patch> patches = PatchHelper.CropToPatches(largeImage, 512, 512, 384, 384);
List<Result> allResults = new List<Result>();
foreach (var patch in patches)
{
// 对每个块进行处理(如模型推理)
Result result = ProcessPatch(patch.Image);
// 将结果坐标转换回原图坐标系
foreach (var detection in result.Detections)
{
detection.X += patch.Region.X;
detection.Y += patch.Region.Y;
}
allResults.Add(result);
}
// 合并重叠区域的检测结果
List<Detection> finalDetections = MergeResults(allResults);
四、参数调优
4.1 块尺寸选择
| 模型输入尺寸 | 推荐块尺寸 | 说明 |
|---|---|---|
| 512 × 512 | 512 × 512 | 常见配置 |
| 640 × 640 | 640 × 640 | YOLOv8默认 |
| 1024 × 1024 | 1024 × 1024 | 高分辨率模型 |
4.2 步长与重叠率
| 重叠率 | 步长 | 检测效果 | 计算量 |
|---|---|---|---|
| 0% | PatchSize | 边缘可能漏检 | 最小 |
| 25% | 0.75×PatchSize | 平衡效果和效率 | 中等 |
| 50% | 0.5×PatchSize | 检测效果好 | 较大 |
4.3 YOLO分割中的实际参数
文件路径: e:\SEM\Utils\ResultProcess.cs
// YOLO分割结果处理中的参数
Mat mask2d = mask1d.Reshape(1, 160); // mask尺寸 160×160
Mat mask3d = new Mat();
Cv2.Resize(mask2d, mask3d, new CvSize(512, 512), 0, 0, InterpolationFlags.Nearest);
// 最终mask尺寸 512×512
4.4 NMS处理重叠区域
滑窗产生的重叠区域会导致同一目标被多次检测:
// 使用NMS合并重叠检测
CvDnn.NMSBoxes(boxes, scores, scoreThreshold, nmsThreshold, out indices);
五、常见问题
Q1: 为什么使用384步长而不是512?
步长384意味着25%的重叠:
- 避免目标被分割到块边缘
- 提高检测的完整性和准确性
- 牺牲部分计算效率换取检测效果
Q2: 边缘对齐处理的意义?
边缘对齐确保:
- 最后一块不超出图像边界
- 覆盖图像所有区域
- 块尺寸保持一致
// 边缘对齐示例
if (x + patchW > imgW)
x = imgW - patchW; // 对齐到右边界
Q3: 如何处理检测结果的重叠?
滑窗重叠区域的检测结果需要合并:
// 方法1:NMS合并
CvDnn.NMSBoxes(boxes, scores, 0.5, 0.5, out indices);
// 方法2:按置信度过滤
var uniqueDetections = allDetections
.GroupBy(d => d.ClassId)
.SelectMany(g => g.OrderByDescending(d => d.Confidence).Take(1));
Q4: 分块数量如何计算?
对于1024×1024图像,512块尺寸,384步长:
N x = ⌈ ( 1024 − 512 ) / 384 ⌉ + 1 = ⌈ 512 / 384 ⌉ + 1 = 2 + 1 = 3 N_x = \lceil(1024 - 512) / 384\rceil + 1 = \lceil 512/384 \rceil + 1 = 2 + 1 = 3 Nx=⌈(1024−512)/384⌉+1=⌈512/384⌉+1=2+1=3
N y = 3 N_y = 3 Ny=3
N t o t a l = 3 × 3 = 9 N_{total} = 3 \times 3 = 9 Ntotal=3×3=9
验证:最后一个块的起始位置 x = 2 × 384 = 768 x = 2 \times 384 = 768 x=2×384=768,覆盖 768 − 1280 768-1280 768−1280,对齐后 x = 1024 − 512 = 512 x = 1024 - 512 = 512 x=1024−512=512。
Q5: 如何优化分块处理性能?
优化策略:
// 1. 并行处理
Parallel.ForEach(patches, patch =>
{
ProcessPatch(patch);
});
// 2. 使用ROI而不是复制
Mat roi = new Mat(largeImage, patch.Region); // 不复制数据
// 3. 批量处理
List<Mat> batch = patches.Select(p => p.Image).ToList();
BatchProcess(batch); // 批量推理
Q6: 分块处理与全图处理的对比?
| 对比项 | 分块处理 | 全图处理 |
|---|---|---|
| 内存占用 | 低 | 高 |
| 计算效率 | 可并行 | 串行 |
| 检测精度 | 边缘需特殊处理 | 一致 |
| 适用场景 | 超大图像 | 常规图像 |
选择建议:
- 图像尺寸 < 模型输入尺寸:全图处理
- 图像尺寸 > 模型输入尺寸:分块处理
- 需要精细检测:使用较小步长(更大重叠)
214

被折叠的 条评论
为什么被折叠?



