個人部落格:haichenyi.com。感謝關注
GPUImage圖檔濾鏡處理的第三方開源庫,對照IOS版的GPUImage寫的,部分功能尚未完善,目前也有很多種濾鏡,常用的濾鏡基本上都有,請先浏覽一遍github上面的用法。
依賴的庫
repositories {
jcenter()
}
dependencies {
//這個版本号2.x.x,具體的數字看,github官方說明
//README.md下方Download後面的版本号
implementation 'jp.co.cyberagent.android:gpuimage:2.x.x'
}
注意事項
一、圖檔變形
Android版目前沒有IOS那麼多類,那麼多用法,我們用的最多的就是GPUImageView這個自定義view,繼承的FrameLayout,并不是繼承的ImageView,是以,它這裡顯示圖檔的時候,會有圖檔變形的問題。我的***處理方法:***
- 先用Glide擷取圖檔的寬高
- 然後擷取GPUImageView的LayoutParams,動态設定控件的寬高
二、記憶體溢出
- 圖檔過大造成的記憶體溢出,壓縮圖檔,推薦使用魯班壓縮
- 頻繁使用GPUImage擷取Bitmap的getBitmapWithFilterApplied()方法,造成Bitmap過多的記憶體洩漏,推薦用WeakReference(弱引用)标記Bitmap,GC自動回收
- 顯示大圖和縮略圖,一般都是一個大圖和多種添加濾鏡後的效果圖(這個是縮略圖),這裡縮略圖再通過getBitmapWithFilterApplied擷取之前,最好吧原圖按照規則縮小之後再擷取顯示,這樣也能盡可能的減少記憶體的占用,點選縮略圖顯示大的效果圖的時候,并不是改變bitmap,是給gpuIamgeView對象設定你點選目标圖使用濾鏡即可,這樣也可以避免記憶體過多的消耗
三、濾鏡添加
- 單一濾鏡的添加
//這裡以添加黑白濾鏡為例
GPUImageView gpuImageView = findViewById(R.id.img);
gpuImageView.setImage(bitmap);
gpuImageView.setFilter(new GPUImageGrayscaleFilter());
- 組合濾鏡的添加GPUImageFilterGroup
GPUImageView gpuImageView = findViewById(R.id.img);
gpuImageView.setImage(bitmap);
GPUImageFilterGroup filterGroup = new GPUImageFilterGroup();
//把你需要添加的濾鏡放到GPUImageFilterGroup容器裡面,
//這裡我添加了灰色濾鏡,曝光度濾鏡和飽和度濾鏡理論上可以添加無數個
filterGroup.add(new GPUImageGrayscaleFilter());
filterGroup.add(new GPUImageExposureFilter());
filterGroup.add(new GPUImageSaturationFilter());
//把這個容器添加到GPUImageView
gpuImageView.setFilter(filterGroup);
- 多張圖檔的濾鏡
//這裡以GPUImageTwoInputFilter為例(可以加到組合濾鏡裡面),它有多個子類
//我們這裡用GPUImageChromaKeyBlendFilter為例
//實作的效果是一個過渡效果,從原圖過渡到目标圖
GPUImageView gpuImageView = findViewById(R.id.img);
//設定原圖
gpuImageView.setImage(bitmap);
//建立濾鏡對象,并且把目标圖設定給濾鏡
GPUImageChromaKeyBlendFilter keyBlendFilter = new GPUImageChromaKeyBlendFilter();
//設定目标圖
keyBlendFilter.setBitmap(bitmap1);
//平滑的過渡方法,改變參數的值即可
keyBlendFilter.setSmoothing(progressFloat);
//把濾鏡設定給GPUImageView
gpuImageView.setFilter(keyBlendFilter);
四、微調(敏感度問題)
隻要構造方法,方法帶參數的,都可以微調,這裡微調的取值範圍,Filter源碼的類注釋上面都有。如果,我們把seekBar的取值範圍設定成類注釋上面的範圍,你滑動很小距離的seekBar,圖檔變化就會很大,是以,我們一般都是縮小範圍再使用。
//第一個參數seekBar是最大值,第二個參數是最小值,第三個參數是預設值,第四個參數是seekbar分幾段
//mapSeekBarBean.put(TYPE_SATURATION, new SeekBarBean(2, 0, 0.5f, 10));
mapSeekBarBean.put(TYPE_SATURATION, new SeekBarBean(100, 0, 50f, 2));
//mapSeekBarBean.put(TYPE_BRIGHTNESS, new SeekBarBean(1, -1, 0.5f, 10));
mapSeekBarBean.put(TYPE_BRIGHTNESS, new SeekBarBean(100, 0, 50f, 2));
//mapSeekBarBean.put(TYPE_EXPOSURE, new SeekBarBean(10, -10, 0.5f, 0));
mapSeekBarBean.put(TYPE_EXPOSURE, new SeekBarBean(100, 0, 50f, 2));
//mapSeekBarBean.put(TYPE_CONTRAST, new SeekBarBean(4, 0, 0.25f, 0));
mapSeekBarBean.put(TYPE_CONTRAST, new SeekBarBean(100, 0, 25f, 2));
mapSeekBarBean.put(TYPE_POSTERIZE, new SeekBarBean(256, 0, 100f, 3));
//mapSeekBarBean.put(TYPE_HIGH_LIGHT_SHADOW, new SeekBarBean(1, 0, 0f, 0));
mapSeekBarBean.put(TYPE_HIGH_LIGHT_SHADOW, new SeekBarBean(100, 0, 0f, 2));
mapSeekBarBean.put(TYPE_SHARPEN, new SeekBarBean(100, 0, 50f, 3));
//mapSeekBarBean.put(TYPE_GAMMA, new SeekBarBean(3, 0, 0.33f, 0));
mapSeekBarBean.put(TYPE_GAMMA, new SeekBarBean(100, 0, 33f, 3));
//mapSeekBarBean.put(TYPE_OPACITY, new SeekBarBean(1, 0, 1f, 0));
mapSeekBarBean.put(TYPE_OPACITY, new SeekBarBean(100, 0, 100f, 2));
//mapSeekBarBean.put(TYPE_VIBRANCE, new SeekBarBean(1, 0, 0f, 0));
mapSeekBarBean.put(TYPE_VIBRANCE, new SeekBarBean(100, 0, 0f, 2));
//這裡是最終設定的值
switch (entrySet.getKey()) {
case TYPE_SATURATION:
//最後面*2是範圍(0,2)
float f1 = (entrySet.getValue().getProgress() / entrySet.getValue().getMax()) * 2;
filters.add(new GPUImageSaturationFilter(f1));
break;
case TYPE_BRIGHTNESS:
float f2 = entrySet.getValue().getProgress();
if (f2 == 50) {
f2 = 0f;
} else {
//後面的*0.7是範圍(-1,1),以中間0為準,分成兩部分(-1,0),(0,1)
//負數為變暗,正數為變亮,本應該*1
f2 = (float) (((f2 - 50) / 50) * 0.4);
}
filters.add(new GPUImageBrightnessFilter(f2));
break;
case TYPE_EXPOSURE:
float f3 = entrySet.getValue().getProgress();
if (f3 == 50) {
f3 = 0f;
} else {
//後面的*1是範圍(-10,10),以中間0為準,分成兩部分(-10,0),(0,10)
//負數為變暗,正數為變亮,本應該*10
f3 = ((f3 - 50) / 50) * 1;
}
filters.add(new GPUImageExposureFilter(f3));
break;
case TYPE_CONTRAST:
//最後面*4是範圍(0,4)
float f4 = (entrySet.getValue().getProgress() / entrySet.getValue().getMax()) * 4;
filters.add(new GPUImageContrastFilter(f4));
break;
case TYPE_POSTERIZE:
filters.add(new GPUImagePosterizeFilter((int) entrySet.getValue().getProgress()));
break;
case TYPE_HIGH_LIGHT_SHADOW:
GPUImageHighlightShadowFilter highlightShadowFilter = new GPUImageHighlightShadowFilter();
float f9 = (entrySet.getValue().getProgress() / entrySet.getValue().getMax()) * 1;
highlightShadowFilter.setHighlights(1 - f9);
highlightShadowFilter.setShadows(f9);
filters.add(highlightShadowFilter);
break;
case TYPE_SHARPEN:
float f5 = entrySet.getValue().getProgress();
if (f5 == 50) {
f5 = 0f;
} else {
//後面的*4是範圍(-4,4),以中間0為準,分成兩部分(-4,0),(0,4)
//負數為變暗,正數為變亮,本應該*4
f5 = ((f5 - 50) / 50) * 4;
}
filters.add(new GPUImageSharpenFilter(f5));
break;
case TYPE_GAMMA:
//最後面*3是範圍(0,3)
float f6 = (entrySet.getValue().getProgress() / entrySet.getValue().getMax()) * 3;
filters.add(new GPUImageGammaFilter(f6));
break;
case TYPE_OPACITY:
float f7 = (entrySet.getValue().getProgress() / entrySet.getValue().getMax()) * 1;
filters.add(new GPUImageOpacityFilter(f7));
break;
case TYPE_VIBRANCE:
float f8 = (entrySet.getValue().getProgress() / entrySet.getValue().getMax()) * 1;
filters.add(new GPUImageVibranceFilter(f8));
break;
default:
}
用法
上面的注意事項裡面已經說了簡單的用法了,怎麼擷取濾鏡後的圖檔呢?
//這個方法是擷取bitmap對象,至于怎麼儲存,那就是你自己做了
gpuImageView.getGPUImage().getBitmapWithFilterApplied();
//當然,庫也提供了儲存圖檔的方法:儲存的檔案夾名稱,檔案名字,回調方法
gpuImageView.saveToPictures(folderName,fileName,OnPictureSavedListener)
//其中回調方法裡面傳回的uri,不能直接傳給File,會找不到路徑,需要轉換一下
/**
* 根據Uri擷取檔案的路徑
*
* @param context context
* @param contentURI uri
* @return 檔案路徑
*/
public static String getRealPathFromURI(Context context, Uri contentURI) {
String result;
Cursor cursor = context.getContentResolver().query(contentURI,
new String[]{MediaStore.Images.ImageColumns.DATA},
null, null, null);
if (cursor == null) {
result = contentURI.getPath();
} else {
cursor.moveToFirst();
int index = cursor.getColumnIndex(MediaStore.Images.ImageColumns.DATA);
result = cursor.getString(index);
cursor.close();
}
return result;
}
項目就不貼出來了。