使用opencv函数 将JPG图像转存为NV12格式分为两步:
- 将BGR格式转为I420;
- 将I420格式转为NV12或NV21;
其中,I420、NV12、NV21格式如下:
I422 属于 YUV422P 格式。三个平面,分别存储 Y U V 分量;
NV12 属于 YUV420SP 格式。两个平面,分别存储 Y 分量 和 UV 分量。其中 UV 分量共用一个平面并且以 U, V, U, V 的顺序交错排列。每四个 Y 分量共享一组 UV 分量;
NV21 属于 YUV420SP,与 NV12 几乎一致,区别是 UV 平面中 U 与 V 的排列顺序颠倒,以 V, U, V, U 的顺序交错排列
Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y
Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y
Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y
Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y
U U U U U U U V U V U V V U V U V U
V V V V V V U V U V U V V U V U V U
- I420 - - NV12 - - NV21 -
测试用例:
static void ConvertBGR2YUV420SP_NV12(const cv::Mat &bgr_image,
unsigned char *buffer) {
int bgr_width = bgr_image.cols;
int bgr_height = bgr_image.rows;
cv::Mat yuv_image;
cvtColor(bgr_image, yuv_image, cv::COLOR_BGR2YUV_I420);
int len_y = bgr_height * bgr_width;
int len_u = len_y >> 2;
unsigned char *pt_yuv_y = yuv_image.data;
unsigned char *pt_yuv_u = pt_yuv_y + len_y;
unsigned char *pt_yuv_v = pt_yuv_u + len_u;
unsigned char *pt_dst_uv = buffer + len_y;
int i, j;
// copy y;
memcpy(buffer, pt_yuv_y, len_y);
// copy uv;
for (i = 0, j = 0; i < len_u; i++) {
pt_dst_uv[j++] = pt_yuv_u[i];
pt_dst_uv[j++] = pt_yuv_v[i];
}
}
int main()
{
vector<cv::String> files_jpg;
glob("D:/data/*.jpg", files_jpg);
for (size_t i = 0; i < files_jpg.size(); i++)
{
cv::Mat bgr_mat = cv::imread(files_jpg[i].c_str(), cv::IMREAD_COLOR);
int width = bgr_mat.cols;
int height = bgr_mat.rows;
int yuvNV12_size = width * height * 3 / 2;
int rgb24_size = width * height;
unsigned char *nv12_buffer = (unsigned char *)malloc(yuvNV12_size*sizeof(uchar));
ConvertBGR2YUV420SP_NV12(bgr_mat, nv12_buffer);
std::string savePath = files_jpg[i] + "_nv12.yuv";
FILE *yuvFd = fopen(savePath.c_str(), "w+");
fwrite(nv12_buffer, sizeof(uchar), yuvNV12_size, yuvFd);
fclose(yuvFd);
free(nv12_buffer);
}
return 0;
}