美文网首页
YUV像素数据处理

YUV像素数据处理

作者: deep_sadness | 来源:发表于2018-05-03 11:16 被阅读372次

    YUV简介

    YUV,是一种颜色编码方法。常使用在各个视频处理组件中。 YUV在对照片或视频编码时,考虑到人类的感知能力,允许降低色度的带宽。

    YUV是编译true-color颜色空间(color space)的种类,Y'UV, YUV, YCbCrYPbPr等专有名词都可以称为YUV,彼此有重叠。“Y”表示明亮度(Luminance、Luma),“U”和“V”则是色度浓度(Chrominance、Chroma),

    格式

    YUV Formats分成两个格式:

    • 紧缩格式(packed formats):将Y、U、V值存储成Macro Pixels数组,和RGB的存放方式类似。
    • 平面格式(planar formats):将Y、U、V的三个分量分别存放在不同的矩阵中。

    紧缩格式(packed format)中的YUV是混合在一起的,对于YUV4:4:4格式而言,用紧缩格式很合适的,因此就有了UYVY、YUYV等。平面格式(planar formats)是指每Y分量,U分量和V分量都是以独立的平面组织的,也就是说所有的U分量必须在Y分量后面,而V分量在所有的U分量后面,此一格式适用于采样(subsample)。平面格式(planar format)有I420(4:2:0)、YV12、IYUV等。

    简介总结

    1. 为了兼容黑白电视而诞生。Y表示灰度。
    2. 对比RBG,需要的带宽小。RGB每一帧需要3*w*h的大小,YUV420这种形式的话,需要w*h*3/2的大小。

    YUV420格式了解

    在Android上通过Camera可以取到 NV21 与 YV12.YUV数据

    YUV420P
    image.png
    YV12:
    yv12.png
    NV21:

    这种格式(NV21)是Android相机预览的标准图片格式。YUV 4:2:0平面图像,8位Y采样,接着是具有8位2x2二次采样色度采样的交织V / U平面。

    NV21.png

    YUV像素处理

    准备

    1. 下载好测试图


      Lenna.png
    2. 通过FFmpeg命令,转成NV21
    ffmpeg -i Lenna.png  -pix_fmt nv21 LennaNv21.yuv 
    
    1. 下载YUV Player Deluxe作为图片的预览
      http://www.yuvplayer.com/

    开始

    NV21 To YUV420P
    • 代码
      //nv21 YYYYYYYY VU VU  => yyyy yyyy uuuu vvvv
        public static void nv21ToYuv420(int size, int frameLength, byte[] frame) {
            //将Y复制过去
            byte[] frameY = new byte[frameLength];
    //        Arrays.fill(frameY, (byte) 128);
            System.arraycopy(frame, 0, frameY, 0, size);
    
            int uvSize = (frameLength - size) / 2;
            byte[] uFrame = new byte[uvSize];
            byte[] vFrame = new byte[uvSize];
            for (int i = 0; i < uvSize * 2; i++) {
                int result = i % 2;
                int resultIndex = i / 2;
    
                if (result == 0) {
                    vFrame[resultIndex] = frame[size + i];
    //                System.out.println("uFrame resultIndex="+resultIndex+", i="+(size+i));
                } else {
                    uFrame[resultIndex] = frame[size + i];
    //                System.out.println("vFrame resultIndex="+resultIndex+", i="+(size+i));
                }
            }
    
            System.arraycopy(uFrame, 0, frameY, size, uvSize);
            System.arraycopy(vFrame, 0, frameY, size + uvSize, uvSize);
        }
    
    • 结果对比(yuv420格式 512x512预览)


      结果1.png
    yuv 420p顺时针 90
    • 代码
    public static void yuv420Rotation90(int srcH, int srcW, int frameLength, byte[] frame) {
            //将Y复制过去
            byte[] frameY = new byte[frameLength];
    
            int size = srcH * srcW;
            int uvSize = (frameLength - size) / 2;
    
            //翻转Y
            //记录旋转的h w
            int rH = 0;
            int rW = 0;
            for (int oH = 0; oH < srcH; oH++) {
                for (int oW = 0; oW < srcW; oW++) {
                    //第一行->最后一列
                    //旋转之后,现在的行数等于原来的横向的index
                    rH = oW;
                    //现在的w= 原来行数的总和-H
                    rW = srcH - oH - 1;
                    frameY[srcW * rH + rW] = frame[srcW * oH + oW];
                }
            }
    
            //翻转U
            //记录旋转的h srcW
            srcH = srcH / 2;
            srcW = srcW / 2;
    
            for (int oH = 0; oH < srcH; oH++) {
                for (int oW = 0; oW < srcW; oW++) {
                    //第一行->最后一列
                    //旋转之后,现在的行数等于原来的横向的index
                    rH = oW;
                    //现在的w= 原来行数的总和-H
                    rW = srcH - oH - 1;
                    frameY[size + srcW * rH + rW] = frame[size + srcW * oH + oW];
                }
            }
    
            //翻转V
            //记录旋转的h srcW
            for (int oH = 0; oH < srcH; oH++) {
                for (int oW = 0; oW < srcW; oW++) {
                    //第一行->最后一列
                    //旋转之后,现在的行数等于原来的横向的index
                    rH = oW;
                    //现在的w= 原来行数的总和-H
                    rW = srcH - oH - 1;
                    int rP = size + uvSize + srcW * rH + rW;
                    int oP = size + uvSize + srcW * oH + oW;
                    frameY[rP] = frame[oP];
                }
            }
        }
    
    • 结果对比(yuv420格式 512x512预览)


      image.png
    翻转
    public static void yuv420Flip(int srcH, int srcW, int frameLength, byte[] frame) {
            //将Y复制过去
            int size = srcW * srcH;
            byte[] frameY = new byte[frameLength];
            int uvSize = (frameLength - size) / 2;
    
            //翻转Y
            //就是讲下面的复制到上面来
            for (int i = 0; i < srcH; i++) {
                System.arraycopy(frame, (srcH - i - 1) * srcW, frameY, i * srcW, srcW);
            }
    
            srcH = srcH / 2;
            srcH = srcH / 2;
            for (int i = 0; i < srcH; i++) {
                System.arraycopy(frame, size + (srcH - i - 1) * srcW, frameY, size + i * srcW, srcW);
            }
    
            for (int i = 0; i < srcH; i++) {
                System.arraycopy(frame, size + uvSize + (srcH - i - 1) * srcW, frameY, size + uvSize + i * srcW, srcW);
            }
        }
    
    • 结果对比(yuv420格式 512x512预览)


      image.png
    裁剪
     public static void yuv420Clip(int srcH, int srcW, int clipH, int clipW, int startW, int startH, byte[] frame) {
            int srcSize = srcH * srcW;
    
            int clipSize = clipH * clipW;
            int clipFrameLength = clipH * clipW * 3 / 2;
    
            byte[] frameY2 = new byte[clipFrameLength];
    
    
            int totalClipH = clipH;
            int totalClipW = clipW;
            int totalW = srcW;
            int totalH = srcH;
            //复制Y
            for (int i = 0; i < totalClipH; i++) {
                //在原来数组中的H
                int oH = startH + i;
                int srcPos = startW + totalW * oH;
                System.arraycopy(frame, srcPos, frameY2, totalClipW * i, totalClipW);
            }
    
            totalClipH = clipH / 2;
            totalClipW = clipW / 2;
            totalW = srcW / 2;
            totalH = srcH / 2;
    
    //        //复制UV
            for (int i = 0; i < totalClipH; i++) {
                //在原来数组中的H
                int oH = startH / 2 + i;
                int srcPos = srcSize + startW / 2 + totalW * oH;
                int desPos = clipSize + totalClipW * i;
                System.arraycopy(frame, srcPos, frameY2, desPos, totalClipW);
            }
    
            for (int i = 0; i < totalClipH; i++) {
                //在原来数组中的H
                int oH = startH / 2 + i;
                int srcPos = srcSize + srcSize / 4 + startW / 2 + totalW * oH;
                int desPos = clipSize + clipSize / 4 + totalClipW * i;
                System.arraycopy(frame, srcPos, frameY2, desPos, totalClipW);
            }
        }
    
    • 结果对比(yuv420格式 左300x300 右512x512预览)


      image.png

    相关文章

      网友评论

          本文标题:YUV像素数据处理

          本文链接:https://www.haomeiwen.com/subject/vdqcrftx.html