来源:转载自https://forum.sophgo.com/t/stb-image-jpg-rvv-milkv-duo/240,原作者nihui
stb_image jpg解码 RVV优化和在 milkv-duo 上的测试
TL;DR
你可以:
- 拿出 opencv-mobile 项目里的 RVV 优化版 stb_image.h 配合
#define STBI_RVV
+stbi_load
或者你也可以:
- 直接使用编译好的 opencv-mobile-4.8.0-milkv-duo.zip,他的
cv::imread
已经包含jpg解码RVV优化
stb_image
小而美的图像库 stb_image,没有任何依赖,include 一个 .h 文件就能实现 JPG PNG 等常用格式解码
相比于 libjpeg libpng,stb_image 移植极度方便,接口十分简单即 stbi_load
opencv-mobile 项目中的 highgui 模块利用 stb_image 实现图片读写功能
stb_image.h 中有 x86 sse2 和 arm neon 的加速代码,没有 risc-v vector 优化
相当多的 risc-v 芯片带有 npu,但图像解码依旧要靠 cpu,因此十分有必要优化一下下
stb_image RVV 优化过程中的一点点吐嘈
具体实现细节就不bb了,想了解的人直接看源码
github.com/nihui/opencv-mobile
- idct 中间有个 16bit transpose 8x8,sse2/neon 指令集都有多寄存器之间的 shuffle/zip 指令,实现多个寄存器的 transpose,但是 RVV 只有单寄存器内 shuffle 的 vgather,这就很难搞了啊。RVV 你也不学着点,看看人家 arm SVE 同样变长宽度simd就有zip,你就只能 stride save + load 从内存倒腾一遍,真是败笔。
(什么?你说你 RVV 可以 compress + merge 或者 left shift + widen add 来搞 shuffle?你也不嫌丑?)
- 128bit 宽度的时候,8个uchar 应该是 vuint8mf2_t 表示,但是 RVV-0.7.1 的工具链遇到 mf2 的东西就罢工,于是只能用 vuint8m1_t 浪费高半部分的计算
stbimage jpg加载测试
简单写个计时函数,循环调用 stbi_load 加载 1280x720 分辨率的 jpg 图片,输出接口耗时(ms)
在 include 前定义 STBI_RVV 开启优化!
#include <sys/time.h> //gettimeofday()
#include <unistd.h> // sleep()
#define STB_IMAGE_IMPLEMENTATION
#define STBI_RVV
#include "stb_image.h"
double get_current_time()
{
struct timeval tv;
gettimeofday(&tv, NULL);
return tv.tv_sec * 1000.0 + tv.tv_usec / 1000.0;
}
int main()
{
for (int i = 0; i < 10; i++)
{
double t0 = get_current_time();
int desired_channels = 3;
int w;
int h;
int c;
unsigned char* pixeldata = stbi_load("in.jpg", &w, &h, &c, desired_channels);
double t1 = get_current_time();
stbi_image_free(pixeldata);
fprintf(stderr, "%.3f\n", t1-t0);
}
return 0;
}
在 milkv-duo 实测下开不开 STBI_RVV 区别
[root@milkv]~# ./stbimage-test
214.697
219.526
214.598
214.787
216.354
214.875
217.516
214.868
217.323
214.912
[root@milkv]~# ./stbimage-test-rvv
149.423
152.059
149.017
148.450
150.817
148.090
148.167
150.507
147.775
147.381
opencv-mobile jpg加载测试和结果验证
优化后的 stb_image 更新到 opencv-mobile 里头,为 cv::imread
/ cv::imdecode
带来提速,同时保存解码后的图片,验证结果是否一致
#include <sys/time.h> //gettimeofday()
#include <unistd.h> // sleep()
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
double get_current_time()
{
struct timeval tv;
gettimeofday(&tv, NULL);
return tv.tv_sec * 1000.0 + tv.tv_usec / 1000.0;
}
int main()
{
for (int i = 0; i < 10; i++)
{
double t0 = get_current_time();
cv::Mat bgr = cv::imread("in.jpg", 1);
double t1 = get_current_time();
fprintf(stderr, "%.3f\n", t1-t0);
if (i == 9)
{
cv::resize(bgr, bgr, cv::Size(200, 200));
cv::imwrite("out-rvv.jpg", bgr);
}
}
return 0;
}
在 milkv-duo 实测下开不开 STBI_RVV 区别
[root@milkv]~# LD_LIBRARY_PATH=. ./opencv-mobile-test
279.231
275.922
278.366
275.949
278.956
278.733
275.571
278.167
275.566
279.943
[root@milkv]~# LD_LIBRARY_PATH=. ./opencv-mobile-test-rvv
226.102
221.550
224.004
220.458
223.346
220.088
219.719
221.991
218.758
222.004
opencv-mobile cv::imread 相对于 stb_image stbi_load 多了 memcpy 和 RGB2BGR 转换的代价
RVV优化后的保存图片,md5sum完全一致!
[nihui@nihui-pc build]$ md5sum out*
a70cc79b13daeddd8b20e31a2fd5f0c6 out.jpg
a70cc79b13daeddd8b20e31a2fd5f0c6 out-rvv.jpg
图片内容也是正常的
最后,如果觉得有用请star和转发