前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >如何使用 RenderScript实现抖音的黑金效果

如何使用 RenderScript实现抖音的黑金效果

作者头像
xiangzhihong
发布2022-11-30 16:06:38
9230
发布2022-11-30 16:06:38
举报
文章被收录于专栏:向治洪

最近,有人问我一个问题,如何使用彩图转为黑白,又如何将黑白图片转换为彩图?对于这个问题,我能想到的最直接的方法是:调用Android的系统Api获取图片生成bitmap文件,然后再使用Android中的二值化技术即可实现;除此之外,还可以使用FFpeg等库的方式实现。不过,我们今天要讲的是另外一种方案,即使用RenderScript方式。

一、RenderScript简介

RenderScript 是用于在 Android 上以高性能运行计算密集型任务的框架。RenderScript 专为数据并行计算而设计,不过串行工作负载也可以从中受益。RenderScript 运行时可以并行安排设备上可用的多个处理器(如多核 CPU 和 GPU)上的工作负载,使开发者能够专注于表达算法而不是调度工作。RenderScript 对于专注于图像处理、计算摄影或计算机视觉的应用来说尤其有用。

RenderScript使用的是一种类似于C/C++的rs 脚本语法,且是在运行时编译、跨平台的。性能比 Java 好,比 Native 略差。下图是RenderScript在Android 8.0 及更高版本的设备上的一个框架示意图。

在这里插入图片描述
在这里插入图片描述

与 Android 7.x 及更低版本中的 RenderScript 之间的区别如下:

  • 一个进程中有两组 RenderScript 内部库的实例。一组用于 CPU 备用路径,直接来源于 /system/lib;另一组用于 GPU 路径,来源于 /system/lib/vndk-sp。
  • /system/lib 中的 RS 内部库是作为平台的一部分构建的,会随着 system.img 的升级而更新。不过,/system/lib/vndk-sp 中的库是面向供应商构建的,不会随着 system.img 的升级而更新(虽然可以针对安全修复程序进行更新,但其 ABI 仍然保持不变)。
  • 供应商代码(RS HAL、RS 驱动程序和 bcc plugin)与位于 /system/lib/vndk-sp 的 RenderScript 内部库相关联。它们无法与 /system/lib 中的库相关联,因为该目录中的库是面向平台构建的,可能与供应商代码不兼容(即,符号可能会被移除)。如此一来可能会导致仅针对框架的 OTA 无法实现。

关于RenderScript的说明,可以参考RenderScript架构组成

二、RenderScript使用

RenderScript的使用分为两个步骤:

  1. 编写 .rs 内核脚本文件;
  2. 使用编写的文件进行渲染方面的处理;

2.1 编写内核脚本文件

RenderScript 内核通常位于 <project_root>/src/ 目录下,由类C语言的.rs语法编写,每个.rs 文件就是一个脚本,每个脚本由一组内核、函数和变量构成。首先,创建一个rs脚本文件代码。 在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

然后,打开 app 的 build.gradle 文件,在 android 的 defaultConfig 结点添加两句:

代码语言:javascript
复制
renderscriptTargetApi 18
renderscriptSupportModeEnabled true

接下来,下面以【将图片置灰】为例来说明如何编写内核脚本文件,新建一个 Gray.rs 文件,如下所示。

代码语言:javascript
复制
#pragma version(1)
#pragma rs java_package_name(com.avatar.rs)

void root(const uchar4 *in, uchar4 *out, uint32_t x, uint32_t y) {
    // a 是透明度,这里不修改透明度。
    out->a = in->a;

    // 快,但并不是真正意义的去色
    out->r = out->g = out->b = (in->r + in->g + in->b) / 3;

    // 慢,但是是真正的去色
    // out->r = out->g = out->b = (in->r * 299 + in->g * 587 + in->b * 114 + 500) / 1000;
}

void init() {
}

其中,第1行声明 RenderScript 的版本;第2行是申明该脚本所在的Java包的包名;root 函数是脚本文件的入口函数,对于图片来说,root函数负责对每一个像素做处理。参数 in 是输入像素点的指针; out 是输出像素点的指针。并且,init 函数是可选的,主要用于做一些初始化的工作。

