【今更だけどandroid.hardware.Cameraを使う】カメラプレビューを保存する
android.hardware.Cameraを使うことで、Android端末にあるカメラデバイスにアクセスして使うことができます。
android.hardware.Cameraは、現在では非推奨となっていますがOpenCV等ではまだまだ使われています。
今回は、カメラプレビューを保存します。
下記の実装はすべて前回までに作ったクラスを修正しています。
Camera#takePicture
Camera#startPreviewを使いカメラプレビューを表示している状態でCamera#takePictureを使うことで、カメラデバイスからデータを取得することができます。Camera#takePictureには下記のオーバーロードが存在します。
このメソッドは多数のコールバックを引数としています。
これらのコールバックはキャプチャから徐々に進行していく各データの加工を知らせます。
- 画像のキャプチャを取得
- キャプチャしたデータをRAW形式に加工
- データをスケーリングした状態に加工
- データをJPEG形式に
final void takePicture(Camera.ShutterCallback shutter, Camera.PictureCallback raw, Camera.PictureCallback postview, Camera.PictureCallback jpeg) |
カメラデバイスが画像をキャプチャした後に行われる、各データ加工の完了を通知するコールバックインターフェースを多数必要とします。 しかし、端末によっては対応していない加工もあるため、すべてが必ずコールバックされないことに注意が必要です。 また、必要の無いコールバックにはnullを設定することができます。
|
||||||||
final void takePicture(Camera.ShutterCallback shutter, Camera.PictureCallback raw, Camera.PictureCallback jpeg) |
カメラデバイスが画像をキャプチャした後に行われる、各データ加工の完了を通知するコールバックインターフェースを多数必要とします。 しかし、端末によっては対応していない加工もあるため、すべてが必ずコールバックされないことに注意が必要です。 また、必要の無いコールバックにはnullを設定することができます。
|
そのため、Camera#takePictureの呼び出した後、すぐにCameraクラスのメソッドを使用するのはお勧めしません。
また、呼び出し後にすぐにカメラプレビューは停止されます。
しかし、前述の理由からCamera#startPreviewをすぐには呼び出してはいけません。
今回は、PEGのデータへの操作が完了したタイミングでCamera#startPreviewを呼び出すようにしました。
private Camera.ShutterCallback mShutterCallback = new Camera.ShutterCallback() { @Override public void onShutter() { Log.d(TAG, "onShutter: "); } }; private Camera.PictureCallback mRawPictureCallback = new Camera.PictureCallback() { @Override public void onPictureTaken(byte[] data, Camera camera) { Log.d(TAG, "Raw onPictureTaken: "); if (data != null) { saveTakenData(data , RAW_EXTENSION); } } }; private Camera.PictureCallback mPostViewPictureCallback = new Camera.PictureCallback() { @Override public void onPictureTaken(byte[] data, Camera camera) { Log.d(TAG, "PostView onPictureTaken: "); if (data != null) { saveTakenData(data , JPG_EXTENSION); } } }; private Camera.PictureCallback mJpegPictureCallback = new Camera.PictureCallback() { @Override public void onPictureTaken(byte[] data, Camera camera) { Log.d(TAG, "Jpeg onPictureTaken: "); if (data != null) { saveTakenData(data , JPG_EXTENSION); } startPreview(); } }; public void takePicture(){ mCamera.takePicture(mShutterCallback, mRawPictureCallback , mPostViewPictureCallback , mJpegPictureCallback); }
Camera.PictureCallbackのデータを保存するメソッド
Camera.PictureCallback#onPictureTakenで得たデータは加工済みのデータになります。また、カメラプレビューの向きを補正しましたが、Camera.PictureCallback#onPictureTakenで得られるデータには関係がなく、Camera.Parameters#setRotationで向き補正する必要があります。
今回はアプリケーションの内部フォルダに”yyyyMMdd_HHmmss.jpg”または”yyyyMMdd_HHmmss.raw”という形式で保存します。
private void saveTakenData(byte[] data, String extension) { SimpleDateFormat date = new SimpleDateFormat("yyyyMMdd_HHmmss"); String fileName = date.format(Calendar.getInstance().getTime()) + "." + extension; FileOutputStream fos = null; try { fos = getContext().openFileOutput(fileName, Context.MODE_APPEND); fos.write(data); } catch (IOException e) { e.printStackTrace(); } finally { try { fos.close(); } catch (IOException e1) { e1.printStackTrace(); } Log.d("Camera", "Done:" + fileName); } }
写真を撮るボタン
CameraActivityに写真を撮影するボタンを追加します。レイアウト
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="yona.mycamera.CameraActivity"> <FrameLayout android:id="@+id/container_camera" android:layout_width="match_parent" android:layout_height="match_parent"/> <Button android:id="@+id/button_take_picture" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:layout_centerHorizontal="true" android:text="撮影"/> </RelativeLayout>
Activity
public class CameraActivity extends AppCompatActivity { public static final String CAMERA_ID = "CameraActivity_CAMERA_ID"; private MyCameraPreview mMyCameraPreview; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_camera); int cameraId = getIntent().getIntExtra(CAMERA_ID, 0); mMyCameraPreview = new MyCameraPreview(this , cameraId); ((FrameLayout)findViewById(R.id.container_camera)).addView(mMyCameraPreview); findViewById(R.id.button_take_picture).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { mMyCameraPreview.takePicture(); } }); } @Override protected void onPause() { super.onPause(); mMyCameraPreview.onPause(); } }