近日用GDI+做一个图片处理的小程序,需要实现调整图片的亮度、对比度、饱和度和Gamma值的功能。一开始试验逐个像素的处理,可是对于一副2592*1944的普通数码照片来说处理一副图像竟然将近20秒,无法忍受,需要寻找效率更高的方法。研究了下MSDN,发现.net给我们提供了一个更为方便的方法,就是使用颜色矩阵(ColorMatrix),或者称为颜色变换矩阵,位于System.Drawing.Imaging命名空间下。.net为我们对图形图像的变换、调节等操作进行了优化,对于一副2592*1944的普通数码照片,要达到同样的效果大约不到逐个像素处理方法的1/10的时间即可完成。
问题来了。在运用颜色变换矩阵的时候有一点需要特别注意:定义的Image对象可以是来自于文件(FromFile方法),也可以是来自于数据流(FromStream方法,数据流可以是根据文件建立的文件数据流,也可以是内存中的数据流即MemoryStream),但是不可以是来自于PictureBox的Image属性,后者的像素格式不受Graphics类支持的。否则使用Graphics类的DrawImage方法生成新的图像时就会产生“内存不足”的错误。如果你的原始图像文件的像素格式也不受支持的话,即使是数据流来自于文件或者内存中也会产生这个问题。归根结底还是在Graphics类中操作了具有不被支持的像素格式的图像。使用PictureBox的Image属性新建一个Image对象是完全可以的,并且获取的也是当前PictureBox中的图像,一开始使用这个方法,Image总是来自于PictureBox,总是产生“内存不足”的错误,困扰了好大一会儿。
那怎样获取具有被Graphics类支持的像素格式的当前PictureBox中显示的图像的数据流呢?我是通过将PictureBox中显示的图像暂存在本地硬盘临时文件夹中,再用一个不显示的OpenFileDialog读取这个文件获取到的。当然一涉及到硬盘的I/O操作,性能肯定会降低。应该也可以在内存中进行相应的操作获取这个图像的数据流,那样应该会快一点,但是对系统的内存要求就稍微高一点,孰优孰劣,视具体应用情况而定吧。
解决了这个问题,接下来就是设计变换矩阵了,没什么好说的,基础知识就是线性代数中的矩阵运算,想快点就查MSDN吧。呵呵。
备案:亮度
float[][] matrix = {
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[] { v, v, v, 0, 1 }
};
对比度: float[][] matrix = {
new float[] { scale, 0, 0, 0, 0 },
new float[] { 0, scale, 0, 0, 0 },
new float[] { 0, 0, scale, 0, 0 },
new float[] { 0, 0, 0, 1, 0 },
new float[] { -offset, -offset, -offset, 0, 1 }
};
饱和度:
float[][] matrix = {
new float[] { red + v, red, red, 0, 0 },
new float[] { green, green + v, green, 0, 0},
new float[] { blue, blue, blue + v, 0, 0 },
new float[] { 0, 0, 0, 1, 0},
new float[] {0, 0, 0, 0, 1 }
};
Gamma值比较简单,设置一个参数就可以了。