前言
上一篇文章介紹了如何調用系統相機進行拍照裁剪等功能,一般情況下這些已經能滿足我們的需求了。但是在有些場景和特殊需求下,比如要進行人臉檢測、要不間斷地抓取多張照片等等,那就需要使用原生Camera來進行開發啦
這里并不打算講如何用代碼去實現,而是先給小伙們介紹相關的知識點,等對這些知識有了大致了解后在動手去寫,這樣既能有目的的去寫又能加深對知識點的理解
本篇文章主要給大家講解進行Camera開發需要用到的類和方法,以及在開發過程中遇到的方向問題的分析
進行Camra開發主要用到了以下兩個類:
- Camera
- 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小結
- SurfaceView是一個view對象,用于在屏幕上顯示相機的預覽畫面
- SurfaceView中有兩個對象,Surface和SuraceHolder。 我們通過SuraceHolder中的回調可以知道Surface的狀態(創建、變化、銷毀)
- 通過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的話,首先要了解以下幾點:
- 相機圖像數據都是來自于相機硬件的圖像傳感器(Image Sensor),這個Sensor被固定到手機之后是有一個默認的取景方向,且不會改變
- 相機在預覽的時候是有一個預覽方向的,可以通過setDisplayOrientation()設置
- 相機所采集的照片也是有一個方向的(就是上面剛剛提到的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 到100leftEye
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對象。如果當前設備沒有后置攝像頭,則返回值為nullopen(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()
釋放相機
三、總結
Camera負責采集數據和各種操作,SurfaceView負責把Camera采集到的數據實時地顯示在屏幕上
我們通過SuraceHolder中的回調可以監聽Surface的狀態(創建、變化、銷毀)
相機圖像數據都是來自于相機硬件的圖像傳感器這個方向是不能改變的
相機在預覽的時候是有一個預覽方向的,可以通過setDisplayOrientation()設置
前置攝像頭在進行角度旋轉之前,圖像會進行一個水平的鏡像翻轉,所以用戶在看預覽圖像的時候就像照鏡子一樣
相機所采集的照片也是有一個方向的,這個方向與預覽時的方向互不相干
我們可以通過setParameters(Parameters params)設置當前相機的參數信息,比如 保存的圖片大小,對焦模式等等
在關閉頁面 或者 打開其他攝像頭之前,一定要先調用release()方法釋放當前相機資源
好,到這里關于Camera開發所需要的知識點已經介紹完了??戳诉@么多枯燥的知識點是不是已經雙手癢癢,迫不及待動手擼代碼了?在下篇文章中,我將會帶著小伙們一塊,從零開始實現自己的Camera相機!