使用NEON指令加速RGB888和RGB565的相互转换
最近在做一个项目需要将RGB888转换为RGB565,用C语言转换的代码很简单,这是从ffmpeg中摘抄的代码
static inline void rgb24to16_c(const uint8_t *src, uint8_t *dst, int src_size)
{
uint16_t *d = (uint16_t *)dst;
const uint8_t *s = src;
const uint8_t *end = s + src_size;
while (s < end) {
const int r = *s++;
const int g = *s++;
const int b = *s++;
*d++ = (b >> 3) | ((g & 0xFC) << 3) | ((r & 0xF8) << 8);
}
}
这个项目需要转换的数据量不多,用C语言进行转换的CPU开销完全可以接受。但我并不满足于此,项目中使用的芯片支持NEON指令加速,所以为什么不用呢?
简单搜索一番发现国很少有相关的文章,最后还是去外面找了一些有用的资料,在学习NEON指令和寻找的过程中发现ARM官网已经提供了例程,那我正好可以偷懒。
这里给出ARM官网的例程链接,防止有人无法无访问,这里也给出源码
RGB888转RGB565
https://developer.arm.com/documentation/den0018/a/NEON-Code-Examples-with-Optimization/Converting-color-depth/Converting-from-RGB888-to-RGB565
uint8_t *src = image_src;
uint16_t *dst = image_dst;
int count = PIXEL_NUMBER;
while (count >= 8) {
uint8x8x3_t vsrc;
uint16x8_t vdst;
vsrc = vld3_u8(src);
vdst = vshll_n_u8(vsrc.val[0], 8);
vdst = vsriq_n_u16(vdst, vshll_n_u8(vsrc.val[1], 8), 5);
vdst = vsriq_n_u16(vdst, vshll_n_u8(vsrc.val[2], 8), 11);
vst1q_u16(dst, vdst);
dst += 8;
src += 8*3;
count -= 8;
}
uint16_t *src = image_src;
uint8_t *dst = image_dst;
int count = PIXEL_NUMBER;
while (count >= 8) {
uint16x8_t vsrc;
uint8x8x3_t vdst;
vsrc = vld1q_u16(src);
vdst.val[0] = vshrn_n_u16(vreinterpretq_u16_u8(vshrq_n_u8(vreinterpretq_u8_u16(vsrc), 3)), 5);
vdst.val[1] = vshl_n_u8(vshrn_n_u16(vsrc, 5) ,2);
vdst.val[2] = vmovn_u16(vshlq_n_u16(vsrc, 3));
vst3_u8(dst, vdst);
dst += 8*3;
src += 8;
count -= 8;
}
NEON的头文件是 arm_neon.h,编译需要加 -mcpu=cortex-a7 -mfloat-abi=softfp -mfpu=neon-vfpv4,其中-mpu参数按自己的芯片填写,我这里用的是一款A7架构芯片,主频800MHz。
接下来就是传统项目,性能测试,随机生成数据的 RGB888 图片转换为 RGB565 图片,重复1000次并计时,测试代码如下
#include <arm_neon.h>
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
void rgb888_to_rgb565(uint8_t *in, uint8_t *out, int h, int v)
{
uint16_t *d = (uint16_t *)out;
const uint8_t *s = in;
const uint8_t *end = s + h * v * 3;
while (s < end) {
const int r = *s++;
const int g = *s++;
const int b = *s++;
*d++ = (b >> 3) | ((g & 0xFC) << 3) | ((r & 0xF8) << 8);
}
}
void rgb888_to_rgb565_neon(uint8_t *in, uint8_t *out, int h, int v)
{
uint8_t *src = in;
uint16_t *dst = (uint16_t *)out;
int count = h * v;
if (count % 8 != 0) {
printf("pixel number must align with 8\n");
return;
}
while (count >= 8) {
uint8x8x3_t vsrc;
uint16x8_t vdst;
vsrc = vld3_u8(src);
vdst = vshll_n_u8(vsrc.val[0], 8);
vdst = vsriq_n_u16(vdst, vshll_n_u8(vsrc.val[1], 8), 5);
vdst = vsriq_n_u16(vdst, vshll_n_u8(vsrc.val[2], 8), 11);
vst1q_u16(dst, vdst);
dst += 8;
src += 8*3;
count -= 8;
}
}
#define WIDTH 320
#define HEIGHT 180
// #define WIDTH 640
// #define HEIGHT 360
// #define WIDTH 960
// #define HEIGHT 540
// #define WIDTH 1280
// #define HEIGHT 720
// #define WIDTH 1600
// #define HEIGHT 900
// #define WIDTH 1920
// #define HEIGHT 1080
#define LOOP 1000
uint8_t rgb888[WIDTH * HEIGHT * 3];
uint16_t rgb565[WIDTH * HEIGHT];
int main(int argc, char **argv)
{
int32_t i;
for (i = 0; i < WIDTH * HEIGHT * 3; i++) {
rgb888[i] = rand() & 0xFF;
}
struct timeval tv1;
struct timeval tv2;
double td = 0; // ms
printf("size %d x %d, loop %d\n", WIDTH, HEIGHT, LOOP);
gettimeofday(&tv1, NULL);
for (i = 0; i < LOOP; i++) {
rgb888_to_rgb565(rgb888, (uint8_t *)rgb565, WIDTH, HEIGHT);
}
gettimeofday(&tv2, NULL);
td = ((double)tv2.tv_sec * 1000.0 + (double)tv2.tv_usec / 1000.0) - ((double)tv1.tv_sec * 1000.0 + (double)tv1.tv_usec / 1000.0);
printf("time c: %f ms\n", td);
gettimeofday(&tv1, NULL);
for (i = 0; i < LOOP; i++) {
rgb888_to_rgb565_neon(rgb888, (uint8_t *)rgb565, WIDTH, HEIGHT);
}
gettimeofday(&tv2, NULL);
td = ((double)tv2.tv_sec * 1000.0 + (double)tv2.tv_usec / 1000.0) - ((double)tv1.tv_sec * 1000.0 + (double)tv1.tv_usec / 1000.0);
printf("time neon: %f ms\n", td);
return 0;
}
来看一下耗时:
size 320 x 180, loop 1000
time c: 1049.035889 ms
time neon: 405.596924 ms
size 640 x 360, loop 1000
time c: 3948.885986 ms
time neon: 2150.033203 ms
size 960 x 540, loop 1000
time c: 9026.308838 ms
time neon: 5033.337891 ms
size 1280 x 720, loop 1000
time c: 16550.081055 ms
time neon: 8756.577881 ms
size 1600 x 900, loop 1000
time c: 25366.738037 ms
time neon: 13618.843994 ms
size 1920 x 1080, loop 1000
time c: 37058.665039 ms
time neon: 20064.520996 ms
对于RGB888转RGB565来说,NEON指令的性能大约是C语言的两倍。其他NEON指令的性能未做测试,以上测试内容仅供参考。
最后给出ARM官方的参考文档
NEON指令开发指南
https://developer.arm.com/documentation/den0018/a
使用NEON指令加速RGB888和RGB565的相互转换的更多相关文章
- (二十三)ARM平台NEON指令的编译和优化
ARM平台NEON指令的编译和优化 本文介绍了ARM平台基于ARM v7-A架构的ARM Cortex-A系列处理器(Cortex-A5, Cortex-A7,Cortex-A8, Cortex-A9 ...
- BMP RGB888转RGB565 +上下翻转+缩放
典型的BMP图像文件由四部分组成: (1) 位图头文件数据结构,它包含BMP图像文件的类型.文件大小和位图起始位置等信息: typedef struct tagBITMAPFILEHEADER { ...
- neon指令,注意事项
1. vbic_s8 (int8x8_t a, int8x8_t b) 是 ~(ai & bi),一开始理解成 (~ai )& bi 导致出错 2.uint8x8_t vqshrn ...
- linux kernel态下使用NEON对算法进行加速
ARM处理器从cortex系列开始集成NEON处理单元,该单元可以简单理解为协处理器,专门为矩阵运算等算法设计,特别适用于图像.视频.音频处理等场景,应用也很广泛. 本文先对NEON处理单元进行简要介 ...
- 【linux】ARM板子开启浮点和neon加速
参考 1. ARM平台NEON指令的编译和优化; 2. 交叉编译器 arm-linux-gnueabi 和 arm-linux-gnueabihf 的区别; 3. https://blog.csdn. ...
- NEON简介【转】
转自:http://blog.csdn.net/fengbingchun/article/details/38020265 版权声明:本文为博主原创文章,未经博主允许不得转载. “ARM Advanc ...
- ARM NEON 编程系列2 - 基本指令集
ARM NEON 编程系列2 - 基本指令集 前言 本系列博文用于介绍ARM CPU下NEON指令优化. 博文github地址:github 相关代码github地址:github NEON指令集 主 ...
- ARM NEON编程系列1-导论
ARM NEON 编程系列1 - 导论 前言 本系列博文用于介绍ARM CPU下NEON指令优化. 博文github地址:github 相关代码github地址:github NEON历史 ARM处理 ...
- NEON简单介绍
个128位四字寄存器Q0-Q15,32个64位双字寄存器D0-D31,两个寄存器是重叠的,在使用的时候须要特别注意,不小心就会被覆盖掉. NEON的数据类型:无符号整数.有符号整数.未指定类型的整数. ...
随机推荐
- 都说Dapper性能好,突然就遇到个坑,还是个性能问题
本来闲来无事,准备看看Dapper扩展的源码学习学习其中的编程思想,同时整理一个自己代码的单元测试,为以后的进一步改进打下基础. 突然就发现问题了,源码也不看了,改了好久. 测试Dapper.Lite ...
- docker hung住问题排查
背景:这个是之前遇到的老问题. # systemctl status lxcfs● lxcfs.service - FUSE filesystem for LXC Loaded: loaded (/u ...
- 数据库简介与MySQL简介
MySQL简介 数据存取演变史 起源······文本文件 在最开始使用计算机都没有相应的规范我们的数据一般都是自己起一个名字然后就根据这个路径存储数据并且存储数据的格式也都五花八门就产生了很多奇奇怪怪 ...
- 【java】学习路径22-关于BigInteger类,大数字类
//int ooo = 19999999999999; //long ooo = 19999999999999; //这么大的整数,int和long都存不下的时候,我们就使用Math类下的BigInt ...
- Python条件语句的用法
python条件语句使用 if 表达式,难度不高,需要注意的是嵌套用法,以及如何设置对应的条件. if 条件判断语句 python 语句是按固定顺序执行的,先执行前面的语句,再执行后面的语句.如果你像 ...
- VS Code C++ 代码格式化(clang-format)
--- # 语言: None, Cpp, Java, JavaScript, ObjC, Proto, TableGen, TextProto Language: Cpp # BasedOnStyle ...
- 果汁 DI 介绍
Guice (英音同 'juice[果汁]') 是一个为 JDK8 及以上提供的轻量依赖注入框架. 目录 三级标题 三级标题 四级标题 三级标题 三级标题 /** * Animal */ interf ...
- WinUI 3 踩坑记:从创建项目到发布
本文是 WinUI 3 踩坑记 的一部分,该系列发布于 GitHub@Scighost/WinUI3Keng,若内容出现冲突以 GitHub 上的为准. 创建项目 现在 WinUI 3 的入门体验比刚 ...
- void指针;函数指针
void 类型指针 void => 空类型 void* => 空类型指针,只存储地址的值,丢失类型,无法访问,要访问其值,我们必须对这个指针做出正确的 类型转换,然后再间接引用指针.voi ...
- kibana访问多个 Elasticsearch 节点间的负载均衡
如果 Elasticsearch 集群有多个节点,分发 Kibana 节点之间请求的最简单的方法就是在 Kibana 机器上运行一个 Elasticsearch 协调(Coordinating onl ...