图片滤镜——GPUImage

    xiaoxiao2022-07-13  148

    个人博客: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; }

    项目就不贴出来了。

    最新回复(0)