NEON 指令集并行技术优化彩色图像转灰度图【Android】

版权声明:涉猎过的知识都像是不断汇入大海的涓涓细流,你怎么知道是哪条汇入的溪流让海洋成为海洋呢【转载请注明出处】 https://blog.csdn.net/panda1234lee/article/details/85179775

参考原文:

android平台的neon优化策略

Neon Intrinsics各函数介绍


目前市面上主流的旗舰android手机搭载的Soc都是64位的CPU,常见的armv7指令集的公版架构如Cortex-A8,Cortex-A9,Cortex-A15,常见的armv8指令集的公版架构如Cortex-A53,Cortex-A57,Cortex-A72,Cortex-A73。arm架构的CPU从armv7a开始已经支持neon(可选项),从而实现并行计算功能。armv8a还具备32个128neon寄存器,并且支持双精度浮点数

下面为我添加的代码注释 :

void method_argb2gray_neon(AndroidBitmapInfo info, void *pixels) {
    // Gray = (R*38 + G*75 + B*15) >> 7
    TickMeter tm3;
    tm3.start();
    unsigned short *dst = (unsigned short *) pixels;
    unsigned char *src = (unsigned char *) pixels;

    uint8x8_t r = vdup_n_u8(38); // 将一个标量扩展城向量 8 bit * 8
    uint8x8_t g = vdup_n_u8(75);
    uint8x8_t b = vdup_n_u8(15);
    uint16x8_t alp = vdupq_n_u16(255 << 8);

    uint16x8_t temp;
    uint8x8_t gray;
    uint8x8x4_t argb;
    uint16x8_t high;
    uint16x8_t low;
    uint16x8x2_t res;

    int i, size = info.height * info.width / 8;
    // 暂不考虑不能被 8 整除的情况
    for (i = 0; i < size; ++i) {

        // 获取r、g、b值,计算灰度值
        argb = vld4_u8(src);  // 从内存中加载数据(以 AOS 存储)到 4 个向量(8 bit * 8)中(SOA)
        temp = vmull_u8(argb.val[1], r); // vmul 指令
        temp = vmlal_u8(temp, argb.val[2], g); // vmla 指令
        temp = vmlal_u8(temp, argb.val[3], b);
        gray = vshrn_n_u16(temp, 7); // vshrn_n_u16 会在做右移 7 位的同时将2字节无符号型转成1字节无符号型
        src += 8 * 4; // src 移到下个位置

        // 赋值 4 通道argb
        // 先用 vmovl_u8 (长指令)符号扩展或零扩展双字向量中的每个元素到其原始长度的两倍
        // vqmovn_* 为窄指令
        high = vorrq_u16(alp, vmovl_u8(gray)); // 再用 vorrq_u16 按位或运算
        low = vorrq_u16(vshlq_n_u16(vmovl_u8(gray), 8), vmovl_u8(gray)); // 注意这里是左移
        res = vzipq_u16(low, high); // 两个向量的元素交错打包成一个新向量
        vst1q_u16(dst, res.val[0]); // 存储一个向量(res 的前半部分)到内存(16bit*8)
        dst += 8;
        vst1q_u16(dst, res.val[1]);
        dst += 8;
    }
    tm3.stop();
    LOGI("method_argb2gray_neon   time: %lf", tm3.getTimeMilli());
    LOGI(" \n");
}
值得注意的是:Android 的 Bitmap 默认是按 ARGB 顺序存储的。

vzipq_u16 函数执行的结果如下:

可以看出 res[0] 和 res[1] 两个向量分别由 low, hight 两个向量一前一后交错打包而成。

程序运行效果如下:

最后记得将 Build Variants 设置为 release(默认为 debug)

猜你喜欢

转载自blog.csdn.net/panda1234lee/article/details/85179775