如何使用OpenCV将BGR(cv::Mat)转换为NV12(cv::Mat)
OpenCV BGR转NV12/NV21实现方案
OpenCV官方目前没有提供直接转换NV12、NV21格式的内置flag,你可以通过手动拼接YUV通道的方式实现,具体逻辑和示例代码如下:
实现原理
NV12和NV21都属于YUV420SP格式,Y通道单独存储,U和V通道交叉打包存储,两者仅UV顺序有区别:
- NV12:UV平面顺序为U在前、V在后
- NV21:UV平面顺序为V在前、U在后
实现步骤
- 先通过OpenCV内置的
cv2.COLOR_BGR2YUV_I420参数将BGR图像转换为I420(YUV420P)格式,转换后的数据按Y平面、U平面、V平面的顺序连续存储 - 拆分得到独立的Y、U、V三个通道,若原图尺寸为
width*height,则Y通道长度为width*height,U、V通道长度均为width*height//4 - 按目标格式要求交叉拼接U、V通道得到UV平面
- 最后将Y平面和拼接完成的UV平面按顺序拼接,即可得到最终的NV12/NV21数据
示例代码(Python)
import cv2 import numpy as np # BGR转NV12 def bgr_to_nv12(bgr_img): height, width = bgr_img.shape[:2] # 转I420格式 i420 = cv2.cvtColor(bgr_img, cv2.COLOR_BGR2YUV_I420) # 拆分Y/U/V通道 y_channel = i420[:height, :] u_channel = i420[height : height + height//4, :].reshape(-1) v_channel = i420[height + height//4 : , :].reshape(-1) # 交叉拼接UV平面(U在前V在后) uv_plane = np.zeros(u_channel.size + v_channel.size, dtype=u_channel.dtype) uv_plane[0::2] = u_channel uv_plane[1::2] = v_channel uv_plane = uv_plane.reshape(height//2, width) # 拼接得到NV12 nv12_data = np.vstack((y_channel, uv_plane)) return nv12_data # BGR转NV21 def bgr_to_nv21(bgr_img): height, width = bgr_img.shape[:2] i420 = cv2.cvtColor(bgr_img, cv2.COLOR_BGR2YUV_I420) y_channel = i420[:height, :] u_channel = i420[height : height + height//4, :].reshape(-1) v_channel = i420[height + height//4 : , :].reshape(-1) # 交叉拼接UV平面(V在前U在后) uv_plane = np.zeros(u_channel.size + v_channel.size, dtype=u_channel.dtype) uv_plane[0::2] = v_channel uv_plane[1::2] = u_channel uv_plane = uv_plane.reshape(height//2, width) # 拼接得到NV21 nv21_data = np.vstack((y_channel, uv_plane)) return nv21_data
C++版本实现说明
C++版本逻辑和Python版本完全一致,先转换为I420格式,再拆分通道交叉拼接UV即可,无需依赖额外第三方组件。
内容的提问来源于stack exchange,提问作者yahuuu
相关产品推荐
相关产品推荐

