【今更だけど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();
}
}