android 摄像头保存照片两种方式

方式1、 直接保存视频预览的数据帧

在预览方法中imgData数据就是视频帧 

android默认的视频采集格式是NV21,(属于YUV420)

在onPreviewFrame中传进来的byte[] data即为NV21格式。

NV21 的存储格式是,以4 X 4 图片为例子
占用内存为 4 X 4 X 3 / 2 = 24 个字节

Y Y Y Y
Y Y Y Y
Y Y Y Y
Y Y Y Y
V U V U
V U V U

NV12 的存储格式是,以4 X 4 图片为例子
Y Y Y Y
Y Y Y Y
Y Y Y Y
Y Y Y Y
U V U V
U V U V
————————————————
 

public void onPreviewFrame(final byte[] imgData, final Camera camera) {

int width = camera.getParameters().getPreviewSize().width;//获取视频的宽度
int height = camera.getParameters().getPreviewSize().height;//获取照片的高度
//得到宽和高  就知道了数组大小  
//imgdata数组长度和 width height 关系 =>  imgdata.length = width * height *3/2
//byte[] outdata;
//outdata = rotateYUV420Degree180(data, width, height); //进行180旋转
//outdata = rotateYUV420Degree90(data, width, height); //进行90旋转

//outdata = rotateYUV420Degree270(data, width, height); //进行270旋转

//如果保存数据帧  需要将nv21进行转换

YuvImage yuvImage = new YuvImage(outdata, camera.getParameters()
        .getPreviewFormat(), width, height, null);
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
yuvImage.compressToJpeg(new Rect(0, 0, width, height), 80,
        byteArrayOutputStream);
byte[] jpegData = byteArrayOutputStream.toByteArray();

File file = new File("/sdcard"+ "/"+i+".png");

i++;

FileOutputStream fos = new FileOutputStream(file);

fos.write(jpegData, 0, jpegData.length); 
fos.flush(); 
fos.close();


}
 

不同的android手机 ,可能存在多种情况,比如预览正常,保存的数据帧出现了90度 180度 270度旋转。


对NV21进行顺时针旋转90度,180度和270度算法。

旋转90度

privatebyte[] rotateYUV420Degree90(byte[] data, int imageWidth, int imageHeight){
byte[] yuv =newbyte[imageWidth*imageHeight*3/2];
// Rotate the Y luma
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++;}
 
		    }
// Rotate the U and V color components 
		    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;}
用法:

//clockwise90:IplImage.create(480, 640) && new NewFFmpegFrameRecorder(480, 640)顺时针旋转90度,
将IplImage.create和new NewFFmpegFrameRecorder处源图像的宽高640x480对换成旋转后的真实宽高480x640
byte[] outdata;		
outdata = rotateYUV420Degree90(data, 640, 480);
旋转180度
privatebyte[] rotateYUV420Degree180(byte[] data, int imageWidth, int imageHeight){
byte[] yuv =newbyte[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;}
用法:

//clockwise180:IplImage.create(640, 480) && new NewFFmpegFrameRecorder(640, 480)上述2处无需改动
byte[] outdata;		
outdata = rotateYUV420Degree180(data, 640, 480);
旋转270度
private byte[] rotateYUV420Degree270(byte[] data, int imageWidth, int imageHeight){
    byte[] yuv =new byte[imageWidth*imageHeight*3/2];
    // Rotate the Y luma
    int i =0;
    for(int x = imageWidth-1;x >=0;x--){
        for(int y =0;y < imageHeight;y++){
		            yuv[i]= data[y*imageWidth+x];
		            i++;
        }
  }// Rotate the U and V color components 
	i = imageWidth*imageHeight;
    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-1)];
		         i++;
		       yuv[i]= data[(imageWidth*imageHeight)+(y*imageWidth)+x];
		            i++;
        }
    }
    return yuv;
}
用法:

//clockwise270:IplImage.create(480, 640) && new NewFFmpegFrameRecorder(480, 640),设置与旋转90度相同
byte[] outdata;		
outdata = rotateYUV420Degree270(data, 640, 480);
裁剪NV21
publicbyte[] cropYUV420(byte[] data,int imageW,int imageH,int newImageH){int cropH;int i,j,count,tmp;byte[] yuv =newbyte[imageW*newImageH*3/2];
 
		cropH =(imageH - newImageH)/2;
 
		count =0;for(j=cropH;j<cropH+newImageH;j++){for(i=0;i<imageW;i++){
				yuv[count++]= data[j*imageW+i];}}
 
		//Cr Cb
		tmp = imageH+cropH/2;for(j=tmp;j<tmp + newImageH/2;j++){for(i=0;i<imageW;i++){
				yuv[count++]= data[j*imageW+i];}}
 
		return yuv;}
用法:

将640x480裁剪成480x480时用法如下:

在onPreviewFrame(byte[] data, Camera camera)中调用

byte[] outdata2;	
byte[] outdata;
outdata2 = rotateYUV420Degree90(data, 640, 480);//将640x480旋转成480x640
outdata = cropYUV420(outdata2, 480, 640,480);//将480x640裁剪成480x480
在initVideoRecorder中

videoRecorder = new NewFFmpegFrameRecorder(strVideoPath, 480, 480, 1);

在handleSurfaceChanged中

yuvIplImage = IplImage.create(480, 480, IPL_DEPTH_8U, 2);
————————————————
版权声明:本文为CSDN博主「github.com/starRTC」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/elesos/article/details/53220309

 

这样 就可以获取到数据帧并完成旋转保存或者裁剪保存。

不要尝试将文件保存到本地之后,再对照片进行读取旋转,本人尝试过通过

android.graphics.Matrix matrix = new  android.graphics.Matrix();
matrix.setRotate(degrees, bitmap.getWidth() / 2, bitmap.getHeight() / 2);
Bitmap bmp = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
bitmap.recycle();

方式旋转,但是Bitmap  是获取不到的。而且此方式不是常规做法

方式2、 使用照相机拍照功能

//照相机拍照后的回调    
private final Camera.PictureCallback mPictureCallback = new Camera.PictureCallback() {
        @Override
        public void onPictureTaken(final byte[] data, Camera camera) {
            System.out.println(Thread.currentThread());
            new Thread(new Runnable() {
                @Override
                public void run() {
                    File file = new File("/sdcard"+ "/facenew"+i+".png");

                    FileOutputStream fos = null;
                    try {
                            fos = new FileOutputStream(file);
                            fos.write(data, 0, data.length);
                            fos.flush();
                            fos.close();
                            i++;
                         } catch (FileNotFoundException e) {
                                e.printStackTrace();
                         } catch (IOException e) {
                                e.printStackTrace();
                        }
                     }
                }).start();

            camera.startPreview();//重新开始预览


        }
    };

    @Override
    public void onPreviewFrame(final byte[] imgData, final Camera camera) {
        camera.takePicture(null, null, mPictureCallback);//执行拍照功能,拍照成功后执行回调
}

 

网上有网友提到 预览是正向的,保存照片是旋转的,可以按照方式1进行照片旋转。

 

两种方式的对比:

相机拍照功能 照片出现模糊的概率会小很多,但是每次执行takePicture的时候会推出预览。保存之后重新开始预览,会导致页面出现顿卡。

直接保存数据帧流畅度会好很多,不过照片的清晰度不如拍照的好 ,(实测)原因不清楚

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值