RenderScript使用教程(四)实现美颜功能

4 篇文章 0 订阅
2 篇文章 0 订阅

前言

最近在做一个相机APP,通过OpenGL实现了实时美颜功能,但是网上很难找到通过RS实现美颜的例子,因为相机预览通过OpenGL实现美颜后,用户点击拍照后的数据依然是原始数据,并没有美颜,所以我通过同样的算法用RS实现了一遍。有需要的同学可以参考一下

Java调用代码

    public static Bitmap beauty(Context context, Bitmap inBitmap) {
        // Creates a RS context.
        RenderScript mRS = RenderScript.create(context);

        // Creates the input Allocation and copies all Bitmap contents into it.
        Allocation inAllocation = Allocation.createFromBitmap(mRS, inBitmap);

        // Defines the output Type, which will be a RGBA pixel.
        // The Allocation will be composed by four unsigned chars (0-255) for each pixel,
        // so that R-G-B-A values can be stored.
        // It is necessary to use a Type-based approach whenever there is a multi-dimensional sizing (X,Y).
        int bitmapWidth = inBitmap.getWidth();
        int bitmapHeight = inBitmap.getHeight();

        Type.Builder outType = new Type.Builder(mRS, Element.RGBA_8888(mRS)).setX(bitmapWidth).setY(bitmapHeight);

        // Creates the output Allocation wherein to store the conversion result.
        Allocation outAllocation = Allocation.createTyped(mRS, outType.create(), Allocation.USAGE_SCRIPT);

        // Creates the conversion script wrapper.
        ScriptC_beauty processScript = new ScriptC_beauty(mRS);

        // Binds the inAllocation variable with the actual Allocation.
        processScript.set_inAllocation(inAllocation);

        // Performs the conversion. RS kernel will use outAllocation size for its iterations.
        processScript.forEach_beauty(outAllocation);

        // Creates output Bitmap, matching input one size.
        Bitmap outBitmap = Bitmap.createBitmap(bitmapWidth, bitmapHeight, inBitmap.getConfig());

        // Copy calculation result to the output Bitmap.
        outAllocation.copyTo(outBitmap);

        mRS.destroy();
        return outBitmap;
    }

RenderScript代码

// Needed directive for RS to work
#pragma version(1)
// Change java_package_name directive to match your Activity's package path
#pragma rs java_package_name(com.dong.opencamera)

rs_allocation inAllocation;
static const float beautyParam = 0.5f;
static const float4 weight = {0.299f, 0.587f, 0.114f, 0.0f};

static float hardLight(float color) {
    if (color <= 0.5) {
        color = color * color * 2.0;
    } else {
        color = 1.0 - ((1.0 - color) * (1.0 - color) * 2.0);
    }
    return color;
}

uchar4 __attribute__((kernel)) beauty(uint32_t x, uint32_t y) {
            const uint32_t imageWidth = rsAllocationGetDimX(inAllocation);
            const uint32_t imageHeight = rsAllocationGetDimY(inAllocation);
            float4 in = rsUnpackColor8888(rsGetElementAt_uchar4(inAllocation, x, y));

            //process border
            if(x < 10 || y < 10 || x >= (imageWidth - 10) || y >= (imageHeight - 10)) {
                    return rsPackColorTo8888(in);
             }

             // 对绿色通道进行模糊操作
            float4 sampleColor = in.g * 20.0;
            sampleColor += rsUnpackColor8888(rsGetElementAt_uchar4(inAllocation, x, y - 10));
            sampleColor += rsUnpackColor8888(rsGetElementAt_uchar4(inAllocation, x, y + 10));
            sampleColor += rsUnpackColor8888(rsGetElementAt_uchar4(inAllocation, x - 10, y));
            sampleColor += rsUnpackColor8888(rsGetElementAt_uchar4(inAllocation, x + 10, y));
            sampleColor += rsUnpackColor8888(rsGetElementAt_uchar4(inAllocation, x + 5, y - 8));
            sampleColor += rsUnpackColor8888(rsGetElementAt_uchar4(inAllocation, x + 5, y + 8));
            sampleColor += rsUnpackColor8888(rsGetElementAt_uchar4(inAllocation, x - 5, y + 8));
            sampleColor += rsUnpackColor8888(rsGetElementAt_uchar4(inAllocation, x - 5, y - 8));
            sampleColor += rsUnpackColor8888(rsGetElementAt_uchar4(inAllocation, x + 8, y - 5));
            sampleColor += rsUnpackColor8888(rsGetElementAt_uchar4(inAllocation, x + 8, y - 5));
            sampleColor += rsUnpackColor8888(rsGetElementAt_uchar4(inAllocation, x - 8, y + 5));
            sampleColor += rsUnpackColor8888(rsGetElementAt_uchar4(inAllocation, x - 8, y - 5));
            sampleColor += rsUnpackColor8888(rsGetElementAt_uchar4(inAllocation, x, y - 6)) * 2.0;
            sampleColor += rsUnpackColor8888(rsGetElementAt_uchar4(inAllocation, x, y + 6)) * 2.0;
            sampleColor += rsUnpackColor8888(rsGetElementAt_uchar4(inAllocation, x + 6, y)) * 2.0;
            sampleColor += rsUnpackColor8888(rsGetElementAt_uchar4(inAllocation, x - 6, y)) * 2.0;
            sampleColor += rsUnpackColor8888(rsGetElementAt_uchar4(inAllocation, x - 4, y - 4)) * 2.0;
            sampleColor += rsUnpackColor8888(rsGetElementAt_uchar4(inAllocation, x - 4, y + 4))* 2.0;
            sampleColor += rsUnpackColor8888(rsGetElementAt_uchar4(inAllocation, x + 4, y - 4)) * 2.0;
            sampleColor += rsUnpackColor8888(rsGetElementAt_uchar4(inAllocation, x + 4, y + 4))* 2.0;

            sampleColor = sampleColor / 48.0;
            float highPass = in.g - sampleColor.g + 0.5;
            highPass = clamp(highPass, 0.0, 1.0);

             for (int i = 0; i < 5; i++) {
                   highPass = hardLight(highPass);
             }
             highPass = clamp(highPass, 0.0, 1.0);
             float luminance = dot(in, weight);
             float alpha = pow(luminance, beautyParam);

             alpha = clamp(alpha, 0.0, 1.0);

             float3 offset = (in.rgb - (float3){highPass, highPass, highPass}) * alpha * 0.1;
             float3 smoothColor = in.rgb + offset;
             smoothColor.r = clamp(smoothColor.r, 0.0, 1.0);
             smoothColor.g = clamp(smoothColor.g, 0.0, 1.0);
             smoothColor.b = clamp(smoothColor.b, 0.0, 1.0);

             // 线性混合
             float4 out;
             out.r = mix(smoothColor.r, max(in.r,smoothColor.r), alpha);
             out.g = mix(smoothColor.g, max(in.g,smoothColor.g), alpha);
             out.b = mix(smoothColor.b, max(in.b,smoothColor.b), alpha);
             out.a = in.a;

            return rsPackColorTo8888(out);
}

相机美颜的原理

1.取出绿色通道,对绿色通道进行模糊处理,例如高斯模糊,得到模糊后的值sampleColor
2.用原图绿色通道值减去sampleColor,加上0.5(即128),1+2两个步骤即PS中的高反差保留
3.对上述结果值进行3-5次强光处理,此步骤可以使得噪声更加突出
4.计算原图的灰度值,公式为0.299R + 0.587G + 0.114*B
5.将灰度值作为阈值,用来排除非皮肤部分,根据灰度值计算,将原图与1-3后的结果图合成
6.对混合后结果增加亮度
7.以灰度值作为透明度将原图与混合后结果进行滤色、柔光等混合,并调节饱和度

