在今天的吹牛节目开始之前,先交代一件事:
关于玩WP 8.1开发所使用的VS版本问题。对版本的要求是2013的Update2,这是最低要求,只要是这个版本或以上都可以,而update3,update4,update5是不是必须更新呢?不是的,VS的update是可选的,而且每个update都会累积,所以,update越多,安装包的体积越大。因此,WP开发我们只需update2就行了,我用的也是u2。如果你觉得MSDN原版不好下,可以从下面的地址下,我已经把相关的.iso上传到115。这里面是旗舰版。
开发工具
115网盘礼包码:5lbblgv09y6k
http://115.com/lb/5lbblgv09y6k
如果你还没有安装VS,我这里分享一个小技巧。就是在安装时,不要勾选Windows Phone 8.0 SDK,安装后就没有8.0的模拟器和镜像,也不能开发8.0的应用,只能开发8.1的,而且只能用真机调试。
安装后,你再下载我上面分享的另一个.iso——windowsphone81sdkupdate1.iso,这里不仅包含8.1的SDK,还带了8.1 Update 1的模拟器,即有“小娜”的版本。
如此一来,你只安装了最新的8.1模拟器,而没有8.0的模拟器了。当然如果你要开发8.0的应用,就要安装Windows Phone 8.0 SDK,我上面说的当你只开发8.1应用的时候用的。
=======================================================
好了,吹牛节目正式开始,由于剧组缺钱,本节目的主持人只有老周一人,没有后台工作人员,有点像关老爷单刀赴会的感觉。今天咱们吹一下如何进行图像处理,比如怎么把图片变成灰度、反色、黑色等。
老周不是专业搞图像的,老周是打酱油专业毕业的,所以老周不懂相关的知识,但不要紧,只要我们好好利用现有的API,也可以对图像进行一些非专业性处理。我们可以不明白图像文件的具体结构,只要懂得如何改写像素数据就行了。
要弄清楚像素数据是如何排列的,首先要简单理解一下常见的像素格式是啥样的。这些格式由Windows.Graphics.Imaging.BitmapPixelFormat枚举定义,Unknown成员不管它,我们只关心另外三个。
Bgra8 ——最后面的8表示8位,即每个颜色值占8位,也就是1字符,bgra,第一个字节表示B(蓝)的值,第二个字节表示G(绿)的值,第三个字节表示R(红)的值,第四个字节表示A(不透明度)的值。举个例子,假设有一个图片里面有四个像素,宽为2,高为2,即两行两列。结构如下表所示:
R = 100 G = 0 B = 200 A = 255 | R = 255 G = 50 B = 32 A = 255 |
R = 12 G = 30 B = 90 A = 255 | R = 120 G = 60 B = 75 A = 255 |
像素数据是一个字符数组,比如上面的2*2的图像,转换为像素数据为:
{ 200, 0, 100, 255, 32, 50, 255, 255 …… 75, 64, 120, 255 }
因为像素格式为bgra,所以排第一的是B的值,接着是G,然后是R,最后是A,也就是说,每四个字节表示一个像素点,上述例子中,图像有四个像素点,每个点由4个字节表示,所以整个图像的像素数据共4*4=16字节。
字节的排列就是按像素点顺序排放的,从左到右,从上到下,先排完第一行,再排第二行,第三行……一直排到最后一个像素点。
Rgba8 ……和上面的一样,每个颜色值占一个字节,只是排列时顺序不同,rgba是最常用的,因为这样排列图像不会偏色,即第一个字节是R,第二个字节是G,第三个字节是B,最后是A。这个RGB的通道不同顺序产生的不同效果,大家可以在PS里面查看,PS中“图层”窗口中有个“通道”面板,那里可以看到各个通道的效果。
Rgba16 ——和上面一样,排列顺序也是R -> G -> B -> A,但是,rgba16中每个色值为16位,即占用2个字节。在像素数据中,第一个和第二个字节共同表示R值;第三个,第四个字节共同表示G的值;第五个第六个字节为B的值,第七、八个字节表示A。这个模式很少用,因为不太好套公式,呵呵。
理论知识还是抽象的,咱们来干点实事吧。下面老周给大家演示两个比较简单的处理——灰度 和 反色。
图像处理的各种方法大家可以查书,可以网上查,反正都有固定公式的。
1、灰度处理。
这里我选用平均法,即把每个像素点中R,G,B三个值进行相加,然后除以3,再把这个平均后的值替换原来的R,G,B值,A是不透明度,一般可以不管它,生成新的像素数据值时A值就用原图的A值就行了,主要是针对RGB进行计算。代码如下:
/// <summary> /// 灰度处理 /// </summary> private byte[] GrayScale(byte[] rgbaBuff) { byte[] resbytes = new byte[rgbaBuff.Length]; for (int i = 0; i < rgbaBuff.Length; i += 4) { byte r = rgbaBuff[i]; byte g = rgbaBuff[i + 1]; byte b = rgbaBuff[i + 2]; byte a = rgbaBuff[i + 3]; // 使用平均法 byte ev = Convert.ToByte((Convert.ToDouble(r) + Convert.ToDouble(g) + Convert.ToDouble(b)) / 3d); // 生成新的像素值 resbytes[i] = resbytes[i + 1] = resbytes[i + 2] = ev; resbytes[i + 3] = a; } return resbytes; }
我的示例都是用rgba8格式的,每个像素点需要4个字节,所以在for循环中,i的境量不是i++,而是i += 4,即每次循环跳4个字节,这样才能保证每一轮循环都访问一个像素点。
2、反色。
反色最简单,分别用255去減RGB三个值就行了,即
newR = 255 - oldR, newG= 255 - oldG, newB= 255-oldB
/// <summary> /// 反色 /// </summary> private byte[] InvertPixels(byte[] rgbaBuff) { byte[] res = new byte[rgbaBuff.Length]; for (int i = 0; i < rgbaBuff.Length; i += 4) { byte r = rgbaBuff[i]; byte g = rgbaBuff[i + 1]; byte b = rgbaBuff[i + 2]; byte a = rgbaBuff[i + 3]; // 反色就是用255分别减去R,G,B的值 res[i] = (byte)(255 - r); res[i + 1] = (byte)(255 - g); res[i + 2] = (byte)(255 - b); res[i + 3] = a; } return res; }
3、获取源图像的像素数据。
要得到源图像的像素数据,需要先对图像进行解码,这个应该很多观众都会,就是用Windows.Graphics.Imaging.BitmapDecoder类来解码。
我建议大家通过帧来获取单帧图像的像素数据,一般来说,静态图片只有一帧,所以调用BitmapDecoder实例的GetFrameAsync(0)方法就能得到第一帧的图像,由BitmapFrame类封装,再通过BitmapFrame实例的GetPixelDataAsync方法得到一个PixelDataProvider实例,再访问PixelDataProvider实例的DetachPixelData方法就能得到表示像素数据的字节数组。
enum OperType { Gray, Invert }; private async Task<BitmapSource> ImageToProcAsync(IRandomAccessStream inputStream, OperType opt) { BitmapDecoder decoder = await BitmapDecoder.CreateAsync(BitmapDecoder.JpegDecoderId, inputStream); // 获取第一帧 BitmapFrame frame = await decoder.GetFrameAsync(0); // 获取像素数据 PixelDataProvider pixprd = await frame.GetPixelDataAsync(BitmapPixelFormat.Rgba8, BitmapAlphaMode.Straight, new BitmapTransform(), ExifOrientationMode.IgnoreExifOrientation, ColorManagementMode.DoNotColorManage); byte[] data = pixprd.DetachPixelData(); // 处理 byte[] returnData = null; if (opt == OperType.Gray) { returnData = GrayScale(data); } else { returnData = InvertPixels(data); } // 创建新的位图对象 WriteableBitmap wb = new WriteableBitmap((int)frame.PixelWidth, (int)frame.PixelHeight); // 复制数据 returnData.CopyTo(wb.PixelBuffer); return wb; }
OperType是自定义枚举,Gray表示灰度处理,Invert表示反色处理。
最后,请各位观众一起看看效果吧。
各位观众,感谢您收看由火星电视台转播的老周吹牛节目,本集示例的源代码稍后给出下载地址。Thank you.
示例下载: http://files.cnblogs.com/tcjiaan/imgProcSampleApp.zip
欢迎下个世纪同一时间,准时收看老周吹牛特别节目。本节目由火星移动有限公司独家赞助。
88。