RGB24 To Yuv420 C语言实现(非SIMD版本)
以下代码来自 libyuv
#include <stdint.h>
//下面三个函数为RGB-->>Yuv420的公式
static __inline int RGBToYJ(uint8_t r, uint8_t g, uint8_t b)
{
return (38 * r + 75 * g + 15 * b + 64) >> 7;
}
static __inline int RGBToUJ(uint8_t r, uint8_t g, uint8_t b)
{
return (127 * b - 84 * g - 43 * r + 0x8080) >> 8;
}
static __inline int RGBToVJ(uint8_t r, uint8_t g, uint8_t b)
{
return (127 * r - 107 * g - 20 * b + 0x8080) >> 8;
}
//取平均值
#define AVGB(a, b) (((a) + (b) + 1) >> 1)
#define R 2 //R的顺序
#define G 1 //G的顺序
#define B 0 //B的顺序
#define BPP 3 //BPP是一个像素占的字节数
void RGB24ToYRow_C(const uint8_t* src_rgb, uint8_t* dst_y, int width) {
int x;
for (x = 0; x < width; ++x) {
dst_y[0] = RGBToYJ(src_rgb[R], src_rgb[G], src_rgb[B]);
src_rgb += BPP; /*BPP == 3*/
dst_y += 1;
}
}
void RGB24ToUVRow_C(const uint8_t* src_rgb, int src_stride_rgb, uint8_t* dst_u, uint8_t* dst_v, int width)
{
const uint8_t* src_rgb1 = src_rgb + src_stride_rgb;
int x;
for (x = 0; x < width - 1; x += 2)//一次处理两行
{
//U,V采样方式:四个像素(边长为2像素的正方形,如下)RGB值的平均值,再计算出U,V
// p1 p2 这是第一行的两个像素
// p3 p4 这是第二行的两个像素
uint8_t ab = AVGB(AVGB(src_rgb[B], src_rgb1[B]), AVGB(src_rgb[B + BPP], src_rgb1[B + BPP]));
uint8_t ag = AVGB(AVGB(src_rgb[G], src_rgb1[G]), AVGB(src_rgb[G + BPP], src_rgb1[G + BPP]));
uint8_t ar = AVGB(AVGB(src_rgb[R], src_rgb1[R]), AVGB(src_rgb[R + BPP], src_rgb1[R + BPP]));
dst_u[0] = RGBToUJ(ar, ag, ab);
dst_v[0] = RGBToVJ(ar, ag, ab);
src_rgb += BPP * 2;
src_rgb1 += BPP * 2;
dst_u += 1;
dst_v += 1;
}
if (width & 1)//奇数行的情况下,最后一行只对两个像素求平均
{
uint8_t ab = AVGB(src_rgb[B], src_rgb1[B]);
uint8_t ag = AVGB(src_rgb[G], src_rgb1[G]);
uint8_t ar = AVGB(src_rgb[R], src_rgb1[R]);
dst_u[0] = RGBToUJ(ar, ag, ab);
dst_v[0] = RGBToVJ(ar, ag, ab);
}
}
/*
src_rgb24: rgb24数据,注意顺序为BGR
src_stride_rgb24: 一行像素点占用的字节数,循环的时候换行要用到(此处应该是width * 3,然后结果补成4的倍数)
dst_y: 存放y平面的地址
dst_stride_y: 和src_stride_rgb24类似,只是这里应该是width
..后面的参数参考前面即可
width: 图像宽度(单位:像素)
height: 图像高度 (单位:像素)
*/
int RGB24ToI420(const uint8_t* src_rgb24, int src_stride_rgb24, uint8_t* dst_y, int dst_stride_y, uint8_t* dst_u, int dst_stride_u, uint8_t* dst_v, int dst_stride_v, int width, int height)
{
int y;
void(*RGB24ToUVRow)(const uint8_t* src_rgb24, int src_stride_rgb24,
uint8_t* dst_u, uint8_t* dst_v, int width) =
RGB24ToUVRow_C;
void(*RGB24ToYRow)(const uint8_t* src_rgb24, uint8_t* dst_y, int width) =
RGB24ToYRow_C;
if (!src_rgb24 || !dst_y || !dst_u || !dst_v || width <= 0 || height == 0) {
return -1;
}
if (height < 0) {
height = -height;
src_rgb24 = src_rgb24 + (height - 1) * src_stride_rgb24;
src_stride_rgb24 = -src_stride_rgb24;
}
for (y = 0; y < height - 1; y += 2) //一次处理两行
{
RGB24ToUVRow(src_rgb24, src_stride_rgb24, dst_u, dst_v, width);
RGB24ToYRow(src_rgb24, dst_y, width);
RGB24ToYRow(src_rgb24 + src_stride_rgb24, dst_y + dst_stride_y, width);
src_rgb24 += src_stride_rgb24 * 2;
dst_y += dst_stride_y * 2;
dst_u += dst_stride_u;
dst_v += dst_stride_v;
}
if (height & 1) //高度为奇数的话,再单独处理最后一行
{
RGB24ToUVRow(src_rgb24, 0, dst_u, dst_v, width);
RGB24ToYRow(src_rgb24, dst_y, width);
}
return 0;
}
性能测试: 转换一张1920x1080图片用时:30ms(SIMD版本用时<1ms)
测试机器: