在嵌入式板子ARMv7 上利用neon对彩色图转换为灰度图进行加速

2023-10-30

RGB转GRAY公式如下:

 本实验通过对一张1920*1080 分辨率大小RGB彩色图进行灰度图转换测试耗时时间。

测试条件为 嵌入式开发板ssc9381g A7

通过四种转换方式进行耗时对比

结果如下:

方式1

通过opencv 库函数cvtColor转换耗时为48.325ms

方式2

通过C语言代码转换耗时为 151.006ms

方式3

通过neon Intrinsics函数优化转换算法耗时 40.358ms

方式4

arm neon asm 汇编优化转换算法耗时 1.385ms

以下是参考代码和详细讲解,供大家参考下:

//使用NEON Intrinsics优化
// 关键字restrict只用于限定指针,表明本指针是访问一个数据对象的惟一且初始的方式
__attribute__((target("fpu=neon"))) void neon_convert(uint8_t *__restrict dest, uint8_t *__restrict src, int n)
{
    int i;
    //读取8字节的预设值到64位寄存器
    // 将一个标量扩展城向量 8 bit * 8
    uint8x8_t rfac = vdup_n_u8(77);  // 转换权值 R
    uint8x8_t gfac = vdup_n_u8(151); // 转换权值 G
    uint8x8_t bfac = vdup_n_u8(28);  // 转换权值 B
    n /= 8;

    for (i = 0; i < n; i++)
    {
        uint16x8_t temp;
        // uint8x8 表示将64bit寄存器 分成 8 个 8bit
        uint8x8x3_t rgb = vld3_u8(src); //一次读取3个unit8x8到3个64位寄存器
        uint8x8_t result;

        temp = vmull_u8(rgb.val[0], rfac);       // temp=rgb.val[0]*rfac
        temp = vmlal_u8(temp, rgb.val[1], gfac); // temp=temp+rgb.val[1]*gfac
        temp = vmlal_u8(temp, rgb.val[2], bfac); //temp=temp+rgb.val[2]*bfac

        result = vshrn_n_u16(temp, 8); // vshrn_n_u16 会在temp做右移8 位的同时将2字节无符号型转成1字节无符号型
        vst1_u8(dest, result);         // 转存运算结果到dest
        src += 8 * 3;
        dest += 8;
    }
}

//NEON汇编代码优化:
__attribute__((target("fpu=neon"))) 
static void neon_asm_convert(uint8_t * __restrict dest, uint8_t * __restrict src, int numPixels)
{
  asm volatile("lsr          %2, %2, #3      \n"
               "# build the three constants: \n"
               "mov         r4, #28          \n" // Blue channel multiplier
               "mov         r5, #151         \n" // Green channel multiplier
               "mov         r6, #77          \n" // Red channel multiplier
               "vdup.8      d4, r4           \n"
               "vdup.8      d5, r5           \n"
               "vdup.8      d6, r6           \n"
               ".loop:                       \n"
               "# load 8 pixels:             \n"
               "vld4.8      {d0-d3}, [%1]!   \n"
               "# do the weight average:     \n"
               "vmull.u8    q7, d0, d4       \n"
               "vmlal.u8    q7, d1, d5       \n"
               "vmlal.u8    q7, d2, d6       \n"
               "# shift and store:           \n"
               "vshrn.u16   d7, q7, #8       \n" // Divide q3 by 256 and store in the d7
               "vst1.8      {d7}, [%0]!      \n"
               "subs        %2, %2, #1       \n" // Decrement iteration count
               "bne         .loop            \n" // Repeat unil iteration count is not zero
               :
               : "r"(dest), "r"(src), "r"(numPixels)
               : "r4", "r5", "r6"
               );
}

// C语言转换
void reference_convert(uint8_t *__restrict dest, uint8_t *__restrict src, int n)
{
    int i;
    for (i = 0; i < n; i++)
    {
        int r = *src++; // load red
        int g = *src++; // load green
        int b = *src++; // load blue

        // build weighted average:
        int y = (r * 77) + (g * 151) + (b * 28);

        // undo the scale by 256 and write to memory:
        *dest++ = (y >> 8);
    }
}

