Camera 圖像傳感器取景方向 預(yù)覽方向

手機(jī)攝像頭(Camera)的圖像數(shù)據(jù)來源于攝像頭硬件的圖像傳感器,這個(gè)圖像傳感器被固定到手機(jī)上后會(huì)有一個(gè)默認(rèn)的取景方向,這個(gè)取景方向恰好是當(dāng)手機(jī)向左側(cè)橫放時(shí)的方向,其坐標(biāo)原點(diǎn)位于手機(jī)橫放時(shí)的左上角。手機(jī)的正常方向和圖像傳感器默認(rèn)取景方向示意圖如下:


image.png
  1. Camera預(yù)覽方向處理
    從上面的示意圖可知,圖像傳感器的取景方向與手機(jī)正常方向成90度夾角,按理來說,當(dāng)我們以正常的手機(jī)方向打開相機(jī)(Camera)時(shí),看到的預(yù)覽圖像應(yīng)該是橫向的。但是,當(dāng)我們打開系統(tǒng)相機(jī)后,看到的預(yù)覽圖像卻是正常的,即預(yù)覽圖像與手機(jī)方向一致。這是因?yàn)橄到y(tǒng)自帶的相機(jī)在Android系統(tǒng)底層根據(jù)當(dāng)前手機(jī)屏幕的方向?qū)D像傳感器采集到的數(shù)據(jù)進(jìn)行了旋轉(zhuǎn),所以無論我們?cè)趺葱D(zhuǎn)手機(jī)屏幕,看到的相機(jī)預(yù)覽圖片始終是”正?!钡?。而對(duì)于自定義的相機(jī),如果沒有對(duì)圖像傳感器采集的圖片進(jìn)行旋轉(zhuǎn)處理,那么看到的預(yù)覽圖片就是橫向的,效果如下圖所示:


    image.png

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

首先,通過Display的getOrientation()獲得當(dāng)前手機(jī)的方向,如Surface.ROTATION_0表示手機(jī)豎屏?xí)r正常方向、Surface.ROTATION_90表示手機(jī)方向向右手邊橫向放置等(沿順時(shí)針判斷)。其中,Display display = getWindowManager().getDefaultDisplay()獲得。
其次,對(duì)于后置攝像頭來說,它的預(yù)覽成像為CameraInfo.orientatio- phoneDegree,但由于這個(gè)值可能為負(fù),角度值不能為負(fù)故需要加上360求正;對(duì)于前置攝像頭(front camera)來說,它的預(yù)覽圖像在旋轉(zhuǎn)之前是水平翻轉(zhuǎn)的,也就是前置攝像頭的預(yù)覽成像是沿圖像的中央垂直線翻轉(zhuǎn)過來,就像用戶照鏡子一樣的效果。因此,在得到前置攝像頭的旋轉(zhuǎn)角度后(rotation = CameraInfo.orientatio + degrees),還需要對(duì)其進(jìn)行水平翻轉(zhuǎn)(rotation = 360-rotation),即取rotation的負(fù)數(shù)即可,但是由于旋轉(zhuǎn)的角度不能是負(fù)數(shù),因此再加上360求正。其中,CameraInfo.orientatio是圖像感應(yīng)器相對(duì)于手機(jī)豎直正常方向的角度值、手機(jī)方向?yàn)橄鄬?duì)于豎直正常方向沿順時(shí)針轉(zhuǎn)動(dòng)的方向值。另外,當(dāng)我們得到前后置攝像頭旋轉(zhuǎn)的方向后還需要對(duì)360求余,以防止旋轉(zhuǎn)的角度超過一周360度的情況。


image.png

具體代碼如下:

private int getPreviewRotateDegree(){
    int phoneDegree = 0;
    int result = 0;
    //獲得手機(jī)方向
    int phoneRotate =getWindowManager().getDefaultDisplay().getOrientation();
    //得到手機(jī)的角度
    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
    }
    //分別計(jì)算前后置攝像頭需要旋轉(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;
}
 
//進(jìn)行Camera預(yù)覽旋轉(zhuǎn)
Camera mCamera = Camera.open();
int rotateDegree = getPreviewRotateDegree();
mCamera.setDisplayOrientation(rotateDegree);

注意:上述方法適用于默認(rèn)預(yù)覽為豎屏應(yīng)用,setDisplayOrientation (int degrees)只對(duì)預(yù)覽時(shí)旋轉(zhuǎn)圖片有效,但對(duì)onPreviewFrame(byte[],Camera)、JPEG拍照、視頻錄制的圖片旋轉(zhuǎn)無效。
2.Camera拍照方向處理
由于使用Camera進(jìn)行拍照時(shí),是直接將圖像傳感器采集到的圖像數(shù)據(jù)直接存儲(chǔ)到Sdcard卡,它通常不與預(yù)覽時(shí)看到的畫面方向一致,而是與圖像傳感器的方向一致。也就是說,當(dāng)我們豎著拿著手機(jī)拍攝時(shí),得到的照片看起來是不正常的(橫向的),這是因?yàn)樨Q著拿著手機(jī)正好與圖像傳感器的方向相差了90度;當(dāng)橫著拿著手機(jī)拍攝時(shí),得到的照片看起來才是正常的,。效果如下圖所示。


image.png

Camera拍攝照片方向的處理與手機(jī)的方向緊密相關(guān),而由于拍攝照片時(shí)手機(jī)的方向是不確定的,因此需要手機(jī)的方向感應(yīng)器(OrientationEventListener)來捕獲手機(jī)的實(shí)時(shí)旋轉(zhuǎn)角度,當(dāng)手機(jī)方向發(fā)現(xiàn)偏轉(zhuǎn)時(shí)OrientationEventListener的onOrientationChanged(int orientation)方法會(huì)立即被回調(diào),orientation即為實(shí)時(shí)變化的角度。旋轉(zhuǎn)方法:
首先,為了使相機(jī)對(duì)方向不那么敏感,可以采用一個(gè)范圍來限定手機(jī)當(dāng)前方向的角度值,比如當(dāng)手機(jī)的方向處于45度~ 90度時(shí),我們就認(rèn)定手機(jī)當(dāng)前轉(zhuǎn)動(dòng)的角度為90度,依次類推得到手機(jī)大概的方向角度值。
其次,計(jì)算前后置攝像頭需要旋轉(zhuǎn)的角度。Camera的預(yù)覽效果是獲得圖像傳感器采集的圖像數(shù)據(jù)后再將其顯示在顯示屏上,而拍攝照片則是直接將圖像傳感器采集的圖像數(shù)據(jù)保存到Sdcard上,因此,它們處理旋轉(zhuǎn)時(shí)的角度計(jì)算是不同的。由于圖像傳感器的取景方向與手機(jī)豎直方向恰好相差90度,因此,對(duì)于后置攝像頭來說,其旋轉(zhuǎn)的角度應(yīng)該手機(jī)實(shí)際變化的角度加上圖像傳感器與手機(jī)之間的夾角,即mOrientation=cameraInfo.orientation +phoneDegree;對(duì)于前置攝像頭來說,旋轉(zhuǎn)的角度mOrientation=cameraInfo.orientation – phoneDegree。以手機(jī)方向改變270度為例,效果如下圖(2)所示,后置攝像頭需旋轉(zhuǎn)的角度為(270+90),可見剛好為360度使攝像頭與圖像傳感器方向一致,那么旋轉(zhuǎn)的角度進(jìn)行求余處理后剛好為0。由于前置攝像頭是水平翻轉(zhuǎn)的,因此需要對(duì)需要進(jì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) {
//計(jì)算手機(jī)當(dāng)前方向的角度值
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;
}
//分別計(jì)算前后置攝像頭需要旋轉(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;
}
};
//啟動(dòng)方向感應(yīng)器
mOrEventListener.enable();
}

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

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) {
//獲得圖片拍攝角度,第二個(gè)的作用是如果這個(gè)屬性不存在,則作為默認(rèn)值返回
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)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容