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,InterpolationFlagsflagsInterpolationFlags.Linear,BorderTypesborderModeBorderTypes.Constant,Scalar?borderValuenull)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′​m00​xm01​ym02​m10​xm11​ym12​​这里的m就是 2x3 仿射矩阵。上面的矩阵形式和展开形式分别说明了输出坐标如何由源坐标x、y计算得到。需要注意的是OpenCV 在实现时通常会使用逆向映射也就是从输出像素反推它应该从源图像哪里取值。4. 函数原理说明WarpAffine的本质是把一个二维坐标变换应用到整张图像上。先读取 2x3 矩阵。再把输出图像中的每个像素映射回源图像。对回到源图像的坐标做插值。把结果写入目标图像。初学者最容易混淆的地方是把仿射变换和透视变换混为一谈。忘记WarpAffine需要一个 2x3 矩阵而不是 3x3 矩阵。只会旋转不知道平移和错切也属于仿射变换。忽略插值和边界模式对边缘像素的影响。本页的 WPF 场景会同时展示旋转矩阵和错切矩阵帮助你分清不同参数的作用。5. 参数含义解析参数名类型必填含义srcInputArray是输入图像dstOutputArray是输出图像mInputArray是2x3 仿射矩阵dsizeSize是目标尺寸flagsInterpolationFlags否插值方式borderModeBorderTypes否边界模式borderValueScalar?否常量边界值补充说明m里的六个值会共同决定图像如何移动和变形。dsize只决定输出图像大小不负责决定变换内容。如果输出区域超出源图范围需要结合borderMode理解边缘行为。仿射变换不产生透视消失点。6. 应用场景列表场景名场景说明典型用途场景A轻微旋转图像绕中心小角度旋转对齐、增强场景B平移校正把图像整体挪动到合适位置裁切、定位场景C错切变换让图像发生斜切教学、特效场景D组合变换把旋转和缩放叠加视觉处理、增强7. 函数使用示例下面的 Console 程序演示两个仿射矩阵一个来自GetRotationMatrix2D另一个是手写的错切矩阵。这样更容易看出WarpAffine负责的是“执行变换”而不是“生成矩阵”。usingSystem;usingSystem.Text;usingOpenCvSharp;internalstaticclassProgram{/// summary/// 程序入口。/// /summaryprivatestaticvoidMain(){// 控制台输出切到 UTF-8避免中文说明乱码。Console.OutputEncodingEncoding.UTF8;usingvarsourceCreateSourceImage();usingvarrotationMatrixCv2.GetRotationMatrix2D(newPoint2f(2f,2f),-15,1.0);usingvarshearMatrixCreateShearMatrix();usingvarrotatednewMat();usingvarshearednewMat();// 先演示旋转仿射矩阵的效果。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/// returns5x5 灰度图。/returnsprivatestaticMatCreateSourceImage(){// 用递增数字构造一张简单测试图便于观察坐标变化。varsourcenewMat(5,5,MatType.CV_8UC1);bytevalue1;for(varrow0;rowsource.Rows;row){for(varcol0;colsource.Cols;col){source.Atbyte(row,col)value;}}returnsource;}/// summary/// 创建一个手写错切矩阵。/// /summary/// returns2x3 仿射矩阵。/returnsprivatestaticMatCreateShearMatrix(){// 这里让 x 和 y 方向都带一点斜切便于和旋转结果对比。varmatrixnewMat(2,3,MatType.CV_64FC1);matrix.Atdouble(0,0)1.0;matrix.Atdouble(0,1)0.25;matrix.Atdouble(0,2)0.0;matrix.Atdouble(1,0)0.10;matrix.Atdouble(1,1)1.0;matrix.Atdouble(1,2)0.0;returnmatrix;}/// summary/// 打印单通道矩阵。/// /summaryprivatestaticvoidPrintMat(Matmat){for(varrow0;rowmat.Rows;row){for(varcol0;colmat.Cols;col){// 这里统一按 double 读取便于打印矩阵和图像结果。varvaluemat.Type()MatType.CV_64FC1?mat.Atdouble(row,col):mat.Atbyte(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导致图像没有变化。忘记设置合适的边界模式边缘出现不想要的填充值。把仿射变换误认为只能做旋转。