前回のコードを使ってプレビュー画面の画像を保存できるようにした。まぁ、備忘録程度。
■オートフォーカスを使う
以下のように記述するとオートフォーカスを使うことができる。
package info.justoneplanet.android.camera;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import android.app.Activity;
import android.content.Context;
import android.hardware.Camera;
import android.hardware.Camera.AutoFocusCallback;
import android.hardware.Camera.PictureCallback;
import android.hardware.Camera.ShutterCallback;
import android.os.Bundle;
import android.os.Environment;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.Window;
import android.view.WindowManager;
public class CameraActivity extends Activity {
private Camera mCamera;
private static final String PATH = Environment.getExternalStorageDirectory().getPath() + "/" + "sample.jpg";
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 全画面表示
// 基本的にはcameraは水平方向での利用を想定してるので
// manifestでorientation設定しておく
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
// プレビューのViewをセット
CameraPreview cameraPreview = new CameraPreview(this);
setContentView(cameraPreview);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
mCamera.autoFocus(new AutoFocusCallback() {
@Override
public void onAutoFocus(boolean success, Camera camera) {
camera.autoFocus(null);
camera.takePicture(
new ShutterCallback() {
@Override
public void onShutter() {
}
},
// 生データ
new PictureCallback() {
@Override
public void onPictureTaken(byte[] data, Camera camera) {
}
},
// jpgデータ
new PictureCallback() {
@Override
public void onPictureTaken(byte[] data, Camera camera) {
// 保存処理
FileOutputStream fileOutputStream = null;
try {
fileOutputStream = new FileOutputStream(PATH);
fileOutputStream.write(data);
fileOutputStream.flush();
fileOutputStream.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
);
camera.startPreview();
}
});
return true;
}
return super.onTouchEvent(event);
}
/**
* カメラのプレビューするSurfaceview
* @author justoneplanet
*/
public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback {
private SurfaceHolder mHolder;
public CameraPreview(Context context) {
super(context);
mHolder = getHolder();
mHolder.addCallback(this);
mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
Camera.Parameters parameters = mCamera.getParameters();
parameters.setPreviewSize(width, height);
mCamera.setParameters(parameters);
mCamera.startPreview();
}
/**
* 生成時に実行されカメラをopenする
*/
@Override
public void surfaceCreated(SurfaceHolder holder) {
if (mCamera != null) {
return;
}
mCamera = Camera.open();
try {
mCamera.setPreviewDisplay(holder);
}
catch (IOException e) {
mCamera.release();
mCamera = null;
}
}
/**
* 閉じるときに実行されカメラのリソースを解放する
*/
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
mCamera.stopPreview();
mCamera.release();
mCamera = null;
}
}
}
■プレビュー画像を使う
以下のようにすることでプレビュー画像を保存する事ができる。
package info.justoneplanet.android.camera;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Bitmap.CompressFormat;
import android.graphics.Bitmap.Config;
import android.hardware.Camera;
import android.hardware.Camera.AutoFocusCallback;
import android.hardware.Camera.PreviewCallback;
import android.hardware.Camera.Size;
import android.os.Bundle;
import android.os.Environment;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.Window;
import android.view.WindowManager;
public class CameraActivity extends Activity {
private Camera mCamera;
private static final String PATH = Environment.getExternalStorageDirectory().getPath() + "/" + "sample.jpg";
private byte[] mTempData = null;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 全画面表示
// 基本的にはcameraは水平方向での利用を想定してるので
// manifestでorientation設定しておく
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
// プレビューのViewをセット
CameraPreview cameraPreview = new CameraPreview(this);
setContentView(cameraPreview);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
mCamera.autoFocus(new AutoFocusCallback() {
@Override
public void onAutoFocus(boolean success, Camera camera) {
camera.autoFocus(null);
camera.setPreviewCallback(new PreviewCallback() {
@Override
public void onPreviewFrame(byte[] data, Camera camera) {
Log.e("onPreviewFrame", "onPreviewFrame");
if (data != null) {
mTempData = new byte[data.length];
for (int i = 0; i < mTempData.length; i++) {
mTempData[i] = data[i];
}
Size size = mCamera.getParameters().getPreviewSize();
Bitmap bmp = Bitmap.createBitmap(
Util.decodeYUV(mTempData, size.width, size.height),
size.width,
size.height,
Config.ARGB_8888
);
FileOutputStream fileOutputStream = null;
try {
fileOutputStream = new FileOutputStream(PATH);
bmp.compress(CompressFormat.JPEG, 90, fileOutputStream);
fileOutputStream.flush();
fileOutputStream.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
camera.setPreviewCallback(null);
}
});
camera.startPreview();
}
});
return true;
}
return super.onTouchEvent(event);
}
/**
* カメラのプレビューするSurfaceview
* @author justoneplanet
*/
public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback {
private SurfaceHolder mHolder;
public CameraPreview(Context context) {
super(context);
mHolder = getHolder();
mHolder.addCallback(this);
mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
Camera.Parameters parameters = mCamera.getParameters();
parameters.setPreviewSize(width, height);
mCamera.setParameters(parameters);
mCamera.startPreview();
}
/**
* 生成時に実行されカメラをopenする
*/
@Override
public void surfaceCreated(SurfaceHolder holder) {
if (mCamera != null) {
return;
}
mCamera = Camera.open();
try {
mCamera.setPreviewDisplay(holder);
}
catch (IOException e) {
mCamera.release();
mCamera = null;
}
}
/**
* 閉じるときに実行されカメラのリソースを解放する
*/
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
mCamera.stopPreview();
mCamera.release();
mCamera = null;
}
}
}
但し、プレビューで使用している画像形式はjpg形式ではないのでデコードがひつようになる。
デコード関数
package info.justoneplanet.android.camera;
/**
* ユーティリティ
* @author justoneplanet
*/
public class Util {
/**
* YUV形式から色の配列に変換する
* @param data
* @param width
* @param height
* @return
* @throws NullPointerException
* @throws IllegalArgumentException
*/
public static int[] decodeYUV(byte[] data, int width, int height) throws NullPointerException, IllegalArgumentException {
int size = width * height;
if (data == null) {
throw new NullPointerException("bufffer data is null");
}
if (data.length < size) {
throw new IllegalArgumentException("buffer data is illegal");
}
int[] out = new int[size];
int Y, Cr = 0, Cb = 0;
for (int i = 0; i < height; i++) {
int index = i * width;
int jDiv2 = i >> 1;
for (int i2 = 0; i2 < width; i2++) {
Y = data[index];
if (Y < 0) {
Y += 255;
}
if ((i2 & 0x1) != 1) {
int c0ff = size + jDiv2 * width + (i2 >> 1) * 2;
Cb = data[c0ff];
if (Cb < 0) {
Cb += 127;
}
else {
Cb -= 128;
}
Cr = data[c0ff + 1];
if (Cr < 0) {
Cr += 127;
}
else {
Cr -= 128;
}
}
// red
int R = Y + Cr + (Cr >> 2) + (Cr >> 3) + (Cr >> 5);
if (R < 0) {
R = 0;
}
else if (R > 255) {
R = 255;
}
// green
int G = Y - (Cb >> 2) + (Cb >> 4) + (Cb >> 5) - (Cr >> 1) + (Cr >> 3) + (Cr >> 4) + (Cr >> 5);
if (G < 0) {
G = 0;
}
else if (G > 255) {
G = 255;
}
int B = Y + Cb + (Cb >> 1) + (Cb >> 2) + (Cb >> 6);
if (B < 0) {
B = 0;
}
else if (B > 255) {
B = 255;
}
out[index] = 0xff000000 + (B << 16) + (G << 8) + R;
index++;
}
}
return out;
}
}
ファイルの保存をメインスレッドで行ってるのは手抜き。