前言
上一篇文章介紹了如何調(diào)用系統(tǒng)相機(jī)進(jìn)行拍照裁剪等功能,一般情況下這些已經(jīng)能滿足我們的需求了。但是在有些場景和特殊需求下爱态,比如要進(jìn)行人臉檢測(cè)谭贪、要不間斷地抓取多張照片等等,那就需要使用原生Camera來進(jìn)行開發(fā)啦
這里并不打算講如何用代碼去實(shí)現(xiàn)肢藐,而是先給小伙們介紹相關(guān)的知識(shí)點(diǎn)故河,等對(duì)這些知識(shí)有了大致了解后在動(dòng)手去寫吱韭,這樣既能有目的的去寫又能加深對(duì)知識(shí)點(diǎn)的理解
本篇文章主要給大家講解進(jìn)行Camera開發(fā)需要用到的類和方法吆豹,以及在開發(fā)過程中遇到的方向問題的分析
進(jìn)行Camra開發(fā)主要用到了以下兩個(gè)類:
- Camera
- SurfaceView (當(dāng)然也可以是TextureView,本文我們使用SurfaceView)
這兩者的關(guān)系如下圖:
一理盆、 SurfaceView 痘煤、Surface 、 SurfaceHolder
關(guān)系圖
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是用來處理屏幕顯示內(nèi)容合成器所管理的原始緩存區(qū)的工具姨俩。它通常由圖像緩沖區(qū)的消費(fèi)者來創(chuàng)建(如:SurfaceTexture蘸拔,MediaRecorder),然后被移交給生產(chǎn)者(如: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.
簡單翻譯一下:
一個(gè)抽象接口环葵,給持有surface的對(duì)象使用调窍。它可以控制surface的大小和格式,編輯surface中的像素张遭,以及監(jiān)聽surface的變化邓萨。這個(gè)接口通常通過SurfaceView類獲得
SurfaceHolder中有一個(gè)Callbcak接口,它有3個(gè)回調(diào)方法
surfaceCreated(SurfaceHolder holder)
surface第一次創(chuàng)建時(shí)回調(diào)surfaceChanged(SurfaceHolder holder, int format, int width,
int height)
surface變化的時(shí)候回調(diào)(格式/大小)surfaceDestroyed(SurfaceHolder holder)
surface銷毀的時(shí)候回調(diào)
這個(gè)回調(diào)接口就是源碼中所提到的 “monitor changes to the surface”(監(jiān)聽surface的變化)菊卷,我們后面會(huì)用到
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提供了嵌入視圖層級(jí)中的專用surface缔恳。你可以控制surface的格式或大小。SurfaceView負(fù)責(zé)把surface顯示在屏幕的正確位置
SurfaceView繼承自View洁闰,其中有兩個(gè)成員變量歉甚,一個(gè)是Surface對(duì)象,一個(gè)是SuraceHolder對(duì)象扑眉。(請(qǐng)參考上面第二幅關(guān)系圖)
- SurfaceView把Surface顯示在屏幕上
- SurfaceView通過SuraceHolder告訴我們Surface的狀態(tài)(創(chuàng)建纸泄、變化、銷毀)
- 通過getHolder()方法獲得當(dāng)前SurfaceView的SuraceHolder對(duì)象襟雷,然后就可以對(duì)SuraceHolder對(duì)象添加回調(diào)來監(jiān)聽Surface的狀態(tài)
SurfaceView小結(jié)
- SurfaceView是一個(gè)view對(duì)象刃滓,用于在屏幕上顯示相機(jī)的預(yù)覽畫面
- SurfaceView中有兩個(gè)對(duì)象,Surface和SuraceHolder耸弄。 我們通過SuraceHolder中的回調(diào)可以知道Surface的狀態(tài)(創(chuàng)建咧虎、變化、銷毀)
- 通過getHolder()方法獲得當(dāng)前SurfaceView的SuraceHolder對(duì)象
二计呈、Camera
概覽圖
Camera類中主要的內(nèi)部類和接口砰诵,如下圖:
Camera類中有很多內(nèi)部類和方法,下面分別對(duì)這些類和方法做介紹:
Camera類中的內(nèi)部類
CameraInfo
CameraInfo類用來描述相機(jī)信息茁彭,通過Camera類中g(shù)etCameraInfo(int cameraId, CameraInfo cameraInfo)方法獲得总寒,主要包括以下兩個(gè)成員變量:
-
facing
facing 代表相機(jī)的方向,它的值只能是CAMERA_FACING_BACK(后置攝像頭) 或者CAMERA_FACING_FRONT(前置攝像頭)理肺。
CAMERA_FACING_BACK 和 CAMERA_FACING_FRONT 是CameraInfo類中的靜態(tài)變量
-
orientation
這個(gè)就比較有意思了摄闸,也比較重要,源碼中是這樣描述的:
* <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是相機(jī)采集圖片的角度年枕。這個(gè)值是相機(jī)所采集的圖片需要順時(shí)針旋轉(zhuǎn)至自然方向的角度值。它必須是0乎完,90,180或270中的一個(gè)熏兄。
舉個(gè)栗子:
假如你自然地豎著拿著手機(jī)(就是自拍時(shí)候的樣子...),后置攝像頭的傳感器在手機(jī)里是水平方向的树姨,你現(xiàn)在看著手機(jī)摩桶,如果傳感器的頂部在自然方向上手機(jī)屏幕的右邊(此時(shí),手機(jī)是豎屏帽揪,傳感器是橫屏)硝清,那么這個(gè)orientation的值就是90。 如果前置攝像頭的傳感器頂部在手機(jī)屏幕右邊台丛,那么這個(gè)值就是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.
翻譯一下:
設(shè)置預(yù)覽畫面順時(shí)針旋轉(zhuǎn)的角度耍缴。這個(gè)方法會(huì)影響預(yù)覽圖像和拍照后顯示的照片。這個(gè)方法對(duì)豎屏應(yīng)用非常有用挽霉。
注意防嗡,前置攝像頭在進(jìn)行角度旋轉(zhuǎn)之前,圖像會(huì)進(jìn)行一個(gè)水平的鏡像翻轉(zhuǎn)侠坎。
所以用戶在看預(yù)覽圖像的時(shí)候就像照鏡子蚁趁,看到的是現(xiàn)實(shí)的水平方向的鏡像。
注:setDisplayOrientation(int degrees)是Camea類中的一個(gè)方法实胸,之所以穿插在這里來講他嫡,是為了和上面提到的orientation做一個(gè)統(tǒng)一講解,因?yàn)檫@兩個(gè)都涉及到了方向問題
看了上面的介紹是不是有點(diǎn)懵逼庐完。钢属。。沒關(guān)系门躯,下面給小伙伴們?cè)敿?xì)介紹一下Android手機(jī)上幾個(gè)方向的概念以及在使用Camera過程中會(huì)遇到的方向問題:
注:如果你是第一次使用Camera的話淆党,首先要了解以下幾點(diǎn):
- 相機(jī)圖像數(shù)據(jù)都是來自于相機(jī)硬件的圖像傳感器(Image Sensor),這個(gè)Sensor被固定到手機(jī)之后是有一個(gè)默認(rèn)的取景方向,且不會(huì)改變
- 相機(jī)在預(yù)覽的時(shí)候是有一個(gè)預(yù)覽方向的染乌,可以通過setDisplayOrientation()設(shè)置
- 相機(jī)所采集的照片也是有一個(gè)方向的(就是上面剛剛提到的orientation)山孔,這個(gè)方向與預(yù)覽時(shí)的方向互不相干
屏幕坐標(biāo): 在Android系統(tǒng)中,屏幕的左上角是坐標(biāo)系統(tǒng)的原點(diǎn)(0,0)坐標(biāo)荷憋。原點(diǎn)向右延伸是X軸正方向台颠,原點(diǎn)向下延伸是Y軸正方向
自然方向:每個(gè)設(shè)備都有一個(gè)自然方向,手機(jī)和平板的自然方向不同勒庄。手機(jī)的自然方向是portrait(豎屏)串前,平板的自然方向是landscape(橫屏)
圖像傳感器(Image Sensor)方向:手機(jī)相機(jī)的圖像數(shù)據(jù)都是來自于攝像頭硬件的圖像傳感器,這個(gè)傳感器在被固定到手機(jī)上后有一個(gè)默認(rèn)的取景方向
- 相機(jī)的預(yù)覽方向:將圖像傳感器捕獲的圖像酪呻,顯示在屏幕上的方向。在默認(rèn)情況下盐须,與圖像傳感器方向一致。在相機(jī)API中可以通過setDisplayOrientation()設(shè)置相機(jī)預(yù)覽方向漆腌。在默認(rèn)情況下贼邓,這個(gè)值為0,與圖像傳感器方向一致
-
相機(jī)采集的圖像方向
相機(jī)采集圖像后需要進(jìn)行順時(shí)針旋轉(zhuǎn)的角度塑径,即上面介紹的orientation的值:
這里先做個(gè)小結(jié):
絕大部分安卓手機(jī)中圖像傳感器方向是橫向的填具,且不能改變统舀,所以orientation是90或是270,也就是說劳景,當(dāng)點(diǎn)擊拍照后保存圖片的時(shí)候誉简,需要對(duì)圖片做旋轉(zhuǎn)處理,使其為"自然方向"盟广。 (可能存在一些特殊的定制或是能外接攝像頭的安卓機(jī)闷串,他們的orientation會(huì)是0或者180)
通過setDisplayOrientation方法設(shè)置預(yù)覽方向,使預(yù)覽畫面為"自然方向"筋量。前置攝像頭在進(jìn)行角度旋轉(zhuǎn)之前烹吵,圖像會(huì)進(jìn)行一個(gè)水平的鏡像翻轉(zhuǎn),所以用戶在看預(yù)覽圖像的時(shí)候就像照鏡子一樣桨武。
這些都是些理論肋拔,在下篇文章中,會(huì)通過實(shí)例代碼一步步地驗(yàn)證呀酸。
好凉蜂,我們接著看Camera類中的內(nèi)部類:
Size
圖片大小,里面包含兩個(gè)變量:width和height(圖片的寬和高)
Parameters
Parameters是相機(jī)服務(wù)設(shè)置,不同的相機(jī)可能是不相同的跃惫。比如相機(jī)所支持的圖片大小叮叹,對(duì)焦模式等等。下面介紹一下這個(gè)類中常用的方法
getSupportedPreviewSizes()
獲得相機(jī)支持的預(yù)覽圖片大小爆存,返回值是一個(gè)List<Size>數(shù)組setPreviewSize(int width, int height)
設(shè)置相機(jī)預(yù)覽圖片的大小getSupportedPreviewFormats()
獲得相機(jī)支持的圖片預(yù)覽格式蛉顽,所有的相機(jī)都支持ImageFormat.NV21
更多的圖片格式可以自行百度或是查看ImageFormat類setPreviewFormat(int pixel_format)
設(shè)置預(yù)覽圖片的格式getSupportedPictureSizes()
獲得相機(jī)支持的采集的圖片大小(即拍照后保存的圖片的大小)setPictureSize(int width, int height)
設(shè)置保存的圖片的大小getSupportedPictureFormats()
獲得相機(jī)支持的圖片格式setPictureFormat(int pixel_format)
設(shè)置保存的圖片的格式getSupportedFocusModes()
獲得相機(jī)支持的對(duì)焦模式setFocusMode(String value)
設(shè)置相機(jī)的對(duì)焦模式getMaxNumDetectedFaces()
返回當(dāng)前相機(jī)所支持的最大的人臉檢測(cè)個(gè)數(shù)
比如,我的手機(jī)是vivo x9先较,后置攝像頭所支持最大的人臉檢測(cè)個(gè)數(shù)是10携冤,也就是說當(dāng)畫面中人臉數(shù)超過10個(gè)的時(shí)候,只能檢測(cè)到10張人臉
PreviewCallback
PreviewCallback是一個(gè)抽象接口
- void onPreviewFrame(byte[] data, Camera camera)
通過onPreviewFrame方法來獲取到相機(jī)預(yù)覽的數(shù)據(jù)闲勺,第一個(gè)參數(shù)data曾棕,就是相機(jī)預(yù)覽到的原始數(shù)據(jù)。
這些預(yù)覽到的原始數(shù)據(jù)是非常有用的菜循,比如我們可以保存下來當(dāng)做一張照片翘地,還有很多第三方的人臉檢測(cè)及靜默活體檢測(cè)的sdk,都需要我們把相機(jī)預(yù)覽的數(shù)據(jù)實(shí)時(shí)地傳遞過去癌幕。
Face
Face類用來描述通過Camera的人臉檢測(cè)功能檢測(cè)到的人臉信息
- rect
rect 是一個(gè)Rect對(duì)象衙耕,它所表示的就是檢測(cè)到的人臉的區(qū)域。
注意:這個(gè)Rect對(duì)象中的坐標(biāo)系并不是安卓屏幕的坐標(biāo)系勺远,需要進(jìn)行轉(zhuǎn)換后才能使用橙喘,具體會(huì)在后面實(shí)現(xiàn)人臉檢測(cè)功能的時(shí)候詳細(xì)介紹
score
檢測(cè)到的人臉的可信度,范圍是1 到100leftEye
leftEye 是一個(gè)Point對(duì)象胶逢,表示檢測(cè)到的左眼的位置坐標(biāo)rightEye
rightEye是一個(gè)Point對(duì)象厅瞎,表示檢測(cè)到的右眼的位置坐標(biāo)mouth
mouth是一個(gè)Point對(duì)象,表示檢測(cè)到的嘴的位置坐標(biāo)
leftEye 初坠,rightEye和mouth這3個(gè)人臉中關(guān)鍵點(diǎn)和簸,并不是所有相機(jī)都支持的,如果相機(jī)不支持的話某筐,這3個(gè)的值為null
FaceDetectionListener
這是一個(gè)抽象接口比搭,當(dāng)開始人臉檢測(cè)時(shí)開始回調(diào)
- onFaceDetection(Face[] faces, Camera camera)
第一參數(shù)代表檢測(cè)到的人臉,是一個(gè)Face數(shù)組(畫面內(nèi)可能存在多張人臉)
Camera類中的方法
getNumberOfCameras()
返回當(dāng)前設(shè)備上可用的攝像頭個(gè)數(shù)open()
使用當(dāng)前設(shè)備上第一個(gè)后置攝像頭創(chuàng)建一個(gè)Camera對(duì)象南誊。如果當(dāng)前設(shè)備沒有后置攝像頭身诺,則返回值為nullopen(int cameraId)
使用傳入id所表示的攝像頭創(chuàng)建一個(gè)Camera對(duì)象,如果id所表示的攝像頭已經(jīng)被打開抄囚,則會(huì)拋出異常
設(shè)備上每一個(gè)物理攝像都是有一個(gè)id的霉赡,id從0開始,到getNumberOfCameras() - 1 結(jié)束
例如幔托,一般的手機(jī)上都有前后兩個(gè)攝像頭穴亏,那么后置攝像頭id就是0蜂挪,前置攝像頭id就是1
getCameraInfo(int cameraId, CameraInfo cameraInfo)
返回指定id所表示的攝像頭的信息setDisplayOrientation(int degrees)
設(shè)置相機(jī)預(yù)覽畫面旋轉(zhuǎn)的角度
上面已經(jīng)講過,見圖五setPreviewDisplay(SurfaceHolder holder)
設(shè)置一個(gè)Surface對(duì)象用來實(shí)時(shí)預(yù)覽
我們看一下源碼:
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;
可以看到 嗓化,這個(gè)方法調(diào)用了setPreviewSurface(Surface surface)棠涮,傳入的surface對(duì)象是holder.getSurface()
那么holder.getSurface()所表示的surface是什么呢?
在SurfaceView的源碼中(1088行)找到了答案:
@Override
public Surface getSurface() {
return mSurface;
}
holder.getSurface()所返回的surface對(duì)象就是SurfaceView中的mSurface對(duì)象! 這三者的關(guān)系見圖二
setPreviewCallback(PreviewCallback cb)
設(shè)置相機(jī)預(yù)覽數(shù)據(jù)的回調(diào)刺覆,參數(shù)是一個(gè)PreviewCallback對(duì)象严肪,上面在介紹內(nèi)部類的時(shí)候已講過getParameters()
返回當(dāng)前相機(jī)的參數(shù)信息,返回值是一個(gè)Parameters對(duì)象setParameters(Parameters params)
設(shè)置當(dāng)前相機(jī)的參數(shù)信息startPreview()
開始預(yù)覽
調(diào)用此方法之前谦屑,如果沒有setPreviewDisplay(SurfaceHolder) 或 setPreviewTexture(SurfaceTexture)的話驳糯,是沒有效果的stopPreview()
停止預(yù)覽startFaceDetection()
開始人臉檢測(cè)
這個(gè)方法必須在開始預(yù)覽之后調(diào)用stopFaceDetection()
停止人臉檢測(cè)setFaceDetectionListener(FaceDetectionListener listener)
設(shè)置人臉檢測(cè)監(jiān)聽回調(diào)release()
釋放相機(jī)
三、總結(jié)
Camera負(fù)責(zé)采集數(shù)據(jù)和各種操作氢橙,SurfaceView負(fù)責(zé)把Camera采集到的數(shù)據(jù)實(shí)時(shí)地顯示在屏幕上
我們通過SuraceHolder中的回調(diào)可以監(jiān)聽Surface的狀態(tài)(創(chuàng)建酝枢、變化、銷毀)
相機(jī)圖像數(shù)據(jù)都是來自于相機(jī)硬件的圖像傳感器這個(gè)方向是不能改變的
相機(jī)在預(yù)覽的時(shí)候是有一個(gè)預(yù)覽方向的悍手,可以通過setDisplayOrientation()設(shè)置
前置攝像頭在進(jìn)行角度旋轉(zhuǎn)之前帘睦,圖像會(huì)進(jìn)行一個(gè)水平的鏡像翻轉(zhuǎn),所以用戶在看預(yù)覽圖像的時(shí)候就像照鏡子一樣
相機(jī)所采集的照片也是有一個(gè)方向的谓苟,這個(gè)方向與預(yù)覽時(shí)的方向互不相干
我們可以通過setParameters(Parameters params)設(shè)置當(dāng)前相機(jī)的參數(shù)信息官脓,比如 保存的圖片大小,對(duì)焦模式等等
在關(guān)閉頁面 或者 打開其他攝像頭之前涝焙,一定要先調(diào)用release()方法釋放當(dāng)前相機(jī)資源
好,到這里關(guān)于Camera開發(fā)所需要的知識(shí)點(diǎn)已經(jīng)介紹完了孕暇÷刈玻看了這么多枯燥的知識(shí)點(diǎn)是不是已經(jīng)雙手癢癢,迫不及待動(dòng)手?jǐn)]代碼了妖滔?在下篇文章中隧哮,我將會(huì)帶著小伙們一塊,從零開始實(shí)現(xiàn)自己的Camera相機(jī)座舍!