NV12 、NV21 和 BGR转换

embedded/2024/12/22 3:41:30/

文章目录

  • 前置阅读:YUV格式
  • 效果
  • 源码

opencv没有提供BGR转NV12或者NV21的方法,这里借助中间过程实现一下。

前置阅读:YUV格式

https://blog.csdn.net/qq_40622955/article/details/144427710

效果

  • 原图
    在这里插入图片描述

  • NV12
    在这里插入图片描述

  • NV12转BGR
    在这里插入图片描述

  • NV21
    在这里插入图片描述

  • NV21转BGR
    在这里插入图片描述
    可以看出NV12、NV21和BGR的互转都是OK的。

源码

  • head
#ifndef SRC_CVT_COLOR_HPP_#define SRC_CVT_COLOR_HPP_#include "opencv2/core.hpp"
#include "opencv2/imgproc.hpp"
#include "opencv2/highgui.hpp"cv::Mat ConvertBgrToNv21(const cv::Mat &mat);
cv::Mat ConvertBgrToNv12(const cv::Mat &mat);cv::Mat ConvertNv21ToBgr(const unsigned char *data, int width, int height);
cv::Mat ConvertNv12ToBgr(const unsigned char *data, int width, int height);#endif // SRC_CVT_COLOR_HPP_
  • cpp
#include "cvt_color.hpp"#include <iostream>cv::Mat ConvertBgrToNv21(const cv::Mat &mat)
{if(mat.empty()){std::cout << "Mat is empty" << std::endl;return cv::Mat();}cv::Mat yuvi420;cv::cvtColor(mat, yuvi420, cv::COLOR_BGR2YUV_I420);int width = mat.cols;int height = mat.rows;cv::Mat nv21_mat(height * 3 / 2, width, CV_8UC1);int frame_size = width * height;int chroma_size = frame_size / 4;memcpy((void *)nv21_mat.data, (void *)yuvi420.data, frame_size);unsigned char *u_data = yuvi420.data + frame_size;unsigned char *v_data = u_data + chroma_size;for (int i = 0; i < chroma_size; i ++){nv21_mat.data[frame_size + i * 2] = v_data[i];nv21_mat.data[frame_size + i * 2 + 1] = u_data[i];}return nv21_mat;
}cv::Mat ConvertBgrToNv12(const cv::Mat &mat)
{if(mat.empty()){std::cout << "Mat is empty" << std::endl;return cv::Mat();}cv::Mat yuvi420;cv::cvtColor(mat, yuvi420, cv::COLOR_BGR2YUV_I420);int width = mat.cols;int height = mat.rows;cv::Mat nv12_mat(height * 3 / 2, width, CV_8UC1);int frame_size = width * height;int chroma_size = frame_size / 4;memcpy((void *)nv12_mat.data, (void *)yuvi420.data, frame_size);unsigned char *v_data = yuvi420.data + frame_size;unsigned char *u_data = v_data + chroma_size;for (int i = 0; i < chroma_size; i ++){nv12_mat.data[frame_size + i * 2] = v_data[i];nv12_mat.data[frame_size + i * 2 + 1] = u_data[i];}return nv12_mat;
}cv::Mat ConvertNv21ToBgr(const unsigned char *data, int width, int height)
{if(!data){std::cout << "Mat is empty" << std::endl;return cv::Mat();}cv::Mat nv21(height * 3 / 2, width, CV_8UC1, (void *)data);cv::Mat cv_mat;cv::cvtColor(nv21, cv_mat, cv::COLOR_YUV2BGR_NV21);return cv_mat;
}cv::Mat ConvertNv12ToBgr(const unsigned char *data, int width, int height)
{if(!data){std::cout << "Mat is empty" << std::endl;return cv::Mat();}cv::Mat nv12(height * 3 / 2, width, CV_8UC1, (void *)data);cv::Mat cv_mat;cv::cvtColor(nv12, cv_mat, cv::COLOR_YUV2BGR_NV12);return cv_mat;
}
  • demo

