YUV图像格式转换方法实践

news2024/9/28 3:29:19

一 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)代码:

bool convert_i420_nv12(const char *i420_file_path, size_t width, size_t height, const char *nv12_file_path) {
    if (!i420_file_path || !nv12_file_path) {
        return false;
    }
    FILE *fp = fopen(i420_file_path, "rb");
    if (!fp) {
        return false;
    }
    fseek(fp, 0, SEEK_END);
    size_t file_size = ftell(fp);
    fseek(fp, 0, SEEK_SET);
    size_t y_size = width * height;
    size_t uv_size = y_size / 4;
    if (file_size != (y_size + uv_size * 2)) {
        fclose(fp);
        return false;
    }
    char *i420_content = (char *)malloc(sizeof(char) * file_size);
    if (!i420_content) {
        fclose(fp);
        return false;
    }
    if (file_size != fread(i420_content, 1, file_size, fp)) {
        free(i420_content);
        fclose(fp);
        return false;
    }
    fclose(fp);
    // convert i420 to nv12
    char *nv12_content = (char *)malloc(sizeof(char) * file_size);
    if (!nv12_content) {
        free(i420_content);
        return false;
    }
    // copy y channel
    memcpy(nv12_content, i420_content, y_size);
    // copy uv channel
    char *i420_u_base = i420_content + y_size;
    char *i420_v_base = i420_u_base + uv_size;
    char *nv12_uv_base = nv12_content + y_size;
    int i = 0, j = 0;
    for (;i < uv_size;i++) {
        nv12_uv_base[j] = i420_u_base[i];
        nv12_uv_base[j + 1] = i420_v_base[i];
        j += 2;
    }
    free(i420_content);
    fp = fopen(nv12_file_path, "wb");
    if (!fp) {
        free(nv12_content);
        return false;
    }
    if (file_size != fwrite(nv12_content, 1, file_size, fp)) {
        free(nv12_content);
        fclose(fp);
        return false;
    }
    free(nv12_content);
    fclose(fp);
    return true;
}

 

二 I420转NV21

1.NV21格式

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

2.转换代码

bool convert_i420_nv21(const char *i420_file_path, size_t width, size_t height, const char *nv21_file_path) {
    if (!i420_file_path || !nv21_file_path) {
        return false;
    }
    FILE *fp = fopen(i420_file_path, "rb");
    if (!fp) {
        return false;
    }
    fseek(fp, 0, SEEK_END);
    size_t file_size = ftell(fp);
    fseek(fp, 0, SEEK_SET);
    size_t y_size = width * height;
    size_t uv_size = y_size / 4;
    if (file_size != (y_size + uv_size * 2)) {
        fclose(fp);
        return false;
    }
    char *i420_content = (char *)malloc(sizeof(char) * file_size);
    if (!i420_content) {
        fclose(fp);
        return false;
    }
    if (file_size != fread(i420_content, 1, file_size, fp)) {
        free(i420_content);
        fclose(fp);
        return false;
    }
    fclose(fp);
    // convert i420 to nv21
    char *nv21_content = (char *)malloc(sizeof(char) * file_size);
    if (!nv21_content) {
        free(i420_content);
        return false;
    }
    // copy y channel
    memcpy(nv21_content, i420_content, y_size);
    // copy uv channel
    char *i420_u_base = i420_content + y_size;
    char *i420_v_base = i420_u_base + uv_size;
    char *nv21_uv_base = nv21_content + y_size;
    int i = 0, j = 0;
    for (;i < uv_size;i++) {
        nv21_uv_base[j] = i420_v_base[i];
        nv21_uv_base[j + 1] = i420_u_base[i];
        j += 2;
    }
    free(i420_content);
    fp = fopen(nv21_file_path, "wb");
    if (!fp) {
        free(nv21_content);
        return false;
    }
    if (file_size != fwrite(nv21_content, 1, file_size, fp)) {
        free(nv21_content);
        fclose(fp);
        return false;
    }
    free(nv21_content);
    fclose(fp);
    return true;
}

 

三 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)转换代码:

bool convert_uyvy_nv12(const char *uyvy_file_path, size_t width, size_t height, const char *nv12_file_path) {
    if (!uyvy_file_path || !nv12_file_path) {
        return false;
    }
    FILE *fp = fopen(uyvy_file_path, "rb");
    if (!fp) {
        return false;
    }
    fseek(fp, 0, SEEK_END);
    size_t file_size = ftell(fp);
    fseek(fp, 0, SEEK_SET);
    size_t frame_size = width * height * 2;
    if (file_size != frame_size) {
        fclose(fp);
        return false;
    }
    char *uyvy_content = (char *)malloc(sizeof(char) * file_size);
    if (!uyvy_content) {
        fclose(fp);
        return false;
    }
    if (file_size != fread(uyvy_content, 1, file_size, fp)) {
        free(uyvy_content);
        fclose(fp);
        return false;
    }
    fclose(fp);
    // convert uyvy to nv12
    frame_size = width * height * 3 / 2;
    char *nv12_content = (char *)malloc(sizeof(char) * frame_size);
    if (!nv12_content) {
        free(uyvy_content);
        return false;
    }
    size_t y_size = width * height;
    size_t pixels_in_a_row = width * 2;
    char *nv12_y_ptr = nv12_content;
    char *nv12_uv_ptr = nv12_content + y_size;
    int lines = 0;
    for (int i = 0;i < file_size;i += 4) {
        // copy y channel
        *nv12_y_ptr++ = uyvy_content[i + 1];
        *nv12_y_ptr++ = uyvy_content[i + 3];
        if (0 == i % pixels_in_a_row) {
            ++lines;
        }
        if (lines % 2) {       // extract the UV value of odd rows
            // copy uv channel
            *nv12_uv_ptr++ = uyvy_content[i];
            *nv12_uv_ptr++ = uyvy_content[i + 2];
        }
    }
    free(uyvy_content);
    fp = fopen(nv12_file_path, "wb");
    if (!fp) {
        free(nv12_content);
        return false;
    }
    if (frame_size != fwrite(nv12_content, 1, frame_size, fp)) {
        free(nv12_content);
        fclose(fp);
        return false;
    }
    free(nv12_content);
    fclose(fp);
    return true;
}

 

四 UYVY转NV21

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

bool convert_uyvy_nv21(const char *uyvy_file_path, size_t width, size_t height, const char *nv21_file_path) {
    if (!uyvy_file_path || !nv21_file_path) {
        return false;
    }
    FILE *fp = fopen(uyvy_file_path, "rb");
    if (!fp) {
        return false;
    }
    fseek(fp, 0, SEEK_END);
    size_t file_size = ftell(fp);
    fseek(fp, 0, SEEK_SET);
    size_t frame_size = width * height * 2;
    if (file_size != frame_size) {
        fclose(fp);
        return false;
    }
    char *uyvy_content = (char *)malloc(sizeof(char) * file_size);
    if (!uyvy_content) {
        fclose(fp);
        return false;
    }
    if (file_size != fread(uyvy_content, 1, file_size, fp)) {
        free(uyvy_content);
        fclose(fp);
        return false;
    }
    fclose(fp);
    // convert uyvy to nv12
    frame_size = width * height * 3 / 2;
    char *nv21_content = (char *)malloc(sizeof(char) * frame_size);
    if (!nv21_content) {
        free(uyvy_content);
        return false;
    }
    size_t y_size = width * height;
    size_t pixels_in_a_row = width * 2;
    char *nv21_y_ptr = nv21_content;
    char *nv21_uv_ptr = nv21_content + y_size;
    int lines = 0;
    for (int i = 0;i < file_size;i += 4) {
        // copy y channel
        *nv21_y_ptr++ = uyvy_content[i + 1];
        *nv21_y_ptr++ = uyvy_content[i + 3];
        if (0 == i % pixels_in_a_row) {
            ++lines;
        }
        if (lines % 2) {       // extract the UV value of odd rows
            // copy uv channel
            *nv21_uv_ptr++ = uyvy_content[i + 2];
            *nv21_uv_ptr++ = uyvy_content[i];
        }
    }
    free(uyvy_content);
    fp = fopen(nv21_file_path, "wb");
    if (!fp) {
        free(nv21_content);
        return false;
    }
    if (frame_size != fwrite(nv21_content, 1, frame_size, fp)) {
        free(nv21_content);
        fclose(fp);
        return false;
    }
    free(nv21_content);
    fclose(fp);
    return true;
}

 

