十年網(wǎng)站開發(fā)經(jīng)驗(yàn) + 多家企業(yè)客戶 + 靠譜的建站團(tuán)隊(duì)
量身定制 + 運(yùn)營維護(hù)+專業(yè)推廣+無憂售后,網(wǎng)站問題一站解決
前言
創(chuàng)新互聯(lián)建站是一家集網(wǎng)站建設(shè),陳倉企業(yè)網(wǎng)站建設(shè),陳倉品牌網(wǎng)站建設(shè),網(wǎng)站定制,陳倉網(wǎng)站建設(shè)報(bào)價(jià),網(wǎng)絡(luò)營銷,網(wǎng)絡(luò)優(yōu)化,陳倉網(wǎng)站推廣為一體的創(chuàng)新建站企業(yè),幫助傳統(tǒng)企業(yè)提升企業(yè)形象加強(qiáng)企業(yè)競爭力??沙浞譂M足這一群體相比中小企業(yè)更為豐富、高端、多元的互聯(lián)網(wǎng)需求。同時我們時刻保持專業(yè)、時尚、前沿,時刻以成就客戶成長自我,堅(jiān)持不斷學(xué)習(xí)、思考、沉淀、凈化自己,讓我們?yōu)楦嗟钠髽I(yè)打造出實(shí)用型網(wǎng)站。
安卓開發(fā)中經(jīng)常有需要使用攝像頭的應(yīng)用場景,對于初次接觸的同學(xué)攝像頭的方向是一個比較難弄清楚的概念,開發(fā)時很容易處理不當(dāng),本文將詳述該部分內(nèi)容幫助理解。
先看一個簡單的場景,打開手機(jī)的后置攝像頭拍攝,攝像頭捕獲的圖像幀數(shù)據(jù)可通過Camera.PreviewCallback回調(diào)中獲取,也就是攝像頭的輸出數(shù)據(jù),
void?onPreviewFrame(byte[]?data,?Camera?camera);
這里我們先忽略屏幕上的預(yù)覽,只關(guān)注攝像頭的輸出。如果把它保存為圖片或直接顯示出來,可以看到圖像和原始畫面相比逆時針旋轉(zhuǎn)了90度。
而我們?nèi)绻瑯邮褂胕Phone手機(jī)拍攝,輸出的結(jié)果是一個正向的圖片。
為什么輸出的圖像相比原始畫面旋轉(zhuǎn)了90度?因?yàn)樵O(shè)備的攝像頭存在一個“正向角度”,什么是攝像頭的正向?
通俗一點(diǎn)講,設(shè)備相當(dāng)于人的身體,眼睛相當(dāng)于攝像頭,眼睛把接收到的畫面反饋給大腦處理,相當(dāng)于攝像頭把接收到的數(shù)據(jù)給應(yīng)用程序處理。人眼能判斷出我們頭頂向上的方向是我們視覺上的正向,而后置攝像頭判斷的正向并不是手機(jī)物理屏幕向上的方向,而是物理屏幕右側(cè)的方向。我們想象一下,如果人眼是這個攝像頭,它認(rèn)為右側(cè)才是我們的視覺正向,那我們看到的東西是不是都是旋轉(zhuǎn)90度的?這樣就比較好理解了。
上圖是手機(jī)在豎直和水平方向攝像頭“看”到的畫面。
固定設(shè)備,指定的攝像頭,正向角度固定的(0/90/180/270),和屏幕旋轉(zhuǎn)、橫豎屏切換無關(guān),一般都在屏幕的右側(cè)(但不排除某些廠商修改成別的)。
這個角度在代碼中可通過Camera.CameraInfo
的orientation獲取,官方文檔也有解釋:
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.
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.
意思就是輸出的圖片需要順時針旋轉(zhuǎn)多少度,才能在自然方向上正確顯示。這里的自然方向就是以標(biāo)題欄左上角為原點(diǎn)的屏幕渲染坐標(biāo)系,圖片旋轉(zhuǎn)后,把它放到渲染坐標(biāo)系中,能和原始畫面一樣正常顯示。
上圖紅點(diǎn)代表了圖片的坐標(biāo)原點(diǎn),藍(lán)點(diǎn)則代表屏幕渲染坐標(biāo)的原點(diǎn)。只有做了旋轉(zhuǎn)處理,渲染到屏幕上的預(yù)覽圖像才是正確的(和原始畫面一樣),而這個旋轉(zhuǎn)的角度,就是orientation的值。
注意,正向始終在物理屏幕的右側(cè)(想象音量鍵那邊有一個正向箭頭),orientation就等于從攝像頭的角度(想象成人眼)看,從物理設(shè)備的正上方向(想象聽筒位置有個箭頭),需要順時針旋轉(zhuǎn)多少度才能到正向的箭頭。所以,根據(jù)這個想象一下前后攝像頭的區(qū)別,這個值后置攝像頭是90,前置攝像頭是270。
iPhone的攝像頭正向就是物理設(shè)備的正上方,對應(yīng)的正向角度是0,所以輸出的圖像是正向的。
其實(shí)不預(yù)覽應(yīng)用程序也能正確獲取到攝像頭的輸入,但是一般應(yīng)用打開攝像頭后都會在屏幕上顯示當(dāng)前拍攝到的畫面,這是用戶的基本的使用體驗(yàn)。正確的預(yù)覽圖像就是讓攝像頭輸出的圖像能夠正確的在屏幕上顯示給用戶。
如上節(jié)所述,攝像頭采集到的圖像按照orientation旋轉(zhuǎn)和渲染坐標(biāo)系對齊即可,這樣就能正確顯示圖像了。不過這是在屏幕方向鎖定的情況下,就是渲染坐標(biāo)系始終在物理屏幕的左上方。
如果我們打開了設(shè)備陀螺儀(鎖屏),屏幕可以在四個方向上切換,對應(yīng)渲染的坐標(biāo)原點(diǎn)(藍(lán)點(diǎn))會在物理屏幕的四個角上切換。
切換方向的角度可以通過activity.getWindowManager().getDefaultDisplay().getRotation();
獲取,和前后攝像頭無關(guān):
這個角度可以理解為,以物理設(shè)備左上角為原點(diǎn)的渲染坐標(biāo)系(聽筒左邊的角點(diǎn)),需要順時針旋轉(zhuǎn)多少度,才能變成當(dāng)前的渲染坐標(biāo)系。打開陀螺儀后,無論手機(jī)怎么旋轉(zhuǎn),當(dāng)前的渲染坐標(biāo)系永遠(yuǎn)以絕對左上角為原點(diǎn)(視覺左上方角點(diǎn))。這個角度恰好和物理旋轉(zhuǎn)的角度相反。
我們只要根據(jù)當(dāng)前的切換角度+攝像頭正向角度正確地設(shè)置顯示角度就行了,官方文檔也有現(xiàn)成的適配代碼,詳見setDisplayOrientation。
注意,setDisplayOrientation只會對預(yù)覽顯示的圖像有影響,并不會影響onPreviewFrame回調(diào)的數(shù)據(jù)。
?public?static?void?setCameraDisplayOrientation(Activity?activity,?????????int?cameraId,?android.hardware.Camera?camera)?{ ?????android.hardware.Camera.CameraInfo?info?=?????????????new?android.hardware.Camera.CameraInfo(); ?????android.hardware.Camera.getCameraInfo(cameraId,?info);?????int?rotation?=?activity.getWindowManager().getDefaultDisplay() ?????????????.getRotation();?????int?degrees?=?0;?????switch?(rotation)?{?????????case?Surface.ROTATION_0:?degrees?=?0;?break;?????????case?Surface.ROTATION_90:?degrees?=?90;?break;?????????case?Surface.ROTATION_180:?degrees?=?180;?break;?????????case?Surface.ROTATION_270:?degrees?=?270;?break; ?????}?????int?result;?????if?(info.facing?==?Camera.CameraInfo.CAMERA_FACING_FRONT)?{ ?????????result?=?(info.orientation?+?degrees)?%?360; ?????????result?=?(360?-?result)?%?360;??//?compensate?the?mirror ?????}?else?{??//?back-facing ?????????result?=?(info.orientation?-?degrees?+?360)?%?360; ?????} ?????camera.setDisplayOrientation(result); ?}
如果還不理解這段代碼的意思,看下這張圖就明白了:
藍(lán)點(diǎn)是渲染的坐標(biāo)原點(diǎn),紅點(diǎn)是輸出圖片的原點(diǎn)。每一次旋轉(zhuǎn)圖片的原點(diǎn)就會變換到絕對位置的左上角,setDisplayOrientation要設(shè)置的值就是圖像要順時針旋轉(zhuǎn)的角度,使圖片能在渲染坐標(biāo)系中正確顯示。從圖中也能看出來,它就是第二列的箭頭需要順時針旋轉(zhuǎn)到第一列箭頭的角度。