camera (19)---Android 相机开发的基本流程

[Android 相机]Android 相机开发的基本流程


 https://blog.csdn.net/bluewindtalker/article/details/54563910

相机开发现在有2个类,分别为android.hardware.camera2和android.hardware.Camera,其中Camera类官方已经不推荐,不过鉴于有前人踩坑了,为了快速开发也就直接拿来用了

This class was deprecated in API level 21.
We recommend using the new android.hardware.camera2 API for new applications.

关于旧版的Camera类,google官方给了下面的指导步骤,https://developer.android.com/reference/android/hardware/Camera.html

To take pictures with this class, use the following steps:


看了这些后我们可以简单的进行实战,首先是初始化camera的过程。

[java]  view plain  copy
  1. /** 
  2.  * 初始化照片 
  3.  */  
  4. private void initCamera() {  
  5.     if (camera != null) {  
  6.         camera.startPreview();  
  7.     }  
  8.     Log.e(TAG, "initCamera");  
  9.     //1. Obtain an instance of Camera from open(int).  
  10.     //这里可以根据前后摄像头设置  
  11.     camera = openCamera(currentCameraType);  
  12.     if (camera == null) {  
  13.         return;  
  14.     }  
  15.     //2. Get existing (default) settings with getParameters().  
  16.     //获得存在的默认配置属性  
  17.     Camera.Parameters parameters = camera.getParameters();  
  18.   
  19.     //3. If necessary, modify the returned Camera.Parameters object and call setParameters(Camera.Parameters).  
  20.     //可以根据需要修改属性,这些属性包括是否自动持续对焦、拍摄的gps信息、图片视频格式及大小、预览的fps、  
  21.     // 白平衡和自动曝光补偿、自动对焦区域、闪光灯状态等。  
  22.     //具体可以参阅https://developer.android.com/reference/android/hardware/Camera.Parameters.html  
  23.     if (parameters.getSupportedFocusModes().contains(Camera.Parameters  
  24.             .FOCUS_MODE_CONTINUOUS_PICTURE)) {  
  25.         //自动持续对焦  
  26.         parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);  
  27.     }  
  28.     //在设置图片和预览的大小时要注意当前摄像头支持的大小,不同手机支持的大小不同,如果你的SurfaceView不是全屏,有可能被拉伸。  
  29.     // parameters.getSupportedPreviewSizes(),parameters.getSupportedPictureSizes()  
  30.     List<Camera.Size> picSizes = parameters.getSupportedPictureSizes();  
  31.     Resources resources = this.getResources();  
  32.     DisplayMetrics dm = resources.getDisplayMetrics();  
  33.     float density = dm.density;  
  34.     int width = dm.widthPixels;  
  35.     int height = dm.heightPixels;  
  36.     Camera.Size picSize = getPictureSize(picSizes, width, height);  
  37.     parameters.setPictureSize(picSize.width, picSize.height);  
  38.     camera.setParameters(parameters);  
  39.     //4. Call setDisplayOrientation(int) to ensure correct orientation of preview.  
  40.     //你可能会遇到画面方向和手机的方向不一致的问题,竖向手机的时候,但是画面是横的,这是由于摄像头默认捕获的画面横向的  
  41.     // 通过调用setDisplayOrientation来设置PreviewDisplay的方向,可以解决这个问题。  
  42.     setCameraDisplayOrientation(this, currentCameraType, camera);  
  43.   
  44.     //5. Important: Pass a fully initialized SurfaceHolder to setPreviewDisplay(SurfaceHolder).  
  45.     // Without a surface, the camera will be unable to start the preview.  
  46.     //camera必须绑定一个surfaceview才可以正常显示。  
  47.     try {  
  48.         camera.setPreviewDisplay(displaySfv.getHolder());  
  49.     } catch (IOException e) {  
  50.         e.printStackTrace();  
  51.     }  
  52.     //6. Important: Call startPreview() to start updating the preview surface.  
  53.     // Preview must be started before you can take a picture.  
  54.     //在调用拍照之前必须调用startPreview()方法,但是在此时有可能surface还未创建成功。  
  55.     // 所以加上SurfaceHolder.Callback(),在回调再次初始化下。  
  56.     camera.startPreview();  
  57.     //7. When you want, call  
  58.     // takePicture(Camera.ShutterCallback, Camera.PictureCallback, Camera.PictureCallback, Camera.PictureCallback)  
  59.     // to capture a photo. Wait for the callbacks to provide the actual image data.  
  60.     //当如果想要拍照的时候,调用takePicture方法,这个下面我们会讲到。  
  61.   
  62.     //8. After taking a picture, preview display will have stopped. To take more photos, call startPreview() again first.  
  63.     //在拍照结束后相机预览将会关闭,如果要再次拍照需要再次调用startPreview()  
  64.   
  65.     //9. Call stopPreview() to stop updating the preview surface.  
  66.     //通过调用stopPreview方法可以结束预览  
  67.     //10. Important: Call release() to release the camera for use by other applications.  
  68.     // Applications should release the camera immediately in onPause()(and re-open() it in onResume()).  
  69.     //建议在onResume调用open的方法,在onPause的时候执行release方法  
  70. }  
