1. 程式人生 > >Android多媒體技術(一)Camera實時視訊採集預覽、拍照、JPEG圖片方向的處理

Android多媒體技術(一)Camera實時視訊採集預覽、拍照、JPEG圖片方向的處理

Camera實時視訊採集預覽、拍照、JPEG圖片方向的處理                      

   作者:   

 蔣東國

   時間:

 2017年1月12日 星期四                                          

   應用來源:  

 hzzbj

   部落格地址:

 http://blog.csdn.net/andrexpert/article/details/54388929

  手機攝像頭(Camera)的影象資料來源於攝像頭硬體的影象感測器,這個影象感測器被固定到手機上後會有一個預設的取景方向,這個取景方向恰好是當手機向左側橫放時的方向,其座標原點位於手機橫放時的左上角。手機的正常方向和影象感測器預設取景方向示意圖如下:


1. Camera預覽方向處理
  從上面的示意圖可知,影象感測器的取景方向與手機正常方向成90度夾角,按理來說,當我們以正常的手機方向開啟相機(Camera)時,看到的預覽影象應該是橫向的。但是,當我們開啟系統相機後,看到的預覽影象卻是正常的,即預覽影象與手機方向一致。這是因為系統自帶的相機在Android系統底層根據當前手機螢幕的方向對影象感測器採集到的資料進行了旋轉,所以無論我們怎麼旋轉手機螢幕,看到的相機預覽圖片始終是”正常”的。而對於自定義的相機,如果沒有對影象感測器採集的圖片進行旋轉處理,那麼看到的預覽圖片就是橫向的,效果如下圖所示:


  為了解決自定義相機預覽方向不正常情況,Android系統提供了一個API來手動設定Camera的預覽方向,即Camera.setDisplayOrientation(int rotateDegree),預設情況下該方法的值為0,與影象感測器取景方向一致。

旋轉方法:

 首先,通過Display的getOrientation()獲得當前手機的方向,如Surface.ROTATION_0表示手機豎屏時正常方向、Surface.ROTATION_90表示手機方向向右手邊橫向放置等(沿順時針判斷)。其中,Display display = getWindowManager().getDefaultDisplay()獲得。
 其次,對於後置攝像頭來說,它的預覽成像為CameraInfo.orientatio- phoneDegree,但由於這個值可能為負,角度值不能為負故需要加上360求正;對於前置攝像頭(front camera)來說,它的預覽影象在旋轉之前是水平翻轉的,也就是前置攝像頭的預覽成像是沿影象的中央垂直線翻轉過來,就像使用者照鏡子一樣的效果。因此,在得到前置攝像頭的旋轉角度後(rotation = CameraInfo.orientatio + degrees),還需要對其進行水平翻轉(rotation = 360-rotation),即取rotation的負數即可,但是由於旋轉的角度不能是負數,因此再加上360求正。其中,CameraInfo.orientatio是影象感應器相對於手機豎直正常方向的角度值、手機方向為相對於豎直正常方向沿順時針轉動的方向值。另外,當我們得到前後置攝像頭旋轉的方向後還需要對360求餘,以防止旋轉的角度超過一週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
	}
	//分別計算前後置攝像頭需要旋轉的角度
	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預覽旋轉
Camera mCamera = Camera.open();
int rotateDegree = getPreviewRotateDegree();
mCamera.setDisplayOrientation(rotateDegree);

  注意:上述方法適用於預設預覽為豎屏應用,setDisplayOrientation (int degrees)只對預覽時旋轉圖片有效,但對onPreviewFrame(byte[],Camera)、JPEG拍照、視訊錄製的圖片旋轉無效。

2.Camera拍照方向處理
  由於使用Camera進行拍照時,是直接將影象感測器採集到的影象資料直接儲存到Sdcard卡,它通常不與預覽時看到的畫面方向一致,而是與影象感測器的方向一致。也就是說,當我們豎著拿著手機拍攝時,得到的照片看起來是不正常的(橫向的),這是因為豎著拿著手機正好與影象感測器的方向相差了90度;當橫著拿著手機拍攝時,得到的照片看起來才是正常的,。效果如下圖所示。


  Camera拍攝照片方向的處理與手機的方向緊密相關,而由於拍攝照片時手機的方向是不確定的,因此需要手機的方向感應器(OrientationEventListener)來捕獲手機的實時旋轉角度,當手機方向發現偏轉時OrientationEventListener的onOrientationChanged(int orientation)方法會立即被回撥,orientation即為實時變化的角度。旋轉方法:
  首先,為了使相機對方向不那麼敏感,可以採用一個範圍來限定手機當前方向的角度值,比如當手機的方向處於45度~ 90度時,我們就認定手機當前轉動的角度為90度,依次類推得到手機大概的方向角度值。
  其次,計算前後置攝像頭需要旋轉的角度。Camera的預覽效果是獲得影象感測器採集的影象資料後再將其顯示在顯示屏上,而拍攝照片則是直接將影象感測器採集的影象資料儲存到Sdcard上,因此,它們處理旋轉時的角度計算是不同的。由於影象感測器的取景方向與手機豎直方向恰好相差90度,因此,對於後置攝像頭來說,其旋轉的角度應該手機實際變化的角度加上影象感測器與手機之間的夾角,即mOrientation=cameraInfo.orientation +phoneDegree;對於前置攝像頭來說,旋轉的角度mOrientation=cameraInfo.orientation – phoneDegree。以手機方向改變270度為例,效果如下圖(2)所示,後置攝像頭需旋轉的角度為(270+90),可見剛好為360度使攝像頭與影象感測器方向一致,那麼旋轉的角度進行求餘處理後剛好為0。由於前置攝像頭是水平翻轉的,因此需要對需要進行水平翻轉處理,也就是180度的問題,最終旋轉的角度為|(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;
}
//分別計算前後置攝像頭需要旋轉的角度
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();
}
注意:由於上述涉及的角度值都是正數且不大於360度,因此,需要對相關角度進行求正和求餘處理。
3.JPEG圖片方向處理
  有這麼一種情況,如果有一款自定義相機的拍照功能忘記處理圖片旋轉的問題,那麼我們
在使用的過程中就會看到拍下的JPEG照片顯示方向“不正常”。針對於這種情況,可以通過Android API提供的ExifInterface介面來解決,該介面儲存了指定JPEG圖片的詳細資訊,比如拍攝時的角度、曝光度、解析度等等。旋轉方法:
  首先,根據圖片路徑建立一個ExifInterface物件,再呼叫其getAttributeInt(
ExifInterface.TAG_ORIENTATION,ExifInterface.ORIENTATION_NORMAL)方法,得到JPEG拍攝時的角度。

  其次,呼叫Matrix的postRotate(degree)方法對圖片進行旋轉,然後再使用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;
}
/** 旋轉圖片
* @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;
}

效果演示:


關於參考資料:
Android開發實踐:掌握Camera的預覽方向和拍照方向
http://www.tuicool.com/articles/AnUBBnR