基本概念见:
音视频编解码: YUV存储格式中的YUV420P,YUV420SP,NV12, NV21理解(转)
摘录一些笔记:
RGB与YUV的转换
对于图像显示器来说,它是通过 RGB 模型来显示图像的,而在传输图像数据时又是使用 YUV 模型,这是因为 YUV 模型可以节省带宽。因此就需要采集图像时将 RGB 模型转换到 YUV 模型,显示时再将 YUV 模型转换为 RGB 模型。
YUV的采样格式
1. YUV 444 //表示色度频道没有下采样 2. YUV 422 // 表示 2:1 的水平下采样,没有垂直下采样。 //对于每两个 U 样例或 V 样例,每个扫描行都包含四个 Y 样例。 3. YUV 420 //表示 2:1 的水平下采样,2:1 的垂直下采样。
YUV的存储格式
1. planar 平面格式 //指先连续存储所有像素点的 Y 分量,然后存储 U 分量,最后是 V 分量。 //将YUV分量存放在同一个数组中,通常是几个相邻的像素组成一个宏像素; 2. packed 打包模式 // 指每个像素点的 Y、U、V 分量是连续交替存储的。 // 使用三个数组分开存放YUV三个分量,就像是一个三维平面一样
YUV 4:2:0
YUV 4:2:0 并不意味着不采样 V 分量。它指的是对每条扫描线来说,只有一种色度分量以 2:1 的采样率存储,相邻的扫描行存储不同的色度分量。也就是说,如果第一行是 4:2:0,下一行就是 4:0:2,在下一行就是 4:2:0,以此类推。
图像像素为:
[Y0 U0 V0]、[Y1 U1 V1]、 [Y2 U2 V2]、 [Y3 U3 V3]
[Y5 U5 V5]、[Y6 U6 V6]、 [Y7 U7 V7] 、[Y8 U8 V8]
采样的码流为:
Y0 U0 Y1 Y2 U2 Y3
Y5 V5 Y6 Y7 V7 Y8
映射出的像素点为:
[Y0 U0 V5]、[Y1 U0 V5]、[Y2 U2 V7]、[Y3 U2 V7]
[Y5 U0 V5]、[Y6 U0 V5]、[Y7 U2 V7]、[Y8 U2 V7]
更形象的图如下:YUV420 SP nv12
yuv420sp 的存储数据分布
其在码流中的表现形式:
如上图,假设这是一个6*4像素大小的图片
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <stdio.h> 2 #define READ_MAX (1024) 3 4 typedef unsigned char uInt8; 5 typedef unsigned short uInt16; 6 typedef unsigned int uInt32; 7 typedef char Int8; 8 typedef short Int16; 9 typedef int Int32; 10 11 typedef enum 12 { 13 TYPE_YUV422I_UYVY, 14 TYPE_YUV422I_YUYV, 15 TYPE_YUV420SP_NV12, 16 TYPE_YUV420SP_NV21, 17 TYPE_YUV422P, 18 TYPE_YUV444I, 19 TYPE_YUV444P, 20 }enYuvType; 21 22 typedef enum 23 { 24 YUV_GREEN, 25 YUV_RED, 26 YUV_BLUE, 27 YUV_PURPLE, 28 YUV_DARK_GREEN, 29 YUV_YELLOW, 30 YUV_LIGHT_BLUE, 31 YUV_LIGHT_PURPLE, 32 YUV_DARK_BLACK, 33 YUV_GRAY, 34 YUV_WHITE, 35 YUV_COLOR_MAX, 36 }enYuvColorIdx; 37 38 typedef struct 39 { 40 uInt8 Y; 41 uInt8 U; 42 uInt8 V; 43 }stYuvColor; 44 45 typedef struct 46 { 47 uInt16 x; 48 uInt16 y; 49 }stPoint; 50 51 typedef struct 52 { 53 stPoint startPoint; 54 stPoint endPoint; 55 uInt16 lineWidth; 56 enYuvColorIdx clrIdx; 57 }stDrawLineInfo; 58 59 typedef struct 60 { 61 enYuvType yuvType; 62 uInt8 *pYuvBuff; 63 uInt16 width; 64 uInt16 height; 65 }stYuvBuffInfo; 66 67 static stYuvColor s_color_table[YUV_COLOR_MAX] = { 68 {0x00, 0x00, 0x00}, // green 69 {0x00, 0x00, 0xff}, // red 70 {0x00, 0xff, 0x00}, // blue 71 {0x00, 0xff, 0xff}, // purple 72 {0xff, 0x00, 0x00}, // dark green 73 {0xff, 0x00, 0xff}, // yellow 74 {0xff, 0xff, 0x00}, // light blue 75 {0xff, 0xff, 0xff}, // light purple 76 {0x00, 0x80, 0x80}, // dark black 77 {0x80, 0x80, 0x80}, // gray 78 {0xff, 0x80, 0x80}, // white 79 }; 80 81 Int32 read_file(const char* file, uInt8 *pOut) 82 { 83 Int32 size = 0; 84 uInt8 *p = pOut; 85 FILE* fp = fopen(file, "rb"); 86 if(fp && pOut) { 87 int n = 0; 88 while((n = fread(p, 1, READ_MAX, fp)) > 0) { 89 size += n; 90 p += n; 91 if(n < READ_MAX) 92 break; 93 } 94 fclose(fp); 95 printf(" %s, size = %d ", file, size); 96 } 97 else { 98 printf("Open %s png file fail ", file); 99 } 100 101 return size; 102 } 103 104 105 void yuv_setdata( 106 uInt8* YBuff, 107 uInt8* UVBuff, 108 enYuvType yuvType, 109 uInt16 width, 110 uInt16 height, 111 stPoint draw_point, 112 enYuvColorIdx clrIdx) 113 { 114 switch(yuvType) 115 { 116 case TYPE_YUV422I_UYVY: 117 case TYPE_YUV422I_YUYV: 118 { 119 /* 120 UYVY UYVY UYVY UYVY 121 */ 122 uInt32 tmp = draw_point.y * width * 2; 123 uInt32 y_offset = 0, u_offset = 0, v_offset = 0; 124 if(yuvType == TYPE_YUV422I_UYVY) { 125 u_offset = tmp + draw_point.x / 2 * 4; 126 v_offset = u_offset + 2; 127 y_offset = u_offset + 1; 128 } 129 else { 130 y_offset = tmp + draw_point.x / 2 * 4; 131 u_offset = y_offset + 1; 132 v_offset = u_offset + 2; 133 } 134 YBuff[y_offset] = s_color_table[clrIdx].Y; 135 YBuff[y_offset + 2] = s_color_table[clrIdx].Y; 136 YBuff[u_offset] = s_color_table[clrIdx].U; 137 YBuff[v_offset] = s_color_table[clrIdx].V; 138 }break; 139 case TYPE_YUV420SP_NV12: 140 case TYPE_YUV420SP_NV21: 141 { 142 /* 143 YY YY 144 YY YY 145 UV UV 146 */ 147 uInt32 y_offset = draw_point.y * width + draw_point.x; 148 uInt32 u_offset = 0, v_offset = 0; 149 YBuff[y_offset] = s_color_table[clrIdx].Y; 150 #if 0 151 Int32 x_flag = 1, y_flag = 1; 152 if(draw_point.y % 2 == 0) { 153 YBuff[y_offset + width] = s_color_table[clrIdx].Y; 154 y_flag = 1; 155 } 156 else { 157 YBuff[y_offset - width] = s_color_table[clrIdx].Y; 158 y_flag = -1; 159 } 160 161 if(draw_point.x % 2 == 0) { 162 YBuff[y_offset + 1] = s_color_table[clrIdx].Y; 163 x_flag = 1; 164 } 165 else { 166 YBuff[y_offset - 1] = s_color_table[clrIdx].Y; 167 x_flag = -1; 168 } 169 YBuff[y_offset + width * y_flag + 1 * x_flag] = s_color_table[clrIdx].Y; 170 #endif 171 172 if(yuvType == TYPE_YUV420SP_NV12) { 173 u_offset = (draw_point.y / 2) * width + draw_point.x / 2 * 2; 174 v_offset = u_offset + 1; 175 } 176 else { 177 v_offset = (draw_point.y / 2) * width + draw_point.x / 2 * 2; 178 u_offset = v_offset + 1; 179 } 180 UVBuff[u_offset] = s_color_table[clrIdx].U; 181 UVBuff[v_offset] = s_color_table[clrIdx].V; 182 //printf("[%d, %d]: y_offset = %d, u_offset = %d, v_offset = %d ", 183 // draw_point.x, draw_point.y, y_offset, u_offset, v_offset); 184 }break; 185 case TYPE_YUV444P: 186 { 187 /* 188 YYYYYYYY 189 UUUUUUUU 190 VVVVVVVV 191 */ 192 uInt32 y_offset = 0, u_offset = 0, v_offset = 0; 193 uInt32 plane_size = width * height; 194 y_offset = draw_point.y * width + draw_point.x; 195 u_offset = y_offset; 196 v_offset = plane_size + u_offset; 197 YBuff[y_offset] = s_color_table[clrIdx].Y; 198 UVBuff[u_offset] = s_color_table[clrIdx].U; 199 UVBuff[v_offset] = s_color_table[clrIdx].V; 200 }break; 201 case TYPE_YUV444I: 202 { 203 /* 204 YUV YUV YUV YUV YUV YUV YUV YUV 205 */ 206 uInt32 y_offset = 0, u_offset = 0, v_offset = 0; 207 y_offset = draw_point.y * width * 3 + draw_point.x * 3; 208 u_offset = y_offset + 1; 209 v_offset = u_offset + 1; 210 YBuff[y_offset] = s_color_table[clrIdx].Y; 211 YBuff[u_offset] = s_color_table[clrIdx].U; 212 YBuff[v_offset] = s_color_table[clrIdx].V; 213 }break; 214 case TYPE_YUV422P: 215 { 216 /* 217 YYYYYYYY 218 UUUU 219 VVVV 220 */ 221 uInt32 y_offset = 0, u_offset = 0, v_offset = 0; 222 uInt32 plane_size = width * height / 2; 223 y_offset = draw_point.y * width + draw_point.x; 224 u_offset = (draw_point.y / 2) * width + draw_point.x / 2; 225 v_offset = plane_size + u_offset; 226 YBuff[y_offset] = s_color_table[clrIdx].Y; 227 UVBuff[u_offset] = s_color_table[clrIdx].U; 228 UVBuff[v_offset] = s_color_table[clrIdx].V; 229 }break; 230 } 231 } 232 233 void yuv_drawline(stYuvBuffInfo *pYuvBuffInfo, stDrawLineInfo *pDrawLineInfo) 234 { 235 if(!pYuvBuffInfo || !pYuvBuffInfo->pYuvBuff) return; 236 237 uInt8 *YBuff = NULL, *UVBuff = NULL; 238 uInt16 x0 = pDrawLineInfo->startPoint.x, y0 = pDrawLineInfo->startPoint.y; 239 uInt16 x1 = pDrawLineInfo->endPoint.x, y1 = pDrawLineInfo->endPoint.y; 240 241 if(pDrawLineInfo->lineWidth == 0) pDrawLineInfo->lineWidth = 1; 242 x0 = (x0 >= pYuvBuffInfo->width) ? (x0 - pDrawLineInfo->lineWidth) : x0; 243 x1 = (x1 >= pYuvBuffInfo->width) ? (x1 - pDrawLineInfo->lineWidth) : x1; 244 y0 = (y0 >= pYuvBuffInfo->height) ? (y0 - pDrawLineInfo->lineWidth) : y0; 245 y1 = (y1 >= pYuvBuffInfo->height) ? (y1 - pDrawLineInfo->lineWidth) : y1; 246 247 uInt16 dx = (x0 > x1) ? (x0 - x1) : (x1 - x0); 248 uInt16 dy = (y0 > y1) ? (y0 - y1) : (y1 - y0); 249 250 Int16 xstep = (x0 < x1) ? 1 : -1; 251 Int16 ystep = (y0 < y1) ? 1 : -1; 252 Int16 nstep = 0, eps = 0; 253 254 stPoint draw_point; 255 draw_point.x = x0; 256 draw_point.y = y0; 257 258 switch(pYuvBuffInfo->yuvType) 259 { 260 case TYPE_YUV422I_UYVY: 261 case TYPE_YUV422I_YUYV: 262 case TYPE_YUV444I: 263 { 264 YBuff = pYuvBuffInfo->pYuvBuff; 265 UVBuff = NULL; 266 }break; 267 case TYPE_YUV420SP_NV12: 268 case TYPE_YUV420SP_NV21: 269 case TYPE_YUV444P: 270 case TYPE_YUV422P: 271 { 272 YBuff = pYuvBuffInfo->pYuvBuff; 273 UVBuff = pYuvBuffInfo->pYuvBuff + pYuvBuffInfo->width * pYuvBuffInfo->height; 274 }break; 275 default: 276 return; 277 } 278 279 // 布雷森汉姆算法画线 280 if(dx > dy){ 281 while(nstep <= dx) { 282 yuv_setdata(YBuff, UVBuff, pYuvBuffInfo->yuvType, pYuvBuffInfo->width, pYuvBuffInfo->height, draw_point, pDrawLineInfo->clrIdx); 283 eps += dy; 284 if( (eps << 1) >= dx ) { 285 draw_point.y += ystep; 286 eps -= dx; 287 } 288 draw_point.x += xstep; 289 nstep++; 290 } 291 }else { 292 while(nstep <= dy){ 293 yuv_setdata(YBuff, UVBuff, pYuvBuffInfo->yuvType, pYuvBuffInfo->width, pYuvBuffInfo->height, draw_point, pDrawLineInfo->clrIdx); 294 eps += dx; 295 if( (eps << 1) >= dy ) { 296 draw_point.x += xstep; 297 eps -= dy; 298 } 299 draw_point.y += ystep; 300 nstep++; 301 } 302 } 303 } 304 305 void draw_rect(stYuvBuffInfo* yuvBuffInfo) 306 { 307 stDrawLineInfo drawLineInfo; 308 drawLineInfo.clrIdx = YUV_RED; 309 drawLineInfo.lineWidth = 1; 310 drawLineInfo.startPoint.x = 160; 311 drawLineInfo.startPoint.y = 140; 312 drawLineInfo.endPoint.x = 560; 313 drawLineInfo.endPoint.y = 340; 314 yuv_drawline(yuvBuffInfo, &drawLineInfo); 315 316 drawLineInfo.clrIdx = YUV_PURPLE; 317 drawLineInfo.lineWidth = 1; 318 drawLineInfo.startPoint.x = 560; 319 drawLineInfo.startPoint.y = 140; 320 drawLineInfo.endPoint.x = 160; 321 drawLineInfo.endPoint.y = 340; 322 yuv_drawline(yuvBuffInfo, &drawLineInfo); 323 324 drawLineInfo.clrIdx = YUV_YELLOW; 325 drawLineInfo.lineWidth = 1; 326 drawLineInfo.startPoint.x = 160; 327 drawLineInfo.startPoint.y = 140; 328 drawLineInfo.endPoint.x = 560; 329 drawLineInfo.endPoint.y = 140; 330 yuv_drawline(yuvBuffInfo, &drawLineInfo); 331 332 drawLineInfo.clrIdx = YUV_GREEN; 333 drawLineInfo.lineWidth = 1; 334 drawLineInfo.startPoint.x = 160; 335 drawLineInfo.startPoint.y = 140; 336 drawLineInfo.endPoint.x = 160; 337 drawLineInfo.endPoint.y = 340; 338 yuv_drawline(yuvBuffInfo, &drawLineInfo); 339 340 drawLineInfo.clrIdx = YUV_BLUE; 341 drawLineInfo.lineWidth = 1; 342 drawLineInfo.startPoint.x = 160; 343 drawLineInfo.startPoint.y = 340; 344 drawLineInfo.endPoint.x = 560; 345 drawLineInfo.endPoint.y = 340; 346 yuv_drawline(yuvBuffInfo, &drawLineInfo); 347 348 drawLineInfo.clrIdx = YUV_WHITE; 349 drawLineInfo.lineWidth = 1; 350 drawLineInfo.startPoint.x = 560; 351 drawLineInfo.startPoint.y = 140; 352 drawLineInfo.endPoint.x = 560; 353 drawLineInfo.endPoint.y = 340; 354 yuv_drawline(yuvBuffInfo, &drawLineInfo); 355 } 356 357 void main(int argc, char** argv) 358 { 359 stYuvBuffInfo yuvBuffInfo; 360 uInt8 *pBuff = (uInt8*)malloc(sizeof(uInt8) * 10 * 0x100000); // 10M 361 // 测试 NV12 格式 362 Int32 size = read_file("yuv_data.nv12", pBuff); 363 yuvBuffInfo.pYuvBuff = pBuff; 364 yuvBuffInfo.width = 720; 365 yuvBuffInfo.height = 480; 366 yuvBuffInfo.yuvType = TYPE_YUV420SP_NV12; 367 draw_rect(&yuvBuffInfo); 368 FILE* fp_save = fopen("./yuv_data_line.nv12", "wb+"); 369 fwrite(pBuff, size, 1, fp_save); 370 fclose(fp_save); 371 372 // 测试UYVY 格式 373 memset(pBuff, 0, sizeof(uInt8) * 10 * 0x100000); 374 size = read_file("yuv_data.uyvy", pBuff); 375 yuvBuffInfo.pYuvBuff = pBuff; 376 yuvBuffInfo.width = 720; 377 yuvBuffInfo.height = 480; 378 yuvBuffInfo.yuvType = TYPE_YUV422I_UYVY; 379 draw_rect(&yuvBuffInfo); 380 fp_save = fopen("./yuv_data_line.uyvy", "wb+"); 381 fwrite(pBuff, size, 1, fp_save); 382 383 // 测试 NV21 格式 384 size = read_file("yuv_data.nv21", pBuff); 385 yuvBuffInfo.pYuvBuff = pBuff; 386 yuvBuffInfo.width = 720; 387 yuvBuffInfo.height = 480; 388 yuvBuffInfo.yuvType = TYPE_YUV420SP_NV21; 389 draw_rect(&yuvBuffInfo); 390 fp_save = fopen("./yuv_data_line.nv21", "wb+"); 391 fwrite(pBuff, size, 1, fp_save); 392 fclose(fp_save); 393 394 // 测试YUYV 格式 395 memset(pBuff, 0, sizeof(uInt8) * 10 * 0x100000); 396 size = read_file("yuv_data.yuyv", pBuff); 397 yuvBuffInfo.pYuvBuff = pBuff; 398 yuvBuffInfo.width = 720; 399 yuvBuffInfo.height = 480; 400 yuvBuffInfo.yuvType = TYPE_YUV422I_YUYV; 401 draw_rect(&yuvBuffInfo); 402 fp_save = fopen("./yuv_data_line.yuyv", "wb+"); 403 fwrite(pBuff, size, 1, fp_save); 404 405 // 测试YUV444P 格式 406 memset(pBuff, 0, sizeof(uInt8) * 10 * 0x100000); 407 size = read_file("yuv_data.yuv444p", pBuff); 408 yuvBuffInfo.pYuvBuff = pBuff; 409 yuvBuffInfo.width = 720; 410 yuvBuffInfo.height = 480; 411 yuvBuffInfo.yuvType = TYPE_YUV444P; 412 draw_rect(&yuvBuffInfo); 413 fp_save = fopen("./yuv_data_line.yuv444p", "wb+"); 414 fwrite(pBuff, size, 1, fp_save); 415 416 // 测试YUV444I 格式 417 memset(pBuff, 0, sizeof(uInt8) * 10 * 0x100000); 418 size = read_file("yuv_data.yuv444i", pBuff); 419 yuvBuffInfo.pYuvBuff = pBuff; 420 yuvBuffInfo.width = 720; 421 yuvBuffInfo.height = 480; 422 yuvBuffInfo.yuvType = TYPE_YUV444I; 423 draw_rect(&yuvBuffInfo); 424 fp_save = fopen("./yuv_data_line.yuv444i", "wb+"); 425 fwrite(pBuff, size, 1, fp_save); 426 427 // 测试YUV422P 格式 428 memset(pBuff, 0, sizeof(uInt8) * 10 * 0x100000); 429 size = read_file("yuv_data.yuv422p", pBuff); 430 yuvBuffInfo.pYuvBuff = pBuff; 431 yuvBuffInfo.width = 720; 432 yuvBuffInfo.height = 480; 433 yuvBuffInfo.yuvType = TYPE_YUV422P; 434 draw_rect(&yuvBuffInfo); 435 fp_save = fopen("./yuv_data_line.yuv422p", "wb+"); 436 fwrite(pBuff, size, 1, fp_save); 437 438 fclose(fp_save); 439 free(pBuff); 440 return; 441 }