前言:
因为最近在研究地平线旭日xj3AI芯片的使用,其中牵涉到颜色空间转换的问题。因为好时间没有使用过C++,有点生疏,这里做个记录。
旭日xj3中使用的是NV12格式的数据,这里主要基于C++、opencv库完成BGR到NV12的互相转换。
NV12简单介绍:
图像格式为NV12格式,那么图像大小是wxhx3/2,具体就是Y分量大小是wxh,UV分量大小为wxh/2;如果图像是Y格式,那么图像大小是wxh。
代码实现:
NV12转BGR 方式一:
#include <opencv2/highgui.hpp>
#include <opencv2/core.hpp>
#include <opencv2/imgcodecs.hpp>
#include <opencv2/imgproc.hpp>
#include <iostream>
#include <fstream>
using namespace std;
using namespace cv;
int main()
{
const int width = 1920;
const int height = 1080;
std::ifstream file_in;
file_in.open("../grp0_chn6_pym_out_basic_layer_DS0_1920_1080.yuv", std::ios::binary);
std::filebuf *p_filebuf = file_in.rdbuf();
size_t size = p_filebuf->pubseekoff(0, std::ios::end, std::ios::in);
p_filebuf->pubseekpos(0, std::ios::in);
char *buf_src = new char[size];
p_filebuf->sgetn(buf_src, size);
cv::Mat mat_src = cv::Mat(height*1.5, width, CV_8UC1, buf_src);
cv::Mat mat_dst = cv::Mat(height, width, CV_8UC3);
// cv::cvtColor(mat_src, mat_dst, cv::COLOR_YUV2BGR_NV12);
cv::cvtColor(mat_src, mat_dst, cv::COLOR_YUV2BGR_NV21);
cv::imshow("img", mat_dst);
cv::waitKey(0);
return 0;
}
NV12转BGR 方式二:
#include <opencv2/highgui.hpp>
#include <opencv2/core.hpp>
#include <opencv2/imgcodecs.hpp>
#include <opencv2/imgproc.hpp>
#include <iostream>
#include <fstream>
using namespace std;
using namespace cv;
int main()
{
// const int width = 1920;
// const int height = 1080;
const int width = 720;
const int height = 452;
// FILE *fp = fopen("../grp0_chn6_pym_out_basic_layer_DS0_1920_1080.yuv", "rb");
FILE *fp = fopen("../11.yuv", "rb");
uchar *yuvdata = new uchar[height * 3 /2 * width * sizeof(uchar)];
// fread: 从给定流 stream 读取数据到 ptr 所指向的数组中
fread(yuvdata, height * 3 / 2, width, fp);
Mat yuvimg(height * 3 / 2, width, CV_8UC1, yuvdata);
Mat rgbimg(height, width, CV_8UC3);
cvtColor(yuvimg, rgbimg, COLOR_YUV2BGR_NV12);
imwrite("11.jpg", rgbimg);
imshow("img", rgbimg);
waitKey(0);
fclose(fp);
delete[] yuvdata;
return 0;
}
cvtColor(yuvimg, rgbimg, COLOR_YUV2BGR_NV12)
cv::cvtColor(mat_src, mat_dst, cv::COLOR_YUV2BGR_NV21)
这两个可以直接使用,把BGR转换为NV12和NV21
BGR转NV12
#include <opencv2/highgui.hpp>
#include <opencv2/core.hpp>
#include <opencv2/imgcodecs.hpp>
#include <opencv2/imgproc.hpp>
#include <iostream>
using namespace std;
int main()
{
cv::Mat bgr_mat = cv::imread("../1.jpg", cv::IMREAD_COLOR);
int height = bgr_mat.rows;
int width = bgr_mat.cols;
cv::Mat img_nv12;
cv::Mat yuv_mat;
cv::cvtColor(bgr_mat, yuv_mat, cv::COLOR_BGR2YUV_I420);
uint8_t *yuv = yuv_mat.ptr<uint8_t>();
img_nv12 = cv::Mat(height * 3 / 2, width, CV_8UC1);
uint8_t *ynv12 = img_nv12.ptr<uint8_t>();
int32_t uv_height = height / 2;
int32_t uv_width = width / 2;
// copy y data
int32_t y_size = height * width;
memcpy(ynv12, yuv, y_size);
// copy uv data
uint8_t *nv12 = ynv12 + y_size;
uint8_t *u_data = yuv + y_size;
uint8_t *v_data = u_data + uv_height * uv_width;
for (int32_t i = 0; i < uv_width * uv_height; i++) {
*nv12++ = *u_data++;
*nv12++ = *v_data++;
}
int32_t yuv_size = y_size + 2 * uv_height * uv_width;
FILE *yuvFd = fopen("1.yuv", "w+");
fwrite(img_nv12.ptr<uint8_t>(), 1, yuv_size, yuvFd);
fclose(yuvFd);
return 0;
}