使用 .NET GDI+ 进行图像矩阵变换
使用 GDI+ Matrix 类执行图像变换。
引言
在 .NET 中,System.Drawing.Drawing2D
命名空间中的 Matrix
类极大地简化了二维图像变换。在本文中,我将与读者分享 Matrix
类在二维图像变换中的用法。
背景
Matrix
类包含 6 个元素,排列成 3 行 2 列。例如,默认构造函数创建的默认矩阵值为 ( 1,0,0,1,0,0 )。在矩阵表示法中
这是...的简化表示
最后一列始终是:
因此,在 x 轴上移动 3 个单位,在 y 轴上移动 2 个单位的平移变换将表示为
但内部表示为
一个重要的注意事项是,变换矩阵是后乘以图像向量的。例如,我们有一个具有 4 个点的图像:(1,1) ( 2,3) (5,0) (6 7)。图像向量将表示为 4 行 2 列的矩阵
但内部表示为
当变换矩阵作用于图像矩阵时,变换矩阵被乘在图像矩阵的右侧。
结果矩阵的最后一列被忽略。因此,结果图像的点将是 (4,3) (5,5) (8,2) 和 (9,9)。
复合变换由两个或多个矩阵相乘构成。例如,一个缩放因子在 x 轴上为 2,在 y 轴上为 3 的缩放矩阵。
内部表示为
当进行一个先平移后缩放的复合变换时,缩放矩阵将乘以平移矩阵的右侧
同样,如果我们有一个先缩放后平移的复合矩阵,平移矩阵将乘以缩放矩阵的右侧。
向右乘也称为追加,向左乘称为前置。左侧的矩阵总是先进行运算。
矩阵变换
在本文中,我将只关注以下变换:
- 旋转
- 转换
- 拉伸(缩放)
- 翻转(镜像)
创建 Matrix
对象
//This would create an identity matrix (1,0,0,1,0,0)
Matrix m=new Matrix();
'This would create a matrix with elements (1,0,0,1,0,0)
Dim m As New Matrix()
在创建时初始化矩阵的值
//This would create a matrix with elements (1,2,3,4,5,6)
Matrix m=new Matrix(1,2,3,4,5,6);
'This would create a matrix with elements (1,2,3,4,5,6)
Dim m As New Matrix(1, 2, 3, 4, 5, 6)
Matrix
类实现了各种方法
旋转
平移
Scale
Multiply
要创建复合矩阵,首先创建一个单位矩阵。然后使用上述方法追加/前置变换。
Matrix m=new Matrix();
//move the origin to 200,200
m.Translate(200,200);
//rotate 90 deg clockwise
m.Rotate(90,MatrixOrder.Prepend);
Dim m As New Matrix()
'move the origin to 200,200
m.Translate(200, 200)
'rotate 90 deg clockwise
m.Rotate(90, MatrixOrder.Prepend)
在上面的代码中,由于旋转变换被前置到矩阵中,所以旋转变换将首先执行。
在矩阵变换中,运算顺序非常重要。先旋转后平移与先平移后旋转的结果截然不同,如下所示:
使用 Matrix 对象
以下 GDI+ 对象利用了 Matrix
对象:
图形
画笔
GraphicsPath
这些对象都有一个 Transform
属性,它是一个 Matrix
对象。默认的 Transform
属性是单位矩阵。所有涉及 Pen
和 Graphics
对象的绘图操作都将相对于它们的 Transform
属性进行。
因此,例如,如果一个 45 度顺时针旋转矩阵已被赋值给 Graphics
对象 Transform
属性,并且绘制了一条水平线,则该线将以 45 度的倾斜渲染。
矩阵变换在 GraphicsPath
上的操作尤其有趣。当设置其 Transform
属性时,GraphicsPath
的 PathPoints
会被更改以反映变换。
这种行为的一个用途是执行对 GraphicsPath
对象的局部变换,然后使用 DrawImage
方法来渲染变换。
Graphics g=Graphics.FromImage(pictureBox1.Image);
//..
GraphicsPath gp=new GraphicsPath();
Image imgpic=(Image)pictureBoxBase.Image.Clone();
//the coordinate of the polygon must be
//point 1 = left top corner
//point 2 = right top corner
//point 3 = right bottom corner
if(cbFlipY.CheckState ==CheckState.Checked)
gp.AddPolygon(new Point[]{new Point(0,imgpic.Height),
new Point(imgpic.Width,imgpic.Height),
new Point(0,0)});
else
gp.AddPolygon(new Point[]{new Point(0,0),
new Point(imgpic.Width,0),
new Point(0,imgpic.Height)});
//apply the transformation matrix on the graphical path
gp.Transform(mm1);
//get the resulting path points
PointF[] pts=gp.PathPoints;
//draw on the picturebox content of imgpic using the local transformation
//using the resulting parralleogram described by pts
g.DrawImage(imgpic,pts);
Dim g As Graphics = Graphics.FromImage(pictureBox1.Image)
'..
Dim gp As New GraphicsPath()
Dim imgpic As Image = DirectCast(pictureBoxBase.Image.Clone(), Image)
'the coordinate of the polygon must be
'point 1 = left top corner
'point 2 = right top corner
'point 3 = right bottom corner
If cbFlipY.CheckState = CheckState.Checked Then
gp.AddPolygon(New Point() {New Point(0, imgpic.Height), New Point(imgpic.Width, imgpic.Height), New Point(0, 0)})
Else
gp.AddPolygon(New Point() {New Point(0, 0), New Point(imgpic.Width, 0), New Point(0, imgpic.Height)})
End If
'apply the transformation matrix on the graphical path
gp.Transform(mm1)
'get the resulting path points
Dim pts As PointF() = gp.PathPoints
'draw on the picturebox content of imgpic using the local transformation
'using the resulting parralleogram described by pts
g.DrawImage(imgpic, pts)
翻转
不幸的是,Matrix
类没有翻转方法。但是,翻转的矩阵是众所周知的。对于沿 x 轴的翻转,即翻转 y 坐标,矩阵是 (1,0,0,-1,0,0)。对于翻转 x 坐标,矩阵是 (-1,0,0,1,0,0)。
使用变换测试器
演示程序的使用非常直观。启动时,会加载 CodeProject 喜爱的标志性人物。坐标轴基于图表坐标系。有一个复选框可以取消翻转 y 坐标以反映计算机坐标系。原点相对于 picture box 设置为 (200,200)。
调整每个变换操作的滑块,并使用 + 和 - 按钮按列表框中的顺序排列变换。点击 Go 开始操作。
感谢 leppie(一位读者)的评论,我添加了一个新的复选框,允许用户查看实时变换。选中“实时”复选框后,对滑块的所有调整和变换的重新排序都会立即执行变换。这提供了实时更新的效果。
我添加了一个 Region Demo 复选框。选中后,将在桌面上弹出 CodeProject 标志性人物的窗口。这实际上是一个窗体,具有标志性人物的形状和图像。对图像执行的变换也会在此浮动窗口上执行。
结论
代码已充分注释。我希望读者能从本文和代码中受益,并开始解锁 .NET 中 Matrix
对象的强大功能。