YUV 简介及使用

YUV 简介及使用一、YUV简介YUV:是一种颜色编码方法,常使用在各个视频处理组件中Y’UV,YCbCr,YPbPr等专有名词都可以称为YUV,彼此有重叠Y表示明亮度(单取此通道即可得灰度图),U和V则是色度、浓度主流的采样方式有三种,YUV4:4:4,YUV4:2:2,YUV4:2:0可以根据其采样格式来从码流中还原每个像素点的YUV值,进而通过YUV…

大家好,又见面了,我是你们的朋友全栈君。


一、YUV 简介

在这里插入图片描述

  • YUV:是一种颜色编码方法,常使用在各个视频处理组件中
    • Y'UV(模拟), YCbCr(数字), YPbPr等专有名词都可以称为 YUV,彼此有重叠
    • Y表示明亮度(单取此通道即可得灰度图),UV则是描述图像的色彩饱和度,用于指定像素的颜色
    • 编解码:采集到的视频数据一般是 RGB24,为了节省带宽,一般需要经过 编码转换(RGB2YUV) 为 NV12 进行传输;应用时一般需要经过 解码转换(YUV2RGB) 为 RGB 用于显示或后续算法
  • YUV 采样方式及原理:根据人眼的特点,将人眼相对不敏感的色彩信息进行压缩采样(亮度保持不变),得到相对小的文件进行播放和传输
    • YUV4:2:0 数据,每四个 Y 共用一组 UV 分量,在内存中的长度是 h * w + h * w / 4 + h * w / 4 = h * w *1.5,是 RGB(h * w * 3) 格式视频数据内存的一半,每个像素的 Y 数据保留, 两个像素数据只保留一个 U 或者 V 数据
    • YUV4:2:2 数据,每两个 Y 共用一组 UV 分量,在内存中的长度是 h * w + h * w / 2 + h * w / 2 = h * w *2,是 RGB(h * w * 3) 格式视频数据内存 2/3,每两个相邻的像素,一个丢弃 V 数据,一个丢弃 U 数据
    • YUV4:4:4 数据,每一个 Y 共用一组 UV 分量,在内存中的长度是 h * w + h * w + h * w = h * w *3,与 RGB(h * w * 3) 格式视频数据内存一样
  • YUV 存储格式:
    • packed(打包格式):每个像素点的 Y,U,V 是连续交叉存储的(YUVYUVYUVYUV
    • planar(平面格式):先连续存储所有像素点的 Y,紧接着存储所有像素点的 U,随后是所有像素点的V(YYYYUUVV
    • semi-planar(半平面格式):先连续存储所有像素点的 Y,紧接着连续交叉存储所有像素点的U,V(YYYYUV
  • YUV444 & RGB 相互转换:
    • 图形显示时常用 RGB 模型,而 YUV 常用在数据传输场景,所以这两种颜色模型之间经常需要进行转换
    • 可以根据其采样格式来从码流中还原每个像素点的 YUV 值,进而通过 YUVRGB 的转换公式提取出每个像素点的 RGB 值,然后显示出来(可参考博客:YUV 采样与恢复
# BT601(SDTV)/BT709(HDTV)/BT2020(UHDTV) 定义了 RGB 和 YUV 互转的色域标准规范
# BT2020 的 YUV 转换公式和 BT601 BT709 都是不同的,但是两者的取值范围是相同的,一般 HD 视频(1080p)使用 BT601 转换, UHDTV(4k或8K)使用 BT2020 协议转换, 若视频信号中无色域标志或者色彩转换矩阵, 默认使用 BT601 即可, ffmpeg 转 rgb 也是这样操作的。
# 参考:https://www.zhihu.com/question/20656646/answer/261286123
# RGB to YCbCr(YUV444),YCbCr 是 YUV 颜色空间的偏移版本,适用于数字视频
# RGB 范围 [0,255],Y 范围 [16,235] ,UV 范围 [16,239]。如果计算结果超过了取值范围要进行截取。
Y = 0.257 * R + 0.504 * G + 0.098 * B + 16;
U = -0.148 * R - 0.291 * G + 0.439 * B + 128;
V = 0.439 * R - 0.368 * G - 0.071 * B + 128;
# YCbCr(YUV444)to RGB
# RGB 范围 [0,255],Y 范围 [16,235] ,UV 范围 [16,239]。如果计算结果超过了取值范围要进行截取
R = 1.164 * (Y - 16) + 1.596 * (V - 128);
G = 1.164 * (Y - 16) - 0.813 * (V - 128) - 0.391 * (U - 128);
B = 1.164 * (Y - 16) + 2.018 * (U - 128);
# PS:上述符号均在原值基础上进行了伽马校正, 伽马校正有助于弥补在抗锯齿的过程中
# 线性分配伽马值所带来的细节损失,使图像细节更加丰富。在没有采用伽马校正的情况下
# 暗部细节不容易显现出来,而采用了这一图像增强技术以后,图像的层次更加明晰了
# OpenCV 读取图像转换成 YUV444 
for(l32Index1 = 0; l32Index1 < cv_img.rows; l32Index1++)
{ 

for(l32Index2 = 0; l32Index2 < cv_img.cols; l32Index2++)
{ 

# Y = 0.257*R + 0.504*G + 0.098*B + 16
f32YTmp = 0.257 * (int)(cv_img.at<cv::Vec3b>(l32Index1, l32Index2)[2]) + 0.504 * (int)(cv_img.at<cv::Vec3b>(l32Index1, l32Index2)[1]) + 0.098 * (int)(cv_img.at<cv::Vec3b>(l32Index1, l32Index2)[0]) + 16;
u8YTmp = MIN(256, (uchar)(f32YTmp + 0.5f));
cv_img_yuv.at<cv::Vec3b>(l32Index1, l32Index2)[0] = u8YTmp;
# U = -0.148*R - 0.291*G + 0.439*B + 128
f32UTmp = -0.148 * (int)(cv_img.at<cv::Vec3b>(l32Index1, l32Index2)[2]) - 0.291 * (int)(cv_img.at<cv::Vec3b>(l32Index1, l32Index2)[1]) + 0.439 * (int)(cv_img.at<cv::Vec3b>(l32Index1, l32Index2)[0]) + 128;
u8UTmp = (uchar)(f32UTmp + 0.5f);
cv_img_yuv.at<cv::Vec3b>(l32Index1, l32Index2)[1] = u8UTmp;
# V = 0.439*R - 0.368*G - 0.071*B + 128
f32VTmp = 0.439 * (int)(cv_img.at<cv::Vec3b>(l32Index1, l32Index2)[2]) - 0.368 * (int)(cv_img.at<cv::Vec3b>(l32Index1, l32Index2)[1]) - 0.071 * (int)(cv_img.at<cv::Vec3b>(l32Index1, l32Index2)[0]) + 128;
u8VTmp = (uchar)(f32VTmp + 0.5f);
cv_img_yuv.at<cv::Vec3b>(l32Index1, l32Index2)[2] = u8VTmp;
}
}

一、YUV 采样格式介绍

在这里插入图片描述

1、YUV420(NV12、NV21、I420、YV12)

# NV12、NV21 的存储格式为 Y 平面,UV 打包一个平面,共两个平面,即:先连续存储 Y,然后连续交叉存储 UV
# 不同点在于 UV 的排列顺序,SP 为 Semi-Planar 的缩写
# NV12:先是 w * h 长度的 Y,后面跟 w * h * 0.5 长度的 UV(交叉存储),总长度为 w * h * 1.5
# NV21:先是 w * h 长度的 Y,后面跟 w * h * 0.5 长度的 VU(交叉存储),总长度为 w * h * 1.5
NV12: YYYYYYYY UVUV     =>  YUV420SP  # iOS 平台常用 NV12
NV21: YYYYYYYY VUVU     =>  YUV420SP  # Android 平台常用 NV21
# I420、YV12 三个分量均为平面格式,共三个平面,即:先连续存储 Y,然后连续存储 U,最后连续存储 V
# 不同点在于 U V 的排列顺序,P 为 Planar 的缩写
# I420:先是 w * h 长度的 Y,后面跟 w * h * 0.25 长度的 U, 最后是 w * h * 0.25 长度的 V,总长度为 w * h * 1.5
# YV12:先是 w * h 长度的 Y,后面跟 w * h * 0.25 长度的 V, 最后是 w * h * 0.25 长度的 U,总长度为 w * h * 1.5
I420: YYYYYYYY UU VV    =>  YUV420P  # Android 平台常用 I420
YV12: YYYYYYYY VV UU    =>  YUV420P 
  • 假设一个分辨率为6X4YUV 图像(占用内存大小 6 ∗ 4 ∗ 1.5 = 36 6*4*1.5=36 641.5=36),它们的格式如下图:

在这里插入图片描述


2、YUV422(NV16、NV61、I422、YV16、YUVY、VYUY、UYVY)

# NV16、NV61 的存储格式为 Y 平面,UV 打包一个平面,共两个平面,即:先连续存储 Y,然后连续交叉存储 UV
# 不同点在于 UV 的排列顺序,SP 为 Semi-Planar 的缩写
# NV16:先是 w * h 长度的 Y,后面跟 w * h 长度的 UV(交叉存储),总长度为 w * h * 2
# NV61:先是 w * h 长度的 Y,后面跟 w * h 长度的 VU(交叉存储),总长度为 w * h * 2
NV16: YYYYYYYY UVUVUVUV     =>  YUV422SP   
NV61: YYYYYYYY VUVUVUVU     =>  YUV422SP 
# I422、YV16 三个分量均为平面格式,共三个平面,即:先连续存储 Y,然后连续存储 U,最后连续存储 V
# 不同点在于 U V 的排列顺序,P 为 Planar 的缩写
# I420:先是 w * h 长度的 Y,后面跟 w * h * 0.5 长度的 U, 最后是 w * h * 0.5 长度的 V,总长度为 w * h * 2
# YV12:先是 w * h 长度的 Y,后面跟 w * h * 0.5 长度的 V, 最后是 w * h * 0.5 长度的 U,总长度为 w * h * 2
I422: YYYYYYYY UUUU VVVV    =>  YUV422P
YV16: YYYYYYYY VVVV UUUU    =>  YUV422P  
# YUVY、VYUY、UYVY 为打包格式:每个像素点的 Y,U,V 是连续交叉存储
# YUVY:在 Packed 内部,YUV 的排列顺序是 YUVY,两个 Y 共用一组 UV
# VYUY:在 Packed 内部,YUV 的排列顺序是 VYUY,两个 Y 共用一组 UV
# UYVY:在 Packed 内部,YUV 的排列顺序是 UYVY,两个 Y 共用一组 UV
YUVY: YUVY YUVY YUVY YUVY  =>  YUV422
VYUY: VYUY VYUY VYUY VYUY  =>  YUV422 
UYVY: UYVY UYVY UYVY UYVY  =>  YUV422 

在这里插入图片描述


3、YUV444(I444、YV24)

# I444、YV24三个分量均为平面格式,共三个平面,即:先连续存储 Y,然后连续存储 U,最后连续存储 V
# 不同点在于 U V 的排列顺序,P 为 Planar 的缩写
# I444:先是 w * h 长度的 Y,后面跟 w * h 长度的 U, 最后是 w * h 长度的 V,总长度为 w * h * 3
# YV24:先是 w * h 长度的 Y,后面跟 w * h 长度的 V, 最后是 w * h 长度的 U,总长度为 w * h * 3
I444: YYYYYYYY UUUUUUUU VVVVVVVV    =>  YUV420P 
YV24: YYYYYYYY VVVVVVVV UUUUUUUU    =>  YUV420P 

在这里插入图片描述


三、常用视频分辨率及码率、帧率介绍

  • 常用视频分辨率(w*h):
    • 720P(1280*720): 表示视频有 720 行像素(height),大约 100w 像素,P(Progressive 的缩写)本身表示的是逐行扫描
    • 1080P(1920*1080):表示视频有 1080 行像素(height),大约 200w 像素
    • 2K(2048*1080):表示视频有 2048(2*2^10=2K) 列的像素数(width),大约 200w 像素,最常见的是影院级别的 2K,其它还有 2048×1536(QXGA)2560×1600(WQXGA),2560×1440(Quad HD)
    • 4K(3840*2160/4096*2160):表示视频有 4096(4*2^10=4K) 列的像素数(width),大约 800~900 w 像素
  • 码流(Data Rate):
    • 也叫码率,指的是视频文件在单位时间内使用的数据流量,对视频编码画面质量的控制起到重要作用
    • 在同样分辨率下,视频文件码流越大,压缩比就越小,画面质量就越好
  • 帧率(fps):
    • 指的是每秒钟传输图片的帧数,帧率越高,性能越好
  • H.264 和 H.265 的区别:
    • 均指视频编码的标准,H265 在保证清晰度的同时降低了码流,可以节约存储空间,同时降低了网络带宽
      在这里插入图片描述

四、读取 YUV(NV12) 视频文件并保存

  • 1、使用 FFMPEG 工具实现:
    • nv12 数据转 jpg:.\ffmpeg.exe -y -s 1920x1080 -pix_fmt nv12 -i .\output\1_NV12.yuv .\output\1_NV12\image%d.jpg
    • jpg 数据转 nv12: .\ffmpeg.exe -s 1920x1080 -i .\input\image%d.jpg -pix_fmt nv12 test.yuv
    • h265 数据转 nv12:.\ffmpeg.exe -i .\input\1.h265 -s 1920x1080 -pix_fmt nv12 .\output\1_NV12.yuv
    • Note:ffmpeg 还有其它很强大的一些功能,如 imglist 转 mp4、yuv 转 h265 等等
  • 2、python 代码实现如下:
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import cv2
import numpy as np
def yuv2bgr(file_name, height, width, start_frame):
""" :param file_name: 待处理 YUV 视频的名字 :param height: YUV 视频中图像的高 :param width: YUV 视频中图像的宽 :param start_frame: 起始帧 :return: None """
fp = open(file_name, 'rb')
fp.seek(0, 2)  # 设置文件指针到文件流的尾部 + 偏移 0
fp_end = fp.tell()  # 获取文件尾指针位置
frame_size = height * width * 3 // 2  # 一帧图像所占用的内存字节数
num_frame = fp_end // frame_size  # 计算 YUV 文件包含图像数
print("This yuv file has {} frame imgs!".format(num_frame))
fp.seek(frame_size * start_frame, 0)  # 设置文件指针到文件流的起始位置 + 偏移 frame_size * startframe
print("Extract imgs start frame is {}!".format(start_frame + 1))
for i in range(num_frame - start_frame):
yyyy_uv = np.zeros(shape=frame_size, dtype='uint8', order='C')
for j in range(frame_size):
yyyy_uv[j] = ord(fp.read(1))  # 读取 YUV 数据,并转换为 unicode
img = yyyy_uv.reshape((height * 3 // 2, width)).astype('uint8')  # NV12 的存储格式为:YYYY UV 分布在两个平面(其在内存中为 1 维)
bgr_img = cv2.cvtColor(img, cv2.COLOR_YUV2BGR_NV12)  # 由于 opencv 不能直接读取 YUV 格式的文件, 所以要转换一下格式,支持的转换格式可参考资料 5
cv2.imwrite('yuv2bgr/{}.jpg'.format(i + 1), bgr_img)  # 改变后缀即可实现不同格式图片的保存(jpg/bmp/png...)
print("Extract frame {}".format(i + 1))
fp.close()
print("job done!")
return None
if __name__ == '__main__':
yuv2bgr(filename='xxx.yuv', height=1080, width=1920, startfrm=0)
# BGR2NV21
def bgr2nv21(image_path, image_height, image_width):
bgr = cv2.imread(image_path)
B = []
G = []
R = []
YUV = []
for i in range(image_height):
for j in range(image_width):
B.append(bgr[i * image_width * 3 + j * 3])
G.append(bgr[i * image_width * 3 + j * 3 + 1])
R.append(bgr[i * image_width * 3 + j * 3 + 2])
y = ((74 * R + 150 * G + 29 * B) / 256)
u = ((-43 * R - 84 * G + 112 * B) / 255) + 128
v = ((128 * R - 107 * G - 21 * B) / 255) + 128
YUV[i * image_width + j] = min(max(y, 0), 255)
YUV[(i // 2 + image_height) * image_width + (j // 2) * 2] = min(max(v, 0), 255)
YUV[(i // 2 + image_height) * image_width + (j // 2) * 2 + 1] = min(max(u, 0), 255)
yuv_name = image_path.replace("png", "yuv")
np.tofile(yuv_name)
  • 3、OpenCV 中支持的颜色转换如下所示(py opencv 只需把 cv:: 换成 cv. 即可):
enum cv::ColorConversionCodes { 

cv::COLOR_BGR2BGRA = 0,
cv::COLOR_RGB2RGBA = COLOR_BGR2BGRA,
cv::COLOR_BGRA2BGR = 1,
cv::COLOR_RGBA2RGB = COLOR_BGRA2BGR,
cv::COLOR_BGR2RGBA = 2,
cv::COLOR_RGB2BGRA = COLOR_BGR2RGBA,
cv::COLOR_RGBA2BGR = 3,
cv::COLOR_BGRA2RGB = COLOR_RGBA2BGR,
cv::COLOR_BGR2RGB = 4,
cv::COLOR_RGB2BGR = COLOR_BGR2RGB,
cv::COLOR_BGRA2RGBA = 5,
cv::COLOR_RGBA2BGRA = COLOR_BGRA2RGBA,
cv::COLOR_BGR2GRAY = 6,
cv::COLOR_RGB2GRAY = 7,
cv::COLOR_GRAY2BGR = 8,
cv::COLOR_GRAY2RGB = COLOR_GRAY2BGR,
cv::COLOR_GRAY2BGRA = 9,
cv::COLOR_GRAY2RGBA = COLOR_GRAY2BGRA,
cv::COLOR_BGRA2GRAY = 10,
cv::COLOR_RGBA2GRAY = 11,
cv::COLOR_BGR2BGR565 = 12,
cv::COLOR_RGB2BGR565 = 13,
cv::COLOR_BGR5652BGR = 14,
cv::COLOR_BGR5652RGB = 15,
cv::COLOR_BGRA2BGR565 = 16,
cv::COLOR_RGBA2BGR565 = 17,
cv::COLOR_BGR5652BGRA = 18,
cv::COLOR_BGR5652RGBA = 19,
cv::COLOR_GRAY2BGR565 = 20,
cv::COLOR_BGR5652GRAY = 21,
cv::COLOR_BGR2BGR555 = 22,
cv::COLOR_RGB2BGR555 = 23,
cv::COLOR_BGR5552BGR = 24,
cv::COLOR_BGR5552RGB = 25,
cv::COLOR_BGRA2BGR555 = 26,
cv::COLOR_RGBA2BGR555 = 27,
cv::COLOR_BGR5552BGRA = 28,
cv::COLOR_BGR5552RGBA = 29,
cv::COLOR_GRAY2BGR555 = 30,
cv::COLOR_BGR5552GRAY = 31,
cv::COLOR_BGR2XYZ = 32,
cv::COLOR_RGB2XYZ = 33,
cv::COLOR_XYZ2BGR = 34,
cv::COLOR_XYZ2RGB = 35,
cv::COLOR_BGR2YCrCb = 36,
cv::COLOR_RGB2YCrCb = 37,
cv::COLOR_YCrCb2BGR = 38,
cv::COLOR_YCrCb2RGB = 39,
cv::COLOR_BGR2HSV = 40,
cv::COLOR_RGB2HSV = 41,
cv::COLOR_BGR2Lab = 44,
cv::COLOR_RGB2Lab = 45,
cv::COLOR_BGR2Luv = 50,
cv::COLOR_RGB2Luv = 51,
cv::COLOR_BGR2HLS = 52,
cv::COLOR_RGB2HLS = 53,
cv::COLOR_HSV2BGR = 54,
cv::COLOR_HSV2RGB = 55,
cv::COLOR_Lab2BGR = 56,
cv::COLOR_Lab2RGB = 57,
cv::COLOR_Luv2BGR = 58,
cv::COLOR_Luv2RGB = 59,
cv::COLOR_HLS2BGR = 60,
cv::COLOR_HLS2RGB = 61,
cv::COLOR_BGR2HSV_FULL = 66,
cv::COLOR_RGB2HSV_FULL = 67,
cv::COLOR_BGR2HLS_FULL = 68,
cv::COLOR_RGB2HLS_FULL = 69,
cv::COLOR_HSV2BGR_FULL = 70,
cv::COLOR_HSV2RGB_FULL = 71,
cv::COLOR_HLS2BGR_FULL = 72,
cv::COLOR_HLS2RGB_FULL = 73,
cv::COLOR_LBGR2Lab = 74,
cv::COLOR_LRGB2Lab = 75,
cv::COLOR_LBGR2Luv = 76,
cv::COLOR_LRGB2Luv = 77,
cv::COLOR_Lab2LBGR = 78,
cv::COLOR_Lab2LRGB = 79,
cv::COLOR_Luv2LBGR = 80,
cv::COLOR_Luv2LRGB = 81,
cv::COLOR_BGR2YUV = 82,
cv::COLOR_RGB2YUV = 83,
cv::COLOR_YUV2BGR = 84,
cv::COLOR_YUV2RGB = 85,
// YUV 4:2:0 formats family
cv::COLOR_YUV2RGB_NV12 = 90,
cv::COLOR_YUV2BGR_NV12 = 91,
cv::COLOR_YUV2RGB_NV21 = 92,
cv::COLOR_YUV2BGR_NV21 = 93,
cv::COLOR_YUV420sp2RGB = COLOR_YUV2RGB_NV21,
cv::COLOR_YUV420sp2BGR = COLOR_YUV2BGR_NV21,
cv::COLOR_YUV2RGBA_NV12 = 94,
cv::COLOR_YUV2BGRA_NV12 = 95,
cv::COLOR_YUV2RGBA_NV21 = 96,
cv::COLOR_YUV2BGRA_NV21 = 97,
cv::COLOR_YUV420sp2RGBA = COLOR_YUV2RGBA_NV21,
cv::COLOR_YUV420sp2BGRA = COLOR_YUV2BGRA_NV21,
cv::COLOR_YUV2RGB_YV12 = 98,
cv::COLOR_YUV2BGR_YV12 = 99,
cv::COLOR_YUV2RGB_IYUV = 100,
cv::COLOR_YUV2BGR_IYUV = 101,
cv::COLOR_YUV2RGB_I420 = COLOR_YUV2RGB_IYUV,
cv::COLOR_YUV2BGR_I420 = COLOR_YUV2BGR_IYUV,
cv::COLOR_YUV420p2RGB = COLOR_YUV2RGB_YV12,
cv::COLOR_YUV420p2BGR = COLOR_YUV2BGR_YV12,
cv::COLOR_YUV2RGBA_YV12 = 102,
cv::COLOR_YUV2BGRA_YV12 = 103,
cv::COLOR_YUV2RGBA_IYUV = 104,
cv::COLOR_YUV2BGRA_IYUV = 105,
cv::COLOR_YUV2RGBA_I420 = COLOR_YUV2RGBA_IYUV,
cv::COLOR_YUV2BGRA_I420 = COLOR_YUV2BGRA_IYUV,
cv::COLOR_YUV420p2RGBA = COLOR_YUV2RGBA_YV12,
cv::COLOR_YUV420p2BGRA = COLOR_YUV2BGRA_YV12,
cv::COLOR_YUV2GRAY_420 = 106,
cv::COLOR_YUV2GRAY_NV21 = COLOR_YUV2GRAY_420,
cv::COLOR_YUV2GRAY_NV12 = COLOR_YUV2GRAY_420,
cv::COLOR_YUV2GRAY_YV12 = COLOR_YUV2GRAY_420,
cv::COLOR_YUV2GRAY_IYUV = COLOR_YUV2GRAY_420,
cv::COLOR_YUV2GRAY_I420 = COLOR_YUV2GRAY_420,
cv::COLOR_YUV420sp2GRAY = COLOR_YUV2GRAY_420,
cv::COLOR_YUV420p2GRAY = COLOR_YUV2GRAY_420,
// YUV 4:2:2 formats family
cv::COLOR_YUV2RGB_UYVY = 107,
cv::COLOR_YUV2BGR_UYVY = 108,
cv::COLOR_YUV2RGB_Y422 = COLOR_YUV2RGB_UYVY,
cv::COLOR_YUV2BGR_Y422 = COLOR_YUV2BGR_UYVY,
cv::COLOR_YUV2RGB_UYNV = COLOR_YUV2RGB_UYVY,
cv::COLOR_YUV2BGR_UYNV = COLOR_YUV2BGR_UYVY,
cv::COLOR_YUV2RGBA_UYVY = 111,
cv::COLOR_YUV2BGRA_UYVY = 112,
cv::COLOR_YUV2RGBA_Y422 = COLOR_YUV2RGBA_UYVY,
cv::COLOR_YUV2BGRA_Y422 = COLOR_YUV2BGRA_UYVY,
cv::COLOR_YUV2RGBA_UYNV = COLOR_YUV2RGBA_UYVY,
cv::COLOR_YUV2BGRA_UYNV = COLOR_YUV2BGRA_UYVY,
cv::COLOR_YUV2RGB_YUY2 = 115,
cv::COLOR_YUV2BGR_YUY2 = 116,
cv::COLOR_YUV2RGB_YVYU = 117,
cv::COLOR_YUV2BGR_YVYU = 118,
cv::COLOR_YUV2RGB_YUYV = COLOR_YUV2RGB_YUY2,
cv::COLOR_YUV2BGR_YUYV = COLOR_YUV2BGR_YUY2,
cv::COLOR_YUV2RGB_YUNV = COLOR_YUV2RGB_YUY2,
cv::COLOR_YUV2BGR_YUNV = COLOR_YUV2BGR_YUY2,
cv::COLOR_YUV2RGBA_YUY2 = 119,
cv::COLOR_YUV2BGRA_YUY2 = 120,
cv::COLOR_YUV2RGBA_YVYU = 121,
cv::COLOR_YUV2BGRA_YVYU = 122,
cv::COLOR_YUV2RGBA_YUYV = COLOR_YUV2RGBA_YUY2,
cv::COLOR_YUV2BGRA_YUYV = COLOR_YUV2BGRA_YUY2,
cv::COLOR_YUV2RGBA_YUNV = COLOR_YUV2RGBA_YUY2,
cv::COLOR_YUV2BGRA_YUNV = COLOR_YUV2BGRA_YUY2,
cv::COLOR_YUV2GRAY_UYVY = 123,
cv::COLOR_YUV2GRAY_YUY2 = 124,
cv::COLOR_YUV2GRAY_Y422 = COLOR_YUV2GRAY_UYVY,
cv::COLOR_YUV2GRAY_UYNV = COLOR_YUV2GRAY_UYVY,
cv::COLOR_YUV2GRAY_YVYU = COLOR_YUV2GRAY_YUY2,
cv::COLOR_YUV2GRAY_YUYV = COLOR_YUV2GRAY_YUY2,
cv::COLOR_YUV2GRAY_YUNV = COLOR_YUV2GRAY_YUY2,
// alpha premultiplication
cv::COLOR_RGBA2mRGBA = 125,
cv::COLOR_mRGBA2RGBA = 126,
cv::COLOR_RGB2YUV_I420 = 127,
cv::COLOR_BGR2YUV_I420 = 128,
cv::COLOR_RGB2YUV_IYUV = COLOR_RGB2YUV_I420,
cv::COLOR_BGR2YUV_IYUV = COLOR_BGR2YUV_I420,
cv::COLOR_RGBA2YUV_I420 = 129,
cv::COLOR_BGRA2YUV_I420 = 130,
cv::COLOR_RGBA2YUV_IYUV = COLOR_RGBA2YUV_I420,
cv::COLOR_BGRA2YUV_IYUV = COLOR_BGRA2YUV_I420,
cv::COLOR_RGB2YUV_YV12 = 131,
cv::COLOR_BGR2YUV_YV12 = 132,
cv::COLOR_RGBA2YUV_YV12 = 133,
cv::COLOR_BGRA2YUV_YV12 = 134,
cv::COLOR_BayerBG2BGR = 46,
cv::COLOR_BayerGB2BGR = 47,
cv::COLOR_BayerRG2BGR = 48,
cv::COLOR_BayerGR2BGR = 49,
cv::COLOR_BayerBG2RGB = COLOR_BayerRG2BGR,
cv::COLOR_BayerGB2RGB = COLOR_BayerGR2BGR,
cv::COLOR_BayerRG2RGB = COLOR_BayerBG2BGR,
cv::COLOR_BayerGR2RGB = COLOR_BayerGB2BGR,
cv::COLOR_BayerBG2GRAY = 86,
cv::COLOR_BayerGB2GRAY = 87,
cv::COLOR_BayerRG2GRAY = 88,
cv::COLOR_BayerGR2GRAY = 89,
cv::COLOR_BayerBG2BGR_VNG = 62,
cv::COLOR_BayerGB2BGR_VNG = 63,
cv::COLOR_BayerRG2BGR_VNG = 64,
cv::COLOR_BayerGR2BGR_VNG = 65,
cv::COLOR_BayerBG2RGB_VNG = COLOR_BayerRG2BGR_VNG,
cv::COLOR_BayerGB2RGB_VNG = COLOR_BayerGR2BGR_VNG,
cv::COLOR_BayerRG2RGB_VNG = COLOR_BayerBG2BGR_VNG,
cv::COLOR_BayerGR2RGB_VNG = COLOR_BayerGB2BGR_VNG,
cv::COLOR_BayerBG2BGR_EA = 135,
cv::COLOR_BayerGB2BGR_EA = 136,
cv::COLOR_BayerRG2BGR_EA = 137,
cv::COLOR_BayerGR2BGR_EA = 138,
cv::COLOR_BayerBG2RGB_EA = COLOR_BayerRG2BGR_EA,
cv::COLOR_BayerGB2RGB_EA = COLOR_BayerGR2BGR_EA,
cv::COLOR_BayerRG2RGB_EA = COLOR_BayerBG2BGR_EA,
cv::COLOR_BayerGR2RGB_EA = COLOR_BayerGB2BGR_EA,
cv::COLOR_BayerBG2BGRA = 139,
cv::COLOR_BayerGB2BGRA = 140,
cv::COLOR_BayerRG2BGRA = 141,
cv::COLOR_BayerGR2BGRA = 142,
cv::COLOR_BayerBG2RGBA = COLOR_BayerRG2BGRA,
cv::COLOR_BayerGB2RGBA = COLOR_BayerGR2BGRA,
cv::COLOR_BayerRG2RGBA = COLOR_BayerBG2BGRA,
cv::COLOR_BayerGR2RGBA = COLOR_BayerGB2BGRA,
cv::COLOR_COLORCVT_MAX = 143
}

五、libyuv 库的编译和使用

1、libyuv 下载

2、libyuv 编译

# 编译指定平台的库,只需要把相应的编译器换一下就好了
# 1、使用 make 来编译(默认为 gcc/g++ 编译器,生成静态库 libyuv.a)
cd libyuv-master/
vim linux.mk
# 更改为自己的编译器和编译选项 
CC=/home/manzp/projects/19.sigmastar/gcc-sigmastar-9.1.0-2020.07-x86_64_arm-linux-gnueabihf/bin/arm-linux-gnueabihf-sigmastar-9.1.0-gcc
CFLAGS:=-mcpu=cortex-a53 -fno-aggressive-loop-optimizations -O3 -fomit-frame-pointer -ffast-math -Wall -fPIC -fpermissive -mfpu=neon-vfpv4
CFLAGS+=-Iinclude/
CXX=/home/manzp/projects/19.sigmastar/gcc-sigmastar-9.1.0-2020.07-x86_64_arm-linux-gnueabihf/bin/arm-linux-gnueabihf-sigmastar-9.1.0-g++
CXXFLAGS:=-mcpu=cortex-a53 -fno-aggressive-loop-optimizations -O3 -fomit-frame-pointer -ffast-math -Wall -fPIC -fpermissive -mfpu=neon-vfpv4
CXXFLAGS+=-Iinclude/
# 编译和重新编译
make -f linux.mk -j16  # 默认生成静态库
make -f linux.mk clean # 使用不同的编译器重新编译前记得 clean 一下
# 2、使用 cmake 来编译,默认 debug build,out 下面有 libyuv.a libyuv.so 及 yuvconvert 产生
mkdir out
cd out
cmake ..
cmake --build .
# release build/install,如果想用自己的编译器,加上 -DCROSS_COMPILE=arm-himix200-linux
mkdir out
cd out
cmake -DCMAKE_INSTALL_PREFIX="/usr/lib" -DCMAKE_BUILD_TYPE="Release" ..
cmake --build . --config Release
sudo cmake --build . --target install --config Release

3、libyuv 使用

libyuv is an open source project that includes YUV scaling and conversion functionality.

  • Scale YUV to prepare content for compression, with point, bilinear or box filter.
  • Convert to YUV from webcam formats for compression.
  • Convert to RGB formats for rendering/effects.
  • Rotate by 90/180/270 degrees to adjust for mobile devices in portrait mode.
  • Optimized for SSSE3/AVX2 on x86/x64
  • Optimized for Neon on Arm.
  • Optimized for MSA on Mips.
  • 将 libyuv.h 包含到工程,直接调用转换函数即可
void NV12Crop(uint8_t *src_y, uint8_t *src_uv, int src_stride_y,
int* roi_x, int* roi_y, int* roi_w, int* roi_h,
uint8_t *dst_yuv) { 

// 裁剪的坐标 X 和 Y 必须是偶数,否则可能得到 NV21
// 裁剪的宽和高必须是偶数(2n*2n*1.5 = 6n2),正好是 6 的倍数(每四个Y对应一个UV)
if (*roi_x % 2) { 

*roi_x -= 1;
}
if (*roi_y % 2) { 

*roi_y -= 1;
}
if (*roi_w % 2) { 

*roi_w -= 1;
}
if (*roi_h % 2) { 

*roi_h -= 1;
}
// 取得 SRC YUV 首地址
unsigned char *src_roi_y = src_y + src_stride_y * (*roi_y) + (*roi_x);
// NV12在竖直方向上进行了 1/2 下采样,水平方向上并未做下采样,所以只需 roi_y 减半
unsigned char *src_roi_uv = src_uv + src_stride_y * (*roi_y) / 2 + (*roi_x);
unsigned char *dst_y = dst_yuv;
unsigned char *dst_uv = dst_yuv + (*roi_w) * (*roi_h);
for (int h = 0; h < *roi_h; h++) { 

// ROI 按行 copy Y
memcpy(dst_y + (*roi_w) * h, src_roi_y + src_stride_y * h, *roi_w);
if (h < *roi_h / 2) { 

// ROI 按行 copy UV
memcpy(dst_uv + (*roi_w) * h, src_roi_uv + src_stride_y * h, *roi_w);
}
}
}
void NV12RoiScaleToRGB24(u8 *src_y, u8 *src_uv, s32 src_stride_y, s32 src_stride_uv,
s32 *roi_x, s32 *roi_y, s32 *roi_w, s32 *roi_h,
u8 *dst_tmp_buffer, u8 *dst_rgb24, s32 dst_w, s32 dst_h) { 

// 裁剪的坐标 X 和 Y 必须是偶数,否则可能得到 NV21
// 裁剪的宽和高必须是偶数(2n*2n*1.5 = 6n2),正好是 6 的倍数(每四个Y对应一个UV)
if (*roi_x % 2) { 

*roi_x -= 1;
}
if (*roi_y % 2) { 

*roi_y -= 1;
}
if (*roi_w % 2) { 

*roi_w -= 1;
}
if (*roi_h % 2) { 

*roi_h -= 1;
}
// 取得 SRC YUV 首地址
u8 *src_roi_y = (u8 *) (src_y + src_stride_y * (*roi_y) + (*roi_x));
// NV12 在竖直方向上进行了 1/2 下采样,水平方向上并未做下采样,所以只需 roi_y 减半
u16 *src_roi_uv = (u16 *) (src_uv + src_stride_uv * (*roi_y) / 2 + (*roi_x));
u8 *dst_y = dst_tmp_buffer;
u8 *dst_uv = dst_tmp_buffer + dst_w * dst_h;
libyuv::ScalePlane(src_roi_y, src_stride_y, *roi_w, *roi_h, dst_y, dst_w, dst_w, dst_h, libyuv::kFilterBilinear);
libyuv::ScalePlane_16(src_roi_uv, src_stride_uv / 2, (*roi_w) / 2, (*roi_h) / 2, (uint16_t *) dst_uv, dst_w / 2,
dst_w / 2, dst_h / 2, libyuv::kFilterNone);
libyuv::NV12ToRGB24(dst_y, dst_w, dst_uv, dst_w, dst_rgb24, dst_w * 3, dst_w, dst_h);
}
void NV12RoiToRGB24Perspective(u8 *src_y, u8 *src_uv, s32 src_stride_y, s32 src_stride_uv,
s32 LeftTopx, s32 LeftTopy, s32 RightTopx, s32 RightTopy,
s32 LeftBottomx, s32 LeftBottomy, s32 RightBottomx, s32 RightBottomy,
u8 *dst_rgb24, s32 dst_w, s32 dst_h) { 

// 裁剪的坐标 X 和 Y 必须是偶数,否则可能得到 NV21
// 裁剪的宽和高必须是偶数(2n*2n*1.5 = 6n2),正好是 6 的倍数(每四个Y对应一个UV)
s32 offset = 10;  // 上下左右各自外扩 10 个像素,为放射变换做准备
s32 roi_x = s32(LeftTopx - offset);
s32 roi_y = s32(LeftTopy - offset);
s32 roi_w = s32(RightBottomx - LeftTopx + 1 + 2 * offset);
s32 roi_h = s32(RightBottomy - LeftTopy + 1 + 2 * offset);
if (roi_x % 2) { 

roi_x -= 1;
}
if (roi_y % 2) { 

roi_y -= 1;
}
if (roi_w % 2) { 

roi_w -= 1;
}
if (roi_h % 2) { 

roi_h -= 1;
}
// 取得 SRC YUV 首地址
u8 *src_roi_y = src_y + src_stride_y * roi_y + roi_x;
// NV12 在竖直方向上进行了 1/2 下采样,水平方向上并未做下采样,所以只需 roi_y 减半
u8 *src_roi_uv = src_uv + src_stride_uv * roi_y / 2 + roi_x;
u8 *dst_tmp_buffer = (u8 *) calloc(1, roi_w * roi_h * 3);  // h*wc,BGR888
libyuv::NV12ToRGB24(src_roi_y, src_stride_y, src_roi_uv, src_stride_uv, dst_tmp_buffer, roi_w * 3, roi_w, roi_h);
PerspectiveTransform(dst_tmp_buffer, dst_rgb24, roi_w, roi_h, dst_w, dst_h,
LeftTopx - roi_x, LeftTopy - roi_y,
RightTopx - roi_x, RightTopy - roi_y,
LeftBottomx - roi_x, LeftBottomy - roi_y,
RightBottomx - roi_x, RightBottomy - roi_y); // 需输入相对位置
if (dst_tmp_buffer != NULL) { 

free(dst_tmp_buffer);
dst_tmp_buffer = NULL;
}
}

六、参考资料

1、详解YUV数据格式
2、YUV 格式详解,只看这一篇就够了
3、图像原始格式(YUV444 YUV422 YUV420)一探究竟
4、libyuv库简单使用

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/160228.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)
blank

相关推荐

  • 【哈佛幸福课笔记】【1】

    【哈佛幸福课笔记】【1】【哈佛幸福课笔记】【1】​​ 一个月的时间看完了哈佛幸福课,正如Tal所说课程的结束才是学习的开始。对于课程观点的思考,对于个人习惯的培养,对于思维模式的转变还需要花大量的时间去练习。这个系列的帖子将作为我个人的读书笔记,也欢迎机缘巧合点开这个帖子的网友一起讨论。(第一章思维导图)​ 课程在讲述的时候有时候并不是线性完整的逻辑,一个观点有时尚没讲完会进入到新的观点中,这样课程显得不是那么的死板不是单纯的说教而是有趣的互动和对实验数据的分析。课程整体的结构十分清晰,可以说干货很多。1.关于内部

  • js中除法取整数

    js中除法取整数2019独角兽企业重金招聘Python工程师标准>>>…

  • 考研数据库系统概论题目整理总结_数据库系统概论pdf

    考研数据库系统概论题目整理总结_数据库系统概论pdf数据库系统概论题目自整理说复试题目过于牵强,只是自己整理的一些知识点而已,为了便于理解和背诵,有些部分定义和说明尽量简明扼要,如有错误请多多指教!(不可转载)1.试述数据、数据库、数据库系统、数据库管理系统的概念。(l)数据(Data):描述事物的符号记录称为数据。数据的含义称为语义,数据与其语义是不可分的。(2)数据库(DataBase,简称DB):若干个相互之间有关联关系的表的集合,表就是关系。数据库中的数据具有永久存储,易扩展,可共享的特点。(3)数据库系统(Data

  • J2EE架构师之路[通俗易懂]

    J2EE架构师之路[通俗易懂]不经意的回首,工作进入第五个年头了,发现走过了从Java程序员到J2EE架构师的历程。发现电脑上安装了各种各样的J2EE工具:JBuilder,WSAD,Eclipse,Rose,Together,Weblogic,Jtest,Optimizator,Mysql…发现电脑上保存了各种各样的OpenSource项目:Tomcat,JBoss,Ant,Hibernate,Spr

  • 以太网RJ45 接线标准 线序(备忘)「建议收藏」

    以太网RJ45 接线标准 线序(备忘)「建议收藏」RJ是RegisteredJack的缩写,意思是“注册的插座”。在FCC(美国联邦通信委员会标准和规章)中的定义是,RJ是描述公用电信网络的接口,常用的有RJ-11和RJ-45,计算机网络的RJ-45是标准8位模块化接口的俗称。568A的排线顺序从左到右依次为:白绿、绿、白橙、蓝、白蓝、橙、白棕、棕。568B的排线顺序从左到右依次为:白橙、橙、白绿、蓝、白蓝、绿、白棕、棕。所谓的交叉线是指:一端…

  • 磁盘性能指标–IOPS、吞吐量及测试

    磁盘性能指标–IOPS、吞吐量及测试

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号