图像格式基础

一、RAW

plain 复制代码
工作中使用过的场景
1 mipi传输raw12的带宽过高,硬件测试不通过,后来修改raw10 降低带宽

1. RAW8

2. RAW10

3. RAW12

二、YUV

c 复制代码
Y  = 亮度
U/Cb = 蓝色色度
V/Cr = 红色色度

1) 格式

1. YUV422_UYVY(packed 4:2:2,字节序 U Y V Y)
cpp 复制代码
/*
 * 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)
cpp 复制代码
#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)
cpp 复制代码
/*
 * 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)
c 复制代码
/*
 * 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 交织)
cpp 复制代码
先放所有 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 交织)
cpp 复制代码
/*
 * 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-三平面)
cpp 复制代码
/*
 * 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-三平面)
cpp 复制代码
/*
 * 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)
cpp 复制代码
/*
 * 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)
cpp 复制代码
/*
 * 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-三平面)
cpp 复制代码
/*
 * 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-三平面)
cpp 复制代码
/*
 * 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

相关推荐
大江东去浪淘尽千古风流人物4 天前
【Micro-WL Robot】桌面级轮腿机器人全栈解析:LQR平衡控制、SimpleFOC驱动与五连杆腿部机构源码深度拆解
驱动开发·机器人·esp32·lqr·simplefoc·轮腿机器人·平衡控制
咖啡星人k4 天前
自然语言驱动开发(NLDD):全栈开发的新范式与实践指南
驱动开发
阿昭L4 天前
Windows键盘过滤
windows·驱动开发·windows内核·过滤驱动
hai3152475435 天前
# 矩阵算法·算子对齐工具 v6.1 — 技术规格与使用手册
java·开发语言·驱动开发·神经网络·spring·目标检测·矩阵
qq_411262426 天前
sdk不支持分配psarm如何办,能不能象S3一样支持
驱动开发
湉湉家的小虎子6 天前
【科普贴】浅谈UFS接口——偏硬件解析
驱动开发·嵌入式硬件·fpga开发·硬件工程
枳实-叶7 天前
【Linux驱动开发】第18天:I2C驱动深度解析
linux·运维·驱动开发
小此方7 天前
Re:Linux系统篇(二十五)进程篇·十:深度硬核!Linux 进程等待,从 task_struct 源码到位图状态解构
linux·运维·驱动开发
Gentle5867 天前
SENT&SPC协议中的CRC4校验
驱动开发