有时候可能想在Flash中描述灰度图像或者将彩色图像显示成部分灰色,如果在图像处理软件中处理后再输出到Flash中就显得太麻烦而且应用不够灵活。掌握了灰度图像的处理方法,可以在Flash中将彩色图片的部分区域或整个图像以灰度显示。原理其实很简单,就是将图像的每个像素设置成 R = G = B 。但对图像的每个像素进行操作开销是巨大的,想想一个图片可能几十万个像素,每个像素轮流计算一遍,如果还要以动画的形式渐变,那计算量就更可观了!所以问题的关键就是如何高效的实现这个过程。
将彩色图像变成灰度图像通常有三种RGB表示形式。
一、最大值法:即分别设置每个像素的 R = G = B = Max(R,G,B) 。
二、平均值法:即分别设置每个像素的 R = G = B = (R+G+B)/3 。
三、加权平均值法:即分别设置每个像素的 R = G = B = ωrR + ωgG + ωbB ,其中 ωr 、ωg 、ωb分别为R G B分量的权值 。ωr 、ωg 、ωb 分别取不同的值可形成不同的灰度效果,但提示一点,人眼对R G B 的敏感程度不同,对绿色的敏感度最强、红色次之、蓝色最弱。所以一般取值 ωr > ωg > ωb 较为合适。其中ωr = 30% ωg = 59% ωb = 11% 时最感觉最舒适。
而以上三种方法当中,第一种处理结果图像的亮度稍微会大一点,第三种加权平均值法看起来会最舒适!
了解了基本方法之后再使用ActionSript对以上的方法进行实现。查看帮助文档中的flash.diaplay.BitmapData类我们可以看到其中定义的很多方法,通常可以通过getPixel、setPixel等相关方法对像素进行操作,即遍历每一个像素分别进行读取-操作-写入;另一种方法可以通过getVector、setVetor方法,getVector(rect:Rectangle):Vector.<uint>
答案是有的!
那就是ColorMatrixFilter滤镜,flash player 提供这个类可以对每个像素进行操作,它避免了我们去分解和组合R G B分量,其内层操作肯定高效得多,并且它使用非常简单!
要用到ColorMatrixFilter滤镜,我们只需设置ColorMatrixFilter类的一个属性即可。 matrix属性 一个4*5数组。 帮住文档中给出了它的计算方法:
redResult = (a[0] * srcR) + (a[1] * srcG) + (a[2] * srcB) + (a[3] * srcA) + a[4]
greenResult = (a[5] * srcR) + (a[6] * srcG) + (a[7] * srcB) + (a[8] * srcA) + a[9]
blueResult = (a[10] * srcR) + (a[11] * srcG) + (a[12] * srcB) + (a[13] * srcA) + a[14]
alphaResult = (a[15] * srcR) + (a[16] * srcG) + (a[17] * srcB) + (a[18] * srcA) + a[19]
ColorMatrixFilter颜色矩阵滤镜将每个源像素分离成它的红色、绿色、蓝色和 Alpha 成分,分别以 srcR、srcG、srcB 和 srcA 表示。若要计算四个通道中每个通道的结果,可将图像中每个像素的值乘以转换矩阵中的值。(可选)可以将偏移量(介于 -255 至 255 之间)添加到每个结果(矩阵的每行中的第五项)中。 滤镜将各颜色成分重新组合为单一像素,并写出结果。如何设置这个属性呢?
var matrix:Array = new Array();
matrix = matrix.concat([1, 0, 0, 0, 0]); // red
matrix = matrix.concat([0, 1, 0, 0, 0]); // green
matrix = matrix.concat([0, 0, 1, 0, 0]); // blue
matrix = matrix.concat([0, 0, 0, 1, 0]); // alpha
var filter:ColorMatrixFilter = new ColorMatrixFilter(matrix);然后可以对BitmapData对象通过applyFilter()应用滤镜。
接下来将给出上面三种方法的实现:
一、最大值法:(对每个像素分别操作)
function MaxGray(source:BitmapData,rect:Rectangle=null):void
{
if(rect==null) rect = source.rect ;
var vector:Vector.<uint> = source.getVector(rect);
var r:uint,g:uint,b:uint,i:int;
for each(var color:uint in vector)
{
r= Math.max((color&0xff0000)>>16,(color&0xff00)>>8,color&0xff);
color = 0xff000000 ;
color |= r;
color |= (r<<8) ;
color |= (r<<16) ;
vector[i++] = color ;
}
source.setVector(rect,vector);
trace(getTimer());
}
以上的计算同样可以应用平均值法和加权平均法。将r= Math.max((color&0xff0000)>>16,(color&0xff00)>>8,color&0xff)改动一下即可:
//r = (((color&0xff0000)>>16)+((color&0xff00)>>8)+(color&0xff))*0.33333;//平均值法
//r = (((color&0xff0000)>>16)*0.3+((color&0xff00)>>8)*0.59+(color&0xff)*0.11);//加权平均值法
//r = r>128?255:0 ;// 阈值 -------//将这条语句放在以上三种赋值计算的任何一种之后即将图像二值化,得到photoShop中的“阈值”效果,(128只是其中一种选择,可以将其改为0-255之间的任何值,但太小或太大可能导致产生的结果为全白或全黑)。
二、平均值法:(使用flash player 自带滤镜)
function AveGray1(source:BitmapData,rect:Rectangle=null):void
{var average:Number = 0.333333 ;
var matrix:Array = new Array();
matrix = matrix.concat([average,average,average,0,0]);
matrix = matrix.concat([average,average,average,0,0]);
matrix = matrix.concat([average,average,average,0,0]);
matrix = matrix.concat([0,0,0,0,0]);
var filter:ColorMatrixFilter = new ColorMatrixFilter(matrix);
if(rect==null) rect = source.rect ;source.applyFilter(source,rect,new Point(),filter);
trace(getTimer());
}
三、加权平均值法:(与平均值法类似)
function WeiGray(source:BitmapData,rect:Rectangle=null):void
{
var matrix:Array = new Array();
matrix = matrix.concat([0.3,0.59,0.11,0,0]);
matrix = matrix.concat([0.3,0.59,0.11,0,0]);
matrix = matrix.concat([0.3,0.59,0.11,0,0]);
matrix = matrix.concat([0,0,0,0,0]);
var filter:ColorMatrixFilter = new ColorMatrixFilter(matrix);
if(rect==null) rect = source.rect ;
source.applyFilter(source,rect,new Point(),filter);
trace(getTimer());
}
通过 trace(getTimer()) ,你可以比较各种方法的执行效率,我的测试结果:780*670像素的图片,第一种方法用时达到450毫秒,第二种、第三种基本可以保持在150毫秒以内。我的机器配置比较低,这里只是想说明两种方法的执行效率的差别!
所以在处理图像时,应尽可能使用flash player内置的方法,但必须提醒,即使是内置的方法,如果对图像连续处理,其资源消耗也是巨大的。
应用ColorMatrixFilter、ColorTransform类可以创造出很炫的效果,但就存在一个缺点,我想不用说了吧!