根据上文提到的第9、10步骤我们在onResume与onPause做处理。
[java]  view plain  copy
  1. @Override  
  2.     protected void onResume() {  
  3.         super.onResume();  
  4.         Log.e(TAG, "onResume");  
  5.         if (!isRequestPermission) {  
  6.             checkAndInitCamera();  
  7.         }  
  8.     }  
  9.   
  10.     @Override  
  11.     protected void onPause() {  
  12.         super.onPause();  
  13.         Log.e(TAG, "onPause");  
  14.         releaseCamera();  
  15.   
  16.     }  
  17.   
  18.     private void releaseCamera() {  
  19.         if (camera != null) {  
  20.             camera.stopPreview();  
  21.             camera.release();  
  22.             camera = null;  
  23.         }  
  24.     }  

其中checkAndInitCamera()为权限处理的方法

[java]  view plain  copy
  1. private void checkAndInitCamera() {  
  2.         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {  
  3.             // 检查该权限是否已经获取  
  4.             int i = ContextCompat.checkSelfPermission(this, permissions[0]);  
  5.             // 权限是否已经 授权 GRANTED---授权  DINIED---拒绝  
  6.             if (i != PackageManager.PERMISSION_GRANTED) {  
  7.                 // 如果没有授予该权限,就去提示用户请求  
  8.                 isRequestPermission = true;  
  9.                 ActivityCompat.requestPermissions(this, permissions, CAMERA_PERMISSION_CODE);  
  10.             } else {  
  11.                 initCamera();  
  12.             }  
  13.         } else {  
  14.             initCamera();  
  15.         }  
  16.     }  
这里有些细节需要注意,在 parameters.setPictureSize(int  width , int  height ); 这个方法的时候
不能将宽高随意写,必须从 parameters.getSupportedPictureSizes(); 中选择最合适的宽高,否则会出现setParameters failed的运行时错误。

而系统提供的宽高是根据摄像头的参数定的,这个导致需要根据手机和surfaceview宽高来动态适配,否则可能会出现图像失真拉伸压缩的情况,在本文中将直接使用最接近的摄像头像素的算法

[java]  view plain  copy
  1. /** 
  2.      * 获得最合是的宽高size 
  3.      */  
  4.     private Camera.Size getPictureSize(List<Camera.Size> picSizes, int width, int height) {  
  5.         Camera.Size betterSize = null;  
  6.         int diff = Integer.MAX_VALUE;  
  7.         if (picSizes != null && picSizes.size() > 0) {  
  8.             for (Camera.Size size : picSizes) {  
  9.                 int newDiff = Math.abs(size.width - width) + Math.abs(size.height - height);  
  10.                 if(newDiff == 0){  
  11.                     return size;  
  12.                 }  
  13.                 if (newDiff < diff) {  
  14.                     betterSize = size;  
  15.                     diff = newDiff;  
  16.                 }  
  17.             }  
  18.         }  
  19.         return betterSize;  
  20.     }  
还有一个细节是摄像头并不是正的,调用的方法
setCameraDisplayOrientation
[java]  view plain  copy
  1. //设置相机的方向  
  2.     public int setCameraDisplayOrientation(Activity activity, int cameraId, android.hardware.Camera camera) {  
  3.         android.hardware.Camera.CameraInfo info = new android.hardware.Camera.CameraInfo();  
  4.         android.hardware.Camera.getCameraInfo(cameraId, info);  
  5.         int rotation = activity.getWindowManager().getDefaultDisplay().getRotation();  
  6.         int degrees = 0;  
  7.         switch (rotation) {  
  8.             case Surface.ROTATION_0:  
  9.                 degrees = 0;  
  10.                 break;  
  11.             case Surface.ROTATION_90:  
  12.                 degrees = 90;  
  13.                 break;  
  14.             case Surface.ROTATION_180:  
  15.                 degrees = 180;  
  16.                 break;  
  17.             case Surface.ROTATION_270:  
  18.                 degrees = 270;  
  19.                 break;  
  20.             default:  
  21.                 degrees = 0;  
  22.                 break;  
  23.         }  
  24.         int result;  
  25.         if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {  
  26.             result = (info.orientation + degrees) % 360;  
  27.             result = (360 - result) % 360;   // compensate the mirror  
  28.         } else {  
  29.             // back-facing  
  30.             result = (info.orientation - degrees + 360) % 360;  
  31.         }  
  32.         camera.setDisplayOrientation(result);  
  33.         return degrees;  
  34.     }  
https://blog.csdn.net/bluewindtalker/article/details/54563910

这么做运行的时候,我们发现预览图并不能正常显示出来,这是因为surface还没有正常创建出来,这时候我们可以在initCamera方法中加入如下代码,坚挺SurfaceHolder的事件回调

[java]  view plain  copy
  1. SurfaceHolder holder = displaySfv.getHolder();  
  2.         if (holder != null) {  
  3.             holder.addCallback(new SurfaceHolder.Callback() {  
  4.                 @Override  
  5.                 public void surfaceCreated(SurfaceHolder holder) {  
  6.                     Log.e(TAG, "surfaceCreated" + holder);  
  7.                     checkAndInitCamera();  
  8.                 }  
  9.   
  10.                 @Override  
  11.                 public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {  
  12.                     Log.e(TAG, "surfaceChanged" + holder);  
  13.                 }  
  14.   
  15.                 @Override  
  16.                 public void surfaceDestroyed(SurfaceHolder holder) {  
  17.                     Log.e(TAG, "surfaceDestroyed" + holder);  
  18.                 }  
  19.             });  
  20.         }  

以下就是重点了,拍照,拍照就是触发一个回调事件方法。

[java]  view plain  copy
  1. /** 
  2.  * 拍摄照片 
  3.  */  
  4. private void takePicture() {  
  5.     picIV.setImageBitmap(null);  
  6.     if (camera == null) {  
  7.         return;  
  8.     }  
  9.     //如果不加第一个回调,手机会没有拍照音效,第二个回调是返回raw格式图片,  
  10.     // 了解过相机的人可能知道这是原图的意思,这个我们不处理,我们处理第三个回调,jpg格式的数据  
  11.     // 拍摄照片  
  12.     camera.takePicture(new Camera.ShutterCallback() {  
  13.         @Override  
  14.         public void onShutter() {  
  15.         }  
  16.     }, nullnew Camera.PictureCallback() {  
  17.         @Override  
  18.         public void onPictureTaken(byte[] data, Camera camera) {  
  19.             // 将拍照数据data数组转化为Bitmap,这里应该放到线程执行了,这里为了简单处理直接放UI线程了  
  20.             Bitmap imageBitmap = BitmapFactory.decodeByteArray(data, 0, data.length);  
  21.             //一般手机需要旋转90度来适应方向,如果setCameraDisplayOrientation得到的结果不是90度,一般还需要再次旋转180  
  22.             picIV.setImageBitmap(rotate(imageBitmap, 90));  
  23.             picFl.setVisibility(View.VISIBLE);  
  24.         }  
  25.     });  
  26. }  
  27.   
  28. public Bitmap rotate(Bitmap bitmap, int degree) {  
  29.     Matrix matrix = new Matrix();  
  30.     matrix.postRotate(degree);  
  31.     return Bitmap.createBitmap(bitmap, 00, bitmap.getWidth(), bitmap.getHeight(), matrix, false);  
  32. }  

 以下是布局文件内容

[html]  view plain  copy
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  3.     xmlns:app="http://schemas.android.com/apk/res-auto"  
  4.     xmlns:tools="http://schemas.android.com/tools"  
  5.     android:layout_width="match_parent"  
  6.     android:layout_height="match_parent"  
  7.     tools:context="com.bluewindtalker.camera.demo.CameraActivity">  
  8.   
  9.     <SurfaceView  
  10.         android:id="@+id/sf_display_demo_camera"  
  11.         android:layout_width="match_parent"  
  12.         android:layout_height="match_parent" />  
  13.   
  14.     <Button  
  15.         android:id="@+id/btn_take_picture_demo_camera"  
  16.         android:layout_width="150dp"  
  17.         android:layout_height="50dp"  
  18.         android:layout_gravity="center_horizontal|bottom"  
  19.         android:layout_marginBottom="70dp"  
  20.         android:background="@android:color/black"  
  21.         android:gravity="center"  
  22.         android:text="拍照"  
  23.         android:textColor="@android:color/white"  
  24.         android:textSize="20sp" />  
  25.   
  26.     <FrameLayout  
  27.         android:id="@+id/fl_picture_demo_camera"  
  28.         android:layout_width="match_parent"  
  29.         android:layout_height="match_parent"  
  30.         android:background="@android:color/black"  
  31.         android:visibility="gone">  
  32.   
  33.         <ImageView  
  34.             android:id="@+id/iv_picture_demo_camera"  
  35.             android:layout_width="match_parent"  
  36.             android:layout_height="match_parent"  
  37.             android:scaleType="fitCenter" />  
  38.   
  39.         <TextView  
  40.             android:layout_width="wrap_content"  
  41.             android:layout_height="wrap_content"  
  42.             android:layout_gravity="center"  
  43.             android:background="#66666666"  
  44.             android:padding="20dp"  
  45.             android:text="点击任意区域关闭照片"  
  46.             android:textColor="@android:color/white"  
  47.             android:textSize="18sp" />  
  48.     </FrameLayout>  
  49. </FrameLayout>  

下篇将讲解如何通过摄像头识别周围环境光亮强度

[Android相机]光线传感器识别环境光亮强度

[Android相机]通过手机摄像头识别环境亮度

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值