17.3.4 颜色矩阵

版权声明:本文为博主原创文章,转载请在显著位置标明本文出处以及作者网名,未经作者允许不得用于商业目的。
17.3.4.1 矩阵基本概念

矩阵(Matrix)是一个按照长方阵列排列的复数或实数集合,类似于数组。

由于在图像处理时使用到矩阵的乘法,这里只谈谈矩阵的乘法运算。

矩阵乘法运算公式:

图17-56 矩阵乘法运算公式

在C#中,矩阵使用了颜色的四个分量:红色分量R、绿色分量G、蓝色分量B、透明度A。但光是依靠上述4个分量,不足以完全实现矩阵变换,所以再加上一个用来进行颜色增减的分量W,而W始终等于255。

假设变换前的5个颜色分量为R、G、B、A、W;

变换后的5个颜色分量为R'、G'、B'、A'、W',

乘以一个5×5的矩阵,得到公式如下:

图17-57 颜色矩阵乘法公式

R'、G'、B'、A'、W'对应的值分别为:

R'=R*r1+G*g1+B*b1+A*a1+W*w1

G'=R*r2+G*g2+B*b2+A*a2+W*w2

B'=R*r3+G*g3+B*b3+A*a3+W*w3

A'=R*r4+G*g4+B*b4+A*a4+W*w4

W'=R*r5+G*g5+B*b5+A*a5+W*w5

在这个公式基础上,先来看几组比较常见的变换:

1、变换后颜色相同:

图17-58 矩阵变换:相同矩阵

具体计算过程:

R'=R*1+G*0+B*0+A*0+W*0 =R

G'=R*0+G*1+B*0+A*0+W*0 =G

B'=R*0+G*0+B*1+A*0+W*0 =B

A'=R*0+G*0+B*0+A*1+W*0 =A

W'=R*0+G*0+B*0+A*0+W*1 =W

2、仅保留红色分量:

图17-59 矩阵变换:保留红色分量

3、仅保留绿色分量:

图17-60 矩阵变换:保留绿色分量

4、仅保留蓝色分量:

图17-61 矩阵变换:保留蓝色分量

5、灰度变换:平均值法,参看第17.3.1.3节:

图17-62 矩阵变换:灰度平均值

R'=R*0.33+G*0.33+B*0.33+A*0+W*0

G'=R*0.33+G*0.33+B*0.33+A*0+W*0

B'=R*0.33+G*0.33+B*0.33+A*0+W*0

A'=A

W'=0

6、灰度变换:指数加权法,参看第17.3.1.3节:

图17-63 矩阵变换:灰度指数加权

R'=R*0.30+G*0.59+B*0.11

G'=R*0.30+G*0.59+B*0.11

B'=R*0.30+G*0.59+B*0.11

A'=A

W'=0

7、逆反,参看第17.3.1.1节:

图17-64 矩阵变换:逆反

R'=R*-1+G*0+B*0+A*0+W*0 =-R

G'=R*0+G*-1+B*0+A*0+W*0=-G

B'=R*0+G*0+B*-1+A*0+W*0 =-B

A'=R*0+G*0+B*0+A*1+W*0 =A

W'=R*0+G*0+B*0+A*0+W*0 = 0

在理想化的情况下,以红色分量为例:如果R=10,R逆反后为-R,即-10。由于R是Byte(范围是0-255),因此R=255-10=245。但是,实际情况下,C#可以那样运算,VB.Net不行,-10由于小于0,直接被转成了0;同样其它两个分量G、B,由于是负数,都直接成了0。使用上面逆反矩阵的话,得到的图像永远是一片白色的。

下面才是正确的逆反矩阵:

图17-65 矩阵变换:逆反修正

R'=R*-1+G*0+B*0+A*0+W*1 =255-R

G'=R*0+G*-1+B*0+A*0+W*1 =255-G

B'=R*0+G*0+B*-1+A*0+W*1 =255-B

A'=R*0+G*0+B*0+A*1+W*0 =A

W'=R*0+G*0+B*0+A*0+W*0 = 0

下一节我们将实战使用以上的矩阵公式。

17.3.4.2 ColorMatrix类

ColorMatrix(颜色矩阵)类是一个包含 RGBAW 空间坐标的 5 x 5 矩阵。ImageAttributes类的SetColorMatrix和SetColorMatrices方法通过使用ColorMatrix调整图像颜色。

ColorMatrix常用属性:

Item:ColorMatrix中位于指定的行和列的元素。

Matrix00:Single,单精度浮点数。 ColorMatrix 第 0行第 0 列的元素 (注意,同数组,矩阵行列的起始从0开始)。

Matrix01:Single,单精度浮点数。ColorMatrix 第 0行第 1 列的元素。

......

Matrix44:Single,单精度浮点数。ColorMatrix 第 4行第4 列的元素。

Matrix00-Matrix44的位置如下图:

图17-66 ColorMatrix元素位置

ColorMatrix的构造函数包括两个版本的重载:

