使用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的数据类型:无符号整数.有符号整数.未指定类型的整数. ...
随机推荐
- HTTP/3,它来了
HTTP 3.0 是 HTTP 协议的第三个主要版本,前两个分别是 HTTP 1.0 和 HTTP 2.0 ,但其实 HTTP 1.1 我认为才是真正的 HTTP 1.0. 如果你对 HTTP 1.1 ...
- 杀死 Windows 某个端口
进入终端命令行,输入netstat -aon|findstr 3000查找端口号所对应的 PID: 输入指令taskkill /pid 20348 /f:
- 介绍下Java内存区域(运行时数据区)
介绍下Java内存区域(运行时数据区) Java 虚拟机在执行 Java 程序的过程中会把它管理的内存划分成若干个不同的数据区域.JDK 1.8 和之前的版本略有不同. 下图是 JDK 1.8 对JV ...
- Html5新增内容标签
<canvas>画布</canvas> <audio src=""></audio> <video src="&qu ...
- omc.
OMC 099(4b) D 因为 \((abc)^{\dfrac 13} \le \dfrac{a+b+c}3\)(基本不等式),将 \(a = xy, b = yz, c = xz\) 代入得到 \ ...
- Oracle与KingbaseES的NULL在索引使用上的区别
NULL值是关系型数据库系统中比较特殊的一种值,通常称为UNKNOWN或空值,即是未知的,不确定的.由于NULL存在着无数的可能,因此NULL值也不等于NULL值. Oracle在创建索引时,不会存储 ...
- KingbaseES的SQL语句-CTE递归
背景 从上下级关系表中,任意一个节点数据出发,可以获得该节点的上级或下级.CTE的递归语法,或者 connect by 与 start with的 查询语法,能够实现这个需求. 当我们需要制作上下级关 ...
- 利用京东云Web应用防火墙实现Web入侵防护
摘 要 本指南描述如何利用京东云Web应用防火墙(简称WAF),对一个简单的网站(无论运行在京东云.其它公有云或者IDC)进行Web完全防护的全过程.该指南包括如下内容: 1 准备环境 1.1 在京东 ...
- VSCODE 配置远程调试环境
以下内容为本人的著作,如需要转载,请声明原文链接微信公众号「englyf」https://www.cnblogs.com/englyf/p/16691460.html 我的需求是,在Windows桌面 ...
- Typora Markdown 安装包
下载地址: 链接:https://pan.baidu.com/s/1wy0Ik95AjM5WjSC3nzOzqA 提取码:f26j 复制这段内容后打开百度网盘手机App,操作更方便哦 已更新至最新版0 ...