2.2 调用rs脚步文件

使用前,需要先引入RenderScript脚本文件,如下所示。

代码语言:javascript
复制
import com.avatar.rs.ScriptC_greyscale;

这里的类名是 ScriptC_ 加上 .rs 的文件名,包名就是在创建 rs 文件时声明的包名。

代码语言:javascript
复制
import com.avatar.rs.ScriptC_greyscale;

public class MainActivity extends AppCompatActivity {

    private ImageView mImageView;
    private ImageView mRSImageView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mImageView =  findViewById(R.id.image_view);
        mRSImageView =  findViewById(R.id.rs_image_view);

        Bitmap mInBitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.test);
        Bitmap  mOutBitmap = Bitmap.createBitmap(mInBitmap.getWidth(), mInBitmap.getHeight(), mInBitmap.getConfig());
        mImageView.setImageBitmap(mInBitmap);

        Bitmap bitmap=transGray(this,mInBitmap);
        mRSImageView.setImageBitmap(bitmap);
    }


    public static Bitmap transGray(@NonNull Context context, @NonNull Bitmap bitmap) {
        // 创建输出 bitmap
        int width = bitmap.getWidth();
        int height = bitmap.getHeight();
        Bitmap outBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
        // 创建 RenderScript 对象
        RenderScript rs = RenderScript.create(context);
        // 创建输入、输出 Allocation
        Allocation allIn  = Allocation.createFromBitmap(rs, bitmap);
        Allocation allOut = Allocation.createFromBitmap(rs, outBitmap);
        // 创建我们在上面定义的 script
        ScriptC_greyscale script = new ScriptC_greyscale(rs);
        // 对每一个像素执行 root 方法
        script.forEach_root(allIn, allOut);
        // 将执行结果复制到输出 bitmap 上
        // 释放资源
        rs.destroy();
        return outBitmap;
    }
}

然后,我们运行下代码,看看前后的对比效果。

在这里插入图片描述
在这里插入图片描述

2.3 多函数调用

除了 root 函数,我们还可以在 .rs 中定义其他的 kernal 函数,比如:

代码语言:javascript
复制
/** 
 * 黑金色转换
 */
uchar4 __attribute__((kernel)) blackGold(uchar4 in, uint32_t x, uint32_t y) {
    uchar4 out = in;

    if ((in.r < in.b) && (in.g < in.b)) {
        out.r = out.g = out.b = (in.r*299 + in.g*587 + in.b*114 + 500) / 1000;
    }

    return out;
}


uchar4 __attribute__((kernel)) root(uchar4 v_in) {
    float4 f4 = rsUnpackColor8888(v_in);

    float3 mono = dot(f4.rgb, gMonoMult);
    return rsPackColorTo8888(mono);
}

uchar __attribute__((kernel)) toU8(uchar4 v_in) {
    float4 f4 = convert_float4(v_in);
    return (uchar)dot(f4.rgb, gMonoMult);
}

uchar4 __attribute__((kernel)) toU8_4(uchar v_in) {
    return (uchar4)v_in;
}

在定义kernal 函数时,函数返回值必须是 uchar4, 并且用 __attribute__((kernel)) 标记该函数是个 kernal 函数。然后,我们在Java代码中就可以使用下面的方式进行调用了。

代码语言:javascript
复制
script.forEach_blackGold(allIn, allOut);
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2021-09-18,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、RenderScript简介
  • 二、RenderScript使用
    • 2.1 编写内核脚本文件
      • 2.2 调用rs脚步文件
        • 2.3 多函数调用
        相关产品与服务
        GPU 云服务器
        GPU 云服务器(Cloud GPU Service,GPU)是提供 GPU 算力的弹性计算服务,具有超强的并行计算能力,作为 IaaS 层的尖兵利器,服务于生成式AI,自动驾驶,深度学习训练、科学计算、图形图像处理、视频编解码等场景。腾讯云随时提供触手可得的算力,有效缓解您的计算压力,提升业务效率与竞争力。
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档