1、public ColorMatrix()

2、public ColorMatrix( float[][] newColorMatrix )

第一种方法是声明一个ColorMatrix实例,然后使对它的属性Matrix00-Matrix44赋值。

常用的是第二种,定义并初始化一个二位数组,然后用构造函数 ColorMatrix(Single()()) 直接初始化一个Matrix,例如以下代码:

float[][] imgMatrixElement= {

new float[] {1, 0, 0, 0, 0},

new float[] {0, 0, 0, 0, 0},

new float[] {0, 0, 0, 0, 0},

new float[] {0, 0, 0, 1, 0},

new float[] {0, 0, 0, 0, 0}

}

Dim imgMatrix As New ColorMatrix(imgMatrixElement)

当实例化一个ColorMatrix后并给它的各个元素赋值后,就可以使用imageAttributes的SetColorMatrix方法,为图像设置颜色矩阵,例如:

imageAttributes.SetColorMatrix(imgMatrix, ColorMatrixFlag.Default, ColorAdjustType.Bitmap);

上述代码使用的是SetColorMatrix方法的以下重载版本:

public void SetColorMatrix( ColorMatrix newColorMatrix, ColorMatrixFlag mode, ColorAdjustType type )

参数说明:

  1. newColorMatrix:要进行颜色调整的矩阵。
  2. grayMatrix:这是一个ColorMatrixFlag 枚举,包含以下成员:
  1. AltGrays:仅调整灰色底纹。
  2. Default:指定所有的颜色值(包括灰色底纹)都由同样的颜色调整矩阵来调整。
  3. SkipGrays:指定调整所有颜色,但不调整灰色底纹。 灰色底纹是指其红色、绿色和蓝色分量的值都相同的任何颜色。

通常情况下使用的是 ColorMatrixFlag.Default。

  1. 参数flags:这是一个ColorAdjustType枚举,包含以下成员:
  1. Any:指定的类型的数目。
  2. Bitmap:Bitmap 对象的颜色调整信息。
  3. Brush:Brush 对象的颜色调整信息。
  4. Count:指定的类型的数目。
  5. Default:自身没有颜色调整信息的所有 GDI+ 对象所使用的颜色调整信息。
  6. Pen:Pen 对象的颜色调整信息。
  7. Text:文本的颜色调整信息。

通常情况对图像的颜色进行调整,使用ColorAdjustType.Bitmap。

【例 17.57 【项目:code17-035】使用矩阵灰度化图像。

本例中使用了第17.3.4.1节中灰度变换平均值法的矩阵。

窗体级变量、窗体载入、载入图片的代码请参看第17.3.1节【项目:code17-031】。

主要代码如下:

private void btnGray_Click(object sender, EventArgs e)

{

ImageAttributes imageAttributes = new ImageAttributes();

DateTime timeStart, timeEnd;

TimeSpan timeDiff;

timeStart = DateTime.Now;

//灰度平均值法

float [][] imgMatrixElement = {

new float [] { 0.33f, 0.33f, 0.33f, 0, 0},

new float [] { 0.33f, 0.33f, 0.33f, 0, 0},

new float [] { 0.33f, 0.33f, 0.33f, 0, 0},

new float [] { 0, 0, 0, 1, 0},

new float [] { 0, 0, 0, 0, 0}

};

ColorMatrix imgMatrix = new ColorMatrix(imgMatrixElement);

imageAttributes.SetColorMatrix(imgMatrix, ColorMatrixFlag.Default, ColorAdjustType.Bitmap);

Bitmap destImg = new Bitmap(sourceImg.Width, sourceImg.Height);

Graphics g = Graphics.FromImage(destImg);

g.DrawImage(sourceImg, new Rectangle(0, 0, sourceImg.Width, sourceImg.Height), 0, 0, sourceImg.Width, sourceImg.Height,

GraphicsUnit.Pixel, imageAttributes);

picDest.Image = destImg;

timeEnd = DateTime.Now;

timeDiff = timeEnd - timeStart;

lblByMatrix.Text = timeDiff.TotalMilliseconds + "ms";

}

运行结果如下图所示:

图17-67 使用矩阵灰度化图像

从图17-57可以看到,使用矩阵处理图像所耗费的时间约为340.12ms,处理速度间于像素处理与内存处理,使用矩阵的代码比使用内存的代码更简洁,但是像素处理和内存处理更为灵活,应该根据实际需要选择图像处理方式。

【例 17.58 【项目:code17-036】矩阵综合运用。

窗体上放置25个TextBox,名称从"txt00"到"txt44",分别对应矩阵属性Matrix00至Matrix44。放置1个ComboBox,用于设置常见的矩阵值。

主要代码如下:

Bitmap sourceImg;

private void Form1_Load(object sender, EventArgs e)

