Camera 圖像傳感器取景方向 預覽方向

手機攝像頭(Camera)的圖像數(shù)據(jù)來源于攝像頭硬件的圖像傳感器,這個圖像傳感器被固定到手機上后會有一個默認的取景方向祷蝌,這個取景方向恰好是當手機向左側(cè)橫放時的方向,其坐標原點位于手機橫放時的左上角帆卓。手機的正常方向和圖像傳感器默認取景方向示意圖如下:


image.png
  1. Camera預覽方向處理
    從上面的示意圖可知巨朦,圖像傳感器的取景方向與手機正常方向成90度夾角,按理來說鳞疲,當我們以正常的手機方向打開相機(Camera)時罪郊,看到的預覽圖像應該是橫向的。但是尚洽,當我們打開系統(tǒng)相機后悔橄,看到的預覽圖像卻是正常的,即預覽圖像與手機方向一致。這是因為系統(tǒng)自帶的相機在Android系統(tǒng)底層根據(jù)當前手機屏幕的方向?qū)D像傳感器采集到的數(shù)據(jù)進行了旋轉(zhuǎn)癣疟,所以無論我們怎么旋轉(zhuǎn)手機屏幕挣柬,看到的相機預覽圖片始終是”正常”的睛挚。而對于自定義的相機邪蛔,如果沒有對圖像傳感器采集的圖片進行旋轉(zhuǎn)處理,那么看到的預覽圖片就是橫向的扎狱,效果如下圖所示:


    image.png

    為了解決自定義相機預覽方向不正常情況侧到,Android系統(tǒng)提供了一個API來手動設置Camera的預覽方向,即Camera.setDisplayOrientation(int rotateDegree)淤击,默認情況下該方法的值為0匠抗,與圖像傳感器取景方向一致。旋轉(zhuǎn)方法:

首先污抬,通過Display的getOrientation()獲得當前手機的方向汞贸,如Surface.ROTATION_0表示手機豎屏時正常方向、Surface.ROTATION_90表示手機方向向右手邊橫向放置等(沿順時針判斷)印机。其中矢腻,Display display = getWindowManager().getDefaultDisplay()獲得。
其次射赛,對于后置攝像頭來說多柑,它的預覽成像為CameraInfo.orientatio- phoneDegree,但由于這個值可能為負咒劲,角度值不能為負故需要加上360求正顷蟆;對于前置攝像頭(front camera)來說,它的預覽圖像在旋轉(zhuǎn)之前是水平翻轉(zhuǎn)的腐魂,也就是前置攝像頭的預覽成像是沿圖像的中央垂直線翻轉(zhuǎn)過來帐偎,就像用戶照鏡子一樣的效果。因此蛔屹,在得到前置攝像頭的旋轉(zhuǎn)角度后(rotation = CameraInfo.orientatio + degrees)削樊,還需要對其進行水平翻轉(zhuǎn)(rotation = 360-rotation),即取rotation的負數(shù)即可兔毒,但是由于旋轉(zhuǎn)的角度不能是負數(shù)漫贞,因此再加上360求正。其中育叁,CameraInfo.orientatio是圖像感應器相對于手機豎直正常方向的角度值迅脐、手機方向為相對于豎直正常方向沿順時針轉(zhuǎn)動的方向值。另外豪嗽,當我們得到前后置攝像頭旋轉(zhuǎn)的方向后還需要對360求余谴蔑,以防止旋轉(zhuǎn)的角度超過一周360度的情況豌骏。


image.png

具體代碼如下:

private int getPreviewRotateDegree(){
    int phoneDegree = 0;
    int result = 0;
    //獲得手機方向
    int phoneRotate =getWindowManager().getDefaultDisplay().getOrientation();
    //得到手機的角度
    switch (phoneRotate) {
        case Surface.ROTATION_0: phoneDegree = 0; break;        //0
        case Surface.ROTATION_90: phoneDegree = 90; break;      //90
        case Surface.ROTATION_180: phoneDegree = 180; break;    //180
        case Surface.ROTATION_270: phoneDegree = 270; break;    //270
    }
    //分別計算前后置攝像頭需要旋轉(zhuǎn)的角度
    Camera.CameraInfo cameraInfo = new CameraInfo();
    if(isFrontCamera){          
        Camera.getCameraInfo(Camera.CameraInfo.CAMERA_FACING_FRONT, cameraInfo);
        result = (cameraInfo.orientation + phoneDegree) % 360;
        result = (360 - result) % 360;
    }else{
        Camera.getCameraInfo(Camera.CameraInfo.CAMERA_FACING_BACK, cameraInfo);
        result = (cameraInfo.orientation - phoneDegree +360) % 360;
    }
    return result;
}
 
//進行Camera預覽旋轉(zhuǎn)
Camera mCamera = Camera.open();
int rotateDegree = getPreviewRotateDegree();
mCamera.setDisplayOrientation(rotateDegree);

注意:上述方法適用于默認預覽為豎屏應用,setDisplayOrientation (int degrees)只對預覽時旋轉(zhuǎn)圖片有效隐锭,但對onPreviewFrame(byte[],Camera)窃躲、JPEG拍照、視頻錄制的圖片旋轉(zhuǎn)無效钦睡。
2.Camera拍照方向處理
由于使用Camera進行拍照時蒂窒,是直接將圖像傳感器采集到的圖像數(shù)據(jù)直接存儲到Sdcard卡,它通常不與預覽時看到的畫面方向一致荞怒,而是與圖像傳感器的方向一致洒琢。也就是說,當我們豎著拿著手機拍攝時挣输,得到的照片看起來是不正常的(橫向的)纬凤,這是因為豎著拿著手機正好與圖像傳感器的方向相差了90度;當橫著拿著手機拍攝時撩嚼,得到的照片看起來才是正常的,挖帘。效果如下圖所示完丽。


image.png

Camera拍攝照片方向的處理與手機的方向緊密相關(guān),而由于拍攝照片時手機的方向是不確定的拇舀,因此需要手機的方向感應器(OrientationEventListener)來捕獲手機的實時旋轉(zhuǎn)角度逻族,當手機方向發(fā)現(xiàn)偏轉(zhuǎn)時OrientationEventListener的onOrientationChanged(int orientation)方法會立即被回調(diào),orientation即為實時變化的角度骄崩。旋轉(zhuǎn)方法:
首先聘鳞,為了使相機對方向不那么敏感,可以采用一個范圍來限定手機當前方向的角度值要拂,比如當手機的方向處于45度~ 90度時抠璃,我們就認定手機當前轉(zhuǎn)動的角度為90度,依次類推得到手機大概的方向角度值脱惰。
其次搏嗡,計算前后置攝像頭需要旋轉(zhuǎn)的角度。Camera的預覽效果是獲得圖像傳感器采集的圖像數(shù)據(jù)后再將其顯示在顯示屏上拉一,而拍攝照片則是直接將圖像傳感器采集的圖像數(shù)據(jù)保存到Sdcard上采盒,因此,它們處理旋轉(zhuǎn)時的角度計算是不同的蔚润。由于圖像傳感器的取景方向與手機豎直方向恰好相差90度磅氨,因此,對于后置攝像頭來說嫡纠,其旋轉(zhuǎn)的角度應該手機實際變化的角度加上圖像傳感器與手機之間的夾角烦租,即mOrientation=cameraInfo.orientation +phoneDegree决瞳;對于前置攝像頭來說,旋轉(zhuǎn)的角度mOrientation=cameraInfo.orientation – phoneDegree左权。以手機方向改變270度為例皮胡,效果如下圖(2)所示,后置攝像頭需旋轉(zhuǎn)的角度為(270+90)赏迟,可見剛好為360度使攝像頭與圖像傳感器方向一致屡贺,那么旋轉(zhuǎn)的角度進行求余處理后剛好為0。由于前置攝像頭是水平翻轉(zhuǎn)的锌杀,因此需要對需要進行水平翻轉(zhuǎn)處理甩栈,也就是180度的問題,最終旋轉(zhuǎn)的角度為|(90-270)|=180糕再。


image.png

image.png

具體代碼如下:
private void startOrientationListener() {
OrientationEventListener mOrEventListener = new OrientationEventListener(mContext) {
@Override
public void onOrientationChanged(int orientation) {
//計算手機當前方向的角度值
int phoneDegree = 0;
if (((orientation >= 0) && (orientation <= 45))|| (orientation > 315) &&(orientation<=360)) {
phoneDegree = 0;
} else if ((orientation > 45) && (orientation <= 135)) {
phoneDegree = 90;
} else if ((orientation > 135) && (orientation <= 225)) {
phoneDegree = 180;
} else if ((orientation > 225) && (orientation <= 315)) {
phoneDegree = 270;
}
//分別計算前后置攝像頭需要旋轉(zhuǎn)的角度
Camera.CameraInfo cameraInfo = new CameraInfo();
if(mFragment.isFrontCamera()){
Camera.getCameraInfo(Camera.CameraInfo.CAMERA_FACING_FRONT, cameraInfo);
mOrientation = (cameraInfo.orientation - phoneDegree +360) % 360;
}else{
Camera.getCameraInfo(Camera.CameraInfo.CAMERA_FACING_BACK, cameraInfo);
mOrientation = (cameraInfo.orientation + phoneDegree) % 360;
}
};
//啟動方向感應器
mOrEventListener.enable();
}

注意:由于上述涉及的角度值都是正數(shù)且不大于360度量没,因此,需要對相關(guān)角度進行求正和求余處理突想。
3.JPEG圖片方向處理
有這么一種情況殴蹄,如果有一款自定義相機的拍照功能忘記處理圖片旋轉(zhuǎn)的問題,那么我們
在使用的過程中就會看到拍下的JPEG照片顯示方向“不正郴#”袭灯。針對于這種情況,可以通過Android API提供的ExifInterface接口來解決绑嘹,該接口存儲了指定JPEG圖片的詳細信息稽荧,比如拍攝時的角度、曝光度工腋、分辨率等等姨丈。旋轉(zhuǎn)方法:
首先,根據(jù)圖片路徑創(chuàng)建一個ExifInterface對象擅腰,再調(diào)用其getAttributeInt(
ExifInterface.TAG_ORIENTATION,ExifInterface.ORIENTATION_NORMAL)方法蟋恬,得到JPEG拍攝時的角度。
其次惕鼓,調(diào)用Matrix的postRotate(degree)方法對圖片進行旋轉(zhuǎn)筋现,然后再使用Bitmap.createBitmap方法得到最終的位圖對象。
具體代碼如下:

public static int getPictureDegress(String filePath) {
int degree = 0;
ExifInterface exifInterface = null;
try {
exifInterface = new ExifInterface(filePath);
} catch (IOException e) {
e.printStackTrace();
}
if (exifInterface != null) {
//獲得圖片拍攝角度箱歧,第二個的作用是如果這個屬性不存在矾飞,則作為默認值返回
int orientation = exifInterface.getAttributeInt(
ExifInterface.TAG_ORIENTATION,
ExifInterface.ORIENTATION_NORMAL);
switch (orientation) {
case ExifInterface.ORIENTATION_ROTATE_90:
degree = 90;
break;
case ExifInterface.ORIENTATION_ROTATE_180:
degree = 180;
break;
case ExifInterface.ORIENTATION_ROTATE_270:
degree = 270;
break;
default:
degree = 0;
break;
}
return degree;
}
return 0;
}
/** 旋轉(zhuǎn)圖片
* @param imgPath 原圖路徑
* @ param imgPath
*/
public static Bitmap setBitmapDegreeZero(String imgPath) {
Bitmap mBitmap = null;
int degree = getPictureDegress(imgPath);
if (degree != 0) {
mBitmap = BitmapFactory.decodeFile(imgPath);
Matrix matrix = new Matrix();
matrix.postRotate(degree);
mBitmap = Bitmap.createBitmap(mBitmap, 0, 0, mBitmap.getWidth(),
mBitmap.getHeight(), matrix, true);
}
return mBitmap;
}

效果演示:

image.png

————————————————
原文鏈接:https://blog.csdn.net/andrexpert/article/details/54388929

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市呀邢,隨后出現(xiàn)的幾起案子洒沦,更是在濱河造成了極大的恐慌,老刑警劉巖价淌,帶你破解...
    沈念sama閱讀 206,378評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件申眼,死亡現(xiàn)場離奇詭異瞒津,居然都是意外死亡,警方通過查閱死者的電腦和手機括尸,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評論 2 382
  • 文/潘曉璐 我一進店門巷蚪,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人濒翻,你說我怎么就攤上這事屁柏。” “怎么了有送?”我有些...
    開封第一講書人閱讀 152,702評論 0 342
  • 文/不壞的土叔 我叫張陵淌喻,是天一觀的道長。 經(jīng)常有香客問我雀摘,道長裸删,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,259評論 1 279
  • 正文 為了忘掉前任阵赠,我火速辦了婚禮涯塔,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘豌注。我一直安慰自己伤塌,他們只是感情好,可當我...
    茶點故事閱讀 64,263評論 5 371
  • 文/花漫 我一把揭開白布轧铁。 她就那樣靜靜地躺著,像睡著了一般旦棉。 火紅的嫁衣襯著肌膚如雪齿风。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,036評論 1 285
  • 那天绑洛,我揣著相機與錄音救斑,去河邊找鬼。 笑死真屯,一個胖子當著我的面吹牛脸候,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播绑蔫,決...
    沈念sama閱讀 38,349評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼运沦,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了配深?” 一聲冷哼從身側(cè)響起携添,我...
    開封第一講書人閱讀 36,979評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎篓叶,沒想到半個月后烈掠,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體羞秤,經(jīng)...
    沈念sama閱讀 43,469評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,938評論 2 323
  • 正文 我和宋清朗相戀三年左敌,在試婚紗的時候發(fā)現(xiàn)自己被綠了瘾蛋。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,059評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡矫限,死狀恐怖哺哼,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情奇唤,我是刑警寧澤幸斥,帶...
    沈念sama閱讀 33,703評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站咬扇,受9級特大地震影響甲葬,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜懈贺,卻給世界環(huán)境...
    茶點故事閱讀 39,257評論 3 307
  • 文/蒙蒙 一经窖、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧梭灿,春花似錦画侣、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,262評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至皮迟,卻和暖如春搬泥,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背伏尼。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工忿檩, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人爆阶。 一個月前我還...
    沈念sama閱讀 45,501評論 2 354
  • 正文 我出身青樓燥透,卻偏偏與公主長得像,于是被迫代替她去往敵國和親辨图。 傳聞我的和親對象是個殘疾皇子班套,可洞房花燭夜當晚...
    茶點故事閱讀 42,792評論 2 345

推薦閱讀更多精彩內(nèi)容