65.9K
CodeProject 正在变化。 阅读更多。
Home

使用 .NET GDI+ 进行图像矩阵变换

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.86/5 (67投票s)

2004年9月14日

CPOL

4分钟阅读

viewsIcon

404190

downloadIcon

12777

使用 GDI+ Matrix 类执行图像变换。

引言

在 .NET 中,System.Drawing.Drawing2D 命名空间中的 Matrix 类极大地简化了二维图像变换。在本文中,我将与读者分享 Matrix 类在二维图像变换中的用法。

背景

Matrix 类包含 6 个元素,排列成 3 行 2 列。例如,默认构造函数创建的默认矩阵值为 ( 1,0,0,1,0,0 )。在矩阵表示法中

Sample screenshot

这是...的简化表示

Sample screenshot

最后一列始终是:示例截图

因此,在 x 轴上移动 3 个单位,在 y 轴上移动 2 个单位的平移变换将表示为

示例截图 但内部表示为 示例截图

一个重要的注意事项是,变换矩阵是后乘以图像向量的。例如,我们有一个具有 4 个点的图像:(1,1) ( 2,3) (5,0) (6 7)。图像向量将表示为 4 行 2 列的矩阵

示例截图 但内部表示为 示例截图

当变换矩阵作用于图像矩阵时,变换矩阵被乘在图像矩阵的右侧。

Sample screenshot

结果矩阵的最后一列被忽略。因此,结果图像的点将是 (4,3) (5,5) (8,2) 和 (9,9)。

复合变换由两个或多个矩阵相乘构成。例如,一个缩放因子在 x 轴上为 2,在 y 轴上为 3 的缩放矩阵。

示例截图 内部表示为 示例截图

当进行一个先平移后缩放的复合变换时,缩放矩阵将乘以平移矩阵的右侧

Sample screenshot

同样,如果我们有一个先缩放后平移的复合矩阵,平移矩阵将乘以缩放矩阵的右侧。

向右乘也称为追加,向左乘称为前置。左侧的矩阵总是先进行运算。

矩阵变换

在本文中,我将只关注以下变换:

  • 旋转
  • 转换
  • 拉伸(缩放)
  • 翻转(镜像)

创建 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)

在上面的代码中,由于旋转变换被前置到矩阵中,所以旋转变换将首先执行。

在矩阵变换中,运算顺序非常重要。先旋转后平移与先平移后旋转的结果截然不同,如下所示:

Sample screenshot

使用 Matrix 对象

以下 GDI+ 对象利用了 Matrix 对象:

  • 图形
  • 画笔
  • GraphicsPath

这些对象都有一个 Transform 属性,它是一个 Matrix 对象。默认的 Transform 属性是单位矩阵。所有涉及 PenGraphics 对象的绘图操作都将相对于它们的 Transform 属性进行。

因此,例如,如果一个 45 度顺时针旋转矩阵已被赋值给 Graphics 对象 Transform 属性,并且绘制了一条水平线,则该线将以 45 度的倾斜渲染。

矩阵变换在 GraphicsPath 上的操作尤其有趣。当设置其 Transform 属性时,GraphicsPathPathPoints 会被更改以反映变换。

这种行为的一个用途是执行对 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 对象的强大功能。

© . All rights reserved.