#include "cvt_color.hpp"#include <iostream>void Demo()
{std::string image_path = "/media/hello/data/image/sample/1.jpeg";cv::Mat mat = cv::imread(image_path);cv::resize(mat, mat, cv::Size(1920*0.5,1080*0.5));if (mat.empty()){std::cout << "Failed to read image: " << image_path << std::endl;return;}cv::Mat nv21_mat = ConvertBgrToNv21(mat.clone());if (nv21_mat.empty()){std::cout << "Failed to convert BGR to NV21" << std::endl;return;}cv::Mat bgr_mat = ConvertNv21ToBgr(nv21_mat.clone().data, mat.cols, mat.rows);if (bgr_mat.empty()){std::cout << "Failed to convert NV21 to BGR" << std::endl;return;}cv::Mat nv12_mat = ConvertBgrToNv12(mat.clone());if( nv12_mat.empty()){std::cout << "nv12_mat is empty\n"; }cv::Mat bgr_mat_nv12 = ConvertNv12ToBgr(nv12_mat.clone().data, mat.cols, mat.rows);if(bgr_mat_nv12.empty()){std::cout << "bgr_mat_nv12 is empty\n";}cv::imshow("BGR", mat);cv::imshow("NV21", nv21_mat);cv::imshow("BGR2_NV21R", bgr_mat);cv::imshow("NV12", nv12_mat);cv::imshow("BGR_NV12R", bgr_mat_nv12);cv::waitKey(0);}int main()
{Demo();return 0;
}

http://www.ppmy.cn/embedded/147698.html

相关文章

Android 16 关于动态权限使用的变更

权限声明code 在 Android 中&#xff0c;权限的申请分为静态权限和动态权限。 静态权限 静态权限是指在应用的 AndroidManifest.xml 文件中声明的权限。这些权限在应用安装时就会被用户授予。常见的静态权限包括访问互联网、读取用户联系人等。 <manifest xmlns:android&…

地理信息系统(Geographic Information System,GIS)

目录 主要组成部分 主要功能 应用领域 前沿技术与发展趋势 更多学术知识 主要组成部分 数据采集&#xff1a; 通过各种手段&#xff08;如遥感、卫星影像、GPS、地面调研等&#xff09;收集地理和空间数据。这些数据可以是矢量数据&#xff08;点、线、面&#xff09;或栅…

基于时间情境创造与 AI 智能名片 S2B2C 商城小程序源码的零售创新策略研究

摘要&#xff1a;本文聚焦于零售领域的创新发展&#xff0c;深入探讨了时间情境创造在零售中的重要性&#xff0c;并结合 AI 智能名片 S2B2C 商城小程序源码这一新兴技术手段&#xff0c;阐述其如何助力零售企业突破传统模式的局限。通过对国美线上线下融合案例的剖析&#xff…

linux CentOS系统上卸载docker

一、停止Docker服务 首先&#xff0c;需要停止Docker服务。使用systemctl命令来停止Docker服务&#xff1a; bash复制代码sudo systemctl stop docker二、卸载Docker软件包 接下来&#xff0c;使用CentOS的包管理器yum来卸载Docker软件包。根据安装的Docker版本和组件&#…

windows 使用python共享网络给另外一个网卡

# -*- coding: utf-8 -*- import subprocessdef open_share(to_shared_adapter, from_shared_adapter):"""打开以太网的网络共享:return: None"""powershell_script f"""# Register the HNetCfg library (once)# regsvr32 hnetc…

mac编译ijkplayer遇到问题

问题&#xff1a;./init-android.sh git version 2.44.0 pull ffmpeg base : command not founde.sh: line 2: : command not founde.sh: line 5: : command not founde.sh: line 6: tools/pull-repo-base.sh: line 9: syntax error near unexpected token elif ools/pull-re…

3D造型软件solvespace在windows下的编译

3D造型软件solvespace在windows下的编译 在逛开源社区的时候发现了几款开源CAD建模软件&#xff0c;一直囿于没有合适的建模软件&#xff0c;虽然了解了很多的模拟分析软件&#xff0c;却不能使之成为整体的解决方案&#xff0c;从而无法产生价值。opencascad之流虽然可行&…

我的“双胞同体”发布模式的描述与展望

当被“激情”晕染&#xff0c;重创标题、摘要探索“吸睛”。 (笔记模板由python脚本于2024年12月19日 15:23:44创建&#xff0c;本篇笔记适合喜欢编撰csdn博客的coder翻阅) 【学习的细节是欢悦的历程】 Python 官网&#xff1a;https://www.python.org/ Free&#xff1a;大咖免…