1. 程式人生 > >圖片影象處理技術及開源圖片框架demo- Android(Picasso/Glide等)

圖片影象處理技術及開源圖片框架demo- Android(Picasso/Glide等)

  1972年11月的《花花公子》雜誌的封面人物名叫Lena Soderberg模特照片,成為數字影象處理和壓縮的標準格式,研究人員用它來測試自己的演算法,還常被用作數字視訊處理各種實驗及科學出版物的例圖。
  規避圖片OOM或省流量及快速上傳給後臺。圖片類社交App可選Fresco框架。

-- ImageView.ScaleType
Android ImageView的scaleType屬性與adjustViewBounds屬性- http://www.2cto.com/kf/201411/348601.html
[Android] ImageView.ScaleType設定圖解- http://blog.csdn.net/larryl2003/article/details/6919513
自定義ImageView控制元件 | 一個可以縮放, 平移的自定義ImageView控制元件- http://best.factj.com/sephiroth74/ImageViewZoom

> 圖片壓縮、處理,Gif,大圖
-- 圖片壓縮
  在 Android 中進行圖片壓縮是非常常見的開發場景,主要的壓縮方法有兩種:其一是質量壓縮,其二是下采樣壓縮
  如何提高 Android 的壓縮率,這裡需要提到兩個庫,一個是 mozilla/mozjpeg,另一個是 libjpeg-turbo,前者是一個來自 Mozilla 實驗室的 JPEG 影象編碼器專案,目標是在不降低影象質量且相容主流的解碼器的情況下,提供產品級的 JPEG 格式編碼器來提高壓縮率以減小 JPEG 檔案的大小,後者相當於是一個 libjpeg 的增強版,前者也是基於後者,在後者的基礎上進行了一些優化。
  第三方編碼解碼庫或者硬體編解碼庫,例如 libpng 和 libjpeg,libgif 等.
  libjpeg是廣泛使用的開源JPEG影象庫(

http://en.wikipedia.org/wiki/Libjpeg ),安卓也依賴libjpeg來壓縮圖片。通過檢視原始碼,我們會發現安卓並不是直接封裝的libjpeg,而是基於了另一個叫Skia的開源專案(http://en.wikipedia.org/wiki/Skia_Graphics_Engine)來作為的影象處理引擎。Skia是谷歌自己維護著的一個大而全的引擎,各種影象處理功能均在其中予以實現,並且廣泛的應用於谷歌自己和其它公司的產品中(如:Chrome、Firefox、Android等)。Skia對libjpeg進行了良好的封裝,基於這個引擎可以很方便為作業系統、瀏覽器等開發影象處理功能。libjpeg在壓縮影象時,有一個引數叫optimize_coding。壓縮工具及原理:tinypng ,pngquant

  針對圖片尺寸的修改其實就是一個影象重新取樣的過程,放大影象稱為上取樣(upsamping),縮小影象稱為下采樣(downsampling).
  在 Android 中圖片重取樣提供了兩種方法,一種叫做鄰近取樣(Nearest Neighbour Resampling),另一種叫做雙線性取樣(Bilinear Resampling)。
  除了 Android 中這兩種常用的重取樣方法之外,還有另外比較常見的兩種:雙立方/雙三次取樣(Bicubic Resampling) 和 Lanczos Resampling。除此之外,還有一些其他個人或機構發明的演算法 Hermite Resampling,Bell Resampling,Mitchell Resampling。

-- 圖片無失真壓縮:ImageOptim ; 圖片有失真壓縮:TinyPNG/ImageAlpha
TinyPNG支援對 PNG/JPEG 檔案做壓縮處理,效果不錯- https://tinypng.com/
pngquant支援 PNG 壓縮,有時候 TinyPNG 處理過的圖片噪點會稍多,可以考慮用 pngquant 來處理- https://pngquant.org/
ImageOptim支援壓縮 PNG/JPEG/GIF,只支援 Mac- https://imageoptim.com/mac
mozjpeg:ImageOptim online用於 PNG 轉 JPEG、JPEG 壓縮- https://imageoptim.com/online

-- 影象處理
影象處理技術- https://blog.csdn.net/jinshengtao/article/category/2776655
Android開發常用開源框架:圖片處理- http://blog.csdn.net/axi295309066/article/details/56984137
Android影象處理整理- http://blog.csdn.net/luzhenyuxfcy/article/details/49427781
  人們普遍認為角點是二維影象亮度變化劇烈的點或影象邊緣曲線上曲率極大值的點。
  角點的檢測主要有兩類基於影象邊緣的方法和基於影象灰度的方法。前者很大程度上依賴於影象的分割和邊緣提取,一旦待檢測目標發生區域性變化,很可能導致操作失敗,因此該類方法使用範圍較小;後者有很多方法,包括Harris運算元,Moravec運算元,Susan運算元等等。

兩個影象處理庫:libjpeg、Skia。

  Android多點觸控技術實戰,自由地對圖片進行縮放和移動- http://blog.csdn.net/guolin_blog/article/details/11100327
  PinchImageView(http://best.factj.com/boycy815/PinchImageView)|國人寫的, 可能是體驗最好的圖片手勢控制元件| 支援雙擊放大,雙擊縮小,超出邊界會回彈, 滑動慣性,不同解析度無縫切換,可與ViewPager結合使用。
  GestureViews(http://best.factj.com/alexvasilkov/GestureViews)|包含ImageView的自定義FrameLayout | 專案目的是讓圖片的查看盡可能流暢平滑, 讓開發者更加方便地整合到自己的應用中, 支援手勢控制和動畫 。
  PhotoView(http://best.factj.com/chrisbanes/PhotoView)| 致力於幫助開發者高效的建立可縮放的ImageView | 重寫ImageView的實現, 支援多點觸控的圖片縮放      
  subsampling-scale-image-view(http://best.factj.com/davemorrissey/subsampling-scale-image-view) | 一個Android自定義圖片檢視, 專為圖片畫廊設計| 豐富的配置選項, 更方便的實現圖片的手勢縮放, 旋轉, 平移. 無損展示大圖, 完美的地嵌入畫廊, 地圖等.可顯示大圖(地圖, 建築設計圖)等而不造成OutOfMemoryErrors(OOM記憶體溢位異常) 
  TouchImageView(http://best.factj.com/MikeOrtiz/TouchImageView) | 一個ImageView的拓展類 | 支援ImageView所有功能, 添加了平移, 縮放, 拖拽, 滑動, 雙擊縮放等動畫.

 - Tiny Jpeg Decoder (JPEG解碼)
Tiny Jpeg Decoder是一個可以用於嵌入式系統的JPEG解碼器。也可以在Windows上編譯通過。在此分析一下它部分的原始碼,輔助學習JPEG解碼知識。通過TinyJpeg可以將JPEG(*.jpg)檔案解碼為YUV(*.yuv)或者RGB(*.tga)檔案。

 - ImageMagik處理圖片的功能很強大 vs PhotoShop。ImageMagick是一套功能強大、穩定而且開源的工具集和開發包,可以用來讀、寫和處理超過89種基本格式的圖片檔案,包括流行的TIFF、JPEG、GIF、PNG等格式。利用ImageMagick,你可以根據web應用程式的需要動態生成圖片, 還可以對一個(或一組)圖片進行改變大小、旋轉、銳化、減色或增加特效等操作,並將操作的結果以相同格式或其它格式儲存,對圖片的操作,即可以通過命令列進行,也可以用C/C++、Perl、Java、PHP、Python或Ruby程式設計來完成。

-- # 騰訊WebP圖片的探索,大致的示例程式碼
InputStream is = getAssets().open("weixin.webp");
Bitmap bitmap = null;
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.JELLY_BEAN) {
    bitmap = WebPDecoder.getInstance().decodeWebP(streamToBytes(is));
} else {
    bitmap = BitmapFactory.decodeStream(is);
}
imageView.setImageBitmap(bitmap);
private static byte[] streamToBytes(InputStream is) {
    ByteArrayOutputStream os = new ByteArrayOutputStream(1024);
    byte[] buffer = new byte[1024];
    int len;
    try {
        while ((len = is.read(buffer)) >= 0) {
            os.write(buffer, 0, len);
        }
    } catch (java.io.IOException e) {
    }
    return os.toByteArray();
}

-- 載入巨圖方案(製作長圖及載入長圖)
Android 高清載入巨圖方案 拒絕壓縮圖片- http://blog.csdn.net/trent1985/article/details/49332877
很多的手勢檢測- https://github.com/rharter/android-gesture-detectors
顯示巨圖的方案- https://github.com/johnnylambada/WorldMap
Android大圖片處理- http://blog.csdn.net/geofferysun/article/details/52805619

-- 製作/播放GIf  Android (Glide)
glide可以支援gif和短視訊。
Android實現播放GIF動畫的強大ImageView- http://www.cnblogs.com/qingchen1984/p/5056390.html
Android螢幕內容製作成GIF圖方式- http://blog.csdn.net/u014686875/article/details/51124969
android app動圖優化:原始碼giflib載入gif動圖,效能秒殺glide- https://blog.csdn.net/wxk105/article/details/71698515
GIF動態圖製作- http://blog.csdn.net/q4878802/article/details/49274557

> 其他Android圖片壓縮開源庫Demo
Luban(魯班) —— Android圖片壓縮工具,仿微信朋友圈壓縮策略。
Android 圖片壓縮LuBan魯班eclipse版專案- http://download.csdn.net/detail/pengpenggxp/9828742
圖片壓縮LuBan demo- http://download.csdn.net/detail/shareus/9853438
Luban - https://github.com/Curzibn/Luban
Google 官方多媒體和圖形影象相關的處理:https://github.com/google/grafika

> 圖片處理中的軟引用、強化引用、弱引用;三級快取策略
  在記憶體引用上做些處理,常用的有軟引用、強化引用、弱引用(如圖片處理)。
  在過去,我們經常會使用一種非常流行的記憶體快取技術的實現,即軟引用或弱引用 (SoftReference or WeakReference)。但是現在已經不再推薦使用這種方式了,因為從 Android 2.3 (API Level 9)開始,垃圾回收器會更傾向於回收持有軟引用或弱引用的物件,這讓軟引用和弱引用變得不再可靠。另外,Android 3.0 (API Level 11)中,圖片的資料會儲存在本地的記憶體當中,因而無法用一種可預見的方式將其釋放,這就有潛在的風險造成應用程式的記憶體溢位並崩潰。
  軟引用(SoftReference)、虛引用(PhantomRefrence)、弱引用(WeakReference),這三個類是對heap中java物件的應用
heap中物件有強可及物件、軟可及物件、弱可及物件、虛可及物件和不可到達物件。應用的強弱順序是強、軟、弱、和虛。對於物件是屬於哪種可及的物件,由他的最強的引用決定。
 String abc=new String("abc");  //1、強可及物件      
 SoftReference<String> abcSoftRef=new SoftReference<String>(abc);  //2、軟可及物件     
 WeakReference<String> abcWeakRef = new WeakReference<String>(abc); //3、弱可及物件 
 Object obj = new Object();
 PhantomReference<Object> pf = new PhantomReference<Object>(obj); //4、虛可及物件   abc=null;     
 abcSoftRef.clear();//5、不可到達物件  

-- 三級快取策略,記憶體 硬碟策略及佔用大小及限制. 圖片快取技術
  圖片的三級快取機制一般是指應用載入圖片的時候,分別去訪問記憶體,檔案和網路而獲取圖片資料的一種行為。快取策略,採用“記憶體-本地-網路”三級快取策略:
 1.記憶體快取使用的就是LruCache. 
 2.檔案快取,我們使用的是DiskLruCache
 3.網路請求,這裡我們使用Volley網路請求框架。

> 開源圖片處理框架,Picasso/Glide/ImagLoader/Fresco
圖片載入庫:ImageLoader,UIL,Volley,主流的有,Picasso,Glide,Fresco

 -- 圖片處理
Picasso-transformations 一個為Picasso提供多種圖片變換的庫;
Glide-transformations   一個為Glide提供多種圖片變換的庫;
Android-gpuimage,基於OpenGL的Android過濾器;

1.Picasso是著名的開源組織Square出品的圖片處理框架,使用的比較多;
2.Glide是Google的員工基於Picasso開發,優化的,Android官網推薦使用的圖片處理框架;
3.Fresco是Facebook開源的圖片處理框架,真正做到了三級快取,功能強大,強烈推薦使用;
4.Android Universal Image Loader是一個老牌的圖片處理框架,但是在2015年的9月份已經停止更新維護,所以不推薦使用。

  說到記憶體快取的實現,非常容易就讓人想到LruCache演算法(Least Recently Used),也叫近期最少使用演算法。它的主要演算法原理就是把最近使用的物件用強引用儲存在LinkedHashMap中,並且把最近最少使用的物件在快取值達到預設定值之前從記憶體中移除。LruCache的用法也比較簡單.
  Glide記憶體快取的實現自然也是使用的LruCache演算法。不過除了LruCache演算法之外,Glide還結合了一種弱引用的機制,共同完成了記憶體快取功能

Android圖片載入框架最全解析(三),深入探究Glide的快取機制- http://blog.csdn.net/guolin_blog/article/details/54895665
Picasso,Glide,Fresco對比分析- http://blog.csdn.net/github_33304260/article/details/70213300

Picasso框架:獲取並顯示遠端圖片,強大的圖片下載和快取的第三方庫。
 Picasso的基本用法:Picasso.with(context).load(imageUrl).into(imageView);
 剪裁大小:Picasso.with(context).load(imageUrl).resize(50,50).centerCrop().into(imageView);
 佔位符圖片:Picasso.with(context).load(imageUrl).placeholder(R.drawable.image_placeholder).error(R.drawable.image_error_placeholder);
 載入本地資源:Picasso.with(context).load(R.drawable.icon).into(imageView);
                     Picasso.with(context).load("file:///android_asset/Adnroid.png").into(imageView);
                     Picasso.wiht(context).load(new File(...)).into(imageView);
  除錯:為了方便除錯,可以通過呼叫Picasso的setIndicatiorEnabled(true);可以讓不同來源的圖片顯示一個不同的色彩標記.
  解決問題:
  自動將影象快取在本地;通過圖片壓縮轉換以減少記憶體消耗;自動處理了ImageView的回收,即自動取消不在視野範圍內的ImageView檢視資源的載入;在介面卡中自動發現和重用以前取消的下載;影象格式轉換:很多時候需要將圖片進行格式轉換或者剪裁以節省記憶體或者達到我們的佈局效果;佔位符圖片:當圖片未正常顯示時預設的圖片,通過placeholder()設定,Picasso也支援設定圖片顯示錯誤時顯示的預設圖片,通過error()設定;

設定圖片圓角: 步驟1:
public class CircleTransform implements Transformation {
        @Override
        public Bitmap transform(Bitmap source) {
            int size = Math.min(source.getWidth(), source.getHeight());
            int x = (source.getWidth() - size) / 2;
            int y = (source.getHeight() - size) / 2;

            Bitmap squaredBitmap = Bitmap
                    .createBitmap(source, x, y, size, size);
            if (squaredBitmap != source) {
                source.recycle();
            }
            Bitmap bitmap = Bitmap.createBitmap(size, size, source.getConfig());

            Canvas canvas = new Canvas(bitmap);
            Paint paint = new Paint();
            BitmapShader shader = new BitmapShader(squaredBitmap,
                    BitmapShader.TileMode.CLAMP, BitmapShader.TileMode.CLAMP);
            paint.setShader(shader);
            paint.setAntiAlias(true);
            float r = size / 2f;
            canvas.drawCircle(r, r, r, paint);
            squaredBitmap.recycle();
            return bitmap;
        }

        @Override
        public String key() {
            return "circle";
        }
    }

步驟2: Picasso.with(this).load(R.drawable.meinv2).transform(new CircleTransform()).into(img);

 -- 1.Bugs:
com.bumptech.glide.request.target.SquaringDrawable cannot be cast to android.graphics.drawable.BitmapDrawable:
Android 關於Glide的拓展(高斯模糊、載入監聽、圓角圖片)- http://blog.csdn.net/onceing/article/details/73277079
 -- 2.Bugs: Picasso detected an unsupported OkHttp on the classpath- https://stackoverflow.com/questions/24125856/picasso-detected-an-unsupported-okhttp-on-the-classpath/32091981#32091981
 解決方案(增加依賴包):
compile 'com.squareup.okhttp:okhttp:2.4.0'
compile 'com.squareup.okhttp:okhttp-urlconnection:2.2.0'
compile 'com.squareup.picasso:picasso:2.4.0'
 --3.Error “You must not call setTag() on a view Glide is targeting” when use Glide - http://blog.csdn.net/ltym2014/article/details/51558695
報錯原因大致是因為Glide載入的ImageView呼叫了setTag()方法導致的錯誤,因為Glide已經預設為ImageView設定的Tag。
解決辦法:1.自定義一個Application,在裡面加上
public class App extends Application {
    @Override public void onCreate() {
        super.onCreate();
        ViewTarget.setTagId(R.id.glide_tag);
    }
}
2.然後在/values/ids.xml加上
<resources>  
    <item type="id" name="glide_tag" />  
</resources>

> 馬賽克圖與演算法:
1. public class PixelateUtil {
    /
      普通影象->畫素圖,zoneWidth為畫素圖的大畫素的寬度
     /
    public static Bitmap pixelate(Bitmap bitmap, int zoneWidth) {
        return pixelate(bitmap, zoneWidth, 0, 0, bitmap.getWidth(), bitmap.getHeight());
    }
 
    /
      普通圖->畫素圖,left、top、right、bottom可指定打馬賽克區域
     /
    public static Bitmap pixelate(Bitmap bitmap, int zoneWidth, int left, int top, int right, int bottom) {
        int w = bitmap.getWidth();
        int h = bitmap.getHeight();
        Bitmap result = bitmap.copy(Bitmap.Config.ARGB_8888, true);
        Canvas canvas = new Canvas(result);
        Paint paint = new Paint();
        for (int i = left; i < right; i += zoneWidth) {
            for (int j = top; j < bottom; j += zoneWidth) {
                int color = bitmap.getPixel(i, j);
                paint.setColor(color);
                int gridRight = Math.min(w, i + zoneWidth);
                int gridBottom = Math.min(h, j + zoneWidth);
                canvas.drawRect(i, j, gridRight, gridBottom, paint);
            }
        }
        return result;
    }
}

Bitmap result = PixelateUtil.pixelate(bitmap, zoneWidth);  //對全圖打馬賽克
Bitmap result = PixelateUtil.pixelate(bitmap, zoneWidth, left, top, right, bottom); //對指定區域打馬賽克

2. Pixelate是實現基本馬賽克效果的開源專案,它能夠非同步對整個或者部分的Bitmap區域打馬賽克,處理完後會在OnPixelateListener的onPixelated()中回撥,最小的SDK版本為16。
Simple Android library to pixelate images or certain areas of an image- https://github.com/DanielMartinus/Pixelate
new Pixelate(origin)
    .setArea(0,0,origin.getWidth(),origin.getHeight())  //設定區域
    .setDensity(12) //值越大,馬賽克單元越小
    .setListener(new OnPixelateListener() {
        @Override
        public void onPixelated(Bitmap bitmap, int density) {
            //bitmap為馬賽克圖
            Log.v(TAG, "");
        }
    })
    .make();

3. android-close-pixelate能夠實現多樣式的馬賽克效果- https://github.com/bmaslakov/android-close-pixelate
Bitmap pixelated = Pixelate.fromBitmap(
        origin,
        new PixelateLayer.Builder(PixelateLayer.Shape.Circle) //設定馬賽克形狀
                .setResolution(30) //每個畫素的密度(如果該值和size值一樣,那麼圓形之間相鄰)
                .setSize(30) //圓圈的大小
                .build()
);