Android Camera2 拍照入門學習

dingyou110 7年前發布 | 16K 次閱讀 安卓開發 Android開發 移動開發

Android 5.0(21)之后, android.hardware.Camera 被廢棄(下面稱為 Camera1 ),還有一個 android.graphics.Camera ,這個 android.graphics.Camera 不是用來照相的,是用來處理圖像的,可以做出 3D 的圖像效果之類的,之前的 Camera1 則由 android.hardware.Camera2 來代替

Camera2 支持 RAW 輸出,可以調節曝光,對焦模式,快門等,功能比原先 Camera 強大

1.Camera1使用

使用步驟:

  1. 調用 Camera.open() ,打開相機,默認為后置,可以根據攝像頭 ID 來指定打開前置還是后置
  2. 調用 Camera.getParameters() 得到一個 Camera.Parameters 對象
  3. 使用 步驟2 得到的 Camera.Parameters 對象,對拍照參數進行設置
  4. 調用 Camera.setPreviewDispaly(SurfaceHolder holder) ,指定使用哪個 SurfaceView 來顯示預覽圖片
  5. 調用 Camera.startPreview() 方法開始預覽取景
  6. 調用 Camera.takePicture() 方法進行拍照
  7. 拍照結束后,調用 Camera.stopPreview() 結束取景預覽,之后再 replease() 方法釋放資源

這幾個步驟從 瘋狂Android講義 中學到

1.1簡單使用

使用 SurfaceView 進行取景的預覽,點擊屏幕進行拍照,用 ImageView 來展示拍的照片

取景

拍照預覽

想買關于操作系統和C的書看,知乎很多人推薦這兩本,就買了,感覺確實不錯。然而,深入理解操作系統買早了,啃不動

布局文件:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <SurfaceView
        android:id="@+id/surface_view_camera2_activity"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

    <ImageView
        android:id="@+id/iv_show_camera2_activity"
        android:layout_width="180dp"
        android:layout_height="320dp"
        android:visibility="gone"
        android:layout_centerInParent="true"
        android:scaleType="centerCrop" />
</RelativeLayout>

Activity 代碼:

public class CameraActivity extends AppCompatActivity implements View.OnClickListener {

    private SurfaceView mSurfaceView;
    private SurfaceHolder mSurfaceHolder;
    private Camera mCamera;
    private ImageView iv_show;
    private int viewWidth, viewHeight;//mSurfaceView的寬和高

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_camera2);
        initView();
    }

    /**
     * 初始化控件
     */
    private void initView() {
        iv_show = (ImageView) findViewById(R.id.iv_show_camera2_activity);
        //mSurfaceView
        mSurfaceView = (SurfaceView) findViewById(R.id.surface_view_camera2_activity);
        mSurfaceHolder = mSurfaceView.getHolder();
        // mSurfaceView 不需要自己的緩沖區
        mSurfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
        // mSurfaceView添加回調
        mSurfaceHolder.addCallback(new SurfaceHolder.Callback() {
            @Override
            public void surfaceCreated(SurfaceHolder holder) { //SurfaceView創建
                // 初始化Camera
                initCamera();
            }

            @Override
            public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
            }

            @Override
            public void surfaceDestroyed(SurfaceHolder holder) { //SurfaceView銷毀
                // 釋放Camera資源
                if (mCamera != null) {
                    mCamera.stopPreview();
                    mCamera.release();
                }
            }
        });
        //設置點擊監聽
        mSurfaceView.setOnClickListener(this);
    }

    @Override
    public void onWindowFocusChanged(boolean hasFocus) {
        super.onWindowFocusChanged(hasFocus);
        if (mSurfaceView != null) {
            viewWidth = mSurfaceView.getWidth();
            viewHeight = mSurfaceView.getHeight();
        }
    }

    /**
     * SurfaceHolder 回調接口方法
     */
    private void initCamera() {
        mCamera = Camera.open();//默認開啟后置
        mCamera.setDisplayOrientation(90);//攝像頭進行旋轉90°
        if (mCamera != null) {
            try {
                Camera.Parameters parameters = mCamera.getParameters();
                //設置預覽照片的大小
                parameters.setPreviewFpsRange(viewWidth, viewHeight);
                //設置相機預覽照片幀數
                parameters.setPreviewFpsRange(4, 10);
                //設置圖片格式
                parameters.setPictureFormat(ImageFormat.JPEG);
                //設置圖片的質量
                parameters.set("jpeg-quality", 90);
                //設置照片的大小
                parameters.setPictureSize(viewWidth, viewHeight);
                //通過SurfaceView顯示預覽
                mCamera.setPreviewDisplay(mSurfaceHolder);
                //開始預覽
                mCamera.startPreview();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 點擊回調方法
     */
    @Override
    public void onClick(View v) {
        if (mCamera == null) return;
        //自動對焦后拍照
        mCamera.autoFocus(autoFocusCallback);
    }


    /**
     * 自動對焦 對焦成功后 就進行拍照
     */
    Camera.AutoFocusCallback autoFocusCallback = new Camera.AutoFocusCallback() {
        @Override
        public void onAutoFocus(boolean success, Camera camera) {
            if (success) {//對焦成功

                camera.takePicture(new Camera.ShutterCallback() {//按下快門
                    @Override
                    public void onShutter() {
                      //按下快門瞬間的操作
                    }
                }, new Camera.PictureCallback() {
                    @Override
                    public void onPictureTaken(byte[] data, Camera camera) {//是否保存原始圖片的信息

                    }
                }, pictureCallback);
            }
        }
    };
    /**
     * 獲取圖片
     */
    Camera.PictureCallback pictureCallback = new Camera.PictureCallback() {
        @Override
        public void onPictureTaken(byte[] data, Camera camera) {
            final Bitmap resource = BitmapFactory.decodeByteArray(data, 0, data.length);
            if (resource == null) {
                Toast.makeText(CameraActivity.this, "拍照失敗", Toast.LENGTH_SHORT).show();
            }
            final Matrix matrix = new Matrix();
            matrix.setRotate(90);
            final Bitmap bitmap = Bitmap.createBitmap(resource, 0, 0, resource.getWidth(), resource.getHeight(), matrix, true);
            if (bitmap != null && iv_show != null && iv_show.getVisibility() == View.GONE) {
                mCamera.stopPreview();
                iv_show.setVisibility(View.VISIBLE);
                mSurfaceView.setVisibility(View.GONE);
                Toast.makeText(CameraActivity.this, "拍照", Toast.LENGTH_SHORT).show();
                iv_show.setImageBitmap(bitmap);
            }
        }
    };
}

權限:

<uses-permission android:name="android.permission.CAMERA" />

在獲得圖片后,想要顯示的效果是照片是豎直顯示, resource 顯示的卻是逆時針旋轉了 90° ,照片是橫著的,就使用 matrix.setRotate(90) 進行旋轉

2.Camera2

Camera2拍照示意圖

這里引用了管道的概念將安卓設備和攝像頭之間聯通起來,系統向攝像頭發送 Capture 請求,而攝像頭會返回 CameraMetadata。這一切建立在一個叫作 CameraCaptureSession 的會話中。

camera2中主要的類

  • CameraManaer 攝像頭管理器,用于檢測攝像頭,打開系統攝像頭,調用 CameraManager.getCameraCharacteristics(String) 可以獲取指定攝像頭的相關特性
  • CameraCharacteristics 攝像頭的特性
  • CameraDevice 攝像頭,類似 android.hardware.Camera 也就是 Camera1 的 Camera
  • CameraCaptureSession 這個對象控制攝像頭的預覽或者拍照, setRepeatingRequest() 開啟預覽, capture() 拍照, CameraCaptureSession 提供了StateCallback、CaptureCallback兩個接口來監聽 CameraCaptureSession 的創建和拍照過程。
  • CameraRequest和CameraRequest.Builder,預覽或者拍照時,都需要一個 CameraRequest 對象。CameraRequest表示一次捕獲請求,用來對z照片的各種參數設置,比如對焦模式、曝光模式等。CameraRequest.Builder用來生成CameraRequest對象。

2.1 簡單使用

使用的依然是 SurfaceView 來進行展示預覽

主要思路:

  1. 獲得攝像頭管理器 CameraManager mCameraManager , mCameraManager.openCamera() 來打開攝像頭
  2. 指定要打開的攝像頭,并創建 openCamera() 所需要的 CameraDevice.StateCallback stateCallback
  3. 在 CameraDevice.StateCallback stateCallback 中調用 takePreview() ,這個方法中,使用 CaptureRequest.Builder 創建預覽需要的 CameraRequest ,并初始化了 CameraCaptureSession ,最后調用了 setRepeatingRequest(previewRequest, null, childHandler) 進行了預覽
  4. 點擊屏幕,調用 takePicture() ,這個方法內,最終調用了 capture(mCaptureRequest, null, childHandler)
  5. 在 new ImageReader.OnImageAvailableListener(){} 回調方法中,將拍照拿到的圖片進行展示

代碼:

public class Camera2Activity extends AppCompatActivity implements View.OnClickListener {
    private static final SparseIntArray ORIENTATIONS = new SparseIntArray();

    ///為了使照片豎直顯示
    static {
        ORIENTATIONS.append(Surface.ROTATION_0, 90);
        ORIENTATIONS.append(Surface.ROTATION_90, 0);
        ORIENTATIONS.append(Surface.ROTATION_180, 270);
        ORIENTATIONS.append(Surface.ROTATION_270, 180);
    }

    private SurfaceView mSurfaceView;
    private SurfaceHolder mSurfaceHolder;
    private ImageView iv_show;
    private CameraManager mCameraManager;//攝像頭管理器
    private Handler childHandler, mainHandler;
    private String mCameraID;//攝像頭Id 0 為后  1 為前
    private ImageReader mImageReader;
    private CameraCaptureSession mCameraCaptureSession;
    private CameraDevice mCameraDevice;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_camera2);
        initVIew();
    }

    /**
     * 初始化
     */
    private void initVIew() {
        iv_show = (ImageView) findViewById(R.id.iv_show_camera2_activity);
        //mSurfaceView
        mSurfaceView = (SurfaceView) findViewById(R.id.surface_view_camera2_activity);
        mSurfaceView.setOnClickListener(this);
        mSurfaceHolder = mSurfaceView.getHolder();
        mSurfaceHolder.setKeepScreenOn(true);
        // mSurfaceView添加回調
        mSurfaceHolder.addCallback(new SurfaceHolder.Callback() {
            @Override
            public void surfaceCreated(SurfaceHolder holder) { //SurfaceView創建
                // 初始化Camera
                initCamera2();
            }

            @Override
            public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
            }

            @Override
            public void surfaceDestroyed(SurfaceHolder holder) { //SurfaceView銷毀
                // 釋放Camera資源  
                if (null != mCameraDevice) {    
                    mCameraDevice.close(); 
                    Camera2Activity.this.mCameraDevice = null;
                }
            }
        });
    }

    /**
     * 初始化Camera2
     */
    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    private void initCamera2() {
        HandlerThread handlerThread = new HandlerThread("Camera2");
        handlerThread.start();
        childHandler = new Handler(handlerThread.getLooper());
        mainHandler = new Handler(getMainLooper());
        mCameraID = "" + CameraCharacteristics.LENS_FACING_FRONT;//后攝像頭
        mImageReader = ImageReader.newInstance(1080, 1920, ImageFormat.JPEG,1);
        mImageReader.setOnImageAvailableListener(new ImageReader.OnImageAvailableListener() { //可以在這里處理拍照得到的臨時照片 例如,寫入本地
            @Override
            public void onImageAvailable(ImageReader reader) {
                mCameraDevice.close();
                mSurfaceView.setVisibility(View.GONE);
                iv_show.setVisibility(View.VISIBLE);
                // 拿到拍照照片數據
                Image image = reader.acquireNextImage();
                ByteBuffer buffer = image.getPlanes()[0].getBuffer();
                byte[] bytes = new byte[buffer.remaining()];
                buffer.get(bytes);//由緩沖區存入字節數組
                final Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
                if (bitmap != null) {
                    iv_show.setImageBitmap(bitmap);
                }
            }
        }, mainHandler);
        //獲取攝像頭管理
        mCameraManager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);
        try {
            if (ActivityCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
                return;
            }
            //打開攝像頭
            mCameraManager.openCamera(mCameraID, stateCallback, mainHandler);
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }
    }


    /**
     * 攝像頭創建監聽
     */
    private CameraDevice.StateCallback stateCallback = new CameraDevice.StateCallback() {
        @Override
        public void onOpened(CameraDevice camera) {//打開攝像頭
            mCameraDevice = camera;
            //開啟預覽
            takePreview();
        }

        @Override
        public void onDisconnected(CameraDevice camera) {//關閉攝像頭
            if (null != mCameraDevice) {
                mCameraDevice.close();
                Camera2Activity.this.mCameraDevice = null;
            }
        }

        @Override
        public void onError(CameraDevice camera, int error) {//發生錯誤
            Toast.makeText(Camera2Activity.this, "攝像頭開啟失敗", Toast.LENGTH_SHORT).show();
        }
    };

    /**
     * 開始預覽
     */
    private void takePreview() {
        try {
            // 創建預覽需要的CaptureRequest.Builder
            final CaptureRequest.Builder previewRequestBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
            // 將SurfaceView的surface作為CaptureRequest.Builder的目標
            previewRequestBuilder.addTarget(mSurfaceHolder.getSurface());
            // 創建CameraCaptureSession,該對象負責管理處理預覽請求和拍照請求
            mCameraDevice.createCaptureSession(Arrays.asList(mSurfaceHolder.getSurface(), mImageReader.getSurface()), new CameraCaptureSession.StateCallback() // ③
            {
                @Override
                public void onConfigured(CameraCaptureSession cameraCaptureSession) {
                    if (null == mCameraDevice) return;
                    // 當攝像頭已經準備好時,開始顯示預覽
                    mCameraCaptureSession = cameraCaptureSession;
                    try {
                        // 自動對焦
                        previewRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
                        // 打開閃光燈
                        previewRequestBuilder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);
                        // 顯示預覽
                        CaptureRequest previewRequest = previewRequestBuilder.build();
                        mCameraCaptureSession.setRepeatingRequest(previewRequest, null, childHandler);
                    } catch (CameraAccessException e) {
                        e.printStackTrace();
                    }
                }

                @Override
                public void onConfigureFailed(CameraCaptureSession cameraCaptureSession) {
                    Toast.makeText(Camera2Activity.this, "配置失敗", Toast.LENGTH_SHORT).show();
                }
            }, childHandler);
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }
    }

    /**
     * 點擊事件
     */
    @Override
    public void onClick(View v) {
        takePicture();
    }

    /**
     * 拍照
     */
    private void takePicture() {
        if (mCameraDevice == null) return;
       // 創建拍照需要的CaptureRequest.Builder
        final CaptureRequest.Builder captureRequestBuilder;
        try {
            captureRequestBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
            // 將imageReader的surface作為CaptureRequest.Builder的目標
            captureRequestBuilder.addTarget(mImageReader.getSurface());
            // 自動對焦
            captureRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
            // 自動曝光
            captureRequestBuilder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);
            // 獲取手機方向
            int rotation = getWindowManager().getDefaultDisplay().getRotation();
            // 根據設備方向計算設置照片的方向
            captureRequestBuilder.set(CaptureRequest.JPEG_ORIENTATION, ORIENTATIONS.get(rotation));
            //拍照
            CaptureRequest mCaptureRequest = captureRequestBuilder.build();
            mCameraCaptureSession.capture(mCaptureRequest, null, childHandler);
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }
    }
}

布局代碼以及權限與 Camera1 中一樣,效果一樣

預覽時,是將 mSurfaceHolder.getSurface() 作為目標

顯示拍照結果時,是將 mImageReader.getSurface() 作為目標

3.最后

Camera2 的功能很強大,暫時也只是學習了最基本的思路

 

 

 

來自:http://www.jianshu.com/p/7f766eb2f4e7

 

 本文由用戶 dingyou110 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!