ColorMatrix色彩矩阵的C#实践


哇啦哇啦部分

这个东西很多年前就出现了,虽然我近期才接触到…。既然用到了,就干脆把常用的矩阵记一记,总结一下吧。

ColorMatrix是一个4行5列的矩阵,有人叫它色彩矩阵,也有人叫它颜色矩阵,是一个很便利的东西。一张RGB模式存储的图片,其每一个像素点都有自己的RGB(RGBA)值。图像处理时,若想用一个固定算法遍历所有像素点,无疑矩阵的速度非常有优势。毕竟RGBA也可以看做一个向量。

在C#中,色彩矩阵是5行5列的矩阵,多了一行常数便于计算,颜色向量为[R,G,B,A,W]。W在计算结束后会被舍弃。由于颜色向量中每个元素的取值范围均为[0, 255],因此与它相乘的色彩矩阵的每一个元素则为255的倍数。例如色彩矩阵元素取值范围[-1, 1],对应计算结果范围[-255, 255],结果可以超出[0, 255],超出范围的值会被纠正为0或255。

例如色彩矩阵:

     ┌ a b c d e ┐
     │ f g h i j │
     │ k l m n o │
     │ p q r s t │
     └ u v w x y ┘

与颜色向量:

         ┌ R ┐
         │ G │
         │ B │
         │ A │
         └ W ┘ 

相乘,可获得新的颜色向量:

┌ R*a + G*f + B*k + A*p + W*u ┐
│ R*b + G*g + B*l + A*q + W*v │
│ R*c + G*h + B*m + A*r + W*w │
│ R*d + G*i + B*n + A*s + W*x │
└ R*e + G*j + B*o + A*t + W*y ┘

当初学线性代数的时候不好好学,觉得就能解个方程组。现在才发觉这玩意儿真好用,真香真香。

以上述为例,a,g,m取值-1,s,y,u,v,w取1,其他元素取0,那么结果为[ 255-R, 255-G, 255-B ],成功将颜色[ R, G, B ]反相。这就是W存在的便利之处。

实践部分

在C#中用色彩矩阵计算,要比依次取图片每一个像素进行计算要快很多。C#中无法将double型隐式转换为float型,所以色彩矩阵中出现小数时,要在后面加一个”f”。

全部代码如下:

using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.Windows.Forms;

namespace MatrixofColor
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            using (OpenFileDialog openFileDlg = new OpenFileDialog() { Filter = "Images|*.jpg" })
            {
                if (openFileDlg.ShowDialog() == DialogResult.OK)
                {
                    picOriginal.Image = Image.FromFile(openFileDlg.FileName);
                }
            }
        }

        private void button2_Click(object sender, EventArgs e)
        {
            picConvert.Image = TransColor((Bitmap)picOriginal.Image);
        }

        private Bitmap TransColor(Bitmap original)
        {
            Bitmap newBitmap = new Bitmap(original.Width, original.Height);//建立空白bitmap
            Graphics g = Graphics.FromImage(newBitmap);

            ColorMatrix CM0 = new ColorMatrix(
                new float[][]
                {
                    new float[]{1,0,0,0,0 },
                    new float[]{0,1,0,0,0 },
                    new float[]{0,0,1,0,0 },
                    new float[]{0,0,0,.5f,0 },
                    new float[]{0,0,0,0,1 }
                });//建立色彩矩阵

            ImageAttributes attributes = new ImageAttributes();
            attributes.SetColorMatrix(CM0);
            g.DrawImage(original, new Rectangle(0, 0, original.Width, original.Height), 0, 0, original.Width, original.Height, GraphicsUnit.Pixel, attributes);
            g.Dispose();
            return newBitmap;
        }
    }
}
C#

以上代码实现的功能为,点击按钮1选择一张图片打开并显示,点击按钮2,显示运算结果。以下是常见的几个色彩矩阵。

透明度

ColorMatrix CM0 = new ColorMatrix(
    new float[][]
    {
        new float[]{1,0,0,0,0 },
        new float[]{0,1,0,0,0 },
        new float[]{0,0,1,0,0 },
        new float[]{0,0,0,.5f,0 },
        new float[]{0,0,0,0,1 }
    });
C#
降低透明度

灰度

ColorMatrix CM1 = new ColorMatrix(
    new float[][]
    {
        new float[]{.30f,.30f,.30f,0,0 },
        new float[]{.59f,.59f,.59f,0,0 },
        new float[]{.11f,.11f,.11f,0,0 },
        new float[]{0,0,0,1,0 },
        new float[]{0,0,0,0,1 }
    });
C#
灰度图

反相

ColorMatrix CM2 = new ColorMatrix(
    new float[][]
    {
        new float[]{-1,0,0,0,0 },
        new float[]{0,-1,0,0,0 },
        new float[]{0,0,-1,0,0 },
        new float[]{0,0,0,1,0 },
        new float[]{1,1,1,0,1 }
    });
C#
图片反相

亮度

ColorMatrix CM3 = new ColorMatrix(
    new float[][]
    {
        new float[]{1,0,0,0,0 },
        new float[]{0,1,0,0,0 },
        new float[]{0,0,1,0,0 },
        new float[]{0,0,0,1,0 },
        new float[]{.3f,.3f,.3f,0,1 }
    });
C#
调节亮度

单通道

ColorMatrix CM4 = new ColorMatrix(
    new float[][]
    {
        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,1 }
    });
C#
R通道

饱和度

float cmt5 = .3f;//0-1降饱和度,大于1升饱和度
ColorMatrix CM5 = new ColorMatrix(
    new float[][]
    {
        new float[]{.213f+.787f*cmt5,.213f*(1-cmt5),.213f*(1-cmt5),0,0 },
        new float[]{.715f*(1-cmt5),.715f+.285f*cmt5,.715f*(1-cmt5),0,0 },
        new float[]{.072f*(1-cmt5),.072f*(1-cmt5),.072f+.928f*cmt5,0,0 },
        new float[]{0,0,0,1,0 },
        new float[]{0,0,0,0,1 }
    });
C#
降低饱和度
增加饱和度

对比度

float cmt6 = 3;//0-1降对比度,1-10升对比度
ColorMatrix CM6 = new ColorMatrix(
    new float[][]
    {
        new float[]{cmt6,0,0,0,0 },
        new float[]{0,cmt6,0,0,0 },
        new float[]{0,0,cmt6,0,0 },
        new float[]{0,0,0,1,0 },
        new float[]{.5f*(1-cmt6),.5f*(1-cmt6),.5f*(1-cmt6),0,1 }
    });
C#
降低对比度
增加对比度

升温滤镜

ColorMatrix CM7 = new ColorMatrix(
    new float[][]
    {
        new float[]{1,0,0,0,0 },
        new float[]{0,1,0,0,0 },
        new float[]{0,0,1,0,0 },
        new float[]{0,0,0,1,0 },
        new float[]{.1f,.05f,-.03f,0,1 }
    });
C#
升温滤镜 – 橙

冷却滤镜

ColorMatrix CM8 = new ColorMatrix(
    new float[][]
    {
        new float[]{1,0,0,0,0 },
        new float[]{0,1,0,0,0 },
        new float[]{0,0,1,0,0 },
        new float[]{0,0,0,1,0 },
        new float[]{-.03f,.05f,.1f,0,1 }
    });
C#
冷却滤镜 – 蓝

复古滤镜

ColorMatrix CM9 = new ColorMatrix(
    new float[][]
    {
        new float[]{.393f,.349f,.272f,0,0 },
        new float[]{.769f,.686f,.534f,0,0 },
        new float[]{.189f,.168f,.131f,0,0 },
        new float[]{0,0,0,1,0 },
        new float[]{0,0,0,0,1 }
    });
C#
老照片滤镜

除以上示例外,还可以通过修改参数,形成不同的照片滤镜,甚至引入其他变量或函数达到复杂的计算目的。