• YUV图像格式转换方法实践


    一 I420转NV12

    1.I420格式

    (1)I420是每四个Y共用一组UV,如果一帧I420图像宽带是width,高度是height,1个像素占1个字节,那么共有width✖height个Y,U和V都是width✖height / 4个,因此一帧I420图像占用字节数是width✖height✖3/2。

    (2)I420是先存完所有Y后,在接着存完U,最后存V。

    2.NV12格式

    (1)NV12是每四个Y共用一组UV,如果一帧NV12图像宽带是width,高度是height,1个像素占1个字节,那么共有width✖height个Y,U和V都是width✖height / 4个,因此一帧I420图像占用字节数是width✖height✖3/2。

    (2)NV12是先存完所有Y后,随后U、V交替存储。

    3.转换方法

    (1)将一帧I420图像中所有Y拷贝到NV12数据区,将一帧I420图像中所有U和V交替拷贝到NV12数据区。

    (2)代码:

    1. bool convert_i420_nv12(const char *i420_file_path, size_t width, size_t height, const char *nv12_file_path) {
    2. if (!i420_file_path || !nv12_file_path) {
    3. return false;
    4. }
    5. FILE *fp = fopen(i420_file_path, "rb");
    6. if (!fp) {
    7. return false;
    8. }
    9. fseek(fp, 0, SEEK_END);
    10. size_t file_size = ftell(fp);
    11. fseek(fp, 0, SEEK_SET);
    12. size_t y_size = width * height;
    13. size_t uv_size = y_size / 4;
    14. if (file_size != (y_size + uv_size * 2)) {
    15. fclose(fp);
    16. return false;
    17. }
    18. char *i420_content = (char *)malloc(sizeof(char) * file_size);
    19. if (!i420_content) {
    20. fclose(fp);
    21. return false;
    22. }
    23. if (file_size != fread(i420_content, 1, file_size, fp)) {
    24. free(i420_content);
    25. fclose(fp);
    26. return false;
    27. }
    28. fclose(fp);
    29. // convert i420 to nv12
    30. char *nv12_content = (char *)malloc(sizeof(char) * file_size);
    31. if (!nv12_content) {
    32. free(i420_content);
    33. return false;
    34. }
    35. // copy y channel
    36. memcpy(nv12_content, i420_content, y_size);
    37. // copy uv channel
    38. char *i420_u_base = i420_content + y_size;
    39. char *i420_v_base = i420_u_base + uv_size;
    40. char *nv12_uv_base = nv12_content + y_size;
    41. int i = 0, j = 0;
    42. for (;i < uv_size;i++) {
    43. nv12_uv_base[j] = i420_u_base[i];
    44. nv12_uv_base[j + 1] = i420_v_base[i];
    45. j += 2;
    46. }
    47. free(i420_content);
    48. fp = fopen(nv12_file_path, "wb");
    49. if (!fp) {
    50. free(nv12_content);
    51. return false;
    52. }
    53. if (file_size != fwrite(nv12_content, 1, file_size, fp)) {
    54. free(nv12_content);
    55. fclose(fp);
    56. return false;
    57. }
    58. free(nv12_content);
    59. fclose(fp);
    60. return true;
    61. }

     

    二 I420转NV21

    1.NV21格式

    NV21内存布局基本与NV12类似,只是将NV12中U和V次序互换,如下所示:

    2.转换代码

    1. bool convert_i420_nv21(const char *i420_file_path, size_t width, size_t height, const char *nv21_file_path) {
    2. if (!i420_file_path || !nv21_file_path) {
    3. return false;
    4. }
    5. FILE *fp = fopen(i420_file_path, "rb");
    6. if (!fp) {
    7. return false;
    8. }
    9. fseek(fp, 0, SEEK_END);
    10. size_t file_size = ftell(fp);
    11. fseek(fp, 0, SEEK_SET);
    12. size_t y_size = width * height;
    13. size_t uv_size = y_size / 4;
    14. if (file_size != (y_size + uv_size * 2)) {
    15. fclose(fp);
    16. return false;
    17. }
    18. char *i420_content = (char *)malloc(sizeof(char) * file_size);
    19. if (!i420_content) {
    20. fclose(fp);
    21. return false;
    22. }
    23. if (file_size != fread(i420_content, 1, file_size, fp)) {
    24. free(i420_content);
    25. fclose(fp);
    26. return false;
    27. }
    28. fclose(fp);
    29. // convert i420 to nv21
    30. char *nv21_content = (char *)malloc(sizeof(char) * file_size);
    31. if (!nv21_content) {
    32. free(i420_content);
    33. return false;
    34. }
    35. // copy y channel
    36. memcpy(nv21_content, i420_content, y_size);
    37. // copy uv channel
    38. char *i420_u_base = i420_content + y_size;
    39. char *i420_v_base = i420_u_base + uv_size;
    40. char *nv21_uv_base = nv21_content + y_size;
    41. int i = 0, j = 0;
    42. for (;i < uv_size;i++) {
    43. nv21_uv_base[j] = i420_v_base[i];
    44. nv21_uv_base[j + 1] = i420_u_base[i];
    45. j += 2;
    46. }
    47. free(i420_content);
    48. fp = fopen(nv21_file_path, "wb");
    49. if (!fp) {
    50. free(nv21_content);
    51. return false;
    52. }
    53. if (file_size != fwrite(nv21_content, 1, file_size, fp)) {
    54. free(nv21_content);
    55. fclose(fp);
    56. return false;
    57. }
    58. free(nv21_content);
    59. fclose(fp);
    60. return true;
    61. }

     

    三 UYVY转NV12

    1.UYVY格式

    (1)UYVY是每两个Y共用一组UV,如果一帧UYVY图像宽带是width,高度是height,1个像素占1个字节,那么共有width✖height个Y,U和V都是width✖height / 2个,因此一帧I420图像占用字节数是width✖height✖2。

    (2)UYVY是每4个像素中,2个Y共用一组UV,一行像素数是width * 2,如下所示:

    2.转换方法

    (1)将一帧UYVY图像中所有Y存入NV12图像的缓存区,隔行存入相应的U和V,如下所示:

    (2)转换代码:

    1. bool convert_uyvy_nv12(const char *uyvy_file_path, size_t width, size_t height, const char *nv12_file_path) {
    2. if (!uyvy_file_path || !nv12_file_path) {
    3. return false;
    4. }
    5. FILE *fp = fopen(uyvy_file_path, "rb");
    6. if (!fp) {
    7. return false;
    8. }
    9. fseek(fp, 0, SEEK_END);
    10. size_t file_size = ftell(fp);
    11. fseek(fp, 0, SEEK_SET);
    12. size_t frame_size = width * height * 2;
    13. if (file_size != frame_size) {
    14. fclose(fp);
    15. return false;
    16. }
    17. char *uyvy_content = (char *)malloc(sizeof(char) * file_size);
    18. if (!uyvy_content) {
    19. fclose(fp);
    20. return false;
    21. }
    22. if (file_size != fread(uyvy_content, 1, file_size, fp)) {
    23. free(uyvy_content);
    24. fclose(fp);
    25. return false;
    26. }
    27. fclose(fp);
    28. // convert uyvy to nv12
    29. frame_size = width * height * 3 / 2;
    30. char *nv12_content = (char *)malloc(sizeof(char) * frame_size);
    31. if (!nv12_content) {
    32. free(uyvy_content);
    33. return false;
    34. }
    35. size_t y_size = width * height;
    36. size_t pixels_in_a_row = width * 2;
    37. char *nv12_y_ptr = nv12_content;
    38. char *nv12_uv_ptr = nv12_content + y_size;
    39. int lines = 0;
    40. for (int i = 0;i < file_size;i += 4) {
    41. // copy y channel
    42. *nv12_y_ptr++ = uyvy_content[i + 1];
    43. *nv12_y_ptr++ = uyvy_content[i + 3];
    44. if (0 == i % pixels_in_a_row) {
    45. ++lines;
    46. }
    47. if (lines % 2) { // extract the UV value of odd rows
    48. // copy uv channel
    49. *nv12_uv_ptr++ = uyvy_content[i];
    50. *nv12_uv_ptr++ = uyvy_content[i + 2];
    51. }
    52. }
    53. free(uyvy_content);
    54. fp = fopen(nv12_file_path, "wb");
    55. if (!fp) {
    56. free(nv12_content);
    57. return false;
    58. }
    59. if (frame_size != fwrite(nv12_content, 1, frame_size, fp)) {
    60. free(nv12_content);
    61. fclose(fp);
    62. return false;
    63. }
    64. free(nv12_content);
    65. fclose(fp);
    66. return true;
    67. }

     

    四 UYVY转NV21

    方法与转NV12类似,代码如下:

    1. bool convert_uyvy_nv21(const char *uyvy_file_path, size_t width, size_t height, const char *nv21_file_path) {
    2. if (!uyvy_file_path || !nv21_file_path) {
    3. return false;
    4. }
    5. FILE *fp = fopen(uyvy_file_path, "rb");
    6. if (!fp) {
    7. return false;
    8. }
    9. fseek(fp, 0, SEEK_END);
    10. size_t file_size = ftell(fp);
    11. fseek(fp, 0, SEEK_SET);
    12. size_t frame_size = width * height * 2;
    13. if (file_size != frame_size) {
    14. fclose(fp);
    15. return false;
    16. }
    17. char *uyvy_content = (char *)malloc(sizeof(char) * file_size);
    18. if (!uyvy_content) {
    19. fclose(fp);
    20. return false;
    21. }
    22. if (file_size != fread(uyvy_content, 1, file_size, fp)) {
    23. free(uyvy_content);
    24. fclose(fp);
    25. return false;
    26. }
    27. fclose(fp);
    28. // convert uyvy to nv12
    29. frame_size = width * height * 3 / 2;
    30. char *nv21_content = (char *)malloc(sizeof(char) * frame_size);
    31. if (!nv21_content) {
    32. free(uyvy_content);
    33. return false;
    34. }
    35. size_t y_size = width * height;
    36. size_t pixels_in_a_row = width * 2;
    37. char *nv21_y_ptr = nv21_content;
    38. char *nv21_uv_ptr = nv21_content + y_size;
    39. int lines = 0;
    40. for (int i = 0;i < file_size;i += 4) {
    41. // copy y channel
    42. *nv21_y_ptr++ = uyvy_content[i + 1];
    43. *nv21_y_ptr++ = uyvy_content[i + 3];
    44. if (0 == i % pixels_in_a_row) {
    45. ++lines;
    46. }
    47. if (lines % 2) { // extract the UV value of odd rows
    48. // copy uv channel
    49. *nv21_uv_ptr++ = uyvy_content[i + 2];
    50. *nv21_uv_ptr++ = uyvy_content[i];
    51. }
    52. }
    53. free(uyvy_content);
    54. fp = fopen(nv21_file_path, "wb");
    55. if (!fp) {
    56. free(nv21_content);
    57. return false;
    58. }
    59. if (frame_size != fwrite(nv21_content, 1, frame_size, fp)) {
    60. free(nv21_content);
    61. fclose(fp);
    62. return false;
    63. }
    64. free(nv21_content);
    65. fclose(fp);
    66. return true;
    67. }

     

    五 编译及测试

    1.编译:使用g++编译

    2.github:GitHub - wangzhicheng2013/common_utility

     

  • 相关阅读:
    Hive--14---企业级调优2----表的优化
    Qt 设置程序置顶
    SSMBUG之 url +
    java的4种引用类型
    瑞芯微RK3568控制板设计总结
    呼叫中心系统解决方案有哪些
    【OpenGL】五、光照
    武汉新时标文化传媒有限公司掌握这些运营理论才是最重要
    软件工程毕业设计课题(4)基于python的毕业设计python选座订票电影院网站系统毕设作品源码
    Java面试题之分布式/微服务篇
  • 原文地址:https://blog.csdn.net/wangzhicheng2013/article/details/128002108