OpencvSharp 算子学习教案之 - Cv2.WarpAffine
大家好,Opencv在很多工程项目中都会用到,而OpencvSharp则是以C#开发与实现的Opencv操作库,对.NET开发人员友好,但很多API的中文资料、应用场景及常见坑点等缺乏系统性归纳,因此这系列博客将给大家带来Cv2及Mat对象全系列算子学习教案,供大家参考学习。
Cv2.WarpAffine
- 教案版本:V1.0
- 面向对象:OpenCvSharp 初学者
- 所属模块:imgproc
- 源码位置:OpenCvSharp/Cv2/Cv2_imgproc.cs:1022
摘要:Cv2.WarpAffine 用 2x3 仿射矩阵把图像重新采样到新的位置。本文通过旋转、平移和错切三个关键词拆解矩阵含义,并配合 WPF 场景帮助初学者把公式和图像结果对上号。
1. 函数名称(带参数签名)
publicstaticvoidWarpAffine(InputArraysrc,OutputArraydst,InputArraym,Sizedsize,InterpolationFlagsflags=InterpolationFlags.Linear,BorderTypesborderMode=BorderTypes.Constant,Scalar?borderValue=null)2. 函数用途
Cv2.WarpAffine的作用,是把图像按 2x3 仿射矩阵做几何变换。
它最常见的用途有:
- 做旋转、缩放和平移的组合变换。
- 做错切和轻微校正。
- 在数据增强中模拟相机角度变化。
- 在图像预处理里做坐标统一。
这个函数比Resize更通用,因为它不是只改尺寸,而是可以同时改变方向、位置和比例。
3. 函数公式
仿射矩阵可以直接写成矩阵乘法:
x ′ = m 00 x + m 01 y + m 02 y ′ = m 10 x + m 11 y + m 12 \begin{aligned} x' &= m_{00}x + m_{01}y + m_{02} \\ y' &= m_{10}x + m_{11}y + m_{12} \end{aligned}x′y′=m00x+m01y+m02=m10x+m11y+m12
这里的m就是 2x3 仿射矩阵。上面的矩阵形式和展开形式,分别说明了输出坐标如何由源坐标x、y计算得到。
需要注意的是,OpenCV 在实现时通常会使用逆向映射,也就是从输出像素反推它应该从源图像哪里取值。
4. 函数原理说明
WarpAffine的本质,是把一个二维坐标变换应用到整张图像上。
- 先读取 2x3 矩阵。
- 再把输出图像中的每个像素映射回源图像。
- 对回到源图像的坐标做插值。
- 把结果写入目标图像。
初学者最容易混淆的地方是:
- 把仿射变换和透视变换混为一谈。
- 忘记
WarpAffine需要一个 2x3 矩阵,而不是 3x3 矩阵。 - 只会旋转,不知道平移和错切也属于仿射变换。
- 忽略插值和边界模式对边缘像素的影响。
本页的 WPF 场景会同时展示旋转矩阵和错切矩阵,帮助你分清不同参数的作用。
5. 参数含义解析
| 参数名 | 类型 | 必填 | 含义 |
|---|---|---|---|
| src | InputArray | 是 | 输入图像 |
| dst | OutputArray | 是 | 输出图像 |
| m | InputArray | 是 | 2x3 仿射矩阵 |
| dsize | Size | 是 | 目标尺寸 |
| flags | InterpolationFlags | 否 | 插值方式 |
| borderMode | BorderTypes | 否 | 边界模式 |
| borderValue | Scalar? | 否 | 常量边界值 |
补充说明:
m里的六个值会共同决定图像如何移动和变形。dsize只决定输出图像大小,不负责决定变换内容。- 如果输出区域超出源图范围,需要结合
borderMode理解边缘行为。 - 仿射变换不产生透视消失点。
6. 应用场景列表
| 场景名 | 场景说明 | 典型用途 |
|---|---|---|
| 场景A:轻微旋转 | 图像绕中心小角度旋转 | 对齐、增强 |
| 场景B:平移校正 | 把图像整体挪动到合适位置 | 裁切、定位 |
| 场景C:错切变换 | 让图像发生斜切 | 教学、特效 |
| 场景D:组合变换 | 把旋转和缩放叠加 | 视觉处理、增强 |
7. 函数使用示例
下面的 Console 程序演示两个仿射矩阵:一个来自GetRotationMatrix2D,另一个是手写的错切矩阵。这样更容易看出WarpAffine负责的是“执行变换”,而不是“生成矩阵”。
usingSystem;usingSystem.Text;usingOpenCvSharp;internalstaticclassProgram{/// <summary>/// 程序入口。/// </summary>privatestaticvoidMain(){// 控制台输出切到 UTF-8,避免中文说明乱码。Console.OutputEncoding=Encoding.UTF8;usingvarsource=CreateSourceImage();usingvarrotationMatrix=Cv2.GetRotationMatrix2D(newPoint2f(2f,2f),-15,1.0);usingvarshearMatrix=CreateShearMatrix();usingvarrotated=newMat();usingvarsheared=newMat();// 先演示旋转仿射矩阵的效果。Cv2.WarpAffine(source,rotated,rotationMatrix,source.Size(),InterpolationFlags.Nearest,BorderTypes.Constant,Scalar.All(0));// 再演示错切矩阵的效果。Cv2.WarpAffine(source,sheared,shearMatrix,source.Size(),InterpolationFlags.Nearest,BorderTypes.Constant,Scalar.All(0));Console.WriteLine("源图:");PrintMat(source);Console.WriteLine("旋转矩阵:");PrintMat(rotationMatrix);Console.WriteLine("旋转结果:");PrintMat(rotated);Console.WriteLine("错切矩阵:");PrintMat(shearMatrix);Console.WriteLine("错切结果:");PrintMat(sheared);}/// <summary>/// 创建一个单通道测试图。/// </summary>/// <returns>5x5 灰度图。</returns>privatestaticMatCreateSourceImage(){// 用递增数字构造一张简单测试图,便于观察坐标变化。varsource=newMat(5,5,MatType.CV_8UC1);bytevalue=1;for(varrow=0;row<source.Rows;row++){for(varcol=0;col<source.Cols;col++){source.At<byte>(row,col)=value++;}}returnsource;}/// <summary>/// 创建一个手写错切矩阵。/// </summary>/// <returns>2x3 仿射矩阵。</returns>privatestaticMatCreateShearMatrix(){// 这里让 x 和 y 方向都带一点斜切,便于和旋转结果对比。varmatrix=newMat(2,3,MatType.CV_64FC1);matrix.At<double>(0,0)=1.0;matrix.At<double>(0,1)=0.25;matrix.At<double>(0,2)=0.0;matrix.At<double>(1,0)=0.10;matrix.At<double>(1,1)=1.0;matrix.At<double>(1,2)=0.0;returnmatrix;}/// <summary>/// 打印单通道矩阵。/// </summary>privatestaticvoidPrintMat(Matmat){for(varrow=0;row<mat.Rows;row++){for(varcol=0;col<mat.Cols;col++){// 这里统一按 double 读取,便于打印矩阵和图像结果。varvalue=mat.Type()==MatType.CV_64FC1?mat.At<double>(row,col):mat.At<byte>(row,col);Console.Write($"{value,8:F2}");}Console.WriteLine();}Console.WriteLine();}}8. 注意事项
WarpAffine需要的是 2x3 矩阵,不是 3x3 矩阵。- 如果矩阵里写了平移项,图像会整体移动。
- 如果矩阵里写了错切项,图像会倾斜。
- 边界模式会影响被变换到源图外侧的像素。
9. 调优建议
- 教学时先从
GetRotationMatrix2D得到矩阵,再把它交给WarpAffine。 - 如果你想演示错切,手写 2x3 矩阵比直接调用更直观。
- 需要更平滑的结果时,优先尝试
INTER_LINEAR。 - 初学者可以先把
dsize设成与源图相同,便于看懂坐标变化。
10. 运行说明
- 如果你在控制台工程里运行本文示例,直接把代码放进
Program.cs即可。 - 如果你在本仓库里学习,请打开 WPF 控件 Cv2WarpAffineControl.xaml.cs 对应的页面。
- 点击“运行场景A”,可以同时看到旋转矩阵和错切矩阵的效果。
11. 常见错误排查
- 把 3x3 透视矩阵误传给
WarpAffine。 - 只改了矩阵,却没有调用
WarpAffine,导致图像没有变化。 - 忘记设置合适的边界模式,边缘出现不想要的填充值。
- 把仿射变换误认为只能做旋转。