IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 游戏开发 -> Android YUV学习 -> 正文阅读

[游戏开发]Android YUV学习

基础

H.265与H.264是ITU-T VCEG 制定的视频编码标准。H.265是H.264升级版,保留原来的某些技术。H264可以低于1Mbps的速度实现标清数字图像传送;H265则可以实现利用1~2Mbps的传输速度传送720P(分辨率1280720)普通高清音视频传送。以Bitmap大小算,一秒钟视频24720* 1080 * 3 * 8 / 1024/1024= 427.1484375 相对于Bitmap序列压缩了400倍

要注意Android各个平台的实现可能不一样

YUV分类

  • YUV和RGB一样,也是一种图像像素存储格式,起源于电视机产业,其特点是存储占用更小,Y代表亮度,U代表色度,V代表浓度
  • 同样的由于多样性,YUV内部又产生了许多的分支,就像RGB的带A不带A,半精度等

为了维持人的肉眼观感,通常需要每个像素点保存8bit的y亮度,每2x2个点保存至少一个u和v值,如下所示,要理解它的排列就要知道,它在量化8bit之后,每个像素占用大小:

YUV的分类主要依据三个方面:
是按照通道顺序存储还是按照像素顺序
planar格式:连续存储所有像素点Y,然后是所有像素点U,接着是V
packed格式:所有像素点的YUV信息连续交错存储,类似于BItmap
uv的字节大小,
uv的顺序,哪个前,哪个后,话说这玩意有啥意义?分个前后?

YUV,YCbCr,YPbPr写法的含义
它们分别代表在不同领域时使用的名称,总的大类都是一致的。

YUV444采样,4个y,4个uv。

四个像素点: [Y0 U0 V0] [Y1 U1 V1] [Y2 U2 V2] [Y3 U3 V3]
存放码流: Y0 U0 V0 Y1 U1 V1 Y2 U2 V2 Y3 U3 V3

YUV422采样,4个y,两个u两个v

四个像素点: [Y0 U0 V0] [Y1 U1 V1] [Y2 U2 V2] [Y3 U3 V3]
存放码流: Y0 U0 Y1 V1 Y2 U2 Y3 V3

YUV411采样,每4个Y对应一组UV。

四个像素点: [Y0 U0 V0] [Y1 U1 V1] [Y2 U2 V2] [Y3 U3 V3]
存放码流: Y0 U0 Y1 Y2 V2 Y3

YUV420采样,每4个Y对应一组UV,

实际上这个应该是411,但是命名为420了
每个由2x2个2行2列相邻的像素组成的宏像素需要占用6字节内存。,亮度4个字节,两个色度各1个字节。

四个像素点: [Y0 U0 V0] [Y1 U1 V1] [Y2 U2 V2] [Y3 U3 V3]
[Y5 U5 V5] [Y6 U6 V6] [Y7 U7 V7] [Y8 U8 V8]
存放码流: Y0 U0 Y1 Y2 U2 Y3 Y5 V5 Y6 Y7 V7 Y8

YUV422—包含如:YUYV、UYVY、YUV422P

YUV422,大多数的Android机 sensor出来的视频流都是这个格式
YUYV: 像素形式在这里插入图片描述

YVYU:

YUV422P:2个Y对应一对UV,即Y00 Y01对应U00 V00
这个是通道格式
在这里插入图片描述

YUV420—包含如:YV12,YU12、NV12、NV21、YUV420SP、I420

这个420应该是411更好吧
在Android Camera框架中,setPreviewFormat中可传入的格式,API给出的2个可供选择的格式分别是ImageFormat.NV21和ImageFormat.YV12

YV12和YU12都属于YUV420p,其中Y\U\V分别对应一个plane,区别在于UV的位置对调,下面是YU12的存储示意图:

NV12和NV21,其中NV12就是我们Android常见的YUV420SP,他们不像上一个YV12,有3个plane,而是由Y和UV分别两个Plane组成,UV交替排列,U在前的是NV12,V在前为NV21.