{

picSource.SizeMode = PictureBoxSizeMode.StretchImage;

picDest.SizeMode = PictureBoxSizeMode.StretchImage;

cbMatrixType.DropDownStyle = ComboBoxStyle.DropDownList;

cbMatrixType.Items.Add("全部重置");

cbMatrixType.Items.Add("保留红色分量");

cbMatrixType.Items.Add("保留绿色分量");

cbMatrixType.Items.Add("保留蓝色分量");

cbMatrixType.Items.Add("灰度平均值");

cbMatrixType.Items.Add("灰度指数加权");

cbMatrixType.Items.Add("逆反");

cbMatrixType.Text = cbMatrixType.Items[0].ToString();

}

private void btnLoad_Click(object sender, EventArgs e)

{

OpenFileDialog ofd = new OpenFileDialog();

ofd.Filter = "图片文件|*.jpg;*.png";

if (ofd.ShowDialog() != DialogResult.OK)

return;

sourceImg = (Bitmap)Image.FromFile(ofd.FileName);

picSource.Image = sourceImg;

}

private void btnDraw_Click(object sender, EventArgs e)

{

ImageAttributes imageAttributes =new ImageAttributes();

ColorMatrix imgMatrix =new ColorMatrix();

TextBox txtControl;

for (int i = 0; i < 5; i++)

{

for (int j = 0; j < 5; j++)

{

txtControl = (TextBox)(this.Controls["txt" + i + j]);

//矩阵相当于是二维数组

imgMatrix[i, j] = Single.Parse(txtControl.Text);

}

}

imageAttributes.SetColorMatrix(imgMatrix, ColorMatrixFlag.Default, ColorAdjustType.Bitmap);

Bitmap destImg =new Bitmap(sourceImg.Width, sourceImg.Height);

Graphics g = Graphics.FromImage(destImg);

g.DrawImage(sourceImg, new Rectangle(0, 0, sourceImg.Width, sourceImg.Height), 0, 0, sourceImg.Width, sourceImg.Height,

GraphicsUnit.Pixel, imageAttributes);

picDest.Image = destImg;

}

private void cbMatrixType_SelectedIndexChanged(object sender, EventArgs e)

{

setMatrixType();

}

private void setMatrixType()

{

//重置矩阵的值

resetText();

switch(cbMatrixType.Text)

{

case "全部重置":

//不处理

break;

case "保留红色分量":

txt00.Text = "1";

txt33.Text = "1";

break;

case "保留绿色分量":

txt11.Text = "1";

txt33.Text = "1";

break;

case "保留蓝色分量":

txt22.Text = "1";

txt33.Text = "1";

break;

case "灰度平均值":

txt00.Text = "0.33";

txt01.Text = "0.33";

txt02.Text = "0.33";

txt10.Text = "0.33";

txt11.Text = "0.33";

txt12.Text = "0.33";

txt20.Text = "0.33";

txt21.Text = "0.33";

txt22.Text = "0.33";

txt33.Text = "1";

break;

case "灰度指数加权":

txt00.Text = "0.30";

txt01.Text = "0.30";

txt02.Text = "0.30";

txt10.Text = "0.59";

txt11.Text = "0.59";

txt12.Text = "0.59";

txt20.Text = "0.11";

txt21.Text = "0.11";

txt22.Text = "0.11";

txt33.Text = "1";

break;

case "逆反":

txt00.Text = "-1";

txt11.Text = "-1";

txt22.Text = "-1";

txt40.Text = "1";

txt41.Text = "1";

txt42.Text = "1";

txt33.Text = "1";

break;

default:

break;

}

}

//将矩阵关联的文本框全部重置为0

private void resetText()

{

TextBox txtControl;

for( int i = 0;i<5;i++)

{

for(int j = 0;j< 5;j++)

{

//强制类型转换为相应名称的控件

txtControl = (TextBox)(this.Controls["txt" + i + j]);

txtControl.Text = "0";

}

}

}

运行结果如下图所示:

图17-68 使用矩阵处理图像

学习更多vb.net知识,请参看vb.net 教程 目录

学习更多C#知识,请参看C#教程 目录

相关推荐
VB.Net5 小时前
17.2 图形绘制4
c#
幻想趾于现实8 小时前
C# 装箱和拆箱(以及 as ,is)
开发语言·c#
xcLeigh8 小时前
WPF进阶 | WPF 动画特效揭秘:实现炫酷的界面交互效果
c#·wpf·交互
VB.Net8 小时前
17.3.5 添加水印
矩阵·c#·水印
谢大旭9 小时前
ASP.NET Core自定义 MIME 类型配置
后端·c#
酒酿泡芙121710 小时前
前端力扣刷题 | 6:hot100之 矩阵
前端·leetcode·矩阵
鲤籽鲲10 小时前
C# 中 [MethodImpl(MethodImplOptions.Synchronized)] 的使用详解
java·开发语言·c#
xcLeigh11 小时前
WPF进阶 | WPF 样式与模板:打造个性化用户界面的利器
ui·c#·wpf
VB.Net11 小时前
17.2 图形绘制6
c#·图像