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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM