精华内容
下载资源
问答
  • 1)YV12_2_I420; 2)I420_2_YV12; 3)NV12_2_I420; 4)I420_2_NV12; 5)NV21_2_YV12; 6)YV12_2_NV21; 7)I420_2_rgb32; 8)rgb32_2_I420; 9)I420_2_rgb24; 10)rgb24_2_I420
  • Android OpenGL 针对YUV格式(I420,NV12,NV21)数据渲染
  • I420转NV21(NV12)

    2018-04-19 17:17:16
    自封的纯C代码,可实现YUV数据基本的I420转NV21/NV12功能
  • OpenGL如何处理YUV数据(Android开发)(格式:I420、NV12、NV21) OpenGL如何处理YUV数据(Android开发)(格式:I420、NV12、NV21)
  • 自封的纯C函数,YUVI420toNV21(NV12)代码及结果,绘制黑框及结果
  • 完美的将ios上的yuv420p数据绘制到unity控件上去,绝对不骗人,分数已定
  • test.I420(YUV420)

    2020-07-22 11:02:22
    视频格式YUV;图像格式YUV420(I420);时长约3秒;分辨率544x960;帧率30;视频来源,本人手机拍摄。
  • 彻底弄懂I420格式

    千次阅读 2019-08-18 18:12:56
    planar 格式:先连续存储所有像素点的 Y 分量,紧接着存储所有像素点的 U 分量,再是V分量(当然不同存储格式的UV的先后顺序是不一样的,如I420的V在U后,YV12则是U在V后)。 packed 格式:每个像素的Y,U,V分量...

    YUV的概念

    YUV 和我们熟知的 RGB 类似,是一种颜色编码格式。它主要用于电视系统和模拟视频邻域(如 Camera 系统)。YUV 包含三个分量,其中 Y 表示明亮度(Luminance 或 Luma),也就是灰度值。而 U 和 V 则表示色度(Chrominance 或 Chroma),作用是描述图像色彩及饱和度,用于指定像素的颜色。没有 UV 分量信息,一样可以显示完整的图像,只不过是黑白的灰度图像。YUV 格式的好处是很好地解决了彩色电视机与黑白电视机的兼容问题(当只要Y分量时就是黑白图像)。

    YUV420

    YUV的采样方式

    • 4:4:4表示完全取样(每一个Y对应一组UV分量)
    • 4:2:2表示2:1的水平取样,垂直完全采样(每两个Y共用一组UV分量)
    • 4:2:0表示2:1的水平取样,垂直2:1采样(每四个Y共用一组UV分量)
    • 4:1:1表示4:1的水平取样,垂直完全采样(每四个Y共用一组UV分量)

    YUV的存储格式

    我们主要关心的是如何处理YUV数据,所以最关心的是YUV数据的存储格式。内存中存储的方式包括两种:

    1. planar 格式:先连续存储所有像素点的 Y 分量,紧接着存储所有像素点的 U 分量,再是V分量(当然不同存储格式的UV的先后顺序是不一样的,如I420的V在U后,YV12则是U在V后)。

    2. packed 格式:每个像素的Y,U,V分量交替存储。

    Y,U,V分量存储时的先后顺序的不同,代表了不同的格式,以下列出了几种常见的YUV格式:

    • YUYV(属于YUV422)
      在这里插入图片描述
      两个Y共用一组UV分量。Y,U,V分量交替存储。

    • UYVY(属于YUV422)

    在这里插入图片描述
    可以看到与YUYV的分量的顺序不一样。

    • YUV422P(属于YUV422)
      在这里插入图片描述
      是Planar存储模式,依次是Y,U,V。U分量在V分量之前

    • YV12(属于YUV420)

    在这里插入图片描述
    是Planar存储模式,依次是Y,V,U。V分量在U分量之前。I420与此类似,先是存储Y分量,再是U分量,V分量。

    • NV12(属于YUV420)

    在这里插入图片描述
    先是Y,在是U,V分量交替出现

    一个简易存储示意图

    I420: YYYYYYYY UU VV    =>YUV420P
    YV12: YYYYYYYY VV UU    =>YUV420P
    NV12: YYYYYYYY UVUV     =>YUV420SP
    NV21: YYYYYYYY VUVU     =>YUV420SP
    

    YUV420sp与YUV420p的不同之处在,存储UV分量时,YUV420sp中的UV分量是交替存储。

    处理I420

    I420属于YUV420P(存储格式为Planar),这种格式很常用,在x264/265的中要求传入的源数据就是这种格式。在libyuv中,进行YUV图像处理(缩放,剪切,旋转)也是要求以这种格式传入。ffmpeg解码h264/265后数据也是这种格式。这种格式也可以直接通过D3D,OpenGL进行渲染。

    计算占用的字节大小

    一个分量占用一个字节,每一个象素点对应一个Y分量,所以分辨率为w*h的I420格式的图,Y的字节数为 w*h, U的字节数为w*h/4,V的字节数大小为w*h/4,总字节数即为w*h*3/2

    定位像素数据

    要处理I420的数据,首先要能定位到像素分量数据。一个典型的场景是,通过freetype在YUV图像上加字幕时,这是需要将指定坐标点的像素替换为freetype返回的字体图数据。

    那么下面是取像素点YUV(I420格式)分量数据的公式:

    size.total = size.width * size.height;
    y = yuv[position.y * size.width + position.x];
    u = yuv[(position.y / 2) * (size.width / 2) + (position.x / 2) + size.total];
    v = yuv[(position.y / 2) * (size.width / 2) + (position.x / 2) + size.total + (size.total / 4)];
    

    转一张很直观的I420存储的示意图。Y,U,V相同颜色的表述是同一像素的分量。可以结合这个图套用上面的公式推算一下:

    在这里插入图片描述

    一种常用的YUV数据表示方法

    如果直接分配一段内存用以存储图像数据(比如I420格式),是没法知道这段内存的YUV分量特点的(每个分量的数据起始及长度)。那么我们可以下面这种方式定义数据结构来解释存储结构:

    enum enImageFmt
    {
        enImageFmt_YUV420P
        ...
    };
    
    struct ImageSize
    {
        int iWidth;
        int iHeight;
    };
    
    struct VideoFrame
    {
        //Image的格式
        enImageFmt fmt;
        //图像的分辨率
        ImageSize Size;
        //分别指向Y,U,V分量的开头
        unsigned char *data[3];
        //分别指示Y,U,V分量一行(步长)数据大小
        unsigned short linesize[3];
    }
    
    //如下的一个示例代码
    //指针pImage指向一幅YUV420P格式的图像,通过该结构体指示它的内存结构
    
    VideoFrame frame;
    frame.fmt = enImageFmt_YUV420P;
    frame.Size.w = w;
    frame.Size.h = h;
    //Y分量的步长
    frame.linesize[0] = w;
    //U分量的步长(其长度就是Y分量步长的一半,同理V分量)
    frame.linesize[1] = w/2;
    //V分量的步长
    frame.linesize[2] = w/2;
    	
    //那么data[0]指向的就是Y数据的起始
    frame.data[0] = (unsigned char*)pImage;
    //U数据的起始
    frame.data[1] = frame.data[0] + frame.linesize[0]*h;
    //V数据的起始
    frame.data[2] = frame.data[1] + frame.linesize[1]*h/2;
    

    该结构体不止可以指示YUV420P的结构,也可以指示其它YUV格式的结构。在ffmpeg中也有类型的结构定义。

    总结:

    虽然主要是讲解的如果处理I420格式,但是前面介绍了几种不同YUV的存储格式。可以结合存储示意图,类推出如何处理其它YUV格式数据。

    参考

    https://zh.wikipedia.org/wiki/YUV

    https://www.cnblogs.com/azraelly/archive/2013/01/01/2841269.html

    展开全文
  • 常用视频格式转换nv12,i420,i444,p010

    千次阅读 2020-11-25 14:38:25
    文章目录视频存储格式NV12转I420 视频存储格式 P010格式与NV12格式一样,区别就是两个字节存一个像素值。 Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y...

    视频存储格式

    P010格式与NV12格式一样,区别就是两个字节存一个像素值。

    Y Y Y Y Y Y      Y Y Y Y Y Y      Y Y Y Y Y Y      Y Y Y Y Y Y
    Y Y Y Y Y Y      Y Y Y Y Y Y      Y Y Y Y Y Y      Y Y Y Y Y Y
    Y Y Y Y Y Y      Y Y Y Y Y Y      Y Y Y Y Y Y      Y Y Y Y Y Y
    Y Y Y Y Y Y      Y Y Y Y Y Y      Y Y Y Y Y Y      Y Y Y Y Y Y
    U U U U U U      V V V V V V      U V U V U V      V U V U V U
    V V V V V V      U U U U U U      U V U V U V      V U V U V U
     - I420 -          - YV12 -         - NV12 -         - NV21 -
    
    Y Y Y Y Y Y      Y Y Y Y Y Y    
    Y Y Y Y Y Y      Y Y Y Y Y Y
    Y Y Y Y Y Y      Y Y Y Y Y Y
    Y Y Y Y Y Y      Y Y Y Y Y Y
    U U U U U U      V V V V V V
    U U U U U U      V V V V V V
    U U U U U U      V V V V V V
    U U U U U U      V V V V V V
    V V V V V V      U U U U U U
    V V V V V V      U U U U U U
    V V V V V V      U U U U U U
    V V V V V V      U U U U U U
     - I444 -          - YV24 -
    

    NV12转I420

    //NV12 转 I420 
    void NV12_to_I420(uint8_t* nv12data, uint8_t* i420data, int frameWidth, int frameHeight) {
    	uint8_t* nv12_src[2];
    	nv12_src[0] = nv12data;
    	nv12_src[1] = nv12_src[0] + frameWidth * frameHeight;
    
    	uint8_t* yuv420_dst[3];
    	yuv420_dst[0] = i420data;
    	yuv420_dst[1] = yuv420_dst[0] + frameWidth * frameHeight;
    	yuv420_dst[2] = yuv420_dst[1] + frameWidth * frameHeight / 4;
    
    	//Y
    	memcpy(yuv420_dst[0], nv12_src[0], frameWidth * frameHeight);
    	// for(int i = 0; i < frameWidth * frameHeight ; i++){
    	//     *(yuv420_dst[0]++) = *(nv12_src[0] + i);
    	// }
    
    	for (int i = 0; i < frameWidth * frameHeight / 2; i++) {
    		unsigned char b = *(nv12_src[1] + i);
    		if (i % 2 == 0) {//U
    			*(yuv420_dst[1]++) = *(nv12_src[1] + i);
    		}
    		else {//V
    			*(yuv420_dst[2]++) = *(nv12_src[1] + i);
    		}
    	}
    }
    
    

    NV12转I444

    //NV12转YUV444
    void NV12_to_I444(uint8_t* nv12data, uint8_t* i444data, int frameWidth, int frameHeight) {
    	uint8_t* nv12_src[2];
    	nv12_src[0] = nv12data;
    	nv12_src[1] = nv12_src[0] + frameWidth * frameHeight;
    
    	uint8_t* yuv444_dst[3];
    	yuv444_dst[0] = i444data;
    	yuv444_dst[1] = yuv444_dst[0] + frameWidth * frameHeight;
    	yuv444_dst[2] = yuv444_dst[1] + frameWidth * frameHeight;
    
    	//Y
    	memcpy(yuv444_dst[0], nv12_src[0], frameWidth * frameHeight);
    
    	//UV
    	for (int i = 0; i < frameWidth * frameHeight / 2; i++) {
    		if (i % 2 == 0) {//U,NV12中一位对应444中4位
    			*(yuv444_dst[1]) = *(nv12_src[1] + i);
    			*(yuv444_dst[1] + frameWidth) = *(nv12_src[1] + i);
    			yuv444_dst[1]++;
    			*(yuv444_dst[1]) = *(nv12_src[1] + i);
    			*(yuv444_dst[1] + frameWidth) = *(nv12_src[1] + i);
    			yuv444_dst[1]++;
    		}
    		else {//V,NV12中一位对应444中4位
    			*(yuv444_dst[2]) = *(nv12_src[1] + i);
    			*(yuv444_dst[2] + frameWidth) = *(nv12_src[1] + i);
    			yuv444_dst[2]++;
    			*(yuv444_dst[2]) = *(nv12_src[1] + i);
    			*(yuv444_dst[2] + frameWidth) = *(nv12_src[1] + i);
    			yuv444_dst[2]++;
    		}
    		if ((i > frameWidth && i % frameWidth == 0)) {//UV分量跳行
    			yuv444_dst[1] = yuv444_dst[1] + frameWidth;
    			yuv444_dst[2] = yuv444_dst[2] + frameWidth;
    		}
    	}
    }
    

    P010转I420

    p010有多种格式,区别在于数据的存放位置,即10bit的数据是如何放在16bit的空间里面的。
    
    //P010转I420
    void P010le_to_I420(uint8_t* p010data, uint8_t* i420data, int frameWidth, int frameHeight) {
    	uint8_t* p010_src[2];
    	p010_src[0] = p010data;
    	p010_src[1] = p010_src[0] + frameWidth * frameHeight * 2;
    
    	uint8_t* yuv420_dst[3];
    	yuv420_dst[0] = i420data;
    	yuv420_dst[1] = yuv420_dst[0] + frameWidth * frameHeight;
    	yuv420_dst[2] = yuv420_dst[1] + frameWidth * frameHeight / 4;
    	uint16_t Y, U, V;
    
    	//Y
    	for (int i = 0; i < frameWidth * frameHeight; i++) {
    		Y = *((uint16_t*)p010_src[0] + i) >> 6;
    		Y = Y < 64 ? 64 : Y;
    		Y = Y > 940 ? 940 : Y;
    		*(yuv420_dst[0]++) = (uint8_t)(Y >> 2);
    	}
    
    	//UV
    	for (int i = 0; i < frameWidth * frameHeight / 2; i++) {
    		if (i % 2 == 0) {
    			U = (*((uint16_t*)p010_src[1] + i)) & 0x00ff;
    			*(yuv420_dst[1]++) = U;
    		}
    		else {
    			V = (*((uint16_t*)p010_src[1] + i)) & 0x00ff;
    			*(yuv420_dst[2]++) = V;
    		}
    	}
    }
    
    void P010_to_I420(uint8_t* P010data, uint8_t* I420data, int frameWidth, int frameHeight) {
    	uint8_t* p010_src[2];
    	p010_src[0] = P010data;
    	p010_src[1] = p010_src[0] + frameWidth * frameHeight * 2;
    
    	uint8_t* yuv420_dst[3];
    	yuv420_dst[0] = I420data;
    	yuv420_dst[1] = yuv420_dst[0] + frameWidth * frameHeight;
    	yuv420_dst[2] = yuv420_dst[1] + frameWidth * frameHeight / 4;
    	uint8_t Y, U, V;
    
    	//Y
    	for (int i = 0; i < frameWidth * frameHeight; i++) {
    		Y = *((uint16_t*)p010_src[0] + i) >> 8;
    		Y = Y < 16 ? 16 : (Y > 235 ? 235 : Y);
    		*(yuv420_dst[0]++) = Y;
    	}
    
    	//UV
    	for (int i = 0; i < frameWidth * frameHeight / 2; i++) {
    		if (i % 2 == 0) {
    			U = (*((uint16_t*)p010_src[1] + i)) >> 8;
    			//U = (((U - 128) * (200 + 256)) >> 8) + 128;
    			U = U < 16 ? 16 : (U > 235 ? 235 : U);
    			*(yuv420_dst[1]++) = U;
    		}
    		else {
    			V = (*((uint16_t*)p010_src[1] + i)) >> 8;
    			//V = (((V - 128) * (200 + 256)) >> 8) + 128;
    			V = V < 16 ? 16 : (V > 235 ? 235 : V);
    			*(yuv420_dst[2]++) = V;
    		}
    	}
    }
    
    展开全文
  • 今天Android要把Bitmap里的数据转成I420,用的YUV库,但是总有色差。查了好久,最终发现 libyuv里的名字和我们外面常用的正好相反,比如`libyuv::ABGRToI420`,输入的数据格式要是ARGB_8888,这应该是位高低常用...

    今天Android要把Bitmap里的数据转成I420,用的YUV库,但是总有色差。查了好久,最终发现

    libyuv里的名字和我们外面常用的正好相反,比如`libyuv::ABGRToI420`,输入的数据格式要是ARGB_8888,这应该是位高低常用读法不同。

    给个示例:

    Java层代码:

    int width = bitmap.getWidth();
    int height = bitmap.getHeight();
    int byteCount = bitmap.getByteCount();
    ByteBuffer buf = ByteBuffer.allocate(byteCount);
    bitmap.copyPixelsToBuffer(buf);
    byte[] byteArray = buf.array();
    byte[] yBuffer = new byte[width * height];
    byte[] uBuffer = new byte[width * height / 4];
    byte[] vBuffer = new byte[width * height / 4];
    LibYUV.javaABGRToI420(byteArray, width * 4, width, height, yBuffer, width, uBuffer, (width + 1) / 2, vBuffer, (width + 1) / 2);
    byte[] yuvBuffer = new byte[width * height * 3 / 2];
    System.arraycopy(yBuffer, 0, yuvBuffer, 0, width * height);
    System.arraycopy(uBuffer, 0, yuvBuffer, width * height, width * height / 4);
    System.arraycopy(vBuffer, 0, yuvBuffer, width * height * 5 / 4, width * height / 4);

    JavaWrap:

    public class LibYUV {
    
        static {
            System.loadLibrary("yuvwrap");
            System.loadLibrary("yuv");
        }
    
        public static void javaABGRToI420(byte[] argbFrame, int argbStride, int width, int height,
                                          byte[] yBuffer, int yStride,
                                          byte[] uBuffer, int uStride,
                                          byte[] vBuffer, int vStride) {
            ABGRToI420(argbFrame, argbStride, width, height, yBuffer, yStride, uBuffer, uStride, vBuffer, vStride);
        }
    }    

    C++层调用libyuv

        JNIEXPORT
        JNICALL
        void Java_org_android_opensource_libraryyuv_LibYUV_ABGRToI420(
                JNIEnv *env, jclass *jcls,
                jbyteArray src_frame, jint src_stride_frame,
                jint width, jint height,
                jbyteArray yBuffer, jint y_stride,
                jbyteArray uBuffer, jint u_stride,
                jbyteArray vBuffer, jint v_stride) {
    
            uint8_t *srcFrame = (uint8_t *) env->GetByteArrayElements(src_frame, 0);
    
            uint8_t *dst_y = (uint8_t *) env->GetByteArrayElements(yBuffer, 0);
            uint8_t *dst_u = (uint8_t *) env->GetByteArrayElements(uBuffer, 0);
            uint8_t *dst_v = (uint8_t *) env->GetByteArrayElements(vBuffer, 0);
    
            // RGBA_8888实现的数值顺序是ABGR
            // https://github.com/R1NC/LibYUV-Android/blob/6f4862a78bbd70aeeb8a9b3a026fcdfbd8391314/app/src/main/cpp/YuvJni.cpp
            libyuv::ABGRToI420(srcFrame, src_stride_frame,
                           dst_y, y_stride,
                           dst_u, u_stride,
                           dst_v, v_stride, width, height);
    
            //remember release
            env->ReleaseByteArrayElements(src_frame, (jbyte *) srcFrame, 0);
            env->ReleaseByteArrayElements(yBuffer, (jbyte *) dst_y, 0);
            env->ReleaseByteArrayElements(uBuffer, (jbyte *) dst_u, 0);
            env->ReleaseByteArrayElements(vBuffer, (jbyte *) dst_v, 0);
        }

     

     

     

    I420: YYYYYYYY UU VV =>YUV420P
    YV12: YYYYYYYY VV UU =>YUV420P
    NV12: YYYYYYYY UVUV =>YUV420SP
    NV21: YYYYYYYY VUVU =>YUV420SP

     

    转自:https://www.cnblogs.com/azraelly/archive/2013/01/01/2841269.html

     

    YUV格式有两大类:planar和packed。
    对于planar的YUV格式,先连续存储所有像素点的Y,紧接着存储所有像素点的U,随后是所有像素点的V。
    对于packed的YUV格式,每个像素点的Y,U,V是连续交*存储的。

     

    YUV,分为三个分量,“Y”表示明亮度(Luminance或Luma),也就是灰度值;而“U”和“V” 表示的则是色度(Chrominance或Chroma),作用是描述影像色彩及饱和度,用于指定像素的颜色。

        与我们熟知的RGB类似,YUV也是一种颜色编码方法,主要用于电视系统以及模拟视频领域,它将亮度信息(Y)与色彩信息(UV)分离,没有UV信息一样可以显示完整的图像,只不过是黑白的,这样的设计很好地解决了彩色电视机与黑白电视的兼容问题。并且,YUV不像RGB那样要求三个独立的视频信号同时传输,所以用YUV方式传送占用极少的频宽。

    YUV码流的存储格式其实与其采样的方式密切相关,主流的采样方式有三种,YUV4:4:4,YUV4:2:2,YUV4:2:0,关于其详细原理,可以通过网上其它文章了解,这里我想强调的是如何根据其采样格式来从码流中还原每个像素点的YUV值,因为只有正确地还原了每个像素点的YUV值,才能通过YUV与RGB的转换公式提取出每个像素点的RGB值,然后显示出来。

        用三个图来直观地表示采集的方式吧,以黑点表示采样该像素点的Y分量,以空心圆圈表示采用该像素点的UV分量。

     

    先记住下面这段话,以后提取每个像素的YUV分量会用到。

    1. YUV 4:4:4采样,每一个Y对应一组UV分量。
    2. YUV 4:2:2采样,每两个Y共用一组UV分量。 
    3. YUV 4:2:0采样,每四个Y共用一组UV分量。 

    2.  存储方式

        下面我用图的形式给出常见的YUV码流的存储方式,并在存储方式后面附有取样每个像素点的YUV数据的方法,其中,Cb、Cr的含义等同于U、V。

    (1) YUVY 格式 (属于YUV422)

     

    YUYV为YUV422采样的存储格式中的一种,相邻的两个Y共用其相邻的两个Cb、Cr,分析,对于像素点Y'00、Y'01 而言,其Cb、Cr的值均为 Cb00、Cr00,其他的像素点的YUV取值依次类推。 (2) UYVY 格式 (属于YUV422)

    UYVY格式也是YUV422采样的存储格式中的一种,只不过与YUYV不同的是UV的排列顺序不一样而已,还原其每个像素点的YUV值的方法与上面一样。

     

    (3) YUV422P(属于YUV422)

     YUV422P也属于YUV422的一种,它是一种Plane模式,即平面模式,并不是将YUV数据交错存储,而是先存放所有的Y分量,然后存储所有的U(Cb)分量,最后存储所有的V(Cr)分量,如上图所示。其每一个像素点的YUV值提取方法也是遵循YUV422格式的最基本提取方法,即两个Y共用一个UV。比如,对于像素点Y'00、Y'01 而言,其Cb、Cr的值均为 Cb00、Cr00。

    (4)YV12,YU12格式(属于YUV420)

    YU12和YV12属于YUV420格式,也是一种Plane模式,将Y、U、V分量分别打包,依次存储。其每一个像素点的YUV数据提取遵循YUV420格式的提取方式,即4个Y分量共用一组UV。注意,上图中,Y'00、Y'01、Y'10、Y'11共用Cr00、Cb00,其他依次类推。

    (5)NV12、NV21(属于YUV420)

    NV12和NV21属于YUV420格式,是一种two-plane模式,即Y和UV分为两个Plane,但是UV(CbCr)为交错存储,而不是分为三个plane。其提取方式与上一种类似,即Y'00、Y'01、Y'10、Y'11共用Cr00、Cb00

    YUV420 planar数据, 以720×488大小图象YUV420 planar为例,

    其存储格式是: 共大小为(720×480×3>>1)字节,

    分为三个部分:Y,U和V

    Y分量:    (720×480)个字节  

    U(Cb)分量:(720×480>>2)个字节

    V(Cr)分量:(720×480>>2)个字节

    三个部分内部均是行优先存储,三个部分之间是Y,U,V 顺序存储。

    即YUV数据的0--720×480字节是Y分量值,         

    720×480--720×480×5/4字节是U分量    

    720×480×5/4 --720×480×3/2字节是V分量。

    4 :2: 2 和4:2:0 转换:

    最简单的方式:

    YUV4:2:2 ---> YUV4:2:0  Y不变,将U和V信号值在行(垂直方向)在进行一次隔行抽样。 YUV4:2:0 ---> YUV4:2:2  Y不变,将U和V信号值的每一行分别拷贝一份形成连续两行数据。

    在YUV420中,一个像素点对应一个Y,一个4X4的小方块对应一个U和V。对于所有YUV420图像,它们的Y值排列是完全相同的,因为只有Y的图像就是灰度图像。YUV420sp与YUV420p的数据格式它们的UV排列在原理上是完全不同的。420p它是先把U存放完后,再存放V,也就是说UV它们是连续的。而420sp它是UV、UV这样交替存放的。(见下图) 有了上面的理论,我就可以准确的计算出一个YUV420在内存中存放的大小。 width * hight =Y(总和) U = Y / 4   V = Y / 4

     

    所以YUV420 数据在内存中的长度是 width * hight * 3 / 2,

    假设一个分辨率为8X4的YUV图像,它们的格式如下图:

                          YUV420sp格式如下图                                                          

     

           1346422970_2927.pnguploading.4e448015.gif转存失败重新上传取消1346422970_2927.pnguploading.4e448015.gif转存失败重新上传取消1346422970_2927.pnguploading.4e448015.gif转存失败重新上传取消      

                            YUV420p数据格式如下图

               1346422959_6364.pnguploading.4e448015.gif转存失败重新上传取消1346422959_6364.pnguploading.4e448015.gif转存失败重新上传取消1346422959_6364.pnguploading.4e448015.gif转存失败重新上传取消

     

    旋转90度的算法:

    public static void rotateYUV240SP(byte[] src,byte[] des,int width,int height)
     {
       
      int wh = width * height;
      //旋转Y
      int k = 0;
      for(int i=0;i<width;i++) {
       for(int j=0;j<height;j++)
       {
                   des[k] = src[width*j + i];   
             k++;
       }
      }
      
      for(int i=0;i<width;i+=2) {
       for(int j=0;j<height/2;j++)
       { 
                   des[k] = src[wh+ width*j + i]; 
                   des[k+1]=src[wh + width*j + i+1];
             k+=2;
       }
      }
      
      
     }

     

    YV12和I420的区别        一般来说,直接采集到的视频数据是RGB24的格式,RGB24一帧的大小size=width×heigth×3 Bit,RGB32的size=width×heigth×4,如果是I420(即YUV标准格式4:2:0)的数据量是 size=width×heigth×1.5 Bit。       在采集到RGB24数据后,需要对这个格式的数据进行第一次压缩。即将图像的颜色空间由RGB2YUV。因为,X264在进行编码的时候需要标准的YUV(4:2:0)。但是这里需要注意的是,虽然YV12也是(4:2:0),但是YV12和I420的却是不同的,在存储空间上面有些区别。如下: YV12 : 亮度(行×列) + U(行×列/4) + V(行×列/4)

    I420 : 亮度(行×列) + V(行×列/4) + U(行×列/4)

    可以看出,YV12和I420基本上是一样的,就是UV的顺序不同。

    继续我们的话题,经过第一次数据压缩后RGB24->YUV(I420)。这样,数据量将减少一半,为什么呢?呵呵,这个就太基础了,我就不多写了。同样,如果是RGB24->YUV(YV12),也是减少一半。但是,虽然都是一半,如果是YV12的话效果就有很大损失。然后,经过X264编码后,数据量将大大减少。将编码后的数据打包,通过RTP实时传送。到达目的地后,将数据取出,进行解码。完成解码后,数据仍然是YUV格式的,所以,还需要一次转换,这样windows的驱动才可以处理,就是YUV2RGB24。

    YUY2  是 4:2:2  [Y0 U0 Y1 V0]

     

    yuv420p 和 YUV420的区别 在存储格式上有区别

    yuv420p:yyyyyyyy uuuuuuuu vvvvv yuv420: yuv yuv yuv

         YUV420P,Y,U,V三个分量都是平面格式,分为I420和YV12。I420格式和YV12格式的不同处在U平面和V平面的位置不同。在I420格式中,U平面紧跟在Y平面之后,然后才是V平面(即:YUV);但YV12则是相反(即:YVU)。
    YUV420SP, Y分量平面格式,UV打包格式, 即NV12。 NV12与NV21类似,U 和 V 交错排列,不同在于UV顺序。
    I420: YYYYYYYY UU VV    =>YUV420P
    YV12: YYYYYYYY VV UU    =>YUV420P
    NV12: YYYYYYYY UVUV     =>YUV420SP
    NV21: YYYYYYYY VUVU     =>YUV420SP

     
    展开全文
  • 用java实现的,可以把yv12的图片压缩到想要的分辨率。文件中还有YV12和I420转NV21的算法。
  • I420rotate.7z

    2021-08-11 20:21:52
    介绍I420顺时针90、180、270旋转和镜像翻转代码和测试程序
  • Opencv中BGR、YUV、YUV_I420\NV12分析

    千次阅读 2020-08-12 17:09:05
    文章目录一、CV_BGR2YUV二、CV_BGR2YUV_I420 本地模拟视频解码YUV数据传输 无奈OpenCV中没有自带 BGR转NV12的接口 主要分析cv::cvtColor 函数中 CV_BGR2YUV、CV_BGR2YUV_I420区别,并尝试将I420转成NV12 本文默认已...


    本地模拟视频解码YUV数据传输
    无奈OpenCV中没有自带 BGR转NV12的接口
    通过mat.data分析cv::cvtColor 函数中 CV_BGR2YUV、CV_BGR2YUV_I420区别,并尝试将I420转成NV12
    本文默认已清楚 YUV中I420、nv12的原理
    BGR图像大小是 1080 *1920 * 3

    一、CV_BGR2YUV

    得到的YUV是三个通道,分别对应Y通道、U通道和V通道,且宽高大小一样

    int main()
    {
        cv::Mat inImage = imread("./QJPQ1_1_0.jpg");
        std::cout << " bgr H: "<<  inImage.rows << std::endl;
        std::cout << " bgr W: "<< inImage.cols << std::endl;
        std::cout << " bgr C: "<< inImage.channels() << std::endl;
        cv::Mat imageYUV;
        cv::cvtColor(inImage,imageYUV,CV_BGR2YUV);
    
        std::cout <<  "yuv H: "<<  imageYUV.rows << std::endl;
        std::cout << " yuv W: "<< imageYUV.cols << std::endl;
        std::cout << " yuv C: "<< imageYUV.channels() << std::endl;
        cv::Mat imageY(inImage.rows,inImage.cols,1);
        cv::Mat imageU(inImage.rows,inImage.cols,1);
        cv::Mat imageV(inImage.rows,inImage.cols,1);
    
        cv::cvtColor(inImage,imageYUV,CV_BGR2YUV);
        std::vector<Mat> mv;
        split(inImage, (vector<Mat>&)mv);
    
        imageY = mv[0].clone();
        imageU = mv[1].clone();
        imageV = mv[2].clone();
    
        std::cout << "hello world" << std::endl;
        return 0;
    }
    

    输出

     bgr H: 1080
     bgr W: 1920
     bgr C: 3
    yuv H: 1080
     yuv W: 1920
     yuv C: 3
    

    二、CV_BGR2YUV_I420

    经过OpenCV的转换函数,发现经过cvtColor转换后的mat的通道数只有1,且高的大小是BGR的mat的1.5倍。
    以下原理摘自:RGB、YUV420、NV21、I420编码区别

    I420也是YUV420编码格式的一种,由于android手机厂商的原因,摄像头采集到的数据永远都是经过NV21编码的数据,但是对于这种数据不能够显示在苹果或windows平台,那么需要对这个编码格式的数据需要重新编码,其中I420这种编码格式,所有的厂商都是适配的。

    I420编码格式:比如一张19201280的图片,经过I420编码后,会变成前面19201280字节全是Y,从19201280字节长度开始,会先排列U,总字节长度为19201280/4,从19201280+19201280/4开始排列V,字节长度为1920*1280/4,所以总的字节长度适合NV21一样的,只是UV的编码顺序不一样。请看下图:
    在这里插入图片描述
    代码分析,我们利用cv::cvtColor(inImage,imageYUV,CV_BGR2YUV_I420)函数,将BGR转成YUV_I420,并做用imwrite函数保存出来,对比保存出来的图和原图,来进一步理解YUV_I420的数据内存排列

        cv::Mat inImage = imread("./QJPQ1_1_0.jpg");
        std::cout << " bgr H: "<<  inImage.rows << std::endl;
        std::cout << " bgr W: "<< inImage.cols << std::endl;
        std::cout << " bgr C: "<< inImage.channels() << std::endl;
        cv::Mat imageYUV;
        cv::cvtColor(inImage,imageYUV,CV_BGR2YUV_I420);
    //
        std::cout <<  "yuv H: "<<  imageYUV.rows << std::endl;
        std::cout << " yuv W: "<< imageYUV.cols << std::endl;
        std::cout << " yuv C: "<< imageYUV.channels() << std::endl;
        cv::imwrite("./yuv.jpg", imageYUV);
    

    输出:

     bgr H: 1080
     bgr W: 1920
     bgr C: 3
     yuv H: 1620
     yuv W: 1920
     yuv C: 1
    

    原图
    在这里插入图片描述尝试将U通道从I420中提取出来

    
    int main()
    {
        cv::Mat inImage = imread("./demo.jpg");
        cv::Mat imageYUV;
        cv::cvtColor(inImage,imageYUV,CV_BGR2YUV_I420);
        cv::imwrite("./yuv.jpg", imageYUV);
        int s32BufLen  = inImage.rows * inImage.cols ;
        cv::Mat imageY;
        imageY.create( inImage.rows/4 , inImage.cols, CV_8UC1);
        memcpy(imageY.data, imageYUV.data + s32BufLen,   s32BufLen/4 );
        cv::imwrite("./u.jpg", imageY);
    //
        std::cout << "===========" << std::endl;
        return 0;
    }
    

    在这里插入图片描述

    三、I420和NV12的区别以及Opencv中相互转换

    I420的排列为 前面 hw字节都是Y,再排序U,总字节长度是 WH/4,再排序V,总字节长度是 W*H/4
    NV12则是先排序Y,然后uv交替。Opencv没有提供 bgr转NV12的函数,这里根据原理自己实现一遍

    void swapYUV_I420toNV12(unsigned char* i420bytes, unsigned char* nv12bytes, int width, int height)
    {
        int nLenY = width * height;
        int nLenU = nLenY / 4;
    
        memcpy(nv12bytes, i420bytes, width * height);
    
        for (int i = 0; i < nLenU; i++)
        {
            nv12bytes[nLenY + 2 * i] = i420bytes[nLenY + i];                    // U
            nv12bytes[nLenY + 2 * i + 1] = i420bytes[nLenY + nLenU + i];        // V
        }
    }
    
    void BGR2YUV_nv12(cv::Mat src, cv::Mat &dst)
    {
        int w_img = src.cols;
        int h_img = src.rows;
        dst = cv::Mat(h_img*1.5, w_img, CV_8UC1, Scalar(0));
        cv::Mat src_YUV_I420(h_img*1.5, w_img, CV_8UC1, Scalar(0));  //YUV_I420
        cvtColor(src, src_YUV_I420, CV_BGR2YUV_I420);
        swapYUV_I420toNV12(src_YUV_I420.data, dst.data, w_img, h_img);
    }
    

    四、I420或者NV12转RGB

    Opencv有函数提供

    cv::cvtColor(SrcYUV, RGBImage, cv::COLOR_YUV2RGB_NV12); 
    cv::cvtColor(SrcYUV, RGBImage, cv::COLOR_YUV2RGB_I420)
    

    五、补上demo测试

    demo中,先将BGR转成 NV12, 再调用 opencv的 NV12转 BGR

    如果能成功得到原图,证明代码无误

    
    int main()
    {
    	cv::Mat bgrImg = cv::imread("./000000000009.jpg");
    	cv::Mat nv12Img;
    	BGR2YUV_nv12(bgrImg, nv12Img);
     
    	cv::Mat saveImg;
    	cv::cvtColor(nv12Img, saveImg, CV_YUV2BGR_NV12);
    	cv::imshow("name", saveImg);
    	cv::waitKey(0);
    	std::cout << "hello world" << std::endl;
    	return 0;
    }
    
    展开全文
  • I420:又叫YU12,安卓的模式。存储顺序是先存Y,再存U,最后存V。YYYYUUUVVV YV12:存储顺序是先存Y,再存V,最后存U。YYYVVVUUU YUV420sp:又叫bi-planer或two-planer双平面,Y一个平面,UV在同一个平面交叉存储。 ...
  • WebRTC中I420BufferInterface如何取yuv数据

    千次阅读 2019-08-21 15:35:56
    在WebRTC中使用VP8编码I420BufferInterface方式中DataY(),DataU(),DataV(),取出的数据正好是Y,U,V分量,可以直接在OpenGL中渲染,但是改成open264后,由于用的是ffmpeg解码,复用的AVFrame里的数据,涉及到内存对齐...
  • libyuv接口I420Rotate的实际使用

    千次阅读 2020-02-21 16:46:01
    I420Rotate 1.接口定义: // Rotate I420 frame. LIBYUV_API int I420Rotate(const uint8* src_y, int src_stride_y, const uint8* src_u, int src_stride_u, const uint8* src_v, int src_stride_v, uint8* dst_y, ...
  • yuv420格式nv12,nv21,I420,YV12互转

    千次阅读 2020-04-04 14:31:12
    I420: YYYYYYYY UU VV =>YUV420P YV12: YYYYYYYY VV UU =>YUV420P NV12: YYYYYYYY UVUV =>YUV420SP NV21: YYYYYYYY VUVU =>YUV420SP yuvI420ToYV12 void yuvI420ToYV12(char *I420,int w,int h,char *YV12) { ...
  • I420转RGB24

    千次阅读 2017-10-12 17:23:54
    I420也叫IYUV,也叫YUV420 。 采集卡采集视屏数据通常为I420 经过转换成rgb24后 可以对图像进行各种处理。 下面来说说i420转rgb24: //rgbA放在全局 不然每次重新分配浪费时间 unsigned char* I420ToRGBA...
  • RGB、YUV420、NV21、I420编码区别

    千次阅读 2019-05-30 11:02:31
    首先了解视频编码为什么使用YUV420而不是rgb。那么需要了解两者的原理,看图说话 所以从内存的角度来说,yuv单位像素使用的内存更低,但是两者表示的效果是一致的,也可以认为rgb过度绘制了,把人眼无法分辨的区域...
  • Android 视频 YUV i420格式转换为位图Bitmap YUV基础描述 YUV(YCbCr,图像除了RGB,还有YUV) 定义:是电视系统所采用的一种颜色编码方法 Y 标识明亮度,也就是灰阶值 U和V标识的色度,描述影像色彩和饱和度 YUV常见...
  • Unity 工具之 YUV(YUV420 :I420,YV12,NV12,NV21)使用 shader 转为 RGB 显示 封装 YUV420ToRGBWrapper 目录 Unity 工具之 YUV(YUV420 :I420,YV12,NV12,NV21)使用 shader 转为 RGB 显示 封装 YUV420...
  • android平台下基于Camera详解NV21(YUV420P)、NV12(YUV420SP)和RGBA、RGB、BGRA、BGR 概述 NV21 参考: https://blog.csdn.net/leixiaohua1020/article/details/50534150 ...
  • 说简单点,就是如何将 YUV I420 格式转换为 RGBA8888 格式。 在 Camera2 API 中,相机预览不能直接使用 NV21 格式获取了,否则会报错一个 “NV21 format is not supported” 的异常。官方推荐我们使用 YUV_420_888 ...
  • libyuv接口I420ToRGB24的实际使用

    千次阅读 2020-02-21 16:33:25
    I420ToRGB24 1.接口定义: int I420ToRGB24(const uint8* src_y, int src_stride_y, const uint8* src_u, int src_stride_u, const uint8* src_v, int src_stride_v, uint8* dst_frame, int dst_stride_frame, int ...
  • YUV420P,Y,U,V三个分量都是平面格式,分为I420和YV12。I420格式和YV12格式的不同处在U平面和V平面的位置不同。在I420格式中,U平面紧跟在Y平面之后,然后才是V平面(即:YUV);但YV12则是相反(即:YVU)。 YUV...
  • YUV I420裁剪

    2018-03-20 09:26:18
    废话不对说哈,直接上代码:static void Cut_I420(uint8_t* Src, int x, int y, int srcWidth, int srcHeight, uint8_t* Dst, int desWidth, int desHeight)//图片按位置裁剪 { //得到B图像所在A的坐标 int ...
  • 较早的时候,我们在【图像子采样】中,曾详细介绍过YUV...比如今天要讲的,主流的采样格式YUV420,它的存储格式通常就有四种:I420、YV12、NV12、NV21。 1、为什么会有存储格式? 存储格式是个非常容易理解的事情,...
  • android I420转NV21 , NV21转I420

    千次阅读 2018-02-07 09:58:13
    /** * I420转nv21 * @param data * @param width * @param height * @return */ public static byte[] I420Tonv21(byte[] data, int width, int height) { byte[] ret
  • YV12和I420的区别

    千次阅读 2019-01-12 18:45:00
    一般来说,直接采集到的视频数据是RGB24的...如果是I420(即YUV标准格式4:2:0)的数据量是 size=width×heigth×1.5 Bit 在采集到RGB24数据后,需要对这个格式的数据进行第一次压缩。即将图像的颜色空间由RGB2Y...
  • NV21与I420编码格式

    千次阅读 2018-11-09 11:51:31
    NV21与I420 ​ Android Camera对象通过setPreviewCallback 函数,在onPreviewFrame(byte[] data,Camera camera)中回调采集的数据就是NV21格式。而x264编码的输入数据却为I420格式。 因此,当我们采集到摄像头数据...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 55,860
精华内容 22,344
关键字:

I420