android camera之nv21旋转

举报
风吹稻花香 发表于 2021/06/06 00:31:51 2021/06/06
【摘要】   https://www.cnblogs.com/cmai/p/8372607.html   android camera之nv21旋转 这周做的一个android的camera开发,需要获取到视频帧数据,并且需要是nv21格式的byte数组,并且视频帧的图像需要是正方向的。和android相机打过交道的都清楚,android的camera获取到...

 

https://www.cnblogs.com/cmai/p/8372607.html

 

android camera之nv21旋转

这周做的一个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(00, 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); 

在这里边我们需要注意的是我们的宽和高,需要用转移后的,不可使用转移前的,否则会出现看到的图片有重影的现象。

旋转保存:


  
  1. public void saveFacePhoto(Bitmap toSavePhoto, final String fileNameUuid){
  2. String state = Environment.getExternalStorageState();
  3. //如果状态不是mounted,无法读写
  4. if (!state.equals(Environment.MEDIA_MOUNTED)) {
  5. Log.w("Info","状态不是mounted,无法读写!");
  6. }else{
  7. //通过UUID生成字符串文件名
  8. try {
  9. File file = new File(fileNameUuid);
  10. FileOutputStream out = new FileOutputStream(file);
  11. toSavePhoto.compress(Bitmap.CompressFormat.JPEG, 100, out);
  12. out.flush();
  13. out.close();
  14. //保存图片后发送广播通知更新数据库
  15. Uri uri = Uri.fromFile(file);
  16. sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, uri));
  17. } catch (Exception e) {
  18. e.printStackTrace();
  19. }
  20. }
  21. }
  22. byte[] nv21_save(int angle){
  23. byte[] a_data=null;
  24. int a_width=CameraOverlap.PREVIEW_WIDTH;
  25. int a_height= CameraOverlap.PREVIEW_HEIGHT;
  26. if (angle==90){
  27. a_width=CameraOverlap.PREVIEW_HEIGHT;
  28. a_height=CameraOverlap.PREVIEW_WIDTH;
  29. a_data= rotateYUV420Degree90(mNv21Data,a_width,a_height);
  30. }else if(angle==180){
  31. a_data= NV21_rotate_to_180(mNv21Data,CameraOverlap.PREVIEW_WIDTH,CameraOverlap.PREVIEW_HEIGHT);
  32. // a_data= NV21_mirror(a_data,a_width,a_height);
  33. }
  34. YuvImage yuvimageqqq = new YuvImage(a_data, ImageFormat.NV21, a_width, a_height, null);
  35. ByteArrayOutputStream baosaaqqq = new ByteArrayOutputStream();
  36. yuvimageqqq.compressToJpeg(new Rect(0, 0, a_width, a_height), 100, baosaaqqq);// 80--JPG图片的质量[0-100],100最高
  37. byte[] rawImageqqq = baosaaqqq.toByteArray();
  38. //将rawImage转换成bitmap
  39. BitmapFactory.Options optionsAA = new BitmapFactory.Options();
  40. optionsAA.inPreferredConfig = Bitmap.Config.RGB_565;
  41. final Bitmap bitmapQQQ = BitmapFactory.decodeByteArray(rawImageqqq, 0, rawImageqqq.length, optionsAA);
  42. String dirPath = Environment.getExternalStorageDirectory().getAbsolutePath() + "/Pictures/";
  43. final String filePathNameUuid = dirPath + UUID.randomUUID().toString() + "mNv21Data.jpg";
  44. saveFacePhoto(bitmapQQQ, filePathNameUuid);
  45. return a_data;
  46. }

 

文章来源: blog.csdn.net,作者:网奇,版权归原作者所有,如需转载,请联系作者。

原文链接:blog.csdn.net/jacke121/article/details/103309486

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。