Androidカメラの原理解析(ソースコード)
39212 ワード
アプリケーション開発において、画像データは、1つの会社にとって非常に重要であり、例えば、画像資料をアップロードしたり、ユーザーの顔を修正したりするなど、カメラやアルバムの使用から離れられない.iosプラットフォームでは、システムカメラやアルバムを直接呼び出すと、すべてを解決できます.しかし、Androidプラットフォームにとって、システムカメラやアルバムを直接呼び出すのは、適合や体験に問題が多い.具体的な原因は、Androidブランドが多すぎて雑で、性能が異なることも知られている.これに鑑みて、開発の過程で、同様の問題に遭遇した場合、カメラやアルバムの機能を実現して、体験が完全であることを保証することをお勧めします.このブログでは、カメラの実現に重点を置きます.
まず、2つのgithubプロジェクトをお勧めします.直接使えるカメラとアルバムです.また、連絡先セレクタも推奨されます.
カメラ:CameraDemo(カスタムカメラ)
アルバム:ImageSelector
連絡先:ContactSelector(連絡先セレクタ)
一、Cameraを開く
二、Cameraパラメータの設定
デフォルトサイズは自由に設定できますが、ここでは携帯電話の解像度をデフォルトサイズにします.
1.指定された解像度に基づいてカメラの最適な解像度を検索し、設定する
2.指定された解像度に基づいてカメラの最適なプレビュー解像度を検索し、設定する
3.最適解像度適合アルゴリズム(先ソート)
4.フォーカスモード一部のスマートフォンのため、フロントカメラはフォーカスモードがなく、フォーカスパラメータの設定はフロントカメラを区別すべきである
5.画像品質PixelFormatには複数のモードがあり、ソースコードには解がある.
6.点滅灯及び縦画面レンズ調整
7.カメラ異常傍受
完全なパラメータ設定コード:
三、ピント合わせ
クリックフォーカスを実現するには、フォーカスリングを実現するには、フォーカスリングViewをカスタマイズする必要がある.
1.カスタムフォーカスリングView-CameraFocusView
コア機能は、フォーカスリングが縮小し、緑になることです.アニメーションでフォーカスリングの半径を変更すればいいです.
2.レイアウト画面
フォーカスリングのカスタムViewにインタフェース全体のタッチイベントを取得させる
3.フォーカスインタフェースの定義
i.インタフェースの定義
ii.フォーカスリングViewタッチイベントのトリガインタフェース:
4.CameraSurfaceViewでフォーカスを実現
i.合焦領域の算出
ii.パラメータを設定してフォーカス
四、写真を撮る
画像のプレビューを容易にするためには、画像を処理する必要があるので、カメラの撮影時の方向を知る必要があるので、写真を撮るにはまず写真の方向パラメータを設定する必要があります.
1.Camera方向リスナー
2.写真方向パラメータの設定
3.画像の保存
プレビューを容易にするためには,異なる方向の画像に対して順方向の処理が必要である.
五、カメラの切り替え
六、フラッシュをオンまたはオフにする
注意事項 Android6.0以上の権限が締め付けられているので、カメラを使用する前に、PermissionsModelで権限判断をしてください.具体的なAndroid 6.0アクセス権 一部のスマートフォン、フロントカメラのフォーカスモードがなく、フォーカスパラメータの設定はフロントカメラ を区別しなければならない. Android5.0以降は、公式推奨ではCamera 2が使用されているが、この例では新バージョンは使用されていない.
まず、2つのgithubプロジェクトをお勧めします.直接使えるカメラとアルバムです.また、連絡先セレクタも推奨されます.
カメラ:CameraDemo(カスタムカメラ)
アルバム:ImageSelector
連絡先:ContactSelector(連絡先セレクタ)
一、Cameraを開く
try {
mCamera = Camera.open();//
} catch (RuntimeException e) {
e.printStackTrace();
LogUtil.d(TAG, " , ");
ToastUtil.getInstance().toast(" , ");
return;
}
二、Cameraパラメータの設定
デフォルトサイズは自由に設定できますが、ここでは携帯電話の解像度をデフォルトサイズにします.
1.指定された解像度に基づいてカメラの最適な解像度を検索し、設定する
private void setCameraParams(int width, int height) {
LogUtil.i(TAG, "setCameraParams width=" + width + " height=" + height);
Camera.Parameters parameters = mCamera.getParameters();
List pictureSizeList = parameters.getSupportedPictureSizes();
sort(pictureSizeList);//
for (Camera.Size size : pictureSizeList) {
LogUtil.i(TAG, " :" + " size.width=" + size.width + " size.height=" + size.height);
}
Camera.Size picSize = getBestSupportedSize(pictureSizeList, ((float) height / width));//
if (null == picSize) {
picSize = parameters.getPictureSize();
}
LogUtil.e(TAG, " :" + "picSize.width=" + picSize.width + " picSize.height=" + picSize.height);
// PictureSize SurfaceView
parameters.setPictureSize(picSize.width, picSize.height);
....
2.指定された解像度に基づいてカメラの最適なプレビュー解像度を検索し、設定する
private void setCameraParams(int width, int height) {
LogUtil.i(TAG, "setCameraParams width=" + width + " height=" + height);
Camera.Parameters parameters = mCamera.getParameters();
/*************************** PreviewSize ********************/
List previewSizeList = parameters.getSupportedPreviewSizes();
sort(previewSizeList);
for (Camera.Size size : previewSizeList) {
LogUtil.i(TAG, " :" + " size.width=" + size.width + " size.height=" + size.height);
}
Camera.Size preSize = getBestSupportedSize(previewSizeList, ((float) height) / width);
if (null != preSize) {
LogUtil.e(TAG, " :" + "preSize.width=" + preSize.width + " preSize.height=" + preSize.height);
parameters.setPreviewSize(preSize.width, preSize.height);
}
......
3.最適解像度適合アルゴリズム(先ソート)
/**
* , , ,
* : ,1. ->2. ->3.
*
* @param sizes
* @return
*/
private Camera.Size getBestSupportedSize(List sizes, float screenRatio) {
Camera.Size largestSize = null;
int largestArea = 0;
for (Camera.Size size : sizes) {
if ((float) size.height / (float) size.width == screenRatio) {
if (size.width == DEFAULT_PHOTO_WIDTH && size.height == DEFAULT_PHOTO_HEIGHT) {
// ,
largestSize = size;
break;
} else if (size.height == DEFAULT_PHOTO_HEIGHT || size.width == DEFAULT_PHOTO_WIDTH) {
largestSize = size;
break;
}
int area = size.height + size.width;
if (area > largestArea) {//
largestArea = area;
largestSize = size;
}
} else if (size.height == DEFAULT_PHOTO_HEIGHT || size.width == DEFAULT_PHOTO_WIDTH) {
largestSize = size;
break;
}
}
if (largestSize == null) {
largestSize = sizes.get(sizes.size() - 1);
}
return largestSize;
}
4.フォーカスモード一部のスマートフォンのため、フロントカメラはフォーカスモードがなく、フォーカスパラメータの設定はフロントカメラを区別すべきである
//
if(cameraId == Camera.CameraInfo.CAMERA_FACING_BACK){
parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);//
}
5.画像品質PixelFormatには複数のモードがあり、ソースコードには解がある.
//
parameters.setJpegQuality(100); //
parameters.setPreviewFormat(PixelFormat.YCbCr_420_SP); //
parameters.setPictureFormat(PixelFormat.JPEG); // JPEG, NV21
6.点滅灯及び縦画面レンズ調整
//
parameters.setFlashMode(Camera.Parameters.FLASH_MODE_OFF);
//
if (mContext.getResources().getConfiguration().orientation != Configuration.ORIENTATION_LANDSCAPE) {
mCamera.setDisplayOrientation(90);
} else {
mCamera.setDisplayOrientation(0);
}
7.カメラ異常傍受
//
mCamera.setErrorCallback(new Camera.ErrorCallback() {
@Override
public void onError(int error, Camera camera) {
String error_str;
switch (error) {
case Camera.CAMERA_ERROR_SERVER_DIED: //
error_str = " ";
break;
case Camera.CAMERA_ERROR_UNKNOWN:
error_str = " , ";
break;
default:
error_str = " , ";
break;
}
ToastUtil.getInstance().toast(error_str);
Log.i(TAG, error_str);
}
});
完全なパラメータ設定コード:
/**
*
*
* @param width
* @param height
*/
private void setCameraParams(int width, int height) {
LogUtil.i(TAG, "setCameraParams width=" + width + " height=" + height);
Camera.Parameters parameters = mCamera.getParameters();
/*************************** PictureSize ********************/
List pictureSizeList = parameters.getSupportedPictureSizes();
sort(pictureSizeList);//
for (Camera.Size size : pictureSizeList) {
LogUtil.i(TAG, " :" + " size.width=" + size.width + " size.height=" + size.height);
}
Camera.Size picSize = getBestSupportedSize(pictureSizeList, ((float) height / width));//
if (null == picSize) {
picSize = parameters.getPictureSize();
}
LogUtil.e(TAG, " :" + "picSize.width=" + picSize.width + " picSize.height=" + picSize.height);
// PictureSize SurfaceView
parameters.setPictureSize(picSize.width, picSize.height);
/*************************** PreviewSize ********************/
List previewSizeList = parameters.getSupportedPreviewSizes();
sort(previewSizeList);
for (Camera.Size size : previewSizeList) {
LogUtil.i(TAG, " :" + " size.width=" + size.width + " size.height=" + size.height);
}
Camera.Size preSize = getBestSupportedSize(previewSizeList, ((float) height) / width);
if (null != preSize) {
LogUtil.e(TAG, " :" + "preSize.width=" + preSize.width + " preSize.height=" + preSize.height);
parameters.setPreviewSize(preSize.width, preSize.height);
}
/*************************** ********************/
if(cameraId == Camera.CameraInfo.CAMERA_FACING_BACK){
parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);//
}
//
parameters.setJpegQuality(100); //
parameters.setPreviewFormat(PixelFormat.YCbCr_420_SP); //
parameters.setPictureFormat(PixelFormat.JPEG); // JPEG, NV21
//
parameters.setFlashMode(Camera.Parameters.FLASH_MODE_OFF);
//
if (mContext.getResources().getConfiguration().orientation != Configuration.ORIENTATION_LANDSCAPE) {
mCamera.setDisplayOrientation(90);
} else {
mCamera.setDisplayOrientation(0);
}
//
mCamera.setErrorCallback(new Camera.ErrorCallback() {
@Override
public void onError(int error, Camera camera) {
String error_str;
switch (error) {
case Camera.CAMERA_ERROR_SERVER_DIED: //
error_str = " ";
break;
case Camera.CAMERA_ERROR_UNKNOWN:
error_str = " , ";
break;
default:
error_str = " , ";
break;
}
ToastUtil.getInstance().toast(error_str);
Log.i(TAG, error_str);
}
});
mCamera.cancelAutoFocus();
mCamera.setParameters(parameters);
}
三、ピント合わせ
クリックフォーカスを実現するには、フォーカスリングを実現するには、フォーカスリングViewをカスタマイズする必要がある.
1.カスタムフォーカスリングView-CameraFocusView
コア機能は、フォーカスリングが縮小し、緑になることです.アニメーションでフォーカスリングの半径を変更すればいいです.
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if(isShow){
if(radius == GREEN_RADIUS){
mPaint.setColor(Color.GREEN);
}
if(centerPoint!=null){
canvas.drawCircle(centerPoint.x, centerPoint.y, radius, mPaint);
}
}
}
private void showAnimView() {
isShow = true;
if (lineAnimator == null) {
lineAnimator = ValueAnimator.ofInt(0, 20);
lineAnimator.setDuration(DURATION_TIME);
lineAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
int animationValue = (Integer) animation
.getAnimatedValue();
if(lastValue!=animationValue&&radius>=(int) ((mScreenWidth * 0.1)-20)){
radius = radius - animationValue;
lastValue = animationValue;
}
isShow = true;
invalidate();
}
});
lineAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
isShow = false;
lastValue = 0;
mPaint.setColor(Color.WHITE);
radius = (int) (mScreenWidth * 0.1);
invalidate();
}
});
}else{
lineAnimator.end();
lineAnimator.cancel();
lineAnimator.setInterpolator(new AccelerateDecelerateInterpolator());
lineAnimator.start();
}
}
2.レイアウト画面
フォーカスリングのカスタムViewにインタフェース全体のタッチイベントを取得させる
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.awen.camera.widget.CameraSurfaceView
android:id="@+id/cameraSurfaceView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<com.awen.camera.widget.CameraFocusView
android:id="@+id/cameraFocusView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
......
3.フォーカスインタフェースの定義
i.インタフェースの定義
/**
*
*/
public interface IAutoFocus {
void autoFocus(float x,float y);
}
ii.フォーカスリングViewタッチイベントのトリガインタフェース:
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_UP:
int x = (int) event.getX();
int y = (int) event.getY();
lastValue = 0;
mPaint.setColor(Color.WHITE);
radius = (int) (mScreenWidth * 0.1);
centerPoint = null;
if(y>TOP_CONTROL_HEIGHT&&y// ( )
centerPoint = new Point(x, y);
showAnimView();
//
if (mIAutoFocus != null) {
mIAutoFocus.autoFocus(event.getX(),event.getY());
}
}
break;
}
return true;
}
4.CameraSurfaceViewでフォーカスを実現
i.合焦領域の算出
private Rect caculateFocusPoint(int x, int y) {
Rect rect = new Rect(x - 100, y - 100, x + 100, y + 100);
int left = rect.left * 2000 / getWidth() - 1000;
int top = rect.top * 2000 / getHeight() - 1000;
int right = rect.right * 2000 / getWidth() - 1000;
int bottom = rect.bottom * 2000 / getHeight() - 1000;
// (-1000,1000) (1000, 1000) ,
left = left < -1000 ? -1000 : left;
top = top < -1000 ? -1000 : top;
right = right > 1000 ? 1000 : right;
bottom = bottom > 1000 ? 1000 : bottom;
return new Rect(left, top, right, bottom);
}
ii.パラメータを設定してフォーカス
private void camerFocus(Rect rect) {
if (mCamera != null) {
Camera.Parameters parameters = mCamera.getParameters();
if(cameraId == Camera.CameraInfo.CAMERA_FACING_BACK){
parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);//
}
if (parameters.getMaxNumFocusAreas() > 0) {
List focusAreas = new ArrayList();
focusAreas.add(new Camera.Area(rect, 1000));
parameters.setFocusAreas(focusAreas);
}
mCamera.cancelAutoFocus(); //
mCamera.setParameters(parameters);
mCamera.autoFocus(this);
}
四、写真を撮る
画像のプレビューを容易にするためには、画像を処理する必要があるので、カメラの撮影時の方向を知る必要があるので、写真を撮るにはまず写真の方向パラメータを設定する必要があります.
1.Camera方向リスナー
/**
* ,
* Created by AwenZeng on 2017/2/21.
*/
public class CameraOrientationDetector extends OrientationEventListener {
int mOrientation;
public CameraOrientationDetector(Context context, int rate) {
super(context, rate);
}
@Override
public void onOrientationChanged(int orientation) {
this.mOrientation = orientation;
if (orientation == OrientationEventListener.ORIENTATION_UNKNOWN) {
return;
}
// , 0°、90°、180° 270°
int newOrientation = ((orientation + 45) / 90 * 90) % 360;
if (newOrientation != mOrientation) {
mOrientation = newOrientation;
}
}
public int getOrientation() {
return mOrientation;
}
}
2.写真方向パラメータの設定
/**
*
*
* @param callback
*/
public void takePicture(Camera.PictureCallback callback) {
if (mCamera != null) {
int orientation = mCameraOrientation.getOrientation();
Camera.Parameters cameraParameter = mCamera.getParameters();
if (orientation == 90) {
cameraParameter.setRotation(90);
cameraParameter.set("rotation", 90);
} else if (orientation == 180) {
cameraParameter.setRotation(180);
cameraParameter.set("rotation", 180);
} else if (orientation == 270) {
cameraParameter.setRotation(270);
cameraParameter.set("rotation", 270);
} else {
cameraParameter.setRotation(0);
cameraParameter.set("rotation", 0);
}
mCamera.setParameters(cameraParameter);
}
mCamera.takePicture(null, null, callback);
}
3.画像の保存
プレビューを容易にするためには,異なる方向の画像に対して順方向の処理が必要である.
public String handlePhoto(byte[] data, int cameraId) {
String filePath = FileUtil.saveFile(data, "/DCIM");
if (!TextUtils.isEmpty(filePath)) {
int degree = BitmapUtil.getPhotoDegree(filePath);
Log.i(TAG, degree + "");
Bitmap bitmap = BitmapFactory.decodeFile(filePath);
Bitmap tBitmap = null;
try {
Log.i(TAG, " :"+"width = " + bitmap.getWidth() + " ------ height = " + bitmap.getHeight());
if (cameraId == Camera.CameraInfo.CAMERA_FACING_BACK) {
switch (degree) {
case 0:
tBitmap = BitmapUtil.rotateBitmap(bitmap, 90);
filePath = BitmapUtil.saveBitmap(tBitmap == null ? bitmap : tBitmap, filePath);
break;
case 90:
tBitmap = BitmapUtil.rotateBitmap(bitmap, 180);
filePath = BitmapUtil.saveBitmap(tBitmap == null ? bitmap : tBitmap, filePath);
break;
case 180:
tBitmap = BitmapUtil.rotateBitmap(bitmap, 270);
filePath = BitmapUtil.saveBitmap(tBitmap == null ? bitmap : tBitmap, filePath);
break;
case 270:
tBitmap = BitmapUtil.rotateBitmap(bitmap, 360);
filePath = BitmapUtil.saveBitmap(tBitmap == null ? bitmap : tBitmap, filePath);
break;
}
} else if (cameraId == Camera.CameraInfo.CAMERA_FACING_FRONT) {
switch (degree) {
case 0:
tBitmap = BitmapUtil.rotateBitmap(bitmap, 270);
filePath = BitmapUtil.saveBitmap(tBitmap == null ? bitmap : tBitmap, filePath);
break;
case 90:
tBitmap = BitmapUtil.rotateBitmap(bitmap, 180);
filePath = BitmapUtil.saveBitmap(tBitmap == null ? bitmap : tBitmap, filePath);
break;
case 180:
tBitmap = BitmapUtil.rotateBitmap(bitmap, 90);
filePath = BitmapUtil.saveBitmap(tBitmap == null ? bitmap : tBitmap, filePath);
break;
case 270:
tBitmap = BitmapUtil.rotateBitmap(bitmap, 360);
filePath = BitmapUtil.saveBitmap(tBitmap == null ? bitmap : tBitmap, filePath);
break;
}
}
} catch (Exception e) {
e.printStackTrace();
//
return "";
} finally {
if (bitmap != null) {
bitmap.recycle();
bitmap = null;
}
if (tBitmap != null) {
tBitmap.recycle();
tBitmap = null;
}
ScannerByReceiver(mContext, filePath);//
}
return filePath;
}
return null;
}
五、カメラの切り替え
/**
*
*/
public void changeCamera(int camera_id) {
mCamera.stopPreview();
mCamera.release();
try {
openCamera(camera_id);
mCamera.setPreviewDisplay(holder);
setCameraParams(DEFAULT_PHOTO_WIDTH, DEFAULT_PHOTO_HEIGHT);
mCamera.startPreview();
} catch (Exception e) {
e.printStackTrace();
}
}
public boolean openCamera(int camera_id) {
LogUtil.i(TAG, "openCamera id = " + camera_id);
try {
mCamera = Camera.open(camera_id); //
cameraId = camera_id;
} catch (Exception e) {
e.printStackTrace();
ToastUtil.getInstance().toast(" ");
LogUtil.i(TAG, " ");
return false;
}
return true;
}
六、フラッシュをオンまたはオフにする
/**
*
*
* @param isOpen
*/
public void changeFlashMode(boolean isOpen, Camera mCamera, int cameraId) {
if (cameraId == Camera.CameraInfo.CAMERA_FACING_BACK) { //
Camera.Parameters parameters = mCamera.getParameters();
PackageManager pm = mContext.getPackageManager();
FeatureInfo[] features = pm.getSystemAvailableFeatures();
for (FeatureInfo f : features) {
if (PackageManager.FEATURE_CAMERA_FLASH.equals(f.name)) { //
if (isOpen) {
parameters.setFlashMode(Camera.Parameters.FLASH_MODE_TORCH); //
} else {
parameters
.setFlashMode(Camera.Parameters.FLASH_MODE_OFF); //
}
}
}
mCamera.setParameters(parameters);
}
}
注意事項