YUV_420_888介绍及YUV420转RGBA

刚开始做camera相关的开发时,对YUV_420_888这个格式很懵,不同平台的机型,从Image中转换出RGB的方法好像也不同,在终于初步了解YUV420格式后,写下本文,权当笔记总结。
YUV420转RGBA系列共三篇:

本文是其中的第一篇。

1. YUV简介

在了解YUV_420_888之前,我们先来了解一下YUV。我们知道,RGB是一种颜色编码方法,一个像素分别以R、G、B三个分量来表示。YUV也是一种颜色编码方法,一个像素分别以Y、U、V三个分量来表示。Y表示明亮度(Luminance、Luma),U和V则是色度、浓度(Chrominance、Chroma)。

YUV发明于彩色电视与黑白电视的过渡时期。黑白电视使用的黑白图像只有Y(Luma,Luminance)分量,也就是灰阶值。到了彩色电视规格的制定,是以YUV的格式来处理彩色电视图像,把UV视作表示彩度的C(Chrominance或Chroma),如果忽略C信号,那么剩下的Y(Luma)信号就跟之前的黑白电视频号相同,这样一来便解决了彩色电视机与黑白电视机的兼容问题。YUV在对照片或影片编码时,考虑到人类的感知能力,允许降低色度的采样。根据UV的采样不同,YUV分为多种格式:

  • YUV444:每4个Y,配上4个U,4个V
  • YUV422:每4个Y,配上2个U,2个V
  • YUV420:每4个Y,配上1个U,1个V

降低UV的采样后,传输时只需占用极少的带宽。(本节内容来自维基百科:https://zh.wikipedia.org/wiki/YUV)

2. YUV的紧缩格式(packed)和平面格式(planar)

YUV根据Y、U、V存储方式的不同,可以分成两个格式:

  • 紧缩格式(packed):每个像素点的Y、U、V连续存储,Y1U1V1...YnUnVn。
  • 平面格式(planar):先存储所有像素点的Y分量,再存储所有像素点的UV分量,Y和UV分别连续存储在不同矩阵当中。

平面格式(planar)又分为:

  • 平面格式(planar):先存储所有像素的Y,再存储所有像素点U或者V,最后存储V或者U。其中U、V分别连续存储:Y1...Yn U1...Un V1...Vn 或者 Y1...Yn V1...Vn U1...Un。
  • 半平面格式(semi-planar):先存储所有像素的Y,再存储所有像素点UV或者VU。其中U、V交替存储:Y1...Yn U1V1...UnVn 或者 Y1...Yn V1U1...VnUn。

采样方式采用YUV420、存储方式采用平面格式(planar)称为YUV420P。YUV420P根据U和V顺序不同又分为:

  • I420: Y1...Y4n U1...Un V1...Vn (例如:YYYYYYYYUUVV)
  • YV12:Y1...Y4n V1...Vn U1...Un (例如:YYYYYYYYVVUU)

采样方式采用YUV420、存储方式采用半平面格式(semi-planar)称为YUV420SP,YUV420SP根据U和V顺序不同又分为:

  • NV12: Y1...Y4n U1V1...UnVn (例如:YYYYYYYYUVUV)
  • NV21:Y1...Y4n V1U1...VnUn (例如:YYYYYYYYVUVU)

3. Android YUV_420_888

3.1 YUV_420_888API描述

我们先来看Android API中对YUV_420_888的介绍(https://developer.android.google.cn/reference/android/graphics/ImageFormat?hl=en#YUV_420_888):

Multi-plane Android YUV 420 format

This format is a generic YCbCr format, capable of describing any 4:2:0 chroma-subsampled planar or semiplanar buffer (but not fully interleaved), with 8 bits per color sample.

Images in this format are always represented by three separate buffers of data, one for each color plane. Additional information always accompanies the buffers, describing the row stride and the pixel stride for each plane.

The order of planes in the array returned by Image#getPlanes() is guaranteed such that plane #0 is always Y, plane #1 is always U (Cb), and plane #2 is always V (Cr).

The Y-plane is guaranteed not to be interleaved with the U/V planes (in particular, pixel stride is always 1 in yPlane.getPixelStride()).

The U/V planes are guaranteed to have the same row stride and pixel stride (in particular, uPlane.getRowStride() == vPlane.getRowStride() and uPlane.getPixelStride() == vPlane.getPixelStride(); ).

Google翻译下:

多平面Android YUV 420格式

此格式是通用的YCbCr格式,能够描述任何4:2:0色度采样的平面或半平面缓冲区(但不完全交织),每个颜色样本有8位。

这种格式的图像始终由三个单独的数据缓冲区表示,每个颜色缓冲区一个。 缓冲区中始终会附带其他信息,描述每个平面的行步长和像素步长。

确保Image#getPlanes()返回的数组中平面的顺序,使得平面#0始终为Y,平面#1始终为U(Cb),平面#2始终为V(Cr)。

保证Y平面不与U / V平面交错(特别是yPlane.getPixelStride()中的像素步长始终为1)。

确保U / V平面具有相同的行步长和像素步长(尤其是uPlane.getRowStride()== vPlane.getRowStride()和uPlane.getPixelStride()== vPlane.getPixelStride();)。

3.2 YUV_420_888使用实践

根据API中的介绍,我们可以知道,YUV_420_888是可以兼容所有YUV420P和YUV420SP格式的。也就是说上面提到的I420、YV12、NV12、NV21都可以是YUV_420_888的具体实现。虽然同样是Image对象,不同平台的机型可能有不同的实现。

根据本人测试,创建ImageReader时参数format传入YUV_420_888,Image.getPlanes()后,分别获取Plane0、Plane1、Plane2的buffer:

MTK平台:

//MTK

width:3264, height:2448,

yRowStride:3264, yPixelStride:1, yPixelSize:7990272,

uRowStride:1632, uPixelStride:1, uPixelSize:1997568,

vRowStride:1632, vPixelStride:1, vPixelSize:1997568

可以看到 Y:U:V = 4:1:1,说明这是YUV420采样格式。实测:

Plane0 + Plane1 + Plane2 得到的是I420
展锐平台:

展锐平台就比较奇怪了,即使创建ImageReader时传了YUV_420_888,用的似乎还是YUV422,但这明显不符合Andoid API的设计意图。并且用的是YUV422就算了,又还缺了一个UV色度分量。

//SPRD

width:3264, height:2448,

yRowStride:3264, yPixelStride:1, yPixelSize:7990272,

uRowStride:3264, uPixelStride:2, uPixelSize:3995135, //正常是3995136

vRowStride:3264, vPixelStride:2, vPixelSize:3995135 //正常是3995136

可以看到 Y:U:V = 4:2:2,说明这是YUV422采样格式。所以展锐平台的转换有两种方法:

Plane0 + Plane1 得到NV12
Plane0 + Plane2 得到NV21

其它平台暂未测试,有兴趣的童鞋可以自行测试下。采用YUV422虽然也能正常使用,但是个人认为这个明显是不合理的,YUV422不符合YUV_420_888的API描述,并且采用YUV420处理速度、存储速度都会比YUV422更快,传输带宽占用更少。

根据 API 我们可以知道,创建ImageReader时参数format除了YUV_420_888,还可以传,YUV_422_888、YUV_444_888这样就可以得到不同YUV采样模式的图像,但根据展锐平台的异常现象,我们也可以知道这都依赖于平台实现。

4. I420、YV12、NV12、N21转RGBA

I420、YV12、NV12、N21转换时都有一些共性:

  • 每个像素有自己独立的Y分量,Y的数量与像素点数量相等。
  • 4个像素共用一个U分量和V分量。

因此,我们只要找到每个像素Y、U、V分量的对应关系就可以进行转换。

4.1 I420转RGB

i420.png

4.2 YV12转RGB

yv12.png

4.3 NV12转RGB

nv12.png

4.4 NV21转RGB

nv21.png

4.5 代码实现

https://github.com/tensorflow/tensorflow/blob/master/tensorflow/examples/android/src/org/tensorflow/demo/中有相关的YUV转RGB和RGB转YUV的代码。本文也是参考其中的转换代码,分别封装了一下I420转RGBA、YV12转RGBA、NV12转RGBA、NV21转RGBA。

定义常量:
const int K_MAX_CHANNEL_VALUE = 262143;
const int YUV420P_I420 = 1;
const int YUV420P_YV12 = 2;
const int YUV420SP_NV12 = 3;
const int YUV420SP_NV21 = 4;
YUV转RGBA:
/*
 * This function come from:
 * https://github.com/tensorflow/tensorflow/blob/master/tensorflow/examples/android/jni/yuv2rgb.cc
 */
static inline int YUV2RGBA(int nY, int nU, int nV) {
    nY -= 16;
    nU -= 128;
    nV -= 128;
    if (nY < 0) nY = 0;

    // This is the floating point equivalent. We do the conversion in integer
    // because some Android devices do not have floating point in hardware.
    // nR = (int)(1.164 * nY + 2.018 * nU);
    // nG = (int)(1.164 * nY - 0.813 * nV - 0.391 * nU);
    // nB = (int)(1.164 * nY + 1.596 * nV);

    int nR = 1192 * nY + 1634 * nV;
    int nG = 1192 * nY - 833 * nV - 400 * nU;
    int nB = 1192 * nY + 2066 * nU;

    nR = nR > K_MAX_CHANNEL_VALUE ? K_MAX_CHANNEL_VALUE : (nR < 0 ? 0 : nR);
    nG = nG > K_MAX_CHANNEL_VALUE ? K_MAX_CHANNEL_VALUE : (nG < 0 ? 0 : nG);
    nB = nB > K_MAX_CHANNEL_VALUE ? K_MAX_CHANNEL_VALUE : (nB < 0 ? 0 : nB);

    nR = (nR >> 10) & 0xff;
    nG = (nG >> 10) & 0xff;
    nB = (nB >> 10) & 0xff;
    return 0xff000000 | (nR << 16) | (nG << 8) | nB;
}

/*
 * int  order: ARGB
 * byte order: RGBA
 */
static inline void rgbaIntToBytes(int rgba, unsigned char *b) {
    b[0] = (unsigned char) ((rgba >> 16) & 0xff);  //R
    b[1] = (unsigned char) ((rgba >> 8) & 0xff);   //G
    b[2] = (unsigned char) (rgba & 0xff);          //B
    b[3] = (unsigned char) ((rgba >> 24) & 0xff);  //A
}
YUV420P转RGB:
    void YUV420PToRGBAByte(unsigned char *src, unsigned char *dst, int width, int height,
                           int yRowStride, int uvRowStride, int uvPixelStride, int format) {
        if (format == YUV420P_I420 || format == YUV420P_YV12) {
            unsigned char *pRGBA = dst;
            unsigned char *pY = src;
            unsigned char *pU;
            unsigned char *pV;
            if (format == YUV420P_I420) {
                pU = src + width * height;
                pV = src + width * height / 4 * 5;
            } else {
                pU = src + width * height / 4 * 5;
                pV = src + width * height;
            }
            for (int y = 0; y < height; y++) {
                //const int yRowStart = yRowStride * y;
                const int uvRowStart = uvRowStride * (y >> 1);
                for (int x = 0; x < width; x++) {
                    const int uvRowOffset = (x >> 1) * uvPixelStride;
                    rgbaIntToBytes(YUV2RGBA(*pY++,//pY[yRowStart + x],
                                            pU[uvRowStart + uvRowOffset],
                                            pV[uvRowStart + uvRowOffset]),
                                   pRGBA);
                    pRGBA += 4;
                }
            }
        }
    }

    void YUV420PToRGBAInt(unsigned char *src, int *dst, int width, int height,
                          int yRowStride, int uvRowStride, int uvPixelStride, int format) {
        if (format == YUV420P_I420 || format == YUV420P_YV12) {
            unsigned char *pY = src;
            unsigned char *pU;
            unsigned char *pV;
            int rgbaIndex = 0;
            if (format == YUV420P_I420) {
                pU = src + width * height;
                pV = src + width * height / 4 * 5;
            } else {
                pU = src + width * height / 4 * 5;
                pV = src + width * height;
            }
            for (int y = 0; y < height; y++) {
                //const int yRowStart = yRowStride * y;
                const int uvRowStart = uvRowStride * (y >> 1);
                for (int x = 0; x < width; x++) {
                    const int uvRowOffset = (x >> 1) * uvPixelStride;
                    dst[rgbaIndex++] = YUV2RGBA(*pY++,//pY[yRowStart + x],
                                                 pU[uvRowStart + uvRowOffset],
                                                pV[uvRowStart + uvRowOffset]);
                }
            }
        }
    }
YUV420SP转RGB:
    void YUV420SPToRGBAByte(unsigned char *src, unsigned char *dst, int width, int height,
                            int format) {
        if (format == YUV420SP_NV12 || format == YUV420SP_NV21) {
            unsigned char *pRGBA = dst;
            unsigned char *pY = src;
            unsigned char *pUV = src + width * height;
            for (int y = 0; y < height; y++) {
                for (int x = 0; x < width; x++) {
                    int uvOffset = (y >> 1) * width + 2 * (x >> 1);
                    int uOffset;
                    int vOffset;
                    if (format == YUV420SP_NV12) {
                        uOffset = uvOffset;
                        vOffset = uvOffset + 1;
                    } else {
                        uOffset = uvOffset + 1;
                        vOffset = uvOffset;
                    }
                    rgbaIntToBytes(YUV2RGBA(*pY++, pUV[uOffset], pUV[vOffset]), pRGBA);
                    pRGBA += 4;
                }
            }
        }
    }

    void YUV420SPToRGBAInt(unsigned char *src, int *dst, int width, int height,
                           int format) {
        if (format == YUV420SP_NV12 || format == YUV420SP_NV21) {
            unsigned char *pY = src;
            unsigned char *pUV = src + width * height;
            int rgbaIndex = 0;
            for (int y = 0; y < height; y++) {
                for (int x = 0; x < width; x++) {
                    int uvOffset = (y >> 1) * width + 2 * (x >> 1);
                    int uOffset;
                    int vOffset;
                    if (format == YUV420SP_NV12) {
                        uOffset = uvOffset;
                        vOffset = uvOffset + 1;
                    } else {
                        uOffset = uvOffset + 1;
                        vOffset = uvOffset;
                    }
                    dst[rgbaIndex++] = YUV2RGBA(*pY++, pUV[uOffset], pUV[vOffset]);
                }
            }
        }
    }
JNI接口
extern "C"
JNIEXPORT void JNICALL
Java_com_qxt_yuv420_NativeUtils_I420ToRGBAByte(JNIEnv *env, jclass clazz,
                                               jbyteArray src, jbyteArray dst,
                                               jint width, jint height, jint yRowStride,
                                               jint uvRowStride, jint uvPixelStride) {
    jbyte *_src = env->GetByteArrayElements(src, nullptr);
    jbyte *_dst = env->GetByteArrayElements(dst, nullptr);
    YUV420PToRGBAByte(reinterpret_cast<unsigned char *>(_src),
                      reinterpret_cast<unsigned char *>(_dst),
                      width, height, yRowStride, uvRowStride, uvPixelStride, YUV420P_I420);
    env->ReleaseByteArrayElements(src, _src, JNI_ABORT);
    env->ReleaseByteArrayElements(dst, _dst, 0);
}

extern "C"
JNIEXPORT void JNICALL
Java_com_qxt_yuv420_NativeUtils_I420ToRGBAInt(JNIEnv *env, jclass clazz,
                                              jbyteArray src, jintArray dst,
                                              jint width, jint height, jint yRowStride,
                                              jint uvRowStride, jint uvPixelStride) {
    jbyte *_src = env->GetByteArrayElements(src, NULL);
    jint *_dst = env->GetIntArrayElements(dst, NULL);
    YUV420PToRGBAInt(reinterpret_cast<unsigned char *>(_src), _dst, width, height, yRowStride,
                     uvRowStride, uvPixelStride, YUV420P_I420);
    env->ReleaseByteArrayElements(src, _src, JNI_ABORT);
    env->ReleaseIntArrayElements(dst, _dst, 0);
}

extern "C"
JNIEXPORT void JNICALL
Java_com_qxt_yuv420_NativeUtils_YV12ToRGBAByte(JNIEnv *env, jclass clazz,
                                               jbyteArray src, jbyteArray dst,
                                               jint width, jint height, jint yRowStride,
                                               jint uvRowStride, jint uvPixelStride) {
    jbyte *_src = env->GetByteArrayElements(src, nullptr);
    jbyte *_dst = env->GetByteArrayElements(dst, nullptr);
    YUV420PToRGBAByte(reinterpret_cast<unsigned char *>(_src),
                      reinterpret_cast<unsigned char *>(_dst),
                      width, height, yRowStride, uvRowStride, uvPixelStride, YUV420P_YV12);
    env->ReleaseByteArrayElements(src, _src, JNI_ABORT);
    env->ReleaseByteArrayElements(dst, _dst, 0);
}

extern "C"
JNIEXPORT void JNICALL
Java_com_qxt_yuv420_NativeUtils_YV12ToRGBAInt(JNIEnv *env, jclass clazz,
                                              jbyteArray src, jintArray dst,
                                              jint width, jint height, jint yRowStride,
                                              jint uvRowStride, jint uvPixelStride) {
    jbyte *_src = env->GetByteArrayElements(src, NULL);
    jint *_dst = env->GetIntArrayElements(dst, NULL);
    YUV420PToRGBAInt(reinterpret_cast<unsigned char *>(_src),
                     _dst, width, height, yRowStride, uvRowStride, uvPixelStride, YUV420P_YV12);
    env->ReleaseByteArrayElements(src, _src, JNI_ABORT);
    env->ReleaseIntArrayElements(dst, _dst, 0);
}

extern "C"
JNIEXPORT void JNICALL
Java_com_qxt_yuv420_NativeUtils_NV12ToRGBAByte(JNIEnv *env, jclass clazz,
                                               jbyteArray src, jbyteArray dst,
                                               jint width, jint height) {
    jbyte *_src = env->GetByteArrayElements(src, NULL);
    jbyte *_dst = env->GetByteArrayElements(dst, NULL);
    YUV420SPToRGBAByte(reinterpret_cast<unsigned char *>(_src),
                       reinterpret_cast<unsigned char *>(_dst), width, height, YUV420SP_NV12);
    env->ReleaseByteArrayElements(src, _src, JNI_ABORT);
    env->ReleaseByteArrayElements(dst, _dst, 0);
}

extern "C"
JNIEXPORT void JNICALL
Java_com_qxt_yuv420_NativeUtils_NV12ToRGBAInt(JNIEnv *env, jclass clazz,
                                              jbyteArray src, jintArray dst,
                                              jint width, jint height) {
    jbyte *_src = env->GetByteArrayElements(src, NULL);
    jint *_dst = env->GetIntArrayElements(dst, NULL);
    YUV420SPToRGBAInt(reinterpret_cast<unsigned char *>(_src), _dst, width, height, YUV420SP_NV12);
    env->ReleaseByteArrayElements(src, _src, JNI_ABORT);
    env->ReleaseIntArrayElements(dst, _dst, 0);
}

extern "C"
JNIEXPORT void JNICALL
Java_com_qxt_yuv420_NativeUtils_NV21ToRGBAByte(JNIEnv *env, jclass clazz,
                                               jbyteArray src, jbyteArray dst,
                                               jint width, jint height) {
    jbyte *_src = env->GetByteArrayElements(src, NULL);
    jbyte *_dst = env->GetByteArrayElements(dst, NULL);
    YUV420SPToRGBAByte(reinterpret_cast<unsigned char *>(_src),
                       reinterpret_cast<unsigned char *>(_dst), width, height, YUV420SP_NV21);
    env->ReleaseByteArrayElements(src, _src, JNI_ABORT);
    env->ReleaseByteArrayElements(dst, _dst, 0);
}

extern "C"
JNIEXPORT void JNICALL
Java_com_qxt_yuv420_NativeUtils_NV21ToRGBAInt(JNIEnv *env, jclass clazz,
                                              jbyteArray src, jintArray dst,
                                              jint width, jint height) {
    jbyte *_src = env->GetByteArrayElements(src, NULL);
    jint *_dst = env->GetIntArrayElements(dst, NULL);
    YUV420SPToRGBAInt(reinterpret_cast<unsigned char *>(_src), _dst, width, height, YUV420SP_NV21);
    env->ReleaseByteArrayElements(src, _src, JNI_ABORT);
    env->ReleaseIntArrayElements(dst, _dst, 0);
}
java 函数及参数说明
package com.qxt.yuv420;


/**
 * @author Tyler Qiu
 * @date: 2020/05/09
 */
public class NativeUtils {
    /*
     * YYYYYYYY UU VV    =>I420  =>YUV420P
     * YYYYYYYY VV UU    =>YV12  =>YUV420P
     * YYYYYYYY UV UV    =>NV12  =>YUV420SP
     * YYYYYYYY VU VU    =>NV21  =>YUV420SP
     */
    static {
        System.loadLibrary("NativeUtils");
    }

    /**
     * convert I420 to ARGB_8888
     *
     * @param src           src I420 byte array
     * @param dst           dst RGBA byte array, the length of the dst array must be >= width*height*4
     * @param width         image width
     * @param height        image height
     * @param yRowStride    The row stride of plane y.
     * @param uvRowStride   The row stride of plane u or v.
     * @param uvPixelStride The pixel stride of plane u or v.
     */
    public static native void I420ToRGBAByte(byte[] src, byte[] dst, int width, int height,
                                             int yRowStride, int uvRowStride, int uvPixelStride);

    /**
     * convert I420 to ARGB_8888
     *
     * @param src           src I420 byte array
     * @param dst           dst RGBA int array, the length of the dst array must be >= width*height
     * @param width         image width
     * @param height        image height
     * @param yRowStride    The row stride of plane y.
     * @param uvRowStride   The row stride of plane u or v.
     * @param uvPixelStride The pixel stride of plane u or v.
     */
    public static native void I420ToRGBAInt(byte[] src, int[] dst, int width, int height,
                                            int yRowStride, int uvRowStride, int uvPixelStride);

    /**
     * convert YV12 to ARGB_8888
     *
     * @param src           src YV12 byte array
     * @param dst           dst RGBA byte array, the length of the dst array must be >= width*height*4
     * @param width         image width
     * @param height        image height
     * @param yRowStride    The row stride of plane y.
     * @param uvRowStride   The row stride of plane u or v.
     * @param uvPixelStride The pixel stride of plane u or v.
     */
    public static native void YV12ToRGBAByte(byte[] src, byte[] dst, int width, int height,
                                             int yRowStride, int uvRowStride, int uvPixelStride);

    /**
     * convert YV12 to ARGB_8888
     *
     * @param src           src YV12 byte array
     * @param dst           dst RGBA int array, the length of the dst array must be >= width*height
     * @param width         image width
     * @param height        image height
     * @param yRowStride    The row stride of plane y.
     * @param uvRowStride   The row stride of plane u or v.
     * @param uvPixelStride The pixel stride of plane u or v.
     */
    public static native void YV12ToRGBAInt(byte[] src, int[] dst, int width, int height,
                                            int yRowStride, int uvRowStride, int uvPixelStride);

    /**
     * convert NV12 to ARGB_8888
     *
     * @param src      src NV12 byte array
     * @param dst      dst RGBA byte array, the length of the dst array must be >= width*height*4
     * @param width    image width
     * @param height   image height
     */
    public static native void NV12ToRGBAByte(byte[] src, byte[] dst, int width, int height);

    /**
     * convert NV12 to ARGB_8888
     *
     * @param src      src NV12 byte array
     * @param dst      dst RGBA int array, the length of the dst array must be >= width*height
     * @param width    image width
     * @param height   image height
     */
    public static native void NV12ToRGBAInt(byte[] src, int[] dst, int width, int height);

    /**
     * convert NV21 to ARGB_8888
     *
     * @param src      src NV21 byte array
     * @param dst      dst RGBA byte array, the length of the dst array must be >= width*height*4
     * @param width    image width
     * @param height   image height
     */
    public static native void NV21ToRGBAByte(byte[] src, byte[] dst, int width, int height);

    /**
     * convert NV21 to ARGB_8888
     *
     * @param src      src NV21 byte array
     * @param dst      dst RGBA int array, the length of the dst array must be >= width*height
     * @param width    image width
     * @param height   image height
     */
    public static native void NV21ToRGBAInt(byte[] src, int[] dst, int width, int height);
}

本文中的代码和i420/yv12/nv12/n21文件已经上传到github:https://github.com/qiuxintai/YUV420Converter,如果本文代码对你有帮助,烦请在github上给我一个小小的star。除转换外,YUV420Converter中还有旋转RGB和YUV420的代码,如果还想了解RGB和YUV420的旋转,请戳:
RGB和YUV420旋转90/180/270度

5. 使用7yuv

除了用代码转换,我们还可以用7yuv工具来查看和转换YUV格式的图像。
7yuv下载地址:https://www.onlinedown.net/soft/1225925.htm

5.1 查看

打开7yuv,File -> open 或者直接将yuv图像拖入7yuv界面进行查看:


7yuv使用1.png

5.2 转换

选择 Edit -> Convert Format 转换:


7yuv使用2.png

6. 本文参考:

  1. https://zh.wikipedia.org/wiki/YUV
  2. https://developer.android.google.cn/reference/android/graphics/ImageFormat?hl=en#YUV_420_888
  3. https://blog.csdn.net/lbknxy/article/details/54633008?tdsourcetag=s_pcqq_aiomsg
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,012评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,628评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,653评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,485评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,574评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,590评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,596评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,340评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,794评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,102评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,276评论 1 344
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,940评论 5 339
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,583评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,201评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,441评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,173评论 2 366
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,136评论 2 352