一、RAW
工作中使用过的场景
1 mipi传输raw12的带宽过高,硬件测试不通过,后来修改raw10 降低带宽
1. RAW8
2. RAW10
3. RAW12
二、YUV
Y = 亮度
U/Cb = 蓝色色度
V/Cr = 红色色度
1) 格式
1. YUV422_UYVY(packed 4:2:2,字节序 U Y V Y)
/*
* UYVY packed 4:2:2 格式:
*
* 4 bytes = 2 pixels
*
* Byte0 Byte1 Byte2 Byte3
* U0 Y0 V0 Y1
*
* pixel0 = Y0 U0 V0
* pixel1 = Y1 U0 V0
*/
void parse_uyvy(
const uint8_t *uyvy,
int width,
int height,
uint8_t *y_plane,
uint8_t *u_plane,
uint8_t *v_plane
)
{
int pixel_count = width * height;
/*
* UYVY 是 YUV422:
* Y 分量数量 = width * height
* U 分量数量 = width * height / 2
* V 分量数量 = width * height / 2
*/
int y_index = 0;
int uv_index = 0;
/*
* Y + UV
*/
for (int i = 0; i < pixel_count * 2; i += 4)
{
uint8_t u = uyvy[i + 0];
uint8_t y0 = uyvy[i + 1];
uint8_t v = uyvy[i + 2];
uint8_t y1 = uyvy[i + 3];
/*
* 两个像素的 Y 分量分别保存
*/
y_plane[y_index++] = y0;
y_plane[y_index++] = y1;
/*
* 两个像素共用一组 U/V
*/
u_plane[uv_index] = u;
v_plane[uv_index] = v;
uv_index++;
}
}
int main(void)
{
/*
* 假设图像宽 4,高 2
*
* 一共 8 个像素
*
* UYVY 每 2 个像素占 4 字节
* 所以总字节数 = width * height * 2 = 16 字节
*/
int width = 4;
int height = 2;
uint8_t uyvy_data[] = {
/*
* 第 0 行,4 个像素
*
* 像素0、1:
* U0 Y0 V0 Y1
*/
0x10, 0x20, 0x30, 0x21,
/*
* 像素2、3:
* U1 Y2 V1 Y3
*/
0x11, 0x22, 0x31, 0x23,
/*
* 第 1 行,4 个像素
*
* 像素4、5:
* U2 Y4 V2 Y5
*/
0x12, 0x24, 0x32, 0x25,
/*
* 像素6、7:
* U3 Y6 V3 Y7
*/
0x13, 0x26, 0x33, 0x27
};
int pixel_count = width * height;
uint8_t *y_plane = malloc(pixel_count);
uint8_t *u_plane = malloc(pixel_count / 2);
uint8_t *v_plane = malloc(pixel_count / 2);
if (!y_plane || !u_plane || !v_plane)
{
printf("malloc failed\n");
free(y_plane);
free(u_plane);
free(v_plane);
return -1;
}
parse_uyvy(
uyvy_data,
width,
height,
y_plane,
u_plane,
v_plane
);
printf("Y plane:\n");
for (int i = 0; i < pixel_count; i++)
{
printf("Y%d = 0x%02X\n", i, y_plane[i]);
}
printf("\nU plane:\n");
for (int i = 0; i < pixel_count / 2; i++)
{
printf("U%d = 0x%02X\n", i, u_plane[i]);
}
printf("\nV plane:\n");
for (int i = 0; i < pixel_count / 2; i++)
{
printf("V%d = 0x%02X\n", i, v_plane[i]);
}
free(y_plane);
free(u_plane);
free(v_plane);
return 0;
}
3.2 YUV422_VYUY(packed 4:2:2,字节序 V Y U Y)
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
/*
* YUV422_VYUY packed 4:2:2
*
* 4 bytes = 2 pixels
*
* Byte0 Byte1 Byte2 Byte3
* V0 Y0 U0 Y1
*
* pixel0 = Y0 U0 V0
* pixel1 = Y1 U0 V0
*/
void parse_vyuy(
const uint8_t *vyuy,
int width,
int height,
uint8_t *y_plane,
uint8_t *u_plane,
uint8_t *v_plane
)
{
int pixel_count = width * height;
int vyuy_size = pixel_count * 2;
int y_index = 0;
int uv_index = 0;
for (int i = 0; i < vyuy_size; i += 4)
{
/*
* VYUY 字节序:
*
* vyuy[i + 0] = V
* vyuy[i + 1] = Y0
* vyuy[i + 2] = U
* vyuy[i + 3] = Y1
*/
uint8_t v = vyuy[i + 0];
uint8_t y0 = vyuy[i + 1];
uint8_t u = vyuy[i + 2];
uint8_t y1 = vyuy[i + 3];
/*
* 两个像素各自的 Y 分量
*/
y_plane[y_index++] = y0;
y_plane[y_index++] = y1;
/*
* 两个像素共用一组 U/V 分量
*/
u_plane[uv_index] = u;
v_plane[uv_index] = v;
uv_index++;
}
}
3.3 YUV422_YVYU(packed 4:2:2,字节序 Y V Y U)
/*
* YUV422_YVYU packed 4:2:2
*
* 4 bytes = 2 pixels
*
* Byte0 Byte1 Byte2 Byte3
* Y0 V0 Y1 U0
*
* pixel0 = Y0 U0 V0
* pixel1 = Y1 U0 V0
*/
void parse_yvyu(
const uint8_t *yvyu,
int width,
int height,
uint8_t *y_plane,
uint8_t *u_plane,
uint8_t *v_plane
)
{
int pixel_count = width * height;
int yvyu_size = pixel_count * 2;
int y_index = 0;
int uv_index = 0;
for (int i = 0; i < yvyu_size; i += 4)
{
/*
* YVYU 字节序:
*
* yvyu[i + 0] = Y0
* yvyu[i + 1] = V
* yvyu[i + 2] = Y1
* yvyu[i + 3] = U
*/
uint8_t y0 = yvyu[i + 0];
uint8_t v = yvyu[i + 1];
uint8_t y1 = yvyu[i + 2];
uint8_t u = yvyu[i + 3];
/*
* 两个像素各自的 Y 分量
*/
y_plane[y_index++] = y0;
y_plane[y_index++] = y1;
/*
* 两个像素共用一组 U/V 分量
*/
u_plane[uv_index] = u;
v_plane[uv_index] = v;
uv_index++;
}
}
3.4 YUV422_YUYV(packed 4:2:2,字节序 Y U Y V)
/*
* YUV422_YUYV packed 4:2:2
*
* 4 bytes = 2 pixels
*
* Byte0 Byte1 Byte2 Byte3
* Y0 U0 Y1 V0
*
* pixel0 = Y0 U0 V0
* pixel1 = Y1 U0 V0
*/
void parse_yuyv(
const uint8_t *yuyv,
int width,
int height,
uint8_t *y_plane,
uint8_t *u_plane,
uint8_t *v_plane
)
{
int pixel_count = width * height;
int yuyv_size = pixel_count * 2;
int y_index = 0;
int uv_index = 0;
for (int i = 0; i < yuyv_size; i += 4)
{
/*
* YUYV 字节序:
*
* yuyv[i + 0] = Y0
* yuyv[i + 1] = U
* yuyv[i + 2] = Y1
* yuyv[i + 3] = V
*/
uint8_t y0 = yuyv[i + 0];
uint8_t u = yuyv[i + 1];
uint8_t y1 = yuyv[i + 2];
uint8_t v = yuyv[i + 3];
/*
* 两个像素各自的 Y 分量
*/
y_plane[y_index++] = y0;
y_plane[y_index++] = y1;
/*
* 两个像素共用一组 U/V 分量
*/
u_plane[uv_index] = u;
v_plane[uv_index] = v;
uv_index++;
}
}
3.5 YUV422SP_UV(NV16,Y 平面 + UV 交织)
先放所有 Y(每像素1字节)
再放所有 UV(每2像素共2字节,即 U1 V1)
Y 总共 W*H 字节
U 和 V 不是分开存的,而是交织在 UV 平面里
UV 平面总共 W*H 字节
若单独算数量:
U 有 (W/2)*H 字节
V 有 (W/2)*H 字节
/*
* YUV422SP_UV / NV16
*
* Semi-planar 4:2:2
*
* 内存布局:
*
* Y plane:
* Y0 Y1 Y2 Y3 ...
*
* UV interleaved plane:
* U0 V0 U1 V1 ...
*
* 对于 4:2:2:
* 每 2 个水平像素共用一组 U/V
*
* pixel0 = Y0 U0 V0
* pixel1 = Y1 U0 V0
*
* pixel2 = Y2 U1 V1
* pixel3 = Y3 U1 V1
*/
void parse_nv16(
const uint8_t *nv16,
int width,
int height,
uint8_t *y_plane,
uint8_t *u_plane,
uint8_t *v_plane
)
{
int pixel_count = width * height;
/*
* NV16:
* 前 width * height 字节是 Y 平面
* 后 width * height 字节是 UV 交织平面
*/
const uint8_t *src_y = nv16;
const uint8_t *src_uv = nv16 + pixel_count;
/*
* 1. 拷贝 Y 分量
*/
for (int i = 0; i < pixel_count; i++)
{
y_plane[i] = src_y[i];
}
/*
* 2. 解析 UV 交织分量
*
* UV plane:
* U0 V0 U1 V1 U2 V2 ...
*
* 每 2 字节解析出一组 U/V
*/
for (int i = 0; i < pixel_count; i += 2)
{
int uv_index = i / 2;
u_plane[uv_index] = src_uv[i + 0];
v_plane[uv_index] = src_uv[i + 1];
}
}
3.6 YUV422SP_VU(NV61,Y 平面 + VU 交织)
/*
* YUV422SP_VU / NV61
*
* Semi-planar 4:2:2
*
* 内存布局:
*
* Y plane:
* Y0 Y1 Y2 Y3 ...
*
* VU interleaved plane:
* V0 U0 V1 U1 ...
*
* 对于 4:2:2:
* 每 2 个水平像素共用一组 U/V
*
* pixel0 = Y0 U0 V0
* pixel1 = Y1 U0 V0
*
* pixel2 = Y2 U1 V1
* pixel3 = Y3 U1 V1
*/
void parse_nv61(
const uint8_t *nv61,
int width,
int height,
uint8_t *y_plane,
uint8_t *u_plane,
uint8_t *v_plane
)
{
int pixel_count = width * height;
/*
* NV61:
* 前 width * height 字节是 Y 平面
* 后 width * height 字节是 VU 交织平面
*/
const uint8_t *src_y = nv61;
const uint8_t *src_vu = nv61 + pixel_count;
/*
* 1. 拷贝 Y 分量
*/
for (int i = 0; i < pixel_count; i++)
{
y_plane[i] = src_y[i];
}
/*
* 2. 解析 VU 交织分量
*
* VU plane:
* V0 U0 V1 U1 V2 U2 ...
*
* 每 2 字节解析出一组 V/U
*/
for (int i = 0; i < pixel_count; i += 2)
{
int uv_index = i / 2;
v_plane[uv_index] = src_vu[i + 0];
u_plane[uv_index] = src_vu[i + 1];
}
}
3.7 YUV422P_UV(I422-三平面)
/*
* YUV422P_UV / I422
*
* Planar 4:2:2
*
* 内存布局:
*
* Y plane:
* Y0 Y1 Y2 Y3 ...
*
* U plane:
* U0 U1 U2 U3 ...
*
* V plane:
* V0 V1 V2 V3 ...
*
* 对于 4:2:2:
* 每 2 个水平像素共用一组 U/V
*
* pixel0 = Y0 U0 V0
* pixel1 = Y1 U0 V0
*
* pixel2 = Y2 U1 V1
* pixel3 = Y3 U1 V1
*/
void parse_i422(
const uint8_t *i422,
int width,
int height,
uint8_t *y_plane,
uint8_t *u_plane,
uint8_t *v_plane
)
{
int pixel_count = width * height;
int uv_count = pixel_count / 2;
/*
* I422:
* 第 1 段:Y plane,大小 width * height
* 第 2 段:U plane,大小 width * height / 2
* 第 3 段:V plane,大小 width * height / 2
*/
const uint8_t *src_y = i422;
const uint8_t *src_u = i422 + pixel_count;
const uint8_t *src_v = i422 + pixel_count + uv_count;
/*
* 1. 拷贝 Y 分量
*/
memcpy(y_plane, src_y, pixel_count);
/*
* 2. 拷贝 U 分量
*/
memcpy(u_plane, src_u, uv_count);
/*
* 3. 拷贝 V 分量
*/
memcpy(v_plane, src_v, uv_count);
}
3.8 YUV422P_VU(YV16-三平面)
/*
* YUV422P_VU / YV16
*
* Planar 4:2:2
*
* 内存布局:
*
* Y plane:
* Y0 Y1 Y2 Y3 ...
*
* V plane:
* V0 V1 V2 V3 ...
*
* U plane:
* U0 U1 U2 U3 ...
*
* 对于 4:2:2:
* 每 2 个水平像素共用一组 U/V
*
* pixel0 = Y0 U0 V0
* pixel1 = Y1 U0 V0
*
* pixel2 = Y2 U1 V1
* pixel3 = Y3 U1 V1
*/
void parse_yv16(
const uint8_t *yv16,
int width,
int height,
uint8_t *y_plane,
uint8_t *u_plane,
uint8_t *v_plane
)
{
int pixel_count = width * height;
int uv_count = pixel_count / 2;
/*
* YV16:
* 第 1 段:Y plane,大小 width * height
* 第 2 段:V plane,大小 width * height / 2
* 第 3 段:U plane,大小 width * height / 2
*/
const uint8_t *src_y = yv16;
const uint8_t *src_v = yv16 + pixel_count;
const uint8_t *src_u = yv16 + pixel_count + uv_count;
/*
* 1. 拷贝 Y 分量
*/
memcpy(y_plane, src_y, pixel_count);
/*
* 2. 拷贝 U 分量
*/
memcpy(u_plane, src_u, uv_count);
/*
* 3. 拷贝 V 分量
*/
memcpy(v_plane, src_v, uv_count);
}
3.9 YUV420SP_UV(NV12)
/*
* YUV420SP_UV / NV12
*
* Semi-planar 4:2:0
*
* 内存布局:
*
* Y plane:
* Y0 Y1 Y2 Y3 ...
*
* UV interleaved plane:
* U0 V0 U1 V1 ...
*
* 对于 4:2:0:
* 每 2x2 个像素共用一组 U/V
*
* 例如 width = 4:
*
* Y plane:
* Y0 Y1 Y2 Y3
* Y4 Y5 Y6 Y7
*
* UV plane:
* U0 V0 U1 V1
*
* Pixel0, Pixel1, Pixel4, Pixel5 共用 U0/V0
* Pixel2, Pixel3, Pixel6, Pixel7 共用 U1/V1
*/
void parse_nv12(
const uint8_t *nv12,
int width,
int height,
uint8_t *y_plane,
uint8_t *u_plane,
uint8_t *v_plane
)
{
int pixel_count = width * height;
int uv_count = pixel_count / 4;
/*
* NV12:
* 第 1 段:Y plane,大小 width * height
* 第 2 段:UV plane,大小 width * height / 2
*
* UV plane 中:
* U/V 交织存放
*/
const uint8_t *src_y = nv12;
const uint8_t *src_uv = nv12 + pixel_count;
/*
* 1. 拷贝 Y 分量
*
* Y 分量每个像素一个
*/
memcpy(y_plane, src_y, pixel_count);
/*
* 2. 解析 UV 交织分量
*
* UV plane:
* U0 V0 U1 V1 U2 V2 ...
*
* 每 2 字节解析出一组 U/V
*
* U 数量 = width * height / 4
* V 数量 = width * height / 4
*/
for (int i = 0; i < uv_count; i++)
{
u_plane[i] = src_uv[i * 2 + 0];
v_plane[i] = src_uv[i * 2 + 1];
}
}
3.10 YUV420SP_VU(NV21)
/*
* YUV420SP_VU / NV21
*
* Semi-planar 4:2:0
*
* 内存布局:
*
* Y plane:
* Y0 Y1 Y2 Y3 ...
*
* VU interleaved plane:
* V0 U0 V1 U1 ...
*
* 对于 4:2:0:
* 每 2x2 个像素共用一组 U/V
*
* 例如 width = 4:
*
* Y plane:
* Y0 Y1 Y2 Y3
* Y4 Y5 Y6 Y7
*
* VU plane:
* V0 U0 V1 U1
*
* Pixel0, Pixel1, Pixel4, Pixel5 共用 U0/V0
* Pixel2, Pixel3, Pixel6, Pixel7 共用 U1/V1
*/
void parse_nv21(
const uint8_t *nv21,
int width,
int height,
uint8_t *y_plane,
uint8_t *u_plane,
uint8_t *v_plane
)
{
int pixel_count = width * height;
int uv_count = pixel_count / 4;
/*
* NV21:
* 第 1 段:Y plane,大小 width * height
* 第 2 段:VU plane,大小 width * height / 2
*
* VU plane 中:
* V/U 交织存放
*/
const uint8_t *src_y = nv21;
const uint8_t *src_vu = nv21 + pixel_count;
/*
* 1. 拷贝 Y 分量
*
* Y 分量每个像素一个
*/
memcpy(y_plane, src_y, pixel_count);
/*
* 2. 解析 VU 交织分量
*
* VU plane:
* V0 U0 V1 U1 V2 U2 ...
*
* 每 2 字节解析出一组 V/U
*
* U 数量 = width * height / 4
* V 数量 = width * height / 4
*/
for (int i = 0; i < uv_count; i++)
{
v_plane[i] = src_vu[i * 2 + 0];
u_plane[i] = src_vu[i * 2 + 1];
}
}
3.11 YUV420P_UV(I420-三平面)
/*
* 11. YUV420P_UV / I420
*
* 三平面格式:
*
* Y plane:
* Y0 Y1 Y2 Y3
* Y4 Y5 Y6 Y7
* Y8 Y9 Y10 Y11
* Y12 Y13 Y14 Y15
*
* U plane:
* U0 U1
* U2 U3
*
* V plane:
* V0 V1
* V2 V3
*
* 对于 YUV420:
* 每 2x2 个像素共用一组 U/V
*/
void parse_i420(
const uint8_t *i420,
int width,
int height,
uint8_t *y_plane,
uint8_t *u_plane,
uint8_t *v_plane
)
{
int pixel_count = width * height;
int uv_count = pixel_count / 4;
/*
* I420 内存布局:
*
* [ Y plane ][ U plane ][ V plane ]
*/
const uint8_t *src_y = i420;
const uint8_t *src_u = i420 + pixel_count;
const uint8_t *src_v = i420 + pixel_count + uv_count;
memcpy(y_plane, src_y, pixel_count);
memcpy(u_plane, src_u, uv_count);
memcpy(v_plane, src_v, uv_count);
}
3.12 YUV420P_VU(YV12-三平面)
/*
* 12. YUV420P_VU / YV12
*
* 三平面格式:
*
* Y plane:
* Y0 Y1 Y2 Y3
* Y4 Y5 Y6 Y7
* Y8 Y9 Y10 Y11
* Y12 Y13 Y14 Y15
*
* V plane:
* V0 V1
* V2 V3
*
* U plane:
* U0 U1
* U2 U3
*
* 对于 YUV420:
* 每 2x2 个像素共用一组 U/V
*
* 例如:
*
* Y0 Y1 共用 U0 / V0
* Y4 Y5 共用 U0 / V0
*
* Y2 Y3 共用 U1 / V1
* Y6 Y7 共用 U1 / V1
*/
void parse_yv12(
const uint8_t *yv12,
int width,
int height,
uint8_t *y_plane,
uint8_t *u_plane,
uint8_t *v_plane
)
{
int pixel_count = width * height;
int uv_count = pixel_count / 4;
/*
* YV12 内存布局:
*
* [ Y plane ][ V plane ][ U plane ]
*
* 注意:
* I420 是 [ Y ][ U ][ V ]
* YV12 是 [ Y ][ V ][ U ]
*/
const uint8_t *src_y = yv12;
const uint8_t *src_v = yv12 + pixel_count;
const uint8_t *src_u = yv12 + pixel_count + uv_count;
memcpy(y_plane, src_y, pixel_count);
memcpy(u_plane, src_u, uv_count);
memcpy(v_plane, src_v, uv_count);
}
三、RGB系
1. RGB565
2. RGB888
3. BGR888
4. RGB888P
5. ARGB8888
6. RGBA8888
7. BGRA8888