int main(int argc, char *argv[])
{
    int row, col, chn;
    cv::Mat SrcImg = cv::imread("srcimg.jpg", 1);
    row = SrcImg.rows;
    col = SrcImg.cols;
    chn = SrcImg.channels();
    cv::Mat GrayImg(row, col, CV_8UC1, cv::Scalar(0, 0, 0));
    cv::Mat GrayImgTmp;

    struct timeval tv_start;
    struct timeval tv_end;
    float time_use;


    // 计算cvtColor转换耗时
    gettimeofday(&tv_start, NULL);

    cv::cvtColor(SrcImg, GrayImgTmp, cv::COLOR_BGR2GRAY);

    gettimeofday(&tv_end, NULL);
    time_use = (tv_end.tv_sec - tv_start.tv_sec) * 1000000 + (tv_end.tv_usec - tv_start.tv_usec); //微秒
    printf("cvtColor time_use is %.10f\n", time_use);
    imwrite("gray1.jpg", GrayImgTmp);

    //计算自己手动转换算法耗时
    gettimeofday(&tv_start, NULL);

    reference_convert(GrayImg.data, SrcImg.data, row*col);

    gettimeofday(&tv_end, NULL);
    time_use = (tv_end.tv_sec - tv_start.tv_sec) * 1000000 + (tv_end.tv_usec - tv_start.tv_usec); //微秒
    printf("C program time_use is %.10f\n", time_use);

    //arm neon Intrinsics函数优化转换算法耗时
    gettimeofday(&tv_start, NULL);
    // 这个由于每次处理8个像素所以 大小为row*col/8 (不考虑不能被8整除的)
    neon_convert(GrayImg.data, SrcImg.data, row*col);
    gettimeofday(&tv_end, NULL);
    time_use = (tv_end.tv_sec - tv_start.tv_sec) * 1000000 + (tv_end.tv_usec - tv_start.tv_usec); //微秒
    printf("neon Intrinsics time_use is %.10f\n", time_use);
    imwrite("gray2.jpg", GrayImg);

    //arm neon asm 汇编优化转换算法耗时
    gettimeofday(&tv_start, NULL);

    neon_asm_convert(GrayImg.data, SrcImg.data, row*col/8);

    gettimeofday(&tv_end, NULL);
    time_use = (tv_end.tv_sec - tv_start.tv_sec) * 1000000 + (tv_end.tv_usec - tv_start.tv_usec); //微秒
    printf("neon asm time_use is %.10f\n", time_use);
    return 0;
}

原图

opencv  cvtcolor转换后灰度图

neon 转换后灰度图

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

在嵌入式板子ARMv7 上利用neon对彩色图转换为灰度图进行加速 的相关文章