YUV中stride跨距的含义?
跨距的由来,因为需要内存对齐,便于CPU处理,图像实际存储的一行长度通常是cpu字长的倍数,比如32的倍数,64的倍数,
在Android中,setPreviewFormat中就有标注YV12的跨距计算方式:

    {@link android.graphics.ImageFormat#YV12}. For camera callback data,
     * it can be assumed that the stride of the Y and UV data is the
     * smallest possible that meets the alignment requirements. That is, if
     * the preview size is <var>width x height</var>, then the following
     * equations describe the buffer index for the beginning of row
     * <var>y</var> for the Y plane and row <var>c</var> for the U and V
     * planes:
     *
     * <pre>{@code
     * yStride   = (int) ceil(width / 16.0) * 16;
     * uvStride  = (int) ceil( (yStride / 2) / 16.0) * 16;
     * ySize     = yStride * height;
     * uvSize    = uvStride * height / 2;
     * yRowIndex = yStride * y;
     * uRowIndex = ySize + uvSize + uvStride * c;
     * vRowIndex = ySize + uvStride * c;
     * size      = ySize + uvSize * 2;
     * }

Android硬编码对YUV格式的要求

部分Android硬件平台对stride也有要求,比如MTK

// Note: the stride of resolution must be set as 16x for hard encoding with some chip like MTK
// Since Y component is quadruple size as U and V component, the stride must be set as 32x
if (!useSoftEncoder && vOutWidth % 32 != 0 || vOutHeight % 32 != 0) {
if (vmci.getName().contains(“MTK”)) {
throw new AssertionError(“MTK encoding revolution stride must be 32x”);
}
}

API使用

Android MediaFormat.KEY_COLOR_FORMAT代表的含义

常用的YUV ColorFormat项如下, 推荐我们去使用COLOR_FormatYUV420Flexible, COLOR_FormatYUV422Flexible, COLOR_FormatYUV444Flexible

COLOR_FormatYUV420Flexible
它大体意思是,哥们,我是个万能钥匙,对应了ImageFormat中的YUV_420_888,可以代替 COLOR_FormatYUV411PackedPlanar,COLOR_FormatYUV420Planar,COLOR_FormatYUV420PackedPlanar以及COLOR_FormatYUV420SemiPlanar,COLOR_FormatYUV420PackedSemiPlanar使用

PlaneProxy/Plane

Y、U和V三个分量的数据分别保存在三个Plane类中,即通过 getPlanes()得到的数组。 Plane 实际是对ByteBuffer的封装。
Image保证了planes[0]一定是Y,planes[1]一定是U,planes[2]一定是V。且对于plane [0],Y分量数据一定是连续存储的,中间不会有U或V数据穿插,也就是说我们一定能够一次性得到所有Y分量的值。
但是对于UV数据,可能存在以下两种情况:

  1. planes[1] = {UUUU…},planes[2] = {VVVV…}; //I420
  2. planes[1] = {UVUV…},planes[2] = {VUVU…}。

PixelStride

所以在我么取数据时需要在根据Plane中的另一个信息来确定如何取对应的U或者V数据。
// 行内数据值间隔
// 1:表示无间隔取值,即为上面的第一种情况
// 2: 表示需要间隔一个数据取值,即为上面的第二种情况
var pixelStride = plane.pixelStride

根据这个属性,我们将确定数据如何存储,因此如果需要取出代表I420格式的byte[],则为:YUV420中,Y数据长度为: width*height , 而U、V都为:width / 2 * height / 2。

val planes = image.planes
//y数据的这个值只能是:1
val yPixelStride = planes[0].pixelStride
val uPixelStride = planes[1].pixelStride
Log.e("xiao", "yPixelStride: $yPixelStride uPixelStride: $uPixelStride") 
// Y数据 pixelStride一定为1
val pixelStride = planes[0].pixelStride
val y = planes[0].buffer // Y数据
val u = ByteArray(image.width / 2 * image.height / 2)
val pixelStride = planes[1].pixelStride
if (pixelStride == 1) {
    planes[1].buffer // U数据
} else if (pixelStride == 2) {
    val uBuffer = planes[1].buffer
    for (i in 0 until uBuffer.remaining() step 2) {
        u[i] = uBuffer.get();
        //丢弃一个数据,这个数据其实是V数据,但是我们还是到planes[2]中获取V数据
        uBuffer.get()
    }
}


RowStride

但是如果使用上面的代码去获取YUV数据,可能你会惊奇的发现,并不是在所有你设置的Width与 Height(分辨率)下都能够正常运行。我们忽略了什么,为什么会出现问题呢?
因为前面讲的内存对齐
getRowStride没有用到.
代码

val planes = image.planes
val size = image.width * image.height * 3 / 2
val  yuv420 = ByteBuffer.allocate(size)
/**
 * Y数据
 */
val plane = planes[0] //y数据

//pixelStride = 1 : 取值无间隔
//pixelStride = 2 : 间隔1个字节取值
// y的此数据应该都是1
val pixelStride = plane.pixelStride //Y的肯定为1

//大于等于宽, 表示连续的两行数据的间隔
//  如:640x480的数据,
//  可能得到640
//  可能得到650,表示每行最后10个字节为补位的数据
val rowStride = plane.rowStride //rowStride 可能末尾有填充

val buffer = plane.buffer
val row = ByteArray(image.width)
// 每行要排除的无效数据,但是需要注意:实际测试中 最后一行没有这个补位数据
val skipRow = ByteArray(rowStride - image.width)
for (i in 0 until image.height) {
    buffer[row]
    yuv420.put(row)
    // 不是最后一行
    if (i < image.height - 1) {
        buffer[skipRow] //最后一行因为后面跟着U 数据,没有无效占位数据,不需要丢弃
    }
}

而对于U与V数据,对应的行步长可能为:
等于Width;
大于Width;
等于Width/2;
大于Width/2
等于Width
这表示,我们获得planes[1]中不仅包含U数据,还会包含V的数据,此时pixelStride==2。
详见 https://juejin.cn/post/6996678928018440223

转换公式
YUV to RGB
在这里插入图片描述

  游戏开发 最新文章
6、英飞凌-AURIX-TC3XX: PWM实验之使用 GT
泛型自动装箱
CubeMax添加Rtthread操作系统 组件STM32F10
python多线程编程:如何优雅地关闭线程
数据类型隐式转换导致的阻塞
WebAPi实现多文件上传,并附带参数
from origin ‘null‘ has been blocked by
UE4 蓝图调用C++函数(附带项目工程)
Unity学习笔记(一)结构体的简单理解与应用
【Memory As a Programming Concept in C a
上一篇文章      下一篇文章      查看所有文章
加:2022-04-27 11:37:15  更:2022-04-27 11:38:53 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/27 7:19:55-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码