1.Android sdk中的Camera
在Android Studio中敲下Camera,會給出兩個提示類:
- android.graphics.Camera ,一個照相機實例可以被用于計算3D變換,生成一個可以被使用的Matrix矩陣,一個實例,用在畫布上。
- android.hardware.Camera,用于設(shè)置圖像捕獲設(shè)置、開始/停止預(yù)覽、快照圖片以及檢索用于視頻編碼的幀。這個類是相機服務(wù)的客戶端,它管理實際的相機硬件。
今天要講的是相機管理類android.hardware.Camera,但在API 21+之后該類已被廢棄,新的相機管理類在 android.hardware.camera2 包下,所以本文會覆蓋camera和camera2兩種API。
2.Camera開發(fā)中遇到的一些疑問
- 拍攝的照片為什么會旋轉(zhuǎn)90度
- 預(yù)覽的圖像被拉伸
- 預(yù)覽時返回的圖像幀數(shù)據(jù)為什么不能正常解析成bitmap
針對以上的問題我們從camera成像說起,camera其實應(yīng)該叫image sensor。
image sensor有兩大類CMOS和CCD,手機中常常使用的是價格低廉和整合性高的CMOS,sensor的成像原理是景物通過鏡頭(LENS)生成的光學(xué)圖像投射到圖像傳感器(Sensor)表面上,然后轉(zhuǎn)為模擬的電信號,經(jīng)過A/D(模數(shù)轉(zhuǎn)換)轉(zhuǎn)換后變?yōu)閿?shù)字圖像信號,再送到數(shù)字信號處理芯片(DSP)中加工處理,再通過IO接口傳輸?shù)紺PU中處理,通過LCD 就可以看到圖像了。
sensor一般的輸出格式:
1)YUV sensor
YUV sensor輸出的Data格式為YUV,也是使用android.hardware.Camera進行預(yù)覽返回預(yù)覽幀的常見格式。
2)Raw sensor
Raw Sensor輸出的Data格式為原始未經(jīng)處理的Raw,圖像感應(yīng)器將捕捉到的光源信號轉(zhuǎn)化為數(shù)字信號的原始數(shù)據(jù)。RAW文件是一種記錄了數(shù)碼相機傳感器的原始信息,同時記錄了由相機拍攝所產(chǎn)生的一些原數(shù)據(jù)(如ISO的設(shè)置、快門速度、光圈值、白平衡等)的文件,在camera2中可查看相機是否支持raw格式并獲取raw數(shù)據(jù)。
從sensor輸出格式可以知道預(yù)覽時的幀數(shù)據(jù)為什么不能簡單生成bitmap,因為bitmap創(chuàng)建需要的是argb格式,而預(yù)覽幀數(shù)據(jù)一般是YUV格式。
image sensor的成像掃描方向是固定的,但image sensor安裝在手機的成像方向不一定和手機自然方向(一般為豎屏)一致

上圖是常見的后置攝像頭與手機自然方向的對比,image sensor 順時針旋轉(zhuǎn)90度就和手機自然方向一致,這就是為什么我們要給camera設(shè)置90度才能豎屏預(yù)覽。
但90度不是定勢,所以實際編碼中可以獲取image sensor的方向和phone display的方向進行計算得出正確的旋轉(zhuǎn)值。
來看看Android官方給出的建議計算方案

值得注意的是,以上操作只是讓預(yù)覽中顯示的圖像正常,對于預(yù)覽返回幀數(shù)據(jù)和拍照取得的數(shù)據(jù)并不會真正改變其方向,如果要得到和預(yù)覽圖像一致的照片還需要對圖像數(shù)據(jù)進行額外的旋轉(zhuǎn)操作,但如果只需要拍照圖像,則不需要對數(shù)據(jù)進行旋轉(zhuǎn),使用camera.getParameters().setRotation(rotation)可以直接得到正確方向的jpeg圖像,對于rotation值的計算官方給出的算法在setRotation方法注釋中,要特別注意的是這個值只改變jpeg方向,并不能改變預(yù)覽幀YUV數(shù)據(jù)方向,在camera2中的使用會更直觀一點,后面再講。
預(yù)覽時圖像顯示為什么被拉伸? 這和image sensor返回的預(yù)覽圖像大小有關(guān),只有預(yù)覽圖的大小和顯示預(yù)覽視圖大小相近相等時才最自然。
一臺手機image sensor支持的屬性設(shè)置都是有固定值的,絕大多數(shù)時候拍攝的圖片好不好看取決于image sensor的硬件成本,成像不好,再如何p圖也枉然。
3.android.hardware.Camera實例

