Android 中比較“高效”的映像模糊處理演算法

來源:互聯網
上載者:User

 

本文假設讀者擁有基礎的影像處理概念。

這是今天剛剛完成的一個方法,之前不停地在網上找有關 Android 的映像模糊處理代碼。
期間找到了倒影、縮放等參考代碼,卻無一文章對模糊處理有過提及。
最多也就是提到使用 BlurMaskFilter 來進行模糊處理。
為了這個害人的文章,我整整浪費了一下午時間,最後發現它只能用於對 Paint 的邊緣進行處理。
而我們要處理的是整幅映像,所以這完完全全是一個騙人的說法。

由於先前在 VB.NET 上處理過映像,也寫過相關代碼,當時 VB.NET 代碼分兩種形式:
1、死算:即將每個像素點的顏色值與周圍 8 點計算平均值,每計算一次 SetPixel() 一次,這樣做非常慢;
2、讀取整幅映像的記憶體資料,也就是已三原色為單位了,資料量(顏色數組)比上一種方法大一倍。
這樣的做法可以將所有顏色計算好以後再重新寫入記憶體,肯定比 SetPixel() 更高效。
不過由於也並非通過底層函數對記憶體操作,因此雖然有提速,效果和 C++ 相比卻是小巫見大巫了。

下面說正事,其實 JAVA 和 .NET 很類似的(據說 .NET 的架構師就是 SUN 過去的呢)。
當然也無法直接對記憶體操作,當然也無法像 .NET 這樣間接對記憶體操作,很抓狂啊!
要知道,我是死也不會用第一種方法死算的!
那麼怎麼辦?我仍然希望從第二種方法入手,雖然慢了一些,但還是能忍受的。
好在 Android 提供了 setPixels() 方法,看清楚,Pixel 後面有 s 的!
這個方法其實是 setPixel() 的“批量”版本,也就是說允許我們一下子填充多個連續的像素。
既然這樣,我們離類比第二點還差一半,那就是取得包含三原色的數組了,看代碼吧。

/* 設定圖片模糊 */

public static Bitmap SetBlur(Bitmap bmpSource, int Blur)  //源位元影像,模糊強度
{
 int pixels[] = new int[bmpSource.getWidth() * bmpSource.getHeight()];  //顏色數組,一個像素對應一個元素
 int pixelsRawSource[] = new int[bmpSource.getWidth() * bmpSource.getHeight() * 3];  //三原色數組,作為中繼資料,在每一層模糊強度的時候不可更改
 int pixelsRawNew[] = new int[bmpSource.getWidth() * bmpSource.getHeight() * 3];  //三原色數組,接受計算過的三原色值
 bmpSource.getPixels(pixels, 0, bmpSource.getWidth(), 0, 0, bmpSource.getWidth(), bmpSource.getHeight());  //擷取像素點

 //模糊強度,每迴圈一次強度增加一次
 for (int k = 1; k <= Blur; k++)
 {
  //從圖片中擷取每個像素三原色的值
  for (int i = 0; i < pixels.length; i++)
  {
   pixelsRawSource[i * 3 + 0] = Color.red(pixels[i]);
   pixelsRawSource[i * 3 + 1] = Color.green(pixels[i]);
   pixelsRawSource[i * 3 + 2] = Color.blue(pixels[i]);
  }

  //取每個點上下左右點的平均值作自己的值
  int CurrentPixel = bmpSource.getWidth() * 3 + 3; // 當前處理的像素點,從點(2,2)開始
  for (int i = 0; i < bmpSource.getHeight() - 3; i++) // 高度迴圈
  {
   for (int j = 0; j < bmpSource.getWidth() * 3; j++) // 寬度迴圈
   {
    CurrentPixel += 1;
    // 取上下左右,取平均值
    int sumColor = 0; // 顏色和
    sumColor = pixelsRawSource[CurrentPixel - bmpSource.getWidth() * 3]; // 上一點
    sumColor = sumColor + pixelsRawSource[CurrentPixel - 3]; // 左一點
    sumColor = sumColor + pixelsRawSource[CurrentPixel + 3]; // 右一點
    sumColor = sumColor + pixelsRawSource[CurrentPixel + bmpSource.getWidth() * 3]; // 下一點
    pixelsRawNew[CurrentPixel] = Math.round(sumColor / 4); // 設定像素點
   }
  }

  //將新三原色組合成像素顏色
  for (int i = 0; i < pixels.length; i++)
  {
   pixels[i] = Color.rgb(pixelsRawNew[i * 3 + 0], pixelsRawNew[i * 3 + 1], pixelsRawNew[i * 3 + 2]);
  }
 }

 //應用到映像
 Bitmap bmpReturn = Bitmap.createBitmap(bmpSource.getWidth(), bmpSource.getHeight(), Config.ARGB_8888);
 bmpReturn.setPixels(pixels, 0, bmpSource.getWidth(), 0, 0, bmpSource.getWidth(), bmpSource.getHeight());  //必須建立位元影像然後填充,不能直接填充源映像,否則記憶體報錯

 return bmpReturn;
}

 

代碼其實很簡單,總體而言就是分為三步:
1、取得該位元影像所有的位元組對應的資料;
2、計算每個像素的模糊平均值;
3、利用顏色數組重新繪圖。

這就是跟 VB.NET 不同的地方了,我們在 VB.NET 中是改寫記憶體,而這裡是申請新的記憶體地區。
此外,還多了一個大迴圈,將上述三步進行多次迴圈,每次迴圈前將上一次計算好的平均顏色數組作為新的來源資料,這樣就能擷取更模糊的效果了。

要注意的是,此方法對裝置效能要求較高,在 1 GHz 處理器上(三星 Galaxy S i9000)模糊強度為 8 時處理 800 × 400 的映像耗時約 20 秒。
HTC Magic 512 MHz 處理器中約 40 秒,可以說基本保持在一個固定比例中。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.