五 编译及测试

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

2.github:GitHub - wangzhicheng2013/common_utility

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/31347.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

JS 数据结构:队列

队列 定义&#xff1a; 队列(Queue) 是一种只在表的一端进行插入&#xff0c;而在另一端进行删除的数据结构。 队头(front) 允许删除的一端&#xff0c;永远指向第一个元素前一个位置。队尾(rear) 允许插入的一端&#xff0c;永远是指向队列最后一个元素先进先出(First In Fi…

一个漏测Bug能让你想到多少?

一、背景 漏测Bug是指产品逻辑缺陷在测试过程中没有被发现&#xff08;尤其是测试环境可以重现的缺陷&#xff09;&#xff0c;上线版本发布后或者在用户使用体验后发现并反馈回来的缺陷。可能造成线上故障或者资损&#xff0c;在对产品测试过程中&#xff0c;自己也难免出现一…

大二Web课程设计期末考试——基于HTML+CSS+JavaScript+jQuery电商类化妆品购物商城

常见网页设计作业题材有 个人、 美食、 公司、 学校、 旅游、 电商、 宠物、 电器、 茶叶、 家居、 酒店、 舞蹈、 动漫、 服装、 体育、 化妆品、 物流、 环保、 书籍、 婚纱、 游戏、 节日、 戒烟、 电影、 摄影、 文化、 家乡、 鲜花、 礼品、 汽车、 其他等网页设计题目, A…

[C++] std::ranges中的特征和自定义std::ranges::view变换

文章目录1. std::ranges中的特征1.1. std::ranges::range例子细化1.2. std::ranges::sized_range1.3. std::ranges::borrowed_range1.4. std::ranges::view2. std::ranges::subrange 迭代器-哨位对2.1. 构造2.2. 结构化解绑2.3. 操作3. std::views中的std::ranges::view变换3.1…

如何安装 Elasticsearch

实战场景 如何安装 Elasticsearch 知识点 •CentOS •Java •Elasticsearch 安装 •Kibana 安装 菜鸟实战 Elasticsearch 是一个基于 Lucene 的搜索服务器。它提供了一个分布式多用户能力的全文搜索引擎&#xff0c;基于 RESTful web 接口。Elasticsearch 是用 Java 语言开发的…

编译器设计(十一)——标量优化

文章目录一、简介二、消除无用和不可达代码2.1 消除无用代码2.2 消除无用控制流2.3 消除不可达代码三、代码移动3.1 缓式代码移动3.2 代码提升四、特化4.1 尾调用优化4.2 叶调用优化4.3 参数提升五、冗余消除5.1 值相同与名字相同5.2 基于支配者的值编号算法六、为其他变换制造…

ubuntu 创建桌面快捷启动

前言&#xff1a; ubuntu系统通常不会在桌面上生成启动图标&#xff0c;一般需要自己建一个下面提供常用的两个模板&#xff0c; 1.启动其他程序 2.启动文件快捷方式 一、创建其他程序的启动快捷图标 用pycharm2022为例子 touch pycharm2022.desktop gedit pycharm2022.d…

为什么 JVM 叫做基于栈的 RISC 虚拟机?

为什么 JVM 叫做基于栈的 RISC 虚拟机&#xff1f; 其实这个问题比较简单&#xff0c;今天这篇文章的主要目的是想让大家看一下分析这个问题的逻辑&#xff0c;并且如何更好地从一手资料里寻找这些问题的答案。 上图是《深入理解 Java 虚拟机》一书中的截图。其实&#xff0c;…

吊打面试官,HashMap 这一篇就够了

一、HashMap的简单使用 HashMap集合的创建&#xff1a; Map<String,String> map new HashMap<String,String>(); 使用put存储数据&#xff1a; map.put("张三","喜欢打游戏"); map.put("李四","喜欢睡觉"); map.put(…

电网运行信息检索系统的设计与实现