以上通過SurfaceTexture+GLSurfaceView進行六種不同濾鏡效果預(yù)覽。關(guān)于SurfaceView、TextureView、GLSurfaceView、SurfaceTexture的區(qū)別可以看這篇文章。
通常大家拍照只要點擊一下就好,但是現(xiàn)在的手機越來越貴,硬件越來越好有些可以媲美相機效果,但是喜歡攝影的人都喜歡自己控制相機設(shè)置不同的效果進行拍照,一鍵拍照顯得有點傻瓜式了,拍出來的效果都是千遍一律,隨著手機硬件的提升,手機拍照的可控性就越來越專業(yè)。
一鍵拍照為什么拍的還可以呢?對于軟件層面來說其實關(guān)系不大,隨便寫的Camera的小demo拍出的照片一樣好看,這是因為Camera默認情況下使用的是3A模式。
3A就是Auto Exposure(AE 自動曝光) 、Auto Focus(AF自動對焦) 、Auto White Balance(AWB自動白平衡),一個合格的image sensor默認條件下的成像就是這樣。
如何調(diào)節(jié)image sensor參數(shù)拍出不同的效果,還需要了解手機上的image sensor支持什么樣的參數(shù),對于image sensor來說接收的參數(shù)非常嚴格,對于不支持的參數(shù)會直接拋出異常。
下面是榮耀8支持的可調(diào)節(jié)參數(shù)值:

不同手機支持參數(shù)不一樣,對于預(yù)覽大小來說尤其重要,設(shè)置的不合理,那么我們看見的圖像就可能被拉伸,所以在設(shè)置預(yù)覽大小之前,必須計算出和當前顯示畫面大小高寬比例最接近且高考值最相近的預(yù)覽Size。
懂攝影的經(jīng)常說這張照片曝光過度,這張照片曝光不足,專業(yè)照相機都有兩個按鍵+EV/-EV,就是通過加/減曝光補償來彌補當前環(huán)境在曝光的缺陷。
在榮耀8上最大和最小的曝光補償如下圖:

從上圖可見不同的曝光補償值對于成像來說可見的細節(jié)不一樣。
如何使用android.hardware.Camera進行預(yù)覽拍照相信大家都知道,這樣的文章很多,這里不贅述,但是對于預(yù)覽幀的返回數(shù)據(jù)相信大家還是有點疑問的,這里再講一講YUV。
什么是YUV?
YUV,分為三個分量,“Y”表示明亮度(Luminance或Luma),也就是灰度值;而“U”和“V” 表示的則是色度(Chrominance或Chroma),作用是描述影像色彩及飽和度,用于指定像素的顏色。
與我們熟知的RGB類似,YUV也是一種顏色編碼方法,主要用于電視系統(tǒng)以及模擬視頻領(lǐng)域,它將亮度信息(Y)與色彩信息(UV)分離,沒有UV信息一樣可以顯示完整的圖像,只不過是黑白的,這樣的設(shè)計很好地解決了彩色電視機與黑白電視的兼容問題。并且,YUV不像RGB那樣要求三個獨立的視頻信號同時傳輸,所以用YUV方式傳送占用極少的頻寬。
YUV碼流的存儲格式其實與其采樣的方式密切相關(guān),主流的采樣方式有三種,YUV4:4:4,YUV4:2:2,YUV4:2:0,關(guān)于其詳細原理,可以通過網(wǎng)上其它文章了解,這里我想強調(diào)的是如何根據(jù)其采樣格式來從碼流中還原每個像素點的YUV值,因為只有正確地還原了每個像素點的YUV值,才能通過YUV與RGB的轉(zhuǎn)換公式提取出每個像素點的RGB值,然后顯示出來。
用三個圖來直觀地表示采集的方式吧,以黑點表示采樣該像素點的Y分量,以空心圓圈表示采用該像素點的UV分量。

先記住下面這段話,以后提取每個像素的YUV分量會用到。
YUV 4:4:4采樣,每一個Y對應(yīng)一組UV分量。
YUV 4:2:2采樣,每兩個Y共用一組UV分量。
YUV 4:2:0采樣,每四個Y共用一組UV分量。
YUV格式有兩大類:planar和packed。
對于planar的YUV格式,先連續(xù)存儲所有像素點的Y,緊接著存儲所有像素點的U,隨后是所有像素點的V。
對于packed的YUV格式,每個像素點的Y,U,V是連續(xù)交替存儲的。
對于預(yù)覽幀來說在Android API <=20的版本中返回的YUV格式通常只支持兩種格式ImageFormat.NV21和ImageFormat.YV12,它們的區(qū)別就是存儲方式不一樣。
通過camera.getParameters().getPreviewFormat();可以獲取預(yù)覽幀的數(shù)據(jù)格式,一般來說返回的是ImageFormat.NV21
通過camera.getParameters().getSupportedPreviewFormats();可以獲取支持的預(yù)覽幀數(shù)據(jù)格式列表,通過camera.getParameters().setPreviewFormat(ImageFormat.YV12);可以設(shè)置預(yù)覽幀格式,
不同手機返回支持格式可能不同,在榮耀8上支持ImageFormat.NV21和ImageFormat.YV12兩種。
一般來說不需要開發(fā)者自己編寫YUV轉(zhuǎn)RGB,android.graphics.YuvImage類支持ImageFormat.NV21和ImageFormat.YUY2轉(zhuǎn)換成jpeg格式,但通過了解YUV格式的采樣規(guī)則、存儲格式、轉(zhuǎn)成RGB轉(zhuǎn)換公式,可以很方便的編寫轉(zhuǎn)換算法。
轉(zhuǎn)換公式可參考微軟信息網(wǎng)站。
4.camera2實例
最近看見摩托羅拉發(fā)布的z3手機中相機支持微動攝影的文章,還是很有意思的一個功能,因為camera2預(yù)覽返回的數(shù)據(jù)處理起來比較方便,就寫了一個類似微動攝影的demo
![]() BrowserPreview_tmp.gif
|
![]() 截圖 (1) (2).png
|
|---|
camera2相比camera1對應(yīng)用程序來說提供了更全面的API,提供了更接近于底層相機的功能,重新設(shè)計的API更大的提高了對相機子系統(tǒng)的控制能力
在Android 8.0以下,camera1和camera2的相機架構(gòu)

camera1中CameraService運行在mediaserver進程中,雖然camera2在5.0就存在,但CameraServide依然存在于mediaserver進程中,在Android7.0+系統(tǒng)中CameraService才作為一個獨立的系統(tǒng)服務(wù)進程存在。
看下應(yīng)用層的使用流程。
使用camera2進行預(yù)覽拍照最好的例子可查看官方demo。
先看兩個最關(guān)鍵的問題
1、怎么設(shè)置預(yù)覽/拍照圖像參數(shù)?
2、從哪里拿預(yù)覽/拍照的圖像數(shù)據(jù)?
第一點,camera1的參數(shù)設(shè)置都在camera.getParameters()中,camera2在哪里呢?在CaptureRequest.Builder中,比如設(shè)置對焦和曝光模式:

前面講過camera的3A模式,這里看參數(shù)命名就直接明了了,更多參數(shù)詳解可以看3A 模式和狀態(tài)轉(zhuǎn)換。
另外值得注意的是camera2相比camera1支持的數(shù)據(jù)格式、預(yù)覽大小等都有很大的差異,比如在榮耀8上使用camera1進行預(yù)覽獲取的最大預(yù)覽大小是1920x1080, 拍照支持最大size是3968x2240, 使用camera2的最大預(yù)覽尺寸為3968x2240,拍照最大支持3968x2240,camera2可支持的預(yù)覽size更加豐富,使用camera2獲取的Size根據(jù)格式的不同返回不同, 如果不支持輸出的格式則返回null,使用方式如下;

第二點,camera2中預(yù)覽和拍照都沒有直接的圖像數(shù)據(jù)回調(diào),它引入了ImageReader類,在createCaptureSession時可傳入surface數(shù)組入?yún)?,通過CaptureRequest.Builder.addTarget添加多個surface接收圖像數(shù)據(jù):
SurfaceView、TextureView等呈現(xiàn)的surface數(shù)據(jù)不能被直接訪問,而ImageReader類允許應(yīng)用程序直接訪問呈現(xiàn)到surface的圖像數(shù)據(jù),使用方式如下:

在camera1中支持預(yù)覽返回格式有ImageFormat.NV21和ImageFormat.YV12,但是camera2中通常僅開放ImageFormat.JPEG、ImageFormat.RAW_SENSOR和ImageFormat.YUV_420_888,其他格式通常是私有的,不可讀取,尤其不支持ImageFormat.NV21。
本例的實現(xiàn)就是通過鎖定對焦,預(yù)覽計時3秒,從ImageReader的OnImageAvailableListener中獲取可用Image放入list,解析list生成bitmap,生成原始GIF,自定義view創(chuàng)建畫布,獲得畫布上用戶選取繪制像素點,根據(jù)像素點列表,獲取第一幀之后的每一幀相關(guān)像素,循環(huán)替換第一幀選取區(qū)域像素點,生成微動gif,核心代碼如下:

5.總結(jié)
從camera2和camera1的使用體驗來說,新的api帶來了更多的可能性,但廠商定制手機五花八門,同樣版本的手機也不一定都支持同樣的功能,所以做相機開發(fā)需要考慮到相當多的 兼容特性。
從Android API出發(fā),需要考慮camera使用版本、預(yù)覽view使用類,從設(shè)備出發(fā),需要考慮相機支持特性、支持數(shù)據(jù)格式等,谷歌官方給出的一份兼容方案如下:

參考資料:
http://lib.csdn.net/article/embeddeddevelopment/67528
http://www.cnblogs.com/azraelly/archive/2013/01/01/2841269.htm

