• 13、在 uwp应用中,给图片添加高斯模糊滤镜效果(一)


      如果在应用中,如果想要给app 添加模糊滤镜,可能第一想到的是第三方类库,比如 Win2dlumia Imaging SDK 、WriteableBitmapEx,不可否认,这些类库功能强大,效果也挺多的。不足就是增加了生成包尺寸,由于增加了相应 dll  的引用,在 app运行时也会增加内存占用。如果只使用一种滤镜效果,建议直接添加几十行代码自己实现,这样开发、维护成本都会很少。并且由于 .net native 使得 uwp 的运算速度与 C++算法的运行速度没有差别了。

      这里只讨论高斯模糊滤镜,感觉这个滤镜在应用中适当的运用,会让页面形象生动,比如图片背景使用这个滤镜,会有一些磨砂玻璃的效果。针对高斯模糊的算法网上也有很多,这里使用戴震军

    大哥的曾经移植过的 windows phone7 的算法。这里主要解决的就是 silverlight(wpf)中 WriteableBitmap 中图片数据 int[] 数组到 windows runtime(uwp)中 WriteableBitmap中 byte[] 的转换。

      Demo 的运行效果:

    1)当不运用滤镜时 level 为 0(范围 0-40):

    2)当 level 为 2时:

    3)当 level 为 10时:

    4)当 level 为 40时:

    1、颜色值的分析:

    1) 在 silverlight 或者 uwp 中,指定一个字体的前景色为绿色半透明:

    <TextBlock Text="节约用电" FontSize="30" Foreground="#8800FF00"/>

    显示为:

    则在前景色中颜色的设置信息:

    2) 运算符概述:

    << 和 >> 为位移运算符,比如对于二进制来说  0010 << 2 (右移2位)的计算结果就是 1000,等于把十进制的 2 变成了 8

    | 运算符为 “或”运算,比如对于二进制来说 0100 | 0010 的计算结果  0110,等于把十进制  4|2 变为了 6 

    2、分析 Silverlight 中 WriteableBitmap 对象中的 int[] 数组

    对于 silverlight、wpf 平台上图片(jpg、png)的编辑 WriteablBitmap 对象中使用的 int[] 数组,每个 int 值同时包含了 A(alpha)、R(red)、G(green)、B(Blue)四个信息值,每个值的范围是 0~255。

    1)在 silverlight中,获取图片像素数据( int[] 数组 ):

    // 100像素宽,100像素高
    WriteableBitmap bitmap = new WriteableBitmap(100, 100);
    
    // 获取表示位图 2D 纹理的数组。
    int[] data = bitmap.Pixels;

    当然,这个 int[] 数组是一维数组,线性排列的。

    3、分析 UWP 中 WriteableBitmap 类中的 byte[] 数组

    对于 Windows Runtime (uwp) 中 WriteableBitmap 类来说,存储的图片数据为  byte[] 数组。

    例如,获取一张图片中的像素数据(msdn 文档链接,WriteableBitmap):(WriteableBitmap 的图像源数据是基础像素缓冲区。WriteableBitmap.PixelBuffer不能直接写入)

    using (IRandomAccessStream fileStream = await file.OpenAsync(Windows.Storage.FileAccessMode.Read)) 
    {
        BitmapDecoder decoder = await BitmapDecoder.CreateAsync(fileStream); 
        // Scale image to appropriate size 
        BitmapTransform transform = new BitmapTransform() {  
            ScaledWidth = Convert.ToUInt32(Scenario4WriteableBitmap.PixelWidth), 
            ScaledHeight = Convert.ToUInt32(Scenario4WriteableBitmap.PixelHeight)
        }; 
        PixelDataProvider pixelData = await decoder.GetPixelDataAsync( 
            BitmapPixelFormat.Bgra8, // WriteableBitmap 使用的是 BGRA 格式 
            BitmapAlphaMode.Straight, 
            transform, 
            ExifOrientationMode.IgnoreExifOrientation, // This sample ignores Exif orientation 
            ColorManagementMode.DoNotColorManage
        ); 
     
        // 包含图片的解码数据。在呈现之前可以被修改
        byte[] sourcePixels = pixelData.DetachPixelData(); 
     
        // 打开一个 image 的流,并且复制到 WriteableBitmap 的 pixel 缓冲区中
        using (Stream stream = writeableBitmap.PixelBuffer.AsStream()) 
        { 
            await stream.WriteAsync(sourcePixels, 0, sourcePixels.Length); 
        }                     
    }

    同样,对于 1)中描述 100X100 的图片的数据存储,因为一个 int 可以包含 4个 byte位的信息,所以对于  100x100 的 int[] 数组值,也就是 10000个 int值组成。当转换为 byte[] 数组值时,数组长度扩大四倍,变为 100x100x4 = 40000个 byte值。并且 byte 值是按照 B、G、R、A 的顺序排列:

    4、int[] 与 byte[] 的互相转换

    因为戴震军大哥移植的滤镜算法为 windows phone7 的工程,所以图片数据的计算是采用 silverlight中 int[] 数组的计算方式,所以这里我只做了两件事,一个是把  uwp 工程中源 WriteableBitmap 对象中 byte[] 数组的存储方式,转换为 silverlight 中的 int[] 数组的存储方式,然后对图片数据进行添加滤镜算法的处理,当处理完成时,再把处理结果 int[] 数组的数据再转换为 uwp 中 WriteableBitmap 的 byte[] 数组的存储方式:

    1)uwp 中 byte[] 数组,转换为 silverlight 的 int[] 数组:

     // 把 uwp 的 WriteableBitmap 对象的 PixelBuffer属性(IBuffer)转换为  byte[] 数组
     byte[] colorBytes = BufferToBytes(image.PixelBuffer);
    
     // 转换为 silverlight 的 int[] 数组时,长度为 byte[] 数组的四分之一
     colorArray = new int[colorBytes.Length / 4];
    
     int a,r, g, b;
     int i = 0; // 通过 i 自加,来遍历 byte[] 整个数组
    
     // 通过图片的宽、高,分别设置 int[] 数组中每个像素的 ARGB 信息
     for (int y = 0; y < height; y++)
     {
         for (int x = 0; x < width; x++)
         {
             // int[] 数组的索引
             int index = y * width + x;
             b = colorBytes[i++]; // Blue
             g = colorBytes[i++]; // Green
             r = colorBytes[i++]; // Red
             a = colorBytes[i++]; // Alpha
             colorArray[index] = (a << 24) | (r << 16) | (g << 8) | b;  // 4个 byte值存储为一个 int 值
         }
     }

    2)当把上面的图片数据,添加高斯模糊滤镜效果之后,把 silverlight 中 int[] 数组的计算结果,转换为 uwp 的 byte[] 数组:

    // 拷贝
    WriteableBitmap new_bitmap = await Utility.BitmapClone(wb);
    
    // 添加高斯滤镜效果
    MyImage mi = new MyImage(new_bitmap);
    GaussianBlurFilter filter = new GaussianBlurFilter();
    filter.Sigma = level; 
    filter.process(mi);
    
    // 图片添加完滤镜的 int[] 数组
    int[] array = mi.colorArray;
    
    // byte[] 数组的长度是 int[] 数组的 4倍
    byte[] result = new byte[array.Length * 4];
    
    // 通过自加,来遍历 byte[] 数组中的值
    int j = 0;
    for (int i = 0; i < array.Length; i++)
    {
        // 同时把 int 值中 a、r、g、b 的排列方式,转换为 byte数组中 b、g、r、a 的存储方式 
        result[j++] = (byte)(array[i]);       // Blue
        result[j++] = (byte)(array[i] >> 8);  // Green
        result[j++] = (byte)(array[i] >> 16); // Red
        result[j++] = (byte)(array[i] >> 24); // Alpha
    }
    
    // Open a stream to copy the image contents to the WriteableBitmap's pixel buffer 
    using (Stream stream = new_bitmap.PixelBuffer.AsStream())
    {
        await stream.WriteAsync(result, 0, result.Length);
    }
    
    img.Source = new_bitmap;// 把最终 WriteableBitmap 对象赋值给 Image 控件

    5、代码中会用到的自定义帮助类:

    namespace BlurEffect_demo
    {
        class Utility
        {
            /// <summary>
            /// WriteableBitmap 的拷贝
            /// </summary>
            /// <param name="bitmap"></param>
            /// <returns></returns>
            public static async Task<WriteableBitmap> BitmapClone(WriteableBitmap bitmap)
            {
                WriteableBitmap result = new WriteableBitmap(bitmap.PixelWidth, bitmap.PixelHeight);
    
                byte[] sourcePixels = Get_WriteableBitmap_bytes(bitmap);
    
                //byte[] resultPixels = new byte[sourcePixels.Length];
    
                //sourcePixels.CopyTo(resultPixels, 0);
    
                using (Stream resultStream = result.PixelBuffer.AsStream())
                {
                    await resultStream.WriteAsync(sourcePixels, 0, sourcePixels.Length);
                }
                return result;
            }
    
            /// <summary>
            /// 获取 WriteableBitmap 对象中的 byte[] 数组数据
            /// </summary>
            public static byte[] Get_WriteableBitmap_bytes(WriteableBitmap bitmap)
            {
                // 获取对直接缓冲区的访问,WriteableBitmap 的每个像素都写入直接缓冲区。
                IBuffer bitmapBuffer = bitmap.PixelBuffer;
    
                //byte[] sourcePixels = new byte[bitmapBuffer.Length];
                //Windows.Security.Cryptography.CryptographicBuffer.CopyToByteArray(bitmapBuffer, out sourcePixels);
                //bitmapBuffer.CopyTo(sourcePixels);
    
                using (var dataReader = DataReader.FromBuffer(bitmapBuffer))
                {
                    var bytes = new byte[bitmapBuffer.Capacity];
                    dataReader.ReadBytes(bytes);
                    return bytes;
                }
            }
        }
    }

    6、高斯滤镜算法这里就不列出来了,具体可以参考 demo 工程

    工程地址:link

    参考阅读:

    1)戴震军 :

    blog : http://www.cnblogs.com/daizhj

    github : https://github.com/daizhenjun/ImageFilterForWindowsPhone

    2)WriteableBitmap.PixelBuffer property :https://msdn.microsoft.com/en-us/library/windows/apps/windows.ui.xaml.media.imaging.writeablebitmap.pixelbuffer.aspx

    3)WriteableBitmap class : https://msdn.microsoft.com/en-us/library/windows/apps/windows.ui.xaml.media.imaging.writeablebitmap.aspx

    4)WriteableBitmapEx : http://writeablebitmapex.codeplex.com/releases/view/612952

    5)Getting Pixels of an Element(WriteableBitmap) // https://social.msdn.microsoft.com/Forums/en-US/39b3c702-caed-47e4-b7d3-b51d75cbca9b/getting-pixels-of-an-element-writeablebitmap?forum=winappswithcsharp

    6) Windows 8 WriteableBitmap Pixel Arrays in C# and C++ : http://www.tuicool.com/articles/fQNvUz

    7) 各种流转换 : http://www.cnblogs.com/hebeiDGL/p/3428743.html

    8) XAML images sample : https://code.msdn.microsoft.com/windowsapps/0f5d56ae-5e57-48e1-9cd9-993115b027b9/

    9) 重新想象 Windows 8 Store Apps (29) - 图片处理 : http://www.cnblogs.com/webabcd/archive/2013/05/27/3101069.html

  • 相关阅读:
    NOI 2019 网络同步赛 游记
    洛谷 P3695 CYaRon!语 题解 【模拟】【字符串】
    洛谷 P2482 loj #2885 [SDOI2010]猪国杀 题解【模拟】【贪心】【搜索】
    Spring MVC @ResponseBody返回中文字符串乱码问题
    Hibernate4中使用getCurrentSession报Could not obtain transaction-synchronized Session for current thread
    @Value取不到值引出的spring的2种配置文件applicationContext.xml和xxx-servlet.xml
    @RestController注解下返回到jsp视图页面
    Mysql引起的spring事务失效
    Eclipse中启动tomcat报错:A child container failed during start
    xshell不能输入中文,显示为??
  • 原文地址:https://www.cnblogs.com/hebeiDGL/p/5427746.html
Copyright © 2020-2023  润新知