本文主要是介绍视音频数据处理入门:颜色空间(一)---转换理解,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
目录
一、YUV颜色空间
概述
I420的理解
YV12的理解
NV12的理解
NV21的理解
YV12与I420颜色空间相互转换
转换代码
NV12与I420颜色空间相互转换
转换代码
NV21与YV12颜色空间相互转换
转换代码
二、YUV转RGB
概述
RGB32的理解
RGB24的理解
I420与RGB32颜色空间相互转换
转换代码
I420与RGB24颜色空间相互转换
转换代码
三、代码路径
一、YUV颜色空间
概述
简单说一下I420、YV12、NV12、NV21的颜色空间排列方式,更容易理解它们直接相互方式。对于I420、YV12、NV12、NV21四中格式都属于YUV 4:2:0的的排列方式,我的理解是4个Y分量公用一个U和V分量。
I420的理解
I420(YU12
)属于YUV420P又称YUV420plane平面模式;Y、U、V
分别在不同平面,也就是有三个平面,它是YUV标准格式4:2:0
,且与YV12同属于
YUV420P;
对于I420的图像上排列方式如图-I420(1),首先是Y分量的排布,排布Y分量的宽为图像的宽(W),Y分量的高为图像的高(H); 其次是U分量的排布,排布U分量的宽为图像的宽的一半(W/2),U分量的高为图像的高(H/2);再次是V分量的排布,排布V分量的宽与U分量一致,分别为图像的宽的一半(W/2)和图像的高(H/2)。Y分量、U分量、V分量依次排列。
Y0 | Y1 | Y2 | Y3 | Y4 | Y5 | Y6 | Y7 |
Y8 | Y9 | Y10 | Y11 | Y12 | Y13 | Y14 | Y15 |
Y16 | Y17 | Y18 | Y19 | Y20 | Y21 | Y22 | Y23 |
Y24 | Y25 | Y26 | Y27 | Y28 | Y29 | Y30 | Y31 |
U0 | U1 | U2 | U3 | U4 | U5 | U6 | U7 |
V0 | V1 | V2 | V3 | V4 | V5 | V6 | V7 |
图-I420(1)
如 图-I420(1)红色部分Y0、Y1、Y8、Y9共用U0和V0分量;依次类推,对应颜色的Y分量共用对应颜色的U和V分量。
i420的内存排列,如图-I420(2),依次是Y分量、U分量、V分量;
其中Y分量内存大小为:
Y分量buff_size = W * H ;
其中U分量内存大小为:
U分量buff_size = W/2 * H/2 ,即W * H /4 ;
其中V分量内存大小为:
V分量buff_size = W/2 * H/2 ,即W * H /4 ;
i420图像大小 = Y分量大小 + U分量大小 +V分量大小
即:
i420图像大小 =W * H *3/2
图-I420(2)
YV12的理解
YV12属于YUV420P又称YUV420plane平面模式;Y、U、V
分别在不同平面,也就是有三个平面,它是YUV标准格式4:2:0
,且与(I420)YU12同属于
YUV420P;
对于YV12的图像上排列方式如图-YV12(1),首先是Y分量的排布,排布Y分量的宽为图像的宽(W),Y分量的高为图像的高(H); 其次是U分量的排布,排布U分量的宽为图像的宽的一半(W/2),U分量的高为图像的高(H/2);再次是V分量的排布,排布V分量的宽与U分量一致,分别为图像的宽的一半(W/2)和图像的高(H/2)。Y分量、U分量、V分量依次排列。
Y0 | Y1 | Y2 | Y3 | Y4 | Y5 | Y6 | Y7 |
Y8 | Y9 | Y10 | Y11 | Y12 | Y13 | Y14 | Y15 |
Y16 | Y17 | Y18 | Y19 | Y20 | Y21 | Y22 | Y23 |
Y24 | Y25 | Y26 | Y27 | Y28 | Y29 | Y30 | Y31 |
V0 | V1 | V2 | V3 | V4 | V5 | V6 | V7 |
U0 | U1 | U2 | U3 | U4 | U5 | U6 | U7 |
图-YV12(1)
如 图-YV12(1)红色部分Y0、Y1、Y8、Y9共用U0和V0分量;依次类推,对应颜色的Y分量共用对应颜色的U和V分量。
YV12的内存排列,如图-YV12(2),依次是Y分量、U分量、V分量;
其中Y分量内存大小为:
Y分量buff_size = W * H ;
其中U分量内存大小为:
U分量buff_size = W/2 * H/2 ,即W * H /4 ;
其中V分量内存大小为:
V分量buff_size = W/2 * H/2 ,即W * H /4 ;
YV12图像大小 = Y分量大小 + U分量大小 +V分量大小
即:
YV12图像大小 =W * H *3/2
图-YV12(2)
NV12的理解
NV12属于YUV420SP,即Y和UV分为两个Plane,但是UV为交错存储,而不是分为三个plane。对于NV12的图像上排列方式如图-NV12(1),首先是Y分量的排布,排布Y分量的宽为图像的宽(W),Y分量的高为图像的高(H); 其次是UV分量的交错排布。Y分量、UV分量依次排列。
Y0 | Y1 | Y2 | Y3 | Y4 | Y5 | Y6 | Y7 |
Y8 | Y9 | Y10 | Y11 | Y12 | Y13 | Y14 | Y15 |
Y16 | Y17 | Y18 | Y19 | Y20 | Y21 | Y22 | Y23 |
Y24 | Y25 | Y26 | Y27 | Y28 | Y29 | Y30 | Y31 |
U0 | V0 | U1 | V1 | U2 | V2 | U3 | V3 |
U4 | V4 | U5 | V5 | U6 | V6 | U7 | V7 |
图-NV12(1)
如 图-NV12(1)红色部分Y0、Y1、Y8、Y9共用U0和V0分量;依次类推,对应颜色的Y分量共用对应颜色的U和V分量。
NV12的内存排列,如图-NV12(2),依次是Y分量、U分量、V分量;
其中Y分量内存大小为:
Y分量buff_size = W * H ;
其中U分量内存大小为:
U分量buff_size = W/2 * H/2 ,即W * H /4 ;
其中V分量内存大小为:
V分量buff_size = W/2 * H/2 ,即W * H /4 ;
NV12图像大小 = Y分量大小 + U分量大小 +V分量大小
即:
YV12图像大小 =W * H *3/2
图-NV12(2)
NV21的理解
NV21属于YUV420SP,即Y和UV分为两个Plane,但是VU为交错存储,而不是分为三个plane。对于NV21的图像上排列方式如图-NV21(1),首先是Y分量的排布,排布Y分量的宽为图像的宽(W),Y分量的高为图像的高(H); 其次是VU分量的交错排布。Y分量、VU分量依次排列。
NV21与NV12 本质区别是V和U的先后区别。
Y0 | Y1 | Y2 | Y3 | Y4 | Y5 | Y6 | Y7 |
Y8 | Y9 | Y10 | Y11 | Y12 | Y13 | Y14 | Y15 |
Y16 | Y17 | Y18 | Y19 | Y20 | Y21 | Y22 | Y23 |
Y24 | Y25 | Y26 | Y27 | Y28 | Y29 | Y30 | Y31 |
V0 | U0 | V1 | U1 | V2 | U2 | V3 | U3 |
V4 | U4 | V5 | U5 | V6 | U6 | V7 | U7 |
图-NV21(1)
如 图-NV21(1)红色部分Y0、Y1、Y8、Y9共用U0和V0分量;依次类推,对应颜色的Y分量共用对应颜色的U和V分量。
YV12的内存排列,如图-NV21(2),依次是Y分量、U分量、V分量;
其中Y分量内存大小为:
Y分量buff_size = W * H ;
其中U分量内存大小为:
U分量buff_size = W/2 * H/2 ,即W * H /4 ;
其中V分量内存大小为:
V分量buff_size = W/2 * H/2 ,即W * H /4 ;
NV21图像大小 = Y分量大小 + U分量大小 +V分量大小
即:
NV21图像大小 =W * H *3/2
图-NV21(2)
YV12与I420颜色空间相互转换
由YV12和I420的内存排列可知,Y的排列方式是相同的,不同的是U和V的排列;YV12的排列依次是Y 、V 、U ;而I420的排列顺序依次是Y 、U 、V。由此可见只要改变U、V的排列顺序即可实现YV12与I420的相互转换。
转换代码
int YV12_2_I420(uint8_t* src, uint8_t* dst, uint32_t width, uint32_t height)
{uint8_t* src_y = src;uint8_t* src_u = src + width * height;uint8_t* src_v = src + width * height * 5 / 4;uint8_t* dst_y = dst;uint8_t* dst_v = dst + width * height;uint8_t* dst_u = dst + width * height * 5 / 4;memcpy(dst_y, src_y, width * height);memcpy(dst_v, src_v, width * height / 4);memcpy(dst_u, src_u, width * height / 4);return 0;
}int I420_2_YV12(uint8_t* src, uint8_t* dst, uint32_t width, uint32_t height)
{uint8_t* src_y = src;uint8_t* src_u = src + width* height;uint8_t* src_v = src + width * height*5/4;uint8_t* dst_y = dst;uint8_t* dst_v = dst + width * height;uint8_t* dst_u = dst + width * height * 5 / 4;memcpy(dst_y, src_y, width * height);memcpy(dst_v, src_v, width * height/4);memcpy(dst_u, src_u, width * height/4);return 0;
}
NV12与I420颜色空间相互转换
由NV12和I420的内存排列可知,Y的排列方式是相同的,不同的是U和V的排列;NV12的U、V是交错式排列排列顺序依次是Y、UV排列。而I420的U、V依次排序。由此可见只要改变U、V的排列顺序即可实现NV12与I420的相互转换。
转换代码
int I420_2_NV12(uint8_t* src, uint8_t* dst, uint32_t width, uint32_t height)
{uint8_t* src_y = src;uint8_t* src_u = src + width * height;uint8_t* src_v = src + width * height * 5 / 4;uint8_t* dst_y = dst;uint8_t* dst_uv = dst + width * height;memcpy(dst_y, src_y, width * height);//UV交错排列for (int i = 0; i < width / 2 * height / 2; i++){dst_uv[2*i] = src_u[i];dst_uv[2 * i+1] = src_v[i];}return 0;
}int NV21_2_YV12(uint8_t* src, uint8_t* dst, uint32_t width, uint32_t height)
{uint8_t* src_y = src;uint8_t* src_uv = src + width * height;uint8_t* dst_y = dst;uint8_t* dst_v = dst + width * height;uint8_t* dst_u = dst + width * height * 5 / 4;memcpy(dst_y, src_y, width * height);//UV交错排列for (int i = 0; i < width / 2 * height / 2; i++){dst_v[i] = src_uv[2 * i];dst_u[i] = src_uv[2 * i + 1];}return 0;
}
NV21与YV12颜色空间相互转换
由NV12和YV12的内存排列可知,Y的排列方式是相同的,不同的是U和V的排列;NV12的U、V是交错式排列排列顺序依次是Y、UV排列。而YV12的U、V依次排序。由此可见只要改变U、V的排列顺序即可实现NV12与I420的相互转换。基本与I420转换一样。
转换代码
int NV21_2_YV12(uint8_t* src, uint8_t* dst, uint32_t width, uint32_t height)
{uint8_t* src_y = src;uint8_t* src_uv = src + width * height;uint8_t* dst_y = dst;uint8_t* dst_v = dst + width * height;uint8_t* dst_u = dst + width * height * 5 / 4;memcpy(dst_y, src_y, width * height);for (int i = 0; i < width / 2 * height / 2; i++){dst_v[i] = src_uv[2 * i];dst_u[i] = src_uv[2 * i + 1];}return 0;
}int YV12_2_NV21(uint8_t* src, uint8_t* dst, uint32_t width, uint32_t height)
{uint8_t* src_y = src;uint8_t* src_v = src + width * height;uint8_t* src_u = src + width * height * 5 / 4;uint8_t* dst_y = dst;uint8_t* dst_uv = dst + width * height;memcpy(dst_y, src_y, width * height);for (int i = 0; i < width / 2 * height / 2; i++){dst_uv[2 * i] = src_v[i];dst_uv[2 * i + 1] = src_u[i];}return 0;
}
二、YUV转RGB
概述
RGB32与RGB24整体排列方式是一致,RGB32比RGB24多了一个预留位。也称做A通道。
RGB32的理解
RGB32由图-RGB32(1)可以看出来,R、G、B、A组成一个像素点;同事一个像素点占4个字节;R、G、B、A是交错排列的。
R0 | G0 | B0 | A0 | R1 | G1 | B1 | A1 | R2 | G2 | B2 | A2 | R3 | G3 | B3 | A3 | R4 | G4 | B4 | A4 | R5 | G5 | B5 | A5 | R6 | G6 | B6 | A6 | R7 | G7 | B7 | A7 |
R8 | G8 | B8 | A8 | R9 | G9 | B9 | A9 | R10 | G10 | B10 | A10 | R11 | G11 | B11 | A11 | R12 | G12 | B12 | A12 | R13 | G13 | B13 | A13 | R14 | G14 | B14 | A14 | R15 | G15 | B15 | A15 |
R16 | G16 | B16 | A16 | R17 | G17 | B17 | A17 | R18 | G18 | B18 | A18 | R19 | G19 | B19 | A19 | R20 | G20 | B20 | A20 | R21 | G21 | B21 | A21 | R22 | G22 | B22 | A22 | R23 | G23 | B23 | A23 |
R24 | G24 | B24 | A24 | R25 | G25 | B25 | A25 | R26 | G26 | B26 | A26 | R27 | G27 | B27 | A27 | R28 | G28 | B28 | A28 | R29 | G29 | B29 | A29 | R30 | G30 | B30 | A30 | R31 | G31 | B31 | A31 |
图-RGB32(1)
RGB24的理解
RGB32由图-RGB24(1)可以看出来,R、G、B组成一个像素点;同事一个像素点占3个字节;R、G、B是交错排列的。
R0 | G0 | B0 | R1 | G1 | B1 | R2 | G2 | B2 | R3 | G3 | B3 | R4 | G4 | B4 | R5 | G5 | B5 | R6 | G6 | B6 | R7 | G7 | B7 |
R8 | G8 | B8 | R9 | G9 | B9 | R10 | G10 | B10 | R11 | G11 | B11 | R12 | G12 | B12 | R13 | G13 | B13 | R14 | G14 | B14 | R15 | G15 | B15 |
R16 | G16 | B16 | R17 | G17 | B17 | R18 | G18 | B18 | R19 | G19 | B19 | R20 | G20 | B20 | R21 | G21 | B21 | R22 | G22 | B22 | R23 | G23 | B23 |
R24 | G24 | B24 | R25 | G25 | B25 | R26 | G26 | B26 | R27 | G27 | B27 | R28 | G28 | B28 | R29 | G29 | B29 | R30 | G30 | B30 | R31 | G31 | B31 |
图-RGB24(1)
I420与RGB32颜色空间相互转换
I420与RGB32相互转换不同于yuv的转换。I420 2行共用一个U分量和V分量。所以RGB转yuv的时候应该注意排列方式。
转换代码
typedef struct RGB32 {uint8_t rgbBlue; // 蓝色分量uint8_t rgbGreen; // 绿色分量uint8_t rgbRed; // 红色分量uint8_t rgbReserved; // 保留字节(用作Alpha通道或忽略)RGB32(){rgbBlue = 0;rgbGreen = 0;rgbRed = 0;rgbReserved = 0;}
} RGB32;int I420_2_rgb32(uint8_t* src, uint8_t* dst, uint32_t width, uint32_t height)
{uint8_t* src_y = src;uint8_t* src_u = src + width * height;uint8_t* src_v = src + width * height * 5 / 4;RGB32* prgb = reinterpret_cast<RGB32*>(dst);int rgbindex = 0;for (int y = 0; y < height; y++){for (int x = 0; x < width; x++){RGB32* rgbNode = &prgb[rgbindex];//读取YUVuint8_t Y = src_y[y * width + x];uint8_t U = src_u[y / 2 * width / 2 + x / 2];uint8_t V = src_v[y / 2 * width / 2 + x / 2];//计算rgbint tmpr = Y + 1.402 * (V - 128);int tmpg = Y - 0.34413 * (U - 128) - 0.71414 * (V - 128);int tmpb = Y + 1.772 * (U - 128);//越界保护rgbNode->rgbRed = tmpr > 255 ? 255 : (tmpr < 0 ? 0 : tmpr);rgbNode->rgbGreen =tmpg > 255 ? 255 : (tmpg < 0 ? 0 : tmpg);rgbNode->rgbBlue = tmpb > 255 ? 255 : (tmpb < 0 ? 0 : tmpb);rgbindex++;}}return 0;
}int rgb32_2_I420(uint8_t* src, uint8_t* dst, uint32_t width, uint32_t height)
{RGB32* prgb = reinterpret_cast<RGB32*>(src);uint8_t* dst_y = dst;uint8_t* dst_u = dst + width * height;uint8_t* dst_v = dst + width * height * 5 / 4;//Y = 0.298R + 0.612G + 0.117B;//U = -0.168R - 0.330G + 0.498B + 128;//V = 0.449R - 0.435G - 0.083B + 128;int rgbindex = 0;int u_index = 0;int v_index = 0;int y_index = 0;for (int y = 0; y < height; y++){for (int x = 0; x < width; x++){rgbindex = y * width + x;RGB32* rgbNode = &prgb[rgbindex];//Y = 0.298R + 0.612G + 0.117B;int tmp_y = 0.298*rgbNode->rgbRed + 0.612 * rgbNode->rgbGreen + 0.117*rgbNode->rgbBlue;dst_y[y_index] = tmp_y > 255 ? 255 : (tmp_y < 0 ? 0 : tmp_y);y_index++;if (y%2 ==0 &&x%2 == 0){//U = -0.168R - 0.330G + 0.498B + 128;int tmp_u = -0.168 * rgbNode->rgbRed - 0.330 * rgbNode->rgbGreen + 0.498 * rgbNode->rgbBlue + 128;//V = 0.449R - 0.435G - 0.083B + 128;int tmp_v = 0.449 * rgbNode->rgbRed - 0.435 * rgbNode->rgbGreen + 0.083 * rgbNode->rgbBlue + 128;dst_u[u_index] = tmp_u > 255 ? 255 : (tmp_u < 0 ? 0 : tmp_u);dst_v[v_index] = tmp_v > 255 ? 255 : (tmp_v < 0 ? 0 : tmp_v);v_index++;u_index++;}}}return 0;
}
I420与RGB24颜色空间相互转换
rgb24与rgb32计算方式基本一致。却别在于rgb32每个像素点4字节;gb24每个像素点3字节。
转换代码
typedef struct RGB24 {uint8_t rgbBlue; // 蓝色分量uint8_t rgbGreen; // 绿色分量uint8_t rgbRed; // 红色分量RGB24(){rgbBlue = 0;rgbGreen = 0;rgbRed = 0;}
} RGB24;int I420_2_rgb24(uint8_t* src, uint8_t* dst, uint32_t width, uint32_t height)
{uint8_t* src_y = src;uint8_t* src_u = src + width * height;uint8_t* src_v = src + width * height * 5 / 4;RGB24* prgb = reinterpret_cast<RGB24*>(dst);int rgbindex = 0;for (int y = 0; y < height; y++){for (int x = 0; x < width; x++){RGB24* rgbNode = &prgb[rgbindex];//读取YUVuint8_t Y = src_y[y * width + x];uint8_t U = src_u[y / 2 * width / 2 + x / 2];uint8_t V = src_v[y / 2 * width / 2 + x / 2];//计算rgb//R = Y + 1.4075(V - 128);//G = Y - 0.3455(U - 128) - 0.7169(V - 128);//B = Y + 1.779(U - 128);int tmpr = Y + 1.402 * (V - 128);int tmpg = Y - 0.34413 * (U - 128) - 0.71414 * (V - 128);int tmpb = Y + 1.772 * (U - 128);//越界保护rgbNode->rgbRed = tmpr > 255 ? 255 : (tmpr < 0 ? 0 : tmpr);rgbNode->rgbGreen = tmpg > 255 ? 255 : (tmpg < 0 ? 0 : tmpg);rgbNode->rgbBlue = tmpb > 255 ? 255 : (tmpb < 0 ? 0 : tmpb);rgbindex++;}}return 0;
}int rgb24_2_I420(uint8_t* src, uint8_t* dst, uint32_t width, uint32_t height)
{RGB24* prgb = reinterpret_cast<RGB24*>(src);uint8_t* dst_y = dst;uint8_t* dst_u = dst + width * height;uint8_t* dst_v = dst + width * height * 5 / 4;//Y = 0.298R + 0.612G + 0.117B;//U = -0.168R - 0.330G + 0.498B + 128;//V = 0.449R - 0.435G - 0.083B + 128;int rgbindex = 0;int u_index = 0;int v_index = 0;int y_index = 0;for (int y = 0; y < height; y++){for (int x = 0; x < width; x++){rgbindex = y * width + x;RGB24* rgbNode = &prgb[rgbindex];//Y = 0.298R + 0.612G + 0.117B;int tmp_y = 0.298 * rgbNode->rgbRed + 0.612 * rgbNode->rgbGreen + 0.117 * rgbNode->rgbBlue;dst_y[y_index] = tmp_y > 255 ? 255 : (tmp_y < 0 ? 0 : tmp_y);y_index++;if (y % 2 == 0 &&x % 2 == 0){//U = -0.168R - 0.330G + 0.498B + 128;int tmp_u = -0.168 * rgbNode->rgbRed - 0.330 * rgbNode->rgbGreen + 0.498 * rgbNode->rgbBlue + 128;//V = 0.449R - 0.435G - 0.083B + 128;int tmp_v = 0.449 * rgbNode->rgbRed - 0.435 * rgbNode->rgbGreen + 0.083 * rgbNode->rgbBlue + 128;dst_u[u_index] = tmp_u > 255 ? 255 : (tmp_u < 0 ? 0 : tmp_u);dst_v[v_index] = tmp_v > 255 ? 255 : (tmp_v < 0 ? 0 : tmp_v);v_index++;u_index++;}}}return 0;
}
三、代码路径
csdn:https://download.csdn.net/download/u011645307/21113273
github:https://github.com/liangqidong/ColorConversion.git
这篇关于视音频数据处理入门:颜色空间(一)---转换理解的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!