参考文档

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
实现 AlertDialog 高斯模糊的方法与一般的 View 高斯模糊基本相同,只需要在 AlertDialog 的背景中添加高斯模糊效果即可。下面是使用 RenderScript 实现 AlertDialog 高斯模糊的示例代码: 1. 在 res/values/styles.xml 文件中定义一个 MyAlertDialogTheme 主题,用于设置 AlertDialog 的样式: ``` <style name="MyAlertDialogTheme" parent="Theme.AppCompat.Light.Dialog.Alert"> <item name="android:windowBackground">@drawable/dialog_bg_blur</item> </style> ``` 2. 在 res/drawable 文件夹中创建一个 dialog_bg_blur.xml 文件,用于设置 AlertDialog 背景: ``` <layer-list xmlns:android="http://schemas.android.com/apk/res/android"> <!-- 设置底层背景 --> <item android:id="@android:id/background"> <shape android:shape="rectangle"> <solid android:color="@android:color/transparent" /> </shape> </item> <!-- 设置高斯模糊层 --> <item android:id="@+id/dialog_blur_layer"> <bitmap android:src="@drawable/dialog_bg" /> </item> </layer-list> ``` 其中,dialog_bg.xml 是一个不带高斯模糊效果的背景图片。 3. 在 res/drawable-v21 文件夹中创建一个 dialog_bg_blur.xml 文件,用于设置 AlertDialog 背景(仅适用于 Android 5.0 及以上版本): ``` <layer-list xmlns:android="http://schemas.android.com/apk/res/android"> <!-- 设置底层背景 --> <item android:id="@android:id/background"> <shape android:shape="rectangle"> <solid android:color="@android:color/transparent" /> </shape> </item> <!-- 设置高斯模糊层 --> <item android:id="@+id/dialog_blur_layer"> <bitmap android:src="@drawable/dialog_bg" /> <blur android:radius="25dp" android:scale="1" /> </item> </layer-list> ``` 其中,blur 标签用于设置高斯模糊效果,radius 属性表示模糊半径,scale 属性表示模糊程度。 4. 在 Java 代码中创建一个 Bitmap 对象,用于存储高斯模糊后的图片: ``` private Bitmap getBlurBitmap(Context context, Bitmap bitmap) { // 创建一个空的 Bitmap 对象 Bitmap blurBitmap = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Bitmap.Config.ARGB_8888); // 创建一个 RenderScript 对象 RenderScript rs = RenderScript.create(context); // 创建一个 Allocation 对象,用于存储原始图片数据 Allocation input = Allocation.createFromBitmap(rs, bitmap); // 创建一个 Allocation 对象,用于存储高斯模糊后的图片数据 Allocation output = Allocation.createFromBitmap(rs, blurBitmap); // 创建一个 ScriptIntrinsicBlur 对象,用于实现高斯模糊效果 ScriptIntrinsicBlur script = ScriptIntrinsicBlur.create(rs, Element.U8_4(rs)); // 设置高斯模糊半径 script.setRadius(25); // 执行高斯模糊 script.setInput(input); script.forEach(output); // 将高斯模糊后的图片数据保存到 Bitmap 对象中 output.copyTo(blurBitmap); // 销毁 RenderScript 对象 rs.destroy(); return blurBitmap; } ``` 5. 在 Java 代码中创建一个 AlertDialog 对象,并为其设置 MyAlertDialogTheme 主题: ``` AlertDialog.Builder builder = new AlertDialog.Builder(this, R.style.MyAlertDialogTheme); builder.setTitle("AlertDialog"); builder.setMessage("This is an AlertDialog with blur background."); builder.setPositiveButton("OK", null); builder.setNegativeButton("Cancel", null); AlertDialog dialog = builder.create(); ``` 6. 在 Java 代码中获取 dialog_blur_layer 图层,并为其设置高斯模糊后的 Bitmap 对象: ``` // 获取 dialog_blur_layer 图层 ViewGroup decorView = (ViewGroup) dialog.getWindow().getDecorView(); ViewGroup contentContainer = (ViewGroup) decorView.findViewById(android.R.id.content); FrameLayout container = (FrameLayout) contentContainer.getChildAt(0); ImageView blurLayer = (ImageView) container.findViewById(R.id.dialog_blur_layer); // 获取原始图片 Drawable drawable = blurLayer.getDrawable(); Bitmap bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888); Canvas canvas = new Canvas(bitmap); drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight()); drawable.draw(canvas); // 获取高斯模糊后的图片 Bitmap blurBitmap = getBlurBitmap(this, bitmap); // 将高斯模糊后的图片设置给 dialog_blur_layer 图层 blurLayer.setImageBitmap(blurBitmap); ``` 需要注意的是,Android 中的 RenderScript 只能在 Android 4.2(API 17)及以上版本中使用。在使用 RenderScript 时,需要注意内存的使用和回收,以避免内存泄漏和程序崩溃。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值