摘要 电网运行方式管理直接决定了电网企业的经济效益和安全效益,随着我国经济和社会的高速发展&#xff0c;我国电网的覆盖面积、网络节点和电压等级也高速增长。但是,我国当前电网运行方式管理工作水平还相对落后&#xff0c;制约了电网的安全经济效益。本文较为详细的分析了电…

第三章《数组与循环》第2节:多维数组

3.1小节介绍的数组都是把数组元素从逻辑上排成一行,因此它们被称为“一维数组”。如果一个班级内有15个学生,这些学生按照身高又分成3排就坐,其排列形式如图3-2所示: 3-2学生身高排列图 如果程序员希望按照每个学生在班级内的位置来存储他们的身高数据该怎么办呢?一些读者…

基于Java+Spring+Vue+elementUI大学生求职招聘系统详细设计实现

博主介绍&#xff1a;✌全网粉丝20W,csdn特邀作者、博客专家、CSDN新星计划导师、java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取联系&#x1f345;精彩专栏推荐订阅&#x1f447;&#x1f…

【项目设计】自主HTTP服务器

文章目录项目介绍网络协议栈介绍协议分层数据的封装与分用HTTP相关知识介绍HTTP的特点URL格式URI、URL、URNHTTP的协议格式HTTP的请求方法HTTP的状态码HTTP常见的HeaderCGI机制介绍CGI机制的概念CGI机制的实现步骤CGI机制的意义日志编写套接字相关代码编写HTTP服务器主体逻辑HT…

Windows AppData介绍

appdata是什么文件夹?&#xff08;应用程序数据&#xff09; 此文件夹是有关帐户信息、系统桌面、安装文件记录、快速启动文件夹等内容的。appdata下有三个子文件夹local&#xff0c;locallow&#xff0c;loaming。当你解压缩包时如果不指定路径&#xff0c;系统就把压缩包解到…

OpenCV图像处理——停车场车位识别

总目录 图像处理总目录←点击这里 十九、停车场车位识别 19.1、项目说明 唐宇迪老师的——OPENCV项目实战 学习 本项目的目的是设计一个停车场车位识别的系统&#xff0c;能够判断出当前停车场中哪些车位是空的。 任务共包含部分&#xff1a; 对图像预处理 从停车场的监控…

Vue基础之组件通信provide、inject

最近发现竟然还有小伙伴还不清楚provide、inject的用法&#xff0c;是时候普及一下provide、inject了&#xff1b; 常用的组件通信基本是父子组件通过props和emit来进行&#xff0c;一旦层级多了起来&#xff0c;props和emit就不好使了。每级都写props的话&#xff0c;会变得非…

PMP每日一练 | 考试不迷路-11.24(包含敏捷+多选)

11.27PMP考试倒计时 3天 每日5道PMP习题助大家上岸PMP&#xff01; ​题目1-2&#xff1a; ​1.在项目的规划阶段完成以后&#xff0c;但在正式执行之前&#xff0c;项目经理需要就项目目标进行沟通并获得承诺。项目经理下一步应该做什么? ( ) A.与所有相关方召开开工会议…

麻了,别再为难软件测试员了

前言 有不少技术友在测试群里讨论&#xff0c;近期的面试越来越难了&#xff0c;要背的八股文越来越多了,考察得越来越细&#xff0c;越来越底层&#xff0c;明摆着就是想让我们徒手造航母嘛&#xff01;实在是太为难我们这些测试工程师了。 这不&#xff0c;为了帮大家节约时…

深度操作系统20.5发布 deepin 20.5更新内容汇总

深度操作系统&#xff08;deepin&#xff09;是一款致力于为全球用户提供美观易用、安全稳定服务的Linux发行版&#xff0c;同时也一直是排名最高的来自中国团队研发的Linux发行版。深度操作系统20.5升级Stable内核至5.15.24&#xff0c;修复底层漏洞&#xff0c;进一步提升系统…

linux 清理垃圾文件

linux的文件系统比windows的要优秀&#xff0c;不会产生碎片&#xff0c;对于长时间运行的服务器来说尤为重要&#xff0c;而且linux系统本身也不会像windows一样产生大量的垃圾文件。不知道这个说法有没有可信度!至少我们可以确定的是linux系统的文件系统是比较优秀的! linux…