随机推荐

  • Stable Diffusion 硬核生存指南:WebUI 中的 GFPGAN

    本篇文章聊聊 Stable Diffusion WebUI 中的核心组件 强壮的人脸图像面部画面修复模型 GFPGAN 相关的事情 写在前面 本篇文章的主角是开源项目 TencentARC GFPGAN 和上一篇文章 Stable Diff
  • scrollview中使用recyclerview莫名自动上滑

    1 scrollview中加入 android descendantFocusability blocksDescendants 2 为scrollView中的根view加入android focusableInTouchMode true
  • USB详解

    转载自http blog 163 com zhsh email blog static 19786900200611259521640 usb作为一种串行接口 应用日益广泛 如同每个工程设计人员必须掌握I2C RS232这些接口一样 我们也
  • int最大值是多少

    int最大值 根据编译器类型不同而变化 具体如下 1 对于16位编译器 int占16位 2字节 int的最大值为32767 2 对于32位和64位编译器 int占32位 4字节 int的最大值为2147483647 java语言中 int最
  • SQL语句中case、when、then的使用

    使用语法为 select pro id AS id pro title AS title pro name AS name pro ltime AS ltime pro place AS place pro linkman AS linkm
  • 【100天精通Python】Day58:Python 数据分析_Pandas时间序列数据处理,创建和解析时间数据pd.to_datetime(),.loc[],resample() 用法示例

    目录 时间序列数据处理 1 解析日期和时间数据 2 创建时间索引 3 访问时间索引数据 3 1 按年 月 日等级别访问数据 loc 3 2 使用部分日期作为索引 loc loc 3 3 使用时间索引的属性 index 4 时间索引的切片 5
  • 51单片机入门教程(2)——实现流水灯

    51单片机入门教程 2 实现流水灯 一 搭建流水灯电路 二 流水灯程序 2 1 延时程序 2 2 延时函数 2 3 按字节寻址 2 4 逻辑移位 2 5 条件判断 一 搭建流水灯电路 在Proteus中搭建流水灯电路如图 二 流水灯程序 我
  • 为什么我这么累?

    本月十六日 我和媳妇喜爱的歌手姚贝娜去世了 你是个好歌手 真正的热爱音乐 希望你在天堂里面开心的唱歌 我是个参加工作不久的小程序员 过着家 地铁 公司三点一线的生活 每天早上七点二十出门 地铁上一个多小时 8点五十到公司 下午六点下班 一个
  • 【FreeRTOS 信号量】互斥信号量

    互斥信号量与二值信号量类似 但是互斥信号量可以解决二值信号量出现的优先级翻转问题 解决办法就是优先级继承 普通互斥信号量创建及运行 参阅安富莱电子demo 互斥信号量句柄 static SemaphoreHandle t xMutex NU
  • 卷积神经网络中卷积层、池化层、全连接层的作用

    1 卷积层的作用 卷积层的作用是提取输入图片中的信息 这些信息被称为图像特征 这些特征是由图像中的每个像素通过组合或者独立的方式所体现 比如图片的纹理特征 颜色特征 比如下面这张图片 蓝色框框住的地方就是脸部特征 这些特征其实是由一个个像素
  • 消息鉴别码的原理与应用

    消息鉴别码可以确认自己收到的消息是否就是发送者的本意 也就是说 使用消息鉴别码可以判断消息是否被篡改 以及是否有人伪装成发送者发送该消息 消息鉴别码实现鉴别的原理是 用公开函数和密钥产生一个固定长度的值作为认证标识 用这个标识鉴别消息的完整
  • uml活动图

    活动图与交互图的区别 交互图强调的是对象到对象的控制流 而活动图则强调的是从活动到活动的控制流 初始节点和活动终点 用一个实心圆表示初始节点 用一个圆圈内加一个实心圆来表示活动终点 活动节点 是活动图中最主要的元素之一 它用来表示一个活动
  • 基于CNN的人脸表情识别系统

    基于CNN的人脸表情识别系统 主要功能 1 图片识别 可以通过上传本地图片 进行表情识别 2 拍照识别 点击拍照识别按钮 可以调用摄像头实现拍照 并进 行表情识别 实现原理 1 表情库的建立 fer2013人脸数据集 可以从kaggle网站
  • 上/下采样的方法

    下采样方式一般使用池化 pooling 操作 上采样 upsampling 的三种方式 插值法 Interpolation 插值就是在周围像素色彩的基础上用数学公式计算补充插入像素点的色彩 但必须注意的是插值并不能增加图像信息 如双线性插值
  • 数字电子技术-逻辑门电路

    文章目录 一 理想开关 二 基本CMOS逻辑门电路 2 1 MOS管开关特性 2 2 CMOS反相器 2 3 常用CMOS逻辑门电路 2 4 CMOS传输门 2 5 CMOS漏极开路门和三态输出门电路 2 6 CMOS逻辑门电路的重要参数
  • 写一个GitHub图床

    刚刚完成一个作业 涉及到图片的上传服务 因为自己经常会有一些图片管理的需求 七牛云 阿里云的oos存储又是付费的 所以自己根据GitHub的官方API搭建一个自己的图床服务 以便后续自己开发使用 参考地址 GitHubAPI import
  • GoLang - colly爬虫框架

    大家好 我是TheWeiJun 很高兴又和大家见面了 国庆假期马上就要结束了 在国庆假期里小编看了下colly框架 故这篇文章中将提到colly的使用及分析 欢迎各位读者多多阅读与交流 特别声明 本公众号文章只作为学术研究 不作为其它不法用
  • 实践安装minio

    一 下载安装文件 1 在home目录下创建minio文件夹 mkdir home minio 2 进入 home minio 文件夹 cd home minio 3 下载文件 此处下载比较慢 建议手动下载 然后上传到目录中 wget htt
  • springboot集成nacos配置中心踩坑记录

    目录 1 下载安装 准备 下载地址 可以尝试选择最新版本 解压 2 添加数据库配置 特别注意 3 登陆nacos添加配置 4 springboot项目中获取配置中心配置 1 引入依赖 2 创建bootstrap yaml配置文件 3 启动注
  • 在嵌入式板子ARMv7 上利用neon对彩色图转换为灰度图进行加速

    RGB转GRAY公式如下 本实验通过对一张1920 1080 分辨率大小RGB彩色图进行灰度图转换测试耗时时间 测试条件为 嵌入式开发板ssc9381g A7 通过四种转换方式进行耗时对比 结果如下 方式1 通过opencv 库函数cvtC