2013年8月22日 星期四

CCD:高品質、高價格、高耗電的感光元件

來源: T客邦
作者: Dr. J

CCD和CMOS相同,是數位相機等影像產品常使用的感光元件。不過不同於CMOS應用面較為廣泛,CCD技術可以說是專為轉換影像為數位訊號而發展的技術。CCD由於感光效率較高,所以從家用傳真機到科學研究的天文望遠鏡等各類精密器材都使用它做為主要感光元件。

名詞

  • 英:Charge-coupled Device
  • 中:電荷耦合元件

名詞定義

CCD具備低雜訊、高感度的優點,應用在錄影器材上更有使用稜鏡將光源分為RGB三色,讓三個CCD同時感應的技術,能夠獲得極佳的畫質,不過價格也較高,通常用於專業、廣播級器材。不過CCD耗電量較高,而且單價較貴,以至於大尺寸(如全片幅數位單眼)目前仍未採用,多使用CMOS做為感光元件。2009年的諾貝爾物理獎,就由貝爾實驗室發明CCD的Willard Boyle和George E.Smith所獲得。

長年主流的感光元件。CCD由於感光性能好、雜訊少,長期都是影像產品愛用的關鍵元件。近年受到大尺寸、背光式CMOS的反撲才有所改變。(圖片來源:Wikipedia

3CCD專業性能仍然居冠。錄影器材上會使用稜鏡分光給三個CCD元件感應不同顏色的光,使得感度、訊號表現極佳,迄今仍是專業機種的主流。(圖片來源:Wikipedia

未來發展

目前數位相機仍有多數機種採用CCD做為感光元件,例如準專業機種、消費機都多所採用。而需要在低照度環境下錄影的監視攝影機也以CCD做為主要感光元件。不過由於近期CMOS在技術上有所突破,背照式(Backside illuminated)CMOS在感光度上、雜訊上都有著長足的進步,以至於CCD在數位相機的應用上會受到相當的挑戰。長期在CCD技術上有所經營的廠商,如Fujifilm就於前幾年推出Super CCD EXR晶片,旗下機種就充分發揮的CCD感光元件的優點。而許多手機廠商也將手機上的感光元件換成CCD,使得像素、顏色、雜訊上的表現都有著明顯的進步,像Sharp、LG、Samsung等日韓廠商都有推出這樣的產品。當然,專業與廣播級錄影器材因為對畫質要求甚高,目前還是3CCD機種一枝獨秀。

手機採用CCD也有好表現。過往手機上的攝影鏡頭多採用CMOS作為感光元件,日韓廠商採用CCD後,品質大幅提昇,能有與數位相機一較高下的本事。

CMOS,廣泛應用的影像感應元件

來源: T客邦
作者: Dr. J

上從數位單眼相機,下到Webcam、手機,都會在技術規格中看到CMOS這個名詞。CMOS其實是積體電路製程的一種,應用在影像感應上就是CMOS感光元件,與CCD共為兩大主流。CMOS具備省電、價格便宜等優點,所以受到廣泛應用。

名詞

  • 英:Complementary Metal-Oxide-Semiconductor Sensor
  • 中:互補式金屬-氧化層-半導體感光元件

名詞定義

不過早期的CMOS因為在亮度低的狀況下容易產生雜訊,且遇到快速移動的目標時成像容易模糊,加上尺寸大小無法提升,一度受到CCD所取代。但後來隨著技術進步,以及影像處理晶片除去噪訊能力的加強,於2004年後又成為主流。目前像Canon、Sony等廠商的數位相機、DV都採用CMOS作為主要產品的感光元件,手機與Webcam也多所採用。

從DSLR到iPhone都搭配
早期的CMOS品質稍差、價格低廉,一度被CCD所取代。但改良之後一舉成為主流,大多數的影像器材都使用它。

從小到大各尺寸通通有
CMOS感光元件早期因為技術限制而無法做大,現在技術突破後,從APS-C規格到更大的全片幅都能做得出來。

應用

目前是數位單眼與數位相機的主流上都採用改良後的CMOS感光元件,上從Canon EOS 5D Mark II等全片幅、下到隨身相機都不例外。而DV除了專業、準專業機種會使用價格較高的3CCD結構外,也多採用CMOS作為感光元件;甚至連以製作3CCD技術為主的Panasonic也推出了3MOS攝影機,價格降低、表現更優秀。手機除了部分日系的高像素機種採用CCD,iPhone等智慧型手機也是以CMOS為主。WebCam也隨著技術進步,目前甚至有能夠拍攝720p HD影片的產品上市。整體而言,改良後的CMOS能夠應付大多數攝影、錄影的需求。購買數位產品時不要以早期的CMOS品質不良的印象做為判斷標準,技術交替可是十分快速的。

WebCam也能有高畫質
早期採用CMOS的Webcam品質都不好,但技術進步、畫質變好,也能夠拍攝720p的HD影片,價格也不貴。


大,真的比較好? 淺談數位相機的感光元件

來源: T客邦
作者: Bird

這次Bird要談的是數位相機中的重要元件:感光元件。很多人都知道,不論是CMOS還是CCD,感光元件的尺寸都是愈大愈好;但為什麼愈大愈好?道理何在?這次鳥大師就要從量子物理的角度,來替我們解惑:為什麼感光元件真的愈大愈好。由於原文頗長,本文照例分上中下三段刊出。 最近隨著Olympus E-P1的上市,帶動一股高畫質隨身機的熱潮。除了Micro 4/3系統所帶來的交換鏡頭能力外,E-P1在接近隨身機的體積內能達到接近DSLR的畫質,更是為人所津津樂道。
E-P1不只將幾台曾是隨身機機王的高畫質機種如Canon G10、Panasonic LX3等遠拋在後,更號稱能和眾DSLR大哥們平起平坐。在這小巧的機身裡,究竟是什麼魔法讓E-P1能有如此本領?
其實答案很簡單,一切都只是因為E-P1的感光元件比較大片而已。講完了。
不過,我知道你一定還想再多聽一點,所以我們就來好好聊聊「尺寸」這個問題吧。

真的就只是大而已

感光元件,就是數位相機裡用來取代底片的那一小塊半導體元件,在這小小的晶片上,排列著數以千萬計稱為「像素」的小單位,用來感測影像。你有沒有發現,相機越是高檔,感光元件就越大片?圖一的感光元件大小是照正確的比例繪製的,如果把全幅的感光元件和最右邊的1/1.8”感光元件放在一起,就好像雞排和雞米花放在一起一樣。
CCD01
▲各種感光元件的大小比例。
像 Canon EOS 1Ds、Sony Alpha 900 這些高檔相機的感光元件做這麼大片,難道只是為了保有和底片一樣的焦距嗎?我們可以再從另外一個角度來看這個問題:像素的大小。
CCD02
▲常見數位相機的相素大小。
表一列出了一些數位相機的像素大小,從最高檔的專業級單眼到手機都有。像素是一顆顆方形的小單元, 排列在感光元件上。一般來說,因為某些設計上的考量,除了一些很少見的例子外,像素多半是正方型的,因此我們若知道感光元件感測區域的邊長,將邊長除以該方向的解析度,就可以得到像素的大小。
以 Canon EOS 5D 來說,它的感光元件是 135 底片的大小,也就是 36mm * 24mm,而它的解析度是 4368 * 2912,我們可以這樣算出水平方向的像素邊長:
36 (mm) / 4368 = 8.24 (um)
垂直方向亦同:
24 (mm) / 2912 = 8.24 (um)
看到沒,像素真的是正方形的。
8.2um 是多大?人的頭髮大概是 80-100um 粗,所以在頭髮的斷面裡大概可以排下十個以上的像素。看起來很小,對不對?但是在表一列出的數位相機中,8.2um的像素大小已經是第一名了,將近是最後一名的Nokia N95的四倍。
感光元件的大小,跟相機的售價成正相關,但真正決定影像品質的,其實是像素的大小。像素越大,影像的品質就越好。在低照度的場景中,大像素的相機更能拍出清晰、低雜訊且有用的影像,而在高動態範圍的場合下,大像素能保留更多影像的細結。這一切的關鍵,其實是光電二極體。
【編按】在上篇中,我們看到一個事實:愈高檔的相機,感光元件愈大,畫質也愈好;但為什麼是這樣?在這篇中鳥大師要從感光元件的運作原理開始說起。如果沒看到上篇的話,可以先連過去看,再回來讀完本篇喔!

從光電效應說起

光電二極體是影像感測器用來感應入射光線強度的關鍵,它背後的原理就是我們物理課都學過的「光電效應」:光照射在金屬上,金屬內的自由電子接受了光子的能量而變得活蹦亂跳,可以脫離金屬出去闖蕩天涯。不過,在光電二極體內的光電效應和金屬的光電效應有一點點不一樣,因為這裡只有半導體,沒有金屬。
金屬之所以能導電,是因為它有大量的自由電子,可以在材料內部自由移動。而半導體之所以為「半」導體,就是因為它內部的電子不若導體內的電子那般自由。不過,當半導體內的電子接受了光子的激發後,一樣會變得比較 high,只是沒辦法像金屬內的電子一樣,high 起來後就離家出走。
熟悉半導體的人可能知道,在半導體內的電子可以依照它們所攜帶的能量,分別被歸屬在不同的能帶(band)上。簡單的來說,在正常狀態下,也就是氣候涼爽溫度宜人的環境下,半導體內的電子大部份會待在一個稱之為「價電帶」的能帶上。在這個能帶中的電子,因為種種原因(很擠啦,受到束縛之類的)沒辦法自由移動,因此不能參與導電。不過,如果我們想辦法讓這些電子 high 一點,它們就會受到激勵,奮發向上,移動到一個稱之為「導電帶」的能帶上。
導電帶是一個寬闊的世界,電子在那裡可以自由移動,因此可參與導電的任務。現在的問題是:我們要怎麼讓電子受到激勵,願意跑到導電帶上?以及,電子要多high,才能從價電帶移動到導電帶上?
第二個問題的答案比較簡單,電子要多high,跟材料有關。價電帶和導電帶之間的距離,或者說能量的差異,稱之為能隙(Energy Gap),這是材料本身的特性,跟構成材料的原子以及它外面電子的分佈結構有關。對大部份的金屬材料來說,它的能隙非常小,甚至沒有,所以金屬內的電子幾乎都在導電帶晃蕩,這也就是為什麼多數金屬生下來就會導電的原因。
絕緣體的能隙非常大,大到我們沒辦法用一般比較人道的方法,把它的電子從價電帶弄到導電帶,使它可以導電。所以,絕緣體在正常的狀況下,是不會導電的。所謂「比較不人道」的方法,就是對絕緣體施加極大的電場,如高壓電,強迫它的電子離開溫暖的家,進入導電帶,它就導電啦。因此世界上沒有絕對的絕緣體,只有不夠高的高壓電。劍魔獨孤求敗說:「只要練得夠精,草木竹石皆可為劍」。高壓電亦如是:只要電壓夠高,草木竹石皆可導電。
回到材料的特性上。半導體的能隙比較小,因此我們可以用簡單的方法,讓半導體內的電子離開價電帶進入導電帶,而這個簡單的方法,就是用光子去打它。
量子物理告訴我們,光子所攜帶的能量,只與它的波長有關,而與光的強度無關。波長越短的光,它的光子所攜帶的能量越高。而半導體的能隙則是由材料決定的特性,以最常用來製作半導體的矽來說,它在室溫下的能隙是1.1eV(電子伏特)左右,換句話說,要使矽裡面的一顆電子從價電帶移動到導電帶,需要給它1.1eV左右的能量,或者,用一顆帶有1.1eV以上能量的光子去撞它。
為什麼不能用兩顆加起來超過1.1eV的光子去打一顆電子呢?因為量子物理的基本精神就是一個蘿蔔一個坑,上帝把世界造成這個樣子,所以電子和光子也就只能乖乖一個打一個。而前面講過,波長越短的光,光子的能量越高,換句話說,波長越長的光,光子的能量越低,因此我們可以得到一個推論:當光的波長增加到某個程度後,就無法從矽裡面打出能進入導電帶的電子了。
如果這個最大的波長是綠色的光,那麼,因為紅色的光波長比綠色還要長,所以紅色的光就沒辦法在矽裡面打出光電效應的電子,也就是說,如果以矽做為光電二極體的感光材料,它只能感應綠色藍色紫色的光,而無法感應紅色橙色黃色的光。聽起來多糟啊!
還好,這裡有一個物理與工程的美麗巧合。經過精密的計算後,我們發現,以矽做為光電效應的材料,它的最大波長在1100nm左右,剛剛好是近紅外線的光譜,也就是說整個可見光的光譜它都感應得到,還附贈一點點紅外線,多完美的結局啊。
有了光電效應,接下來我們就可以看看光電二極體是怎麼運作的了。
【編按】為什麼數位相機的感光元件愈大愈好?在鳥大師的前兩篇文章中,我們看到感光元件的基本運作原理;在本篇中,我們將會看到在現實世界中影響畫面品質的諸多因素,以及為什麼感光元件的尺寸,真的是愈大愈好。沒看過前兩篇的同學,請先到上篇中篇補修學分。

光電二極體

不管是 CCD 還是 CMOS影像感測器,它們上面的光電二極體其實是一樣的東西。光電二極體是一個用半導體製程做在晶片上的二極體,它有個 PN 接面,所謂PN接面就是P型半導體和N型半導體的接合面。當光照射在PN接面上時,因為前述的光電效應,會在接面附近打出一個可以自由移動的電子。至於這個電子所空下來的座位,我們稱之為電洞。
如果我們能有個辦法來累積這些被光所打出來的電子,就可以測量入射光的強度。至於如何用光電二極體來累積這些電子呢?實務上的做法是:讓光電二極體變成一個電容器。
因為某些很難三言兩語就交代完的理由,擁有PN接面的二極體在被施加逆向偏壓時,會變得像個電容器。所謂逆向偏壓,就是我們對二極體施加一個電壓,而這個電壓的方向是讓二極體不會導通的方向,也就是對半導體的負極(電路圖上三角型尖尖的那頭)加以比正極高的電壓。施加逆偏壓後,會有一些電荷因為二極體變成電容器的關係,而被保存在PN接面附近,即使把偏壓移除後亦然。以上這個動作,稱之為光電二極體的reset。
光電二極體被reset後,因為電荷保留在上面,所以會有個電壓。此時,如果光電二極體的PN接面受到光線照射,就會開始產生因光電效應而來的電子。這些產生出來的電子,會漸漸中和掉reset時被保留在PN接面附近的電子,因此光電二極體上的電壓就會隨著光線照射而下降。光線越強,下降得越快,照得越久,下降得越多。正因如此,我們可以利用這個原理,把光電二極體做成相機中的感光元件。
CCD03
▲各種感光元件的大小比例。

雜訊,雜訊,雜訊

在這整個系統中有一個很重要的關鍵,就是當光電二極體被reset時,那個電容器的大小。這個電容器的大小,決定了能有多少電荷被保留在PN接面附近,等著被光子打出來的電子給中和掉,或者,換個角度來想,這個光電二極體能夠感應多少被光子打出來的電子。
如果這個容量很小,那麼當入射光線很強時,沒多久電荷就被打光光了,用專用的術語來說,叫做光電二極體「飽和」了。飽和就是光電二極體能感應的最大亮度,像是天空中的白雲啊,黃昏的夕陽啊這種很亮很亮的景物,都可以很輕易地讓光電二極體飽和。
世界有光亮就有黑暗,光亮會讓光電二極體飽和,那黑暗呢?照講在黑暗中,如果沒有光線,就沒光電效應,也就沒有電子會被打出來。不過,這是個充滿活力的世界,物理學家告訴我們,其實並不是所有的電子都那麼安份,總是有一些天生比較熱情外向的電子,在還沒被光子擊中前就有本事自己跳出來。而且,這種狂熱份子的比例,會隨著溫度的增加而越來越多,也就是說,溫度越高,會有越來越多的電子不待光子的激發,就變成光電二極體的訊號,或者說,雜訊。這是個現實的世界,雖然它們都是一樣的電子,但我們要的部份,就稱之為訊號,而不要的部份,就是雜訊。
這種雜訊只有在溫度降到絕對零度時才會消失,因此稱之為熱雜訊。而且因為它的出現與入射光無關,就算在黑暗中也會發生,因此它對影像感測器所造成的影響主要是暗電流的增加,也就是在黑暗中仍會造成影像讀數。雖然暗電流的成因還有其它的因素,但熱雜訊是主要的凶手。
不管用什麼樣的影像感測器,數位相機的雜訊表現都會受到溫度的影響,溫度越高雜訊越大。因此同樣的相機,在氣溫40度的夜晚所拍出來的非洲夜空,照片中的雜訊一定會比在零下二十度的阿拉斯加拍出來的星空要大。
除了熱雜訊外,在影像感測器中另一個雜訊的大宗來源是讀取雜訊。所謂讀取雜訊,就是每次從像素中將電荷讀出時,所引入的不確定性,它的單位是電子的數量。如果一個像素的讀取雜訊是 30 顆電子,代表我在從這個像素中讀出一個強度為1000顆電子的訊號時,有可能讀到1030,也有可能讀到970,而且可能每次都不一樣。
當然,除了讀取雜訊和熱雜訊外,在影像感測器中還有各式各樣的的雜訊來源,像是reset雜訊(這個也和溫度有關),光子照射隨機雜訊(和入射光強度有關),以及後段電路像CMOS感測器的像素放大器或列放大器另外引入的電路雜訊等。
雖然有著這麼多的雜訊,讓影像感測器的設計和製造變成一門高深的學問,但重點是,以上大部份的雜訊,都與像素的電荷容量無關,甚至某些雜訊還與像素的電荷量呈負相關。看出這其中的玄機了嗎?如果像素的電荷容量比較大,雖然雜訊的強度不變,但相對來講,雜訊佔有整個訊號的比例就降低了,因而我們可以得到更好的影像品質。
CCD04
▲像素容量和雜訊。

動態範圍

除了雜訊外,另一個與像素大小有關的參數就是動態範圍。動態範圍決定了一台相機所能拍攝的影像中,最亮的部份和最暗的部份能有多大的差距。假設我們有個要拍攝的場景,是在夏日午後的太陽下,畫面中有藍天白雲,還有個美女站在樹蔭下。藍天白雲是畫面中很亮的部份,而樹蔭下的美女則是比較暗的部份。這最亮和最暗的亮度差距,實務上來說可以到數千倍以上。如果相機的動態範圍不足,當我們希望藍天白雲的曝光正確時,美女的臉蛋就會曝光不足,拍攝美女臉蛋部份的像素可能都只有接近全黑的讀數。
換個方法來做,如果我們希望拍攝美女臉蛋的像素要有有效的讀數,讓我們看得到她的五官,就得把曝光往上調整,但這麼一來拍攝藍天白雲的像素統統都會被過量的光子給轟到飽和,而變成白花花的一片,什麼都看不出來。
這其中的關鍵,就是光電二極體那個電容器的大小,它有個專有名詞叫做「full well capacity」。FWC的單位是電子,它代表著這個像素的光電二極體最多能儲存多少經由光電效應打出來的電子,再多它也存不下來,只能丟掉,而且萬一丟得不好還會不小心漏到旁邊的像素去,造成影像品質的劣化。FWC幾乎完全取決於光電二極體的面積大小,也就是說,像素越大,FWC就越大。
通常來說,像素在影像暗部的讀數受限於前面提過的讀取雜訊和熱雜訊,低於讀取雜訊的訊號量無法產生有效的讀數,因此通常在計算動態範圍時,我們會以讀取雜訊當做最低的訊號位準。而要特別強調的是,讀取雜訊雖然會隨著像素尺寸的增加而略為增大,但並不像FWC一樣和像素尺寸成正比。
CCD05
▲像素大小與FWC
表二比較了兩台年代差距不遠的數位相機,可以看出像素的大小決定FWC的大小,但讀取雜訊的差異卻沒有FWC的差異那麼大,因此兩者換算出來的動態範圍有很大的差距。

大小仍是決勝的關鍵

說了這麼多,其實結論還是只有一個:像素的尺寸決定影像的品質。而在同樣的解析度下,越大尺寸的感測器能擁有越大的像素。這就是為什麼同樣是一千多萬像素的機器,E-P1拍出來的影像品質大勝所有的高階隨身機,因為它擁有這個體積的機種中,最大的像素尺寸,4.3um。
難道影像品質要好,只要努力把像素尺寸做大就可以達成嗎?理論上來說沒錯,而實務上也有很多遙測用的影像感測器或是軍事用途的影像感測器,為了達到極高的動態範圍和及大的訊號/雜訊比,而實作出非常巨大的像素。有多大呢?EOS 5D 的像素大小是 8.2um,而這種特殊用途的感測器,像素大小往往是數十um起跳的,甚至還有上百um的。
如果像素要大,解析度又要高,那整塊感測器就會變得非常巨大,而這會讓用來製作感測器的半導體工廠非常頭痛。以一片八吋的晶圓來說,如果拿來做1/1.8吋的感測器,大概可以塞進七八百個。而拿來做APS-C感測器的話,也可以切出200片左右。但如果拿來做135全幅的感測器,能做幾片呢?20片。
因此除非有特殊的需求,或是追求高畫質的表現,否則數位相機的發展都朝著小尺寸高密度的影像感測器走。但隨著畫素數越來越高,像素的尺寸也越來越小。以現在主流的高階消費型數位相機來說,千萬畫素以上的機種,像素尺寸普遍都小於 1.7um,雖然原廠不會公布它的FWC數值,但合理的推算大概都不會超過4000顆電子,這對動態範圍是相當嚴苛的限制,也因此現在許多新的數位相機才會開始有那種利用多成曝光合成而達到高動態範圍的WDR模式,因為光靠感測器本身的動態範圍已經沒辦法拍出令人滿意的影像了。
所以,尺寸重不重要?當然重要啊 !

感光元件大小 (Sensor Size)

來源: 逍遙文工作室

玩攝影的人一定對感光元件大小 (Sensor Size)不陌生,感光元件好比心臟,是相機中最重要的部份,當然我們也要好好瞭解它,才能真正發揮感光元件的潛能!市面上的產品單眼相機、數位相機、手機相機等對感光元件的需求不一,若我們很要求影像品質的話,感光元件勢必得大才行。
我們都看過單眼相機體積相當龐大,同樣有拍照功能的手機相機卻顯得相當迷你,最大的原因就是感光元件尺寸,想要容納大一點的感光元件,機身勢必相對得大一些,所以我們可以猜想而知,單眼的感光元件較大,而手機的感光元漸較小!
我們很好奇想知道,既然電腦的心臟CPU體積可以越做越小,而且性能越來越快,那為什麼相機的心臟感光元件近十年以來,卻不見單眼相機體積越來越小?反而感光元件縮小之後演變成性能介於單眼相機類似單眼相機微單眼相機(輕單眼相機)。感光元件怎麼不像CPU那樣可越做越小?
隨著科技日新月異、一日千里,電腦的CPU性能的發展有所謂的摩爾定律。可惜相機的感光元件的架構卻和電腦的CPU有很大的區別。前者好比平行處理,後者好比序列處理,可以參考這篇CPU與GPU的架構。CPU可以越做越小,但會因為物理性質的極限而不能再縮更小,所以原本單一核心演變成多核心架構。感光元件似乎也限制於物理性質的極限,使得它的體積縮小幅度有限。
我認為感光元件還有很大的進步空間,就好像電腦資訊界的平行架構GPU逐漸崛起那種趨勢,若感光元件能塞入更多處理元件,就能有效防止曝光過度曝光不足的情況!
註:摩爾定律是由英特爾(Intel)創始人之一戈登.摩爾(Gordon Moore)提出來的。其內容為:積體電路(IC)上可容納的電晶體數目,約每隔24個月(1975年摩爾將24個月更改為18個月)便會增加一倍,效能也將提升一倍,當價格不變時;或者說,每一美元所能買到的電腦效能,將每隔18個月翻兩倍以上。
我們外行人總是有個迷思:像素越多,成像品質越好?但實際上像素多寡不能決定影像質量,而真正決定影像質量好壞的最主要因素是感光元件。感光元件分為兩種,一種是CCD,另一種是CMOS。CCD雖然成像略好,但是成本較高。CMOS憑借著較低的功耗和價格以及優異的影像品質,在相機領域應用最為逐漸廣泛。
要是有人覺得非單眼相機拍起來的效果也相當好看,原因之一是內建軟體寫得相當好,捕捉到的影像已經先行在內部軟體處理過,你才會看到品質不錯的影像。總之,越要求成像品質的人,就會越朝單眼相機邁進,但對我而言,成像品質固然重要,然我又要攜帶方便,取捨之下於是我便選購微單眼GX1
我的Panasonic Lumix GX1屬於4/3系統,維基這麼解釋:4/3系統(Four Thirds System,又經常直稱為四三系統)是一種常見的照相機感光元件規格,由奧林巴斯(Olympus)與柯達(Kodak)共同設計研發並推廣。此規格針對數位單鏡反射式相機,目的在於使不同製造商之間生產的交換鏡頭產品可以無差錯的組合使用。4/3系統的名稱由來是其影像感光元件被特別設計為三分之四吋(約33.87mm)與長寬比保持4:3的規格。
原本以為微單眼還有反光鏡和光學觀景窗,但由於採用4/3系統中的Micro 4/3系統,為了降低相機體積而省略反光鏡和光學觀景窗,這就是我所期待的相機發展的趨勢!

Android的Camera架构介绍

來源: Southcamel

第一部分 Camera概述
Android 的Camera包含取景器(viewfinder)和拍摄照片的功能 。目前Android发布版的Camera程序虽然功能比较简单,但是其程序的架构分成客户端和服务器两个部分,它们建立在Android的进程间通讯Binder的结构上。


以开源 的Android为例,Camera的代码主要在以下的目录中:


Camera的JAVA程序的路径:
packages/apps/Camera/src/com/android/camera/
在其中Camera.java 是主要实现的文件


Camera的JAVA本地调用部分(JNI):
frameworks/base/core/jni/android_hardware_Camera.cpp 
这部分内容编译成为目标是libandroid_runtime.so 。


主要的头文件在以下的目录中:
frameworks/base/include/ui/ 


Camera底层库在以下的目录中:
frameworks/base/libs/ui/ 
这部分的内容被编译成库libui.so 。


Camera服务部分:
frameworks/base/camera/libcameraservice/ 
这部分内容被编译成库libcameraservice.so 。


为了实现一个具体功能的Camera,在最底层还需要一个硬件相关的Camer库(例如通过调用video for linux驱动程序和Jpeg编码程序实现)。这个库将被Camera的服务库libcameraservice.so 调用。


第二部分 Camera的接口与架构 


2.1 Camera的整体框架图 
Camera的各个库之间的结构可以用下图的表示:

在 Camera系统的各个库中,libui.so位于核心的位置,它对上层的提供的接口主要是Camera类,类 libandroid_runtime.so通过调用Camera类提供对JAVA的接口,并且实现了android.hardware.camera 类。 libcameraservice.so是Camera的服务器程序,它通过继承libui.so的类实现服务器的功能,并且与libui.so中的另外一部分内容则通过进程间通讯(即Binder机制)的方式进行通讯。


libandroid_runtime.so和libui.so两个库是公用的,其中除了Camera还有其他方面的功能。


Camera部分的头文件在frameworks/base/include/ui/ 目录中,这个目录是和libmedia.so库源文件的目录frameworks/base/libs/ui/ 相对应的。
Camera主要的头文件有以下几个: 
■ICameraClient.h
■Camera.h
■ICamera.h 
■ICameraService.h
■CameraHardwareInterface.h 


在这些头文件Camera.h提供了对上层的接口,而其他的几个头文件都是提供一些接口类(即包含了纯虚函数的类),这些接口类必须被实现类继承才能够使用。


整个Camera在运行的时候,可以大致上分成Client和Server两个部分,它们分别在两个进程中运行,它们之间使用Binder机制实现进程间通讯。这样在客户端调用接口,功能则在服务器中实现,但是在客户端中调用就好像直接调用服务器中的功能,进程间通讯的部分对上层程序不可见。


从框架结构上来看,ICameraService.h、ICameraClient.h和ICamera.h三个类定义了MeidaPlayer的接口和架构,ICameraService.cpp和Camera.cpp两个文件用于Camera架构的实现,Camera的具体功能在下层调用硬件相关的接口来实现。


从Camera的整体结构上,类Camera是整个系统核心,ICamera类提供了Camera主要功能的接口,在客户端方面调用,CameraService是Camera服务,它通过调用实际的Camera硬件接口来实现功能。事实上,图中红色虚线框的部分都是Camera程序的框架部分,它主要利用了Android的系统的Binder机制来完成通讯。蓝色的部分通过调用Camera硬件相关的接口完成具体的Camera服务功能,其它的部分是为上层的JAVA程序提供JNI接口。在整体结构上,左边可以视为一个客户端,右边是一个可以视为服务器,二者通过Android的 Bimder来实现进程间的通讯。


2.2 头文件ICameraClient.h
ICameraClient.h用于描述一个Camera客户端的接口,定义如下所示: 
class ICameraClient: public IInterface 

public: 
DECLARE_META_INTERFACE(CameraClient); 
virtual void shutterCallback() = 0; 
virtual void rawCallback(const sp& picture) = 0; 
virtual void jpegCallback(const sp& picture) = 0; 
virtual void frameCallback(const sp& frame) = 0; 
virtual void errorCallback(status_t error) = 0; 
virtual void autoFocusCallback(bool focused) = 0; 
}; 
class BnCameraClient: public BnInterface 

public: 
virtual status_t onTransact( uint32_t code, 
const Parcel& data, 
Parcel* reply, 
uint32_t flags = 0); 
}; 

在定义中,ICameraClient 类继承IInterface,并定义了一个Camera客户端的接口,BnCameraClient 继承了BnInterface,这是为基于Android的基础类Binder机制实现在进程通讯而构建的。根据BnInterface类模版的定义BnInterface类相当于双继承了BnInterface和 ICameraClient。
IcameraClient这个类的主要接口是几个回调函数shutterCallback、rawCallback和jpegCallback等,它们在相应动作发生的时候被调用。作为Camera的“客户端”,需要自己实现几个回调函数,让服务器程序去“间接地”调用它们。 
2.3 头文件Camera.h 

Camera.h是Camera对外的接口头文件,它被实现Camera JNI的文件android_hardware_Camera.cpp所调用。Camera.h最主要是定义了一个Camera类: 

class Camera : public BnCameraClient, public IBinder::DeathRecipient 

public: 
staticspconnect(); 
~Camera(); 
voiddisconnect(); 
status_tgetStatus() { return mStatus; } 
status_tsetPreviewDisplay(const sp& surface); 
status_tstartPreview(); 
voidstopPreview(); 
status_tautoFocus(); 
status_ttakePicture(); 
status_tsetParameters(const String8& params); 
String8 getParameters() const; 
voidsetShutterCallback(shutter_callback cb, void *cookie); 
voidsetRawCallback(frame_callback cb, void *cookie); 
voidsetJpegCallback(frame_callback cb, void *cookie); 
voidsetFrameCallback(frame_callback cb, void *cookie); 
voidsetErrorCallback(error_callback cb, void *cookie); 
voidsetAutoFocusCallback(autofocus_callback cb, void *cookie); 
// ICameraClient interface 
virtual voidshutterCallback(); 
virtual voidrawCallback(const sp& picture); 
virtual voidjpegCallback(const sp& picture); 
virtual voidframeCallback(const sp& frame); 
virtual voiderrorCallback(status_t error); 
virtual voidautoFocusCallback(bool focused); 
//…… 
}

从接口中可以看出Camera类刚好实现了一个Camera的基本操作,例如播放(startPreview)、停止(stopPreview)、暂停(takePicture)等。在Camera类中connect()是一个静态函数,它用于得到一个Camera的实例。在这个类中,具有设置回调函数的几个函数:setShutterCallback、setRawCallback和setJpegCallback等,这几个函数是为了提供给上层使用,上层利用这几个设置回调函数,这些回调函数在相应的回调函数中调用,例如使用setShutterCallback设置的回调函数指针被 shutterCallback所调用。


在定义中,ICameraClient 类双继承了IInterface和IBinder::DeathRecipient,并定义了一个Camera客户端的接口,BnCameraClient 继承了BnInterface,这是为基于Android的基础类Binder机制实现在进程通讯而构建的。事实上,根据BnInterface类模版的定义BnInterface类相当于双继承了 BnInterface和ICameraClient。这是Android一种常用的定义方式。


继承了DeathNotifier类之后,这样当这个类作为IBinder使用的时候,当这个Binder即将Died的时候被调用其中的binderDied函数。继承这个类基本上实现了一个回调函数的功能。 
2.4 头文件ICamera.h 

ICamera.h描述的内容是一个实现Camera功能的接口,其定义如下所示: 

class ICamera: public IInterface 

public: 
DECLARE_META_INTERFACE(Camera); 
virtual void disconnect() = 0; 
virtual status_tsetPreviewDisplay(const sp& surface) = 0; 
virtual void setHasFrameCallback(bool installed) = 0; 
virtual status_tstartPreview() = 0; 
virtual void stopPreview() = 0; 
virtual status_tautoFocus() = 0; 
virtual status_ttakePicture() = 0; 
virtual status_tsetParameters(const String8& params) = 0; 
virtual String8 getParameters() const = 0; 
}; 
class BnCamera: public BnInterface 

public: 
virtual status_tonTransact( uint32_t code, 
const Parcel& data, 
Parcel* reply, 
uint32_t flags = 0); 
};

在camera类中,主要定义Camera的功能接口,这个类必须被继承才能够使用。值得注意的是,这些接口和Camera类的接口有些类似,但是它们并没有直接的关系。事实上,在Camera类的各种实现中,一般都会通过调用ICamera类的实现类来完成。 
2.5 头文件ICameraService .h 

ICameraService.h用于描述一个Camera的服务,定义方式如下所示: 
class ICameraService : public IInterface 

public: 
DECLARE_META_INTERFACE(CameraService); 
virtual sp connect(const sp& cameraClient) = 0; 
}; 
class BnCameraService: public BnInterface 

public: 
virtual status_t onTransact( uint32_t code, 
const Parcel& data, 
Parcel* reply, 
uint32_t flags = 0); 
}; 

由于具有纯虚函数, ICameraService 以及BnCameraService必须被继承实现才能够使用,在ICameraService 只定义了一个connect()接口,它的返回值的类型是sp,这个ICamera 是提供实现功能的接口。注意,ICameraService只有连接函数connect(),没有断开函数,断开的功能由ICamera接口来提供。 

2.6 头文件CameraHardwareInterface.h 


CameraHardwareInterface.h定义的是一个Camera底层的接口,这个类的实现者是最终实现Camera的。
CameraHardwareInterface 定以Camera硬件的接口,如下所示: 

class CameraHardwareInterface : public virtual RefBase { 
public: 
virtual ~CameraHardwareInterface() { } 
virtual sp getPreviewHeap() const = 0; 
virtual status_t startPreview(preview_callback cb, void* user) = 0; 
virtual voidstopPreview() = 0; 
virtual status_t autoFocus(autofocus_callback, 
void* user) = 0; 
virtual status_t takePicture(shutter_callback, 
raw_callback, 
jpeg_callback, 
void* user) = 0; 
virtual status_t cancelPicture(bool cancel_shutter, 
bool cancel_raw, 
bool cancel_jpeg) = 0; 
virtual status_t setParameters(const CameraParameters& params) = 0; 
virtual CameraParametersgetParameters() const = 0; 
virtual void release() = 0; 
virtual status_t dump(int fd, const Vector& args) const = 0; 
};

使用C语言的方式导出符号: 

extern "C" sp openCameraHardware();

在程序的其他地方,使用openCameraHardware()就可以得到一个 CameraHardwareInterface,然后调用 CameraHardwareInterface的接口完成Camera的功能。 
第三部分 Camera的主要实现分析

3.1 JAVA程序部分


在packages/apps/Camera/src/com/android/camera/ 
目录的Camera.java文件中,包含了对Camera的调用
在Camera.java中包含对包的引用: 

import android.hardware.Camera.PictureCallback; 
import android.hardware.Camera.Size;

在这里定义的Camera类继承了活动Activity类,在它的内部,包含了一个 android.hardware.Camera 

public class Camera extends Activity implements View.OnClickListener, SurfaceHolder.Callback { 
android.hardware.Camera mCameraDevice; 
}

对Camera功能的一些调用如下所示: 
mCameraDevice.takePicture(mShutterCallback, mRawPictureCallback, mJpegPictureCallback); 
mCameraDevice.startPreview(); 
mCameraDevice.stopPreview();

startPreview、stopPreview 和takePicture等接口就是通过JAVA本地调用(JNI)来实现的。
frameworks/base/core/java/android/hardware/ 目录中的Camera.java文件提供了一个JAVA类:Camera。 

public class Camera { 

public void setParameters(Parameters params) { 
Log.e(TAG, "setParameters()"); 
//params.dump(); 
native_setParameters(params.flatten()); 
}

在这个类当中,大部分代码使用JNI调用下层得到,例如:
再者,例如以下代码: 
public final void setPreviewDisplay(SurfaceHolder holder) { 
setPreviewDisplay(holder.getSurface()); 

private native final void setPreviewDisplay(Surface surface);

两个setPreviewDisplay 参数不同,后一个是本地方法,参数为Surface 类型,前一个通过调用后一个实现,但自己的参数以SurfaceHolder 为类型。 

3.2 Camera的JAVA本地调用部分 


Camera的JAVA本地调用(JNI)部分在frameworks/base/core/jni/ 目录的android_hardware_Camera.cpp 中的文件中实现。
android_hardware_Camera.cpp之中定义了一个JNINativeMethod(JAVA本地调用方法)类型的数组gMethods,如下所示: 

static JNINativeMethod camMethods[] = { 
{"native_setup","(Ljava/lang/Object;)V",(void*)android_hardware_Camera_native_setup }, 
{"native_release","()V",(void*)android_hardware_Camera_release }, 
{"setPreviewDisplay","(Landroid/view/Surface;)V",(void *)android_hardware_Camera_setPreviewDisplay }, 
{"startPreview","()V",(void *)android_hardware_Camera_startPreview }, 
{"stopPreview", "()V", (void *)android_hardware_Camera_stopPreview }, 
{"setHasPreviewCallback","(Z)V",(void *)android_hardware_Camera_setHasPreviewCallback }, 
{"native_autoFocus","()V",(void *)android_hardware_Camera_autoFocus }, 
{"native_takePicture", "()V", (void *)android_hardware_Camera_takePicture }, 
{"native_setParameters","(Ljava/lang/String;)V",(void *)android_hardware_Camera_setParameters }, 
{"native_getParameters", "()Ljava/lang/String;",(void*)android_hardware_Camera_getParameters } 
}; 

JNINativeMethod的第一个成员是一个字符串,表示了JAVA本地调用方法的名称,这个名称是在JAVA程序中调用的名称;第二个成员也是一个字符串,表示JAVA本地调用方法的参数和返回值;第三个成员是JAVA本地调用方法对应的C语言函数。


register_android_hardware_Camera 函数将gMethods注册为的类"android/media/Camera",其主要的实现如下所示。 

int register_android_hardware_Camera(JNIEnv *env) 

// Register native functions 
return AndroidRuntime::registerNativeMethods(env, "android/hardware/Camera", 
camMethods, NELEM(camMethods)); 


"android/hardware/Camera"对应JAVA的类android.hardware.Camera。 

3.3 Camera本地库libui.so 


frameworks/base/libs/ui/中的Camera.cpp文件用于实现Camera.h提供的接口,其中一个重要的片段如下所示 
const sp& Camera::getCameraService() 

Mutex::Autolock _l(mLock); 
if (mCameraService.get() == 0) { 
sp sm = defaultServiceManager(); 
sp binder; 
do { 
binder = sm->getService(String16("media.camera")); 
if (binder != 0) 
break; 
LOGW("CameraService not published, waiting..."); 
usleep(500000); // 0.5 s 
} while(true); 
if (mDeathNotifier == NULL) { 
mDeathNotifier = new DeathNotifier(); 

binder->linkToDeath(mDeathNotifier); 
mCameraService = interface_cast(binder); 

LOGE_IF(mCameraService==0, "no CameraService!?"); 
return mCameraService; 
}

其中最重要的一点是binder = sm->getService(String16("media.camera"));;这个调用用来得到一个名称为"media.camera" 的服务,这个调用返回值的类型为IBinder,根据实现将其转换成类型ICameraService使用。
一个函数 connect的实现 如下所示: 

sp Camera::connect() 

sp c = new Camera(); 
const sp& cs = getCameraService(); 
if (cs != 0) { 
c->mCamera = cs->connect(c); 

if (c->mCamera != 0) { 
c->mCamera->asBinder()->linkToDeath(c); 
c->mStatus = NO_ERROR; 

return c; 
}

connect通过调用getCameraService得到一个 ICameraService,再通过 ICameraService的cs->connect(c)得到一个 ICamera类型的指针。 调用connect将得到一个 Camera的指针,正常情况下Camera的成员 mCamera已经初始化完成。


一个具体的函数startPreview 如下所示: 

status_t Camera::startPreview() 

return mCamera->startPreview(); 


这些操作可以直接对 mCamera来进行,它是ICamera类型的指针。
其他一些函数的实现也与setDataSource类似。


libmedia.so中的其他一些文件与头文件的名称相同,它们是:
frameworks/base/libs/ui/ICameraClient.cpp
frameworks/base/libs/ui/ICamera.cpp
frameworks/base/libs/ui/ICameraService.cpp 
在此处,BnCameraClient和BnCameraService类虽然实现了onTransact()函数,但是由于还有纯虚函数没有实现,因此这个类都是不能实例化的。


ICameraClient.cpp中的BnCameraClient在别的地方也没有实现;而ICameraService.cpp中的BnCameraService类在别的地方被继承并实现,继承者实现了Camera服务的具体功能。 

3.4 Camera服务libcameraservice.so

frameworks/base/camera/libcameraservice/ 用于实现一个Camera的服务,这个服务是继承ICameraService的具体实现。


在这里的Android.mk文件中,使用宏USE_CAMERA_STUB决定是否使用真的Camera,如果宏为真,则使用 CameraHardwareStub.cpp和FakeCamera.cpp构造一个假的Camera,如果为假则使用 CameraService.cpp构造一个实际上的Camera服务。


CameraService.cpp是继承BnCameraService 的实现,在这个类的内部又定义了类Client,CameraService::Client继承了BnCamera。在运作的过程中 CameraService::connect()函数用于得到一个CameraService::Client,在使用过程中,主要是通过调用这个类的接口来实现完成Camera的功能,由于CameraService::Client本身继承了BnCamera类,而BnCamera类是继承了 ICamera,因此这个类是可以被当成ICamera来使用的。


CameraService和CameraService::Client两个类的结果如下所示:

class CameraService : public BnCameraService 

class Client : public BnCamera {}; 
wpmClient; 
}

在CameraService中的一个静态函数instantiate()用于初始化一个Camera服务,寒暑如下所示: 

void CameraService::instantiate() { 
defaultServiceManager()->addService( 
String16("media.camera"), new CameraService()); 
}

事实上,CameraService::instantiate()这个函数注册了一个名称为"media.camera"的服务,这个服务和Camera.cpp中调用的名称相对应。


Camera整个运作机制是:在Camera.cpp中可以调用ICameraService的接口,这时实际上调用的是 BpCameraService,而BpCameraService又通过Binder机制和BnCameraService实现两个进程的通讯。而 BpCameraService的实现就是这里的CameraService。因此,Camera.cpp虽然是在另外一个进程中运行,但是调用 ICameraService的接口就像直接调用一样,从connect()中可以得到一个ICamera类型的指针,真个指针的实现实际上是 CameraService::Client。


而这些Camera功能的具体实现,就是CameraService::Client所实现的了,其构造函数如下所示: 

CameraService::Client::Client(const sp& cameraService, 
const sp& cameraClient) : 
mCameraService(cameraService), mCameraClient(cameraClient), mHardware(0) 

mHardware = openCameraHardware(); 
mHasFrameCallback = false; 
}

构造函数中,调用openCameraHardware()得到一个CameraHardwareInterface类型的指针,并作为其成员mHardware。以后对实际的Camera的操作都通过对这个指针进行。这是一个简单的直接调用关系。


事实上,真正的Camera功能己通过实现CameraHardwareInterface类来完成。在这个库当中 CameraHardwareStub.h和CameraHardwareStub.cpp两个文件定义了一个桩模块的接口,在没有Camera硬件的情况下使用,例如在仿真器的情况下使用的文件就是CameraHardwareStub.cpp和它依赖的文件FakeCamera.cpp。


CameraHardwareStub类的结构如下所示: 
class CameraHardwareStub : public CameraHardwareInterface { 
class PreviewThread : public Thread { 
}; 
};

在类CameraHardwareStub当中,包含一个线程类PreviewThread,这个线程用于处理PreView,即负责刷新取景器的内容。实际的Camera硬件接口通常可以通过对v4l2 捕获驱动的调用来实现,同时还需要一个JPEG编码程序将从驱动中取出的数据编码成JPEG文件。