Android: Camera相機開發詳解 前言

和你一起終身學習,這裏是程序員 Android

經典好文推薦,通過閱讀本文,您將收穫以下知識點:

一、 SurfaceView 、Surface 、 SurfaceHolder
二、Camera 概覽
三、總結

前言

  • 上一篇文章介紹瞭如何調用系統相機進行拍照裁剪等功能,一般情況下這些已經能滿足我們的需求了。但是在有些場景和特殊需求下,比如要進行人臉檢測、要不間斷地抓取多張照片等等,那就需要使用原生Camera來進行開發啦

  • 這裏並不打算講如何用代碼去實現,而是先給小夥們介紹相關的知識點,等對這些知識有了大致瞭解後在動手去寫,這樣既能有目的的去寫又能加深對知識點的理解

  • 本篇文章主要給大家講解進行Camera開發需要用到的類和方法,以及在開發過程中遇到的方向問題的分析


進行Camra開發主要用到了以下兩個類:

  1. Camera
  2. SurfaceView (當然也可以是TextureView,本文我們使用SurfaceView)

這兩者的關係如下圖:

一、 SurfaceView 、Surface 、 SurfaceHolder

關係圖

Surface

什麼是Surface?源碼中是這樣描述的:


 * Handle onto a raw buffer that is being managed by the screen compositor.
 *
 * <p>A Surface is generally created by or from a consumer of image buffers (such as a
 * {@link android.graphics.SurfaceTexture}, {@link android.media.MediaRecorder}, or
 * {@link android.renderscript.Allocation}), and is handed to some kind of producer (such as
 * {@link android.opengl.EGL14#eglCreateWindowSurface(android.opengl.EGLDisplay,android.opengl.EGLConfig,java.lang.Object,int[],int) OpenGL},
 * {@link android.media.MediaPlayer#setSurface MediaPlayer}, or
 * {@link android.hardware.camera2.CameraDevice#createCaptureSession CameraDevice}) to draw
 * into.</p>

簡單翻譯一下,大概意思就是:

Surfaces是用來處理屏幕顯示內容合成器所管理的原始緩存區的工具。它通常由圖像緩衝區的消費者來創建(如:SurfaceTexture,MediaRecorder),然後被移交給生產者(如:MediaPlayer)或者是顯示到其上(如:CameraDevice)

SurfaceHolder

源碼描述:

 * Abstract interface to someone holding a display surface.  Allows you to
 * control the surface size and format, edit the pixels in the surface, and
 * monitor changes to the surface.  This interface is typically available
 * through the {@link SurfaceView} class.

簡單翻譯一下:

一個抽象接口,給持有surface的對象使用。它可以控制surface的大小和格式,編輯surface中的像素,以及監聽surface的變化。這個接口通常通過SurfaceView類獲得

SurfaceHolder中有一個Callbcak接口,它有3個回調方法

  • surfaceCreated(SurfaceHolder holder)
    surface第一次創建時回調

  • surfaceChanged(SurfaceHolder holder, int format, int width,
    int height)
    surface變化的時候回調(格式/大小)

  • surfaceDestroyed(SurfaceHolder holder)
    surface銷燬的時候回調

這個回調接口就是源碼中所提到的 “monitor changes to the surface”(監聽surface的變化),我們後面會用到

SurfaceView

源碼描述:

 * Provides a dedicated drawing surface embedded inside of a view hierarchy.
 * You can control the format of this surface and, if you like, its size; the
 * SurfaceView takes care of placing the surface at the correct location on the
 * screen

大致翻譯一下:

SurfaceView提供了嵌入視圖層級中的專用surface。你可以控制surface的格式或大小。SurfaceView負責把surface顯示在屏幕的正確位置

SurfaceView繼承自View,其中有兩個成員變量,一個是Surface對象,一個是SuraceHolder對象。(請參考上面第二幅關係圖)

  • SurfaceView把Surface顯示在屏幕上
  • SurfaceView通過SuraceHolder告訴我們Surface的狀態(創建、變化、銷燬)
  • 通過getHolder()方法獲得當前SurfaceView的SuraceHolder對象,然後就可以對SuraceHolder對象添加回調來監聽Surface的狀態

SurfaceView小結

  1. SurfaceView是一個view對象,用於在屏幕上顯示相機的預覽畫面
  2. SurfaceView中有兩個對象,Surface和SuraceHolder。 我們通過SuraceHolder中的回調可以知道Surface的狀態(創建、變化、銷燬)
  3. 通過getHolder()方法獲得當前SurfaceView的SuraceHolder對象

二、Camera

概覽圖

Camera類中主要的內部類和接口,如下圖:

Camera類中有很多內部類和方法,下面分別對這些類和方法做介紹:

Camera類中的內部類

CameraInfo

CameraInfo類用來描述相機信息,通過Camera類中getCameraInfo(int cameraId, CameraInfo cameraInfo)方法獲得,主要包括以下兩個成員變量:

  • facing
    facing 代表相機的方向,它的值只能是CAMERA_FACING_BACK(後置攝像頭) 或者CAMERA_FACING_FRONT(前置攝像頭)。

CAMERA_FACING_BACK 和 CAMERA_FACING_FRONT 是CameraInfo類中的靜態變量

  • orientation
    這個就比較有意思了,也比較重要,源碼中是這樣描述的:
         * <p>The orientation of the camera image. The value is the angle that the
         * camera image needs to be rotated clockwise so it shows correctly on
         * the display in its natural orientation. It should be 0, 90, 180, or 270.</p>
         *
         * <p>For example, suppose a device has a naturally tall screen. The
         * back-facing camera sensor is mounted in landscape. You are looking at
         * the screen. If the top side of the camera sensor is aligned with the
         * right edge of the screen in natural orientation, the value should be
         * 90. If the top side of a front-facing camera sensor is aligned with
         * the right of the screen, the value should be 270.</p>

翻譯一下,大概意思是:

orientation是相機採集圖片的角度。這個值是相機所採集的圖片需要順時針旋轉至自然方向的角度值。它必須是0,90,180或270中的一個。
舉個栗子:
假如你自然地豎着拿着手機(就是自拍時候的樣子...),後置攝像頭的傳感器在手機裏是水平方向的,你現在看着手機,如果傳感器的頂部在自然方向上手機屏幕的右邊(此時,手機是豎屏,傳感器是橫屏),那麼這個orientation的值就是90。 如果前置攝像頭的傳感器頂部在手機屏幕右邊,那麼這個值就是270.

  • setDisplayOrientation
    源碼描述如下:
     * Set the clockwise rotation of preview display in degrees. This affects
     * the preview frames and the picture displayed after snapshot. This method
     * is useful for portrait mode applications. Note that preview display of
     * front-facing cameras is flipped horizontally before the rotation, that
     * is, the image is reflected along the central vertical axis of the camera
     * sensor. So the users can see themselves as looking into a mirror.

翻譯一下:

設置預覽畫面順時針旋轉的角度。這個方法會影響預覽圖像和拍照後顯示的照片。這個方法對豎屏應用非常有用。
注意,前置攝像頭在進行角度旋轉之前,圖像會進行一個水平的鏡像翻轉。
所以用戶在看預覽圖像的時候就像照鏡子,看到的是現實的水平方向的鏡像。

注:setDisplayOrientation(int degrees)是Camea類中的一個方法,之所以穿插在這裏來講,是爲了和上面提到的orientation做一個統一講解,因爲這兩個都涉及到了方向問題

看了上面的介紹是不是有點懵逼。。。沒關係,下面給小夥伴們詳細介紹一下Android手機上幾個方向的概念以及在使用Camera過程中會遇到的方向問題:

注:如果你是第一次使用Camera的話,首先要了解以下幾點:

  1. 相機圖像數據都是來自於相機硬件的圖像傳感器(Image Sensor),這個Sensor被固定到手機之後是有一個默認的取景方向,且不會改變
  2. 相機在預覽的時候是有一個預覽方向的,可以通過setDisplayOrientation()設置
  3. 相機所採集的照片也是有一個方向的(就是上面剛剛提到的orientation),這個方向與預覽時的方向互不相干
  • 屏幕座標: 在Android系統中,屏幕的左上角是座標系統的原點(0,0)座標。原點向右延伸是X軸正方向,原點向下延伸是Y軸正方向

  • 自然方向:每個設備都有一個自然方向,手機和平板的自然方向不同。手機的自然方向是portrait(豎屏),平板的自然方向是landscape(橫屏)

  • 圖像傳感器(Image Sensor)方向:手機相機的圖像數據都是來自於攝像頭硬件的圖像傳感器,這個傳感器在被固定到手機上後有一個默認的取景方向

  • 相機的預覽方向:將圖像傳感器捕獲的圖像,顯示在屏幕上的方向。在默認情況下,與圖像傳感器方向一致。在相機API中可以通過setDisplayOrientation()設置相機預覽方向。在默認情況下,這個值爲0,與圖像傳感器方向一致
  • 相機採集的圖像方向
    相機採集圖像後需要進行順時針旋轉的角度,即上面介紹的orientation的值:

這裏先做個小結:

  • 絕大部分安卓手機中圖像傳感器方向是橫向的,且不能改變,所以orientation是90或是270,也就是說,當點擊拍照後保存圖片的時候,需要對圖片做旋轉處理,使其爲"自然方向"。 (可能存在一些特殊的定製或是能外接攝像頭的安卓機,他們的orientation會是0或者180)

  • 通過setDisplayOrientation方法設置預覽方向,使預覽畫面爲"自然方向"。前置攝像頭在進行角度旋轉之前,圖像會進行一個水平的鏡像翻轉,所以用戶在看預覽圖像的時候就像照鏡子一樣。

這些都是些理論,在下篇文章中,會通過實例代碼一步步地驗證。

好,我們接着看Camera類中的內部類:

Size

圖片大小,裏面包含兩個變量:width和height(圖片的寬和高)

Parameters

Parameters是相機服務設置,不同的相機可能是不相同的。比如相機所支持的圖片大小,對焦模式等等。下面介紹一下這個類中常用的方法

  • getSupportedPreviewSizes()
    獲得相機支持的預覽圖片大小,返回值是一個List<Size>數組

  • setPreviewSize(int width, int height)
    設置相機預覽圖片的大小

  • getSupportedPreviewFormats()
    獲得相機支持的圖片預覽格式,所有的相機都支持ImageFormat.NV21
    更多的圖片格式可以自行百度或是查看ImageFormat類

  • setPreviewFormat(int pixel_format)
    設置預覽圖片的格式

  • getSupportedPictureSizes()
    獲得相機支持的採集的圖片大小(即拍照後保存的圖片的大小)

  • setPictureSize(int width, int height)
    設置保存的圖片的大小

  • getSupportedPictureFormats()
    獲得相機支持的圖片格式

  • setPictureFormat(int pixel_format)
    設置保存的圖片的格式

  • getSupportedFocusModes()
    獲得相機支持的對焦模式

  • setFocusMode(String value)
    設置相機的對焦模式

  • getMaxNumDetectedFaces()
    返回當前相機所支持的最大的人臉檢測個數
    比如,我的手機是vivo x9,後置攝像頭所支持最大的人臉檢測個數是10,也就是說當畫面中人臉數超過10個的時候,只能檢測到10張人臉

PreviewCallback

PreviewCallback是一個抽象接口

  • void onPreviewFrame(byte[] data, Camera camera)
    通過onPreviewFrame方法來獲取到相機預覽的數據,第一個參數data,就是相機預覽到的原始數據。

這些預覽到的原始數據是非常有用的,比如我們可以保存下來當做一張照片,還有很多第三方的人臉檢測及靜默活體檢測的sdk,都需要我們把相機預覽的數據實時地傳遞過去。

Face

Face類用來描述通過Camera的人臉檢測功能檢測到的人臉信息

  • rect
    rect 是一個Rect對象,它所表示的就是檢測到的人臉的區域。

注意:這個Rect對象中的座標系並不是安卓屏幕的座標系,需要進行轉換後才能使用,具體會在後面實現人臉檢測功能的時候詳細介紹

  • score
    檢測到的人臉的可信度,範圍是1 到100

  • leftEye
    leftEye 是一個Point對象,表示檢測到的左眼的位置座標

  • rightEye
    rightEye是一個Point對象,表示檢測到的右眼的位置座標

  • mouth
    mouth是一個Point對象,表示檢測到的嘴的位置座標

leftEye ,rightEye和mouth這3個人臉中關鍵點,並不是所有相機都支持的,如果相機不支持的話,這3個的值爲null

FaceDetectionListener

這是一個抽象接口,當開始人臉檢測時開始回調

  • onFaceDetection(Face[] faces, Camera camera)
    第一參數代表檢測到的人臉,是一個Face數組(畫面內可能存在多張人臉)

Camera類中的方法

  • getNumberOfCameras()
    返回當前設備上可用的攝像頭個數

  • open()
    使用當前設備上第一個後置攝像頭創建一個Camera對象。如果當前設備沒有後置攝像頭,則返回值爲null

  • open(int cameraId)
    使用傳入id所表示的攝像頭創建一個Camera對象,如果id所表示的攝像頭已經被打開,則會拋出異常

設備上每一個物理攝像都是有一個id的,id從0開始,到getNumberOfCameras() - 1 結束

例如,一般的手機上都有前後兩個攝像頭,那麼後置攝像頭id就是0,前置攝像頭id就是1

  • getCameraInfo(int cameraId, CameraInfo cameraInfo)
    返回指定id所表示的攝像頭的信息

  • setDisplayOrientation(int degrees)
    設置相機預覽畫面旋轉的角度
    上面已經講過,見圖五

  • setPreviewDisplay(SurfaceHolder holder)
    設置一個Surface對象用來實時預覽
    我們看一下源碼:

  public final void setPreviewDisplay(SurfaceHolder holder) throws IOException {
        if (holder != null) {
            setPreviewSurface(holder.getSurface());
        } else {
            setPreviewSurface((Surface)null);
        }
    }

 public native final void setPreviewSurface(Surface surface) throws IOException;

可以看到 ,這個方法調用了setPreviewSurface(Surface surface),傳入的surface對象是holder.getSurface()
那麼holder.getSurface()所表示的surface是什麼呢?
在SurfaceView的源碼中(1088行)找到了答案:

        @Override
        public Surface getSurface() {
            return mSurface;
        }

holder.getSurface()所返回的surface對象就是SurfaceView中的mSurface對象! 這三者的關係見圖二

  • setPreviewCallback(PreviewCallback cb)
    設置相機預覽數據的回調,參數是一個PreviewCallback對象,上面在介紹內部類的時候已講過

  • getParameters()
    返回當前相機的參數信息,返回值是一個Parameters對象

  • setParameters(Parameters params)
    設置當前相機的參數信息

  • startPreview()
    開始預覽
    調用此方法之前,如果沒有setPreviewDisplay(SurfaceHolder) 或 setPreviewTexture(SurfaceTexture)的話,是沒有效果的

  • stopPreview()
    停止預覽

  • startFaceDetection()
    開始人臉檢測
    這個方法必須在開始預覽之後調用

  • stopFaceDetection()
    停止人臉檢測

  • setFaceDetectionListener(FaceDetectionListener listener)
    設置人臉檢測監聽回調

  • release()
    釋放相機

三、總結

  1. Camera負責採集數據和各種操作,SurfaceView負責把Camera採集到的數據實時地顯示在屏幕上

  2. 我們通過SuraceHolder中的回調可以監聽Surface的狀態(創建、變化、銷燬)

  3. 相機圖像數據都是來自於相機硬件的圖像傳感器這個方向是不能改變的

  4. 相機在預覽的時候是有一個預覽方向的,可以通過setDisplayOrientation()設置

  5. 前置攝像頭在進行角度旋轉之前,圖像會進行一個水平的鏡像翻轉,所以用戶在看預覽圖像的時候就像照鏡子一樣

  6. 相機所採集的照片也是有一個方向的,這個方向與預覽時的方向互不相干

  7. 我們可以通過setParameters(Parameters params)設置當前相機的參數信息,比如 保存的圖片大小,對焦模式等等

  8. 在關閉頁面 或者 打開其他攝像頭之前,一定要先調用release()方法釋放當前相機資源

好,到這裏關於Camera開發所需要的知識點已經介紹完了。看了這麼多枯燥的知識點是不是已經雙手癢癢,迫不及待動手擼代碼了?在下篇文章中,我將會帶着小夥們一塊,從零開始實現自己的Camera相機!

鏈接:https://www.jianshu.com/p/f8d0d1467584
至此,本篇已結束。轉載網絡的文章,小編覺得很優秀,歡迎點擊閱讀原文,支持原創作者,如有侵權,懇請聯繫小編刪除,歡迎您的建議與指正。同時期待您的關注,感謝您的閱讀,謝謝!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章