这周做的一个android的camera开发,需要获取到视频帧数据,并且需要是nv21格式的byte数组,并且视频帧的图像需要是正方向的。和android相机打过交道的都清楚,android的camera获取到的图片都是横向的,因此,需要进行旋转,对于图像的旋转,其实bitmap这个类已经可以帮我们实现了,但是前提是你需要将你的数据格式转换为Bitmap才行,但是我们如果通过setPreviewCallback来获取视频帧,获取到的图片都是nv21,如果装换为bitmap后,又很难的转换为nv21格式的数据。因此则需要面临一个问题,如歌旋转nv21格式的byte数组,首先先来讲下nv21格式,讲到nv21就也要说说yuv240和nv12:
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分量: (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格式
图:YUV420p数据格式如下图
具体的旋转代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 |
public static byte [] rotateYUV420Degree90( byte [] data, int imageWidth, int imageHeight) { byte [] yuv = new byte [imageWidth * imageHeight * 3 / 2 ]; int i = 0 ; for ( int x = 0 ; x < imageWidth; x++) { for ( int y = imageHeight - 1 ; y >= 0 ; y--) { yuv[i] = data[y * imageWidth + x]; i++; } } i = imageWidth * imageHeight * 3 / 2 - 1 ; for ( int x = imageWidth - 1 ; x > 0 ; x = x - 2 ) { for ( int y = 0 ; y < imageHeight / 2 ; y++) { yuv[i] = data[(imageWidth * imageHeight) + (y * imageWidth) + x]; i--; yuv[i] = data[(imageWidth * imageHeight) + (y * imageWidth) + (x - 1 )]; i--; } } return yuv; } private static byte [] rotateYUV420Degree180( byte [] data, int imageWidth, int imageHeight) { byte [] yuv = new byte [imageWidth * imageHeight * 3 / 2 ]; int i = 0 ; int count = 0 ; for (i = imageWidth * imageHeight - 1 ; i >= 0 ; i--) { yuv[count] = data[i]; count++; } i = imageWidth * imageHeight * 3 / 2 - 1 ; for (i = imageWidth * imageHeight * 3 / 2 - 1 ; i >= imageWidth * imageHeight; i -= 2 ) { yuv[count++] = data[i - 1 ]; yuv[count++] = data[i]; } return yuv; } public static byte [] rotateYUV420Degree270( byte [] data, int imageWidth, int imageHeight) { byte [] yuv = new byte [imageWidth * imageHeight * 3 / 2 ]; int nWidth = 0 , nHeight = 0 ; int wh = 0 ; int uvHeight = 0 ; if (imageWidth != nWidth || imageHeight != nHeight) { nWidth = imageWidth; nHeight = imageHeight; wh = imageWidth * imageHeight; uvHeight = imageHeight >> 1 ; // uvHeight = height / 2 } int k = 0 ; for ( int i = 0 ; i < imageWidth; i++) { int nPos = 0 ; for ( int j = 0 ; j < imageHeight; j++) { yuv[k] = data[nPos + i]; k++; nPos += imageWidth; } } for ( int i = 0 ; i < imageWidth; i += 2 ) { int nPos = wh; for ( int j = 0 ; j < uvHeight; j++) { yuv[k] = data[nPos + i]; yuv[k + 1 ] = data[nPos + i + 1 ]; k += 2 ; nPos += imageWidth; } } return rotateYuv420Degree180(rotateYuv420Degree90(data, imageWidth, imageHeight), imageWidth, imageHeight); } |
然后,如果你想要查看旋转后的图像,则通过以下代码即可:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
YuvImage yuvimage = new YuvImage( data, ImageFormat.NV21, width, height, null ); baos = new ByteArrayOutputStream(); yuvimage.compressToJpeg( new Rect( 0 , 0 , width, height), 100 , baos); // 80--JPG图片的质量[0-100],100最高 rawImage = baos.toByteArray(); //将rawImage转换成bitmap BitmapFactory.Options options = new BitmapFactory.Options(); options.inPreferredConfig = Bitmap.Config.RGB_565; bitmap = BitmapFactory.decodeByteArray(rawImage, 0 , rawImage.length, options); |
在这里边我们需要注意的是我们的宽和高,需要用转移后的,不可使用转移前的,否则会出现看到的图片有重影的现象。
文章来源: blog.csdn.net,作者:网奇,版权归原作者所有,如需转载,请联系作者。
原文链接:blog.csdn.net/jacke121/article/details/103309467
【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱:
cloudbbs@huaweicloud.com
评论(0)