手機攝像頭(Camera)的圖像數(shù)據(jù)來源于攝像頭硬件的圖像傳感器,這個圖像傳感器被固定到手機上后會有一個默認的取景方向祷蝌,這個取景方向恰好是當手機向左側(cè)橫放時的方向,其坐標原點位于手機橫放時的左上角帆卓。手機的正常方向和圖像傳感器默認取景方向示意圖如下:
-
Camera預覽方向處理
從上面的示意圖可知巨朦,圖像傳感器的取景方向與手機正常方向成90度夾角,按理來說鳞疲,當我們以正常的手機方向打開相機(Camera)時罪郊,看到的預覽圖像應該是橫向的。但是尚洽,當我們打開系統(tǒng)相機后悔橄,看到的預覽圖像卻是正常的,即預覽圖像與手機方向一致。這是因為系統(tǒng)自帶的相機在Android系統(tǒng)底層根據(jù)當前手機屏幕的方向?qū)D像傳感器采集到的數(shù)據(jù)進行了旋轉(zhuǎn)癣疟,所以無論我們怎么旋轉(zhuǎn)手機屏幕挣柬,看到的相機預覽圖片始終是”正常”的睛挚。而對于自定義的相機邪蛔,如果沒有對圖像傳感器采集的圖片進行旋轉(zhuǎn)處理,那么看到的預覽圖片就是橫向的扎狱,效果如下圖所示:
為了解決自定義相機預覽方向不正常情況侧到,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度的情況豌骏。
具體代碼如下:
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度;當橫著拿著手機拍攝時撩嚼,得到的照片看起來才是正常的,挖帘。效果如下圖所示完丽。
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糕再。
具體代碼如下:
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;
}
效果演示:
————————————————
原文鏈接:https://blog.csdn.net/andrexpert/article/details/54388929