Androidカメラのプレビュー画像をいじってみたいと思っています。リアルタイムで白黒反転したりしたいなーと。
単純にカメラのプレビュー画像をアプリケーション上で表示したいなら、SurfaceViewを生成してCamera#setPreviewDisplay()でSurfaceHolderを渡してあげれば良いんだけど、それだとプレビュー画像をいじれない。
プレビュー画像を横取りするには、Camera.PreviewCallbackを利用します。
紆余曲折いろいろなサイトを参考にこんなん作ってみました。
layout/main.xml
<?xml version="1.0" encoding="utf-8"?>
<framelayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<com.example.CameraView
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:id="@+id/SurfaceView01"
/>
</FrameLayout>
CameraTest.java
package com.example;
import android.app.Activity;
import android.os.Bundle;
import android.view.Window;
public class CameraTest extends Activity {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.main);
}
}
CameraView.java
package com.example;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.hardware.Camera;
import android.view.SurfaceView;
import android.view.SurfaceHolder;
import android.util.AttributeSet;
public class CameraView extends SurfaceView implements SurfaceHolder.Callback {
private SurfaceHolder holder;
private Camera camera;
private Bitmap bitmap;
private int[] rgb;
private int width, height;
/**
* プレビューコールバック
* prepareSavePreviewImageコールバックで登録され、プレビュー画像を取得する
*/
private final Camera.PreviewCallback _previewCallback =
new Camera.PreviewCallback() {
public void onPreviewFrame(byte[] data, Camera camera) {
decodeYUV420SP(rgb, data, width, height);
bitmap.setPixels(rgb, 0, width, 0, 0, width, height);
// 描画
Canvas canv = holder.lockCanvas();
canv.drawBitmap(bitmap, 0, 0, null);
holder.unlockCanvasAndPost(canv);
}
};
// コンストラクタ
public CameraView(Context context) {
super(context);
this.initialize();
}
// コンストラクタ
public CameraView(Context context, AttributeSet attrs) {
super(context, attrs);
this.initialize();
}
// 初期化処理
private void initialize() {
// サーフェイスホルダーの生成
holder=getHolder();
holder.addCallback(this);
// サーフェイスホルダーのタイプを設定
holder.setType(SurfaceHolder.SURFACE_TYPE_NORMAL);
// 画像を読み込んでおく
}
// サーフェイス生成イベントの処理
public void surfaceCreated(SurfaceHolder holder) {
// カメラの初期化
try {
camera=Camera.open();
camera.setPreviewCallback(_previewCallback);
} catch (Exception e) {
}
}
// サーフェイス変更イベントの処理
public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
// 描画データの準備
width = w;
height = h;
bitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
rgb = new int[w * h];
// カメラのプレビュー開始
Camera.Parameters parameters = camera.getParameters();
parameters.setPreviewSize(w, h);
camera.setParameters(parameters);
camera.startPreview();
}
//サーフェイス解放イベントの処理
public void surfaceDestroyed(SurfaceHolder holder) {
// カメラのプレビュー停止
camera.stopPreview();
camera.setPreviewCallback(null);
camera.release();
camera = null;
}
// YUV420 to BMP
static public void decodeYUV420SP(int[] rgb, byte[] yuv420sp, int width, int height) {
final int frameSize = width * height;
for (int j = 0, yp = 0; j < height; j++) {
int uvp = frameSize + (j >> 1) * width, u = 0, v = 0;
for (int i = 0; i < width; i++, yp++) {
int y = (0xff & ((int) yuv420sp[yp])) - 16;
if (y < 0) y = 0;
if ((i & 1) == 0) {
v = (0xff & yuv420sp[uvp++]) - 128;
u = (0xff & yuv420sp[uvp++]) - 128;
}
int y1192 = 1192 * y;
int r = (y1192 + 1634 * v);
int g = (y1192 - 833 * v - 400 * u);
int b = (y1192 + 2066 * u);
if (r < 0) r = 0; else if (r > 262143) r = 262143;
if (g < 0) g = 0; else if (g > 262143) g = 262143;
if (b < 0) b = 0; else if (b > 262143) b = 262143;
rgb[yp] = 0xff000000 | ((r << 6) & 0xff0000) | ((g >> 2) & 0xff00) | ((b >> 10) & 0xff);
}
}
}
}
少し動かすとOutOfMemoryになってちゃんと動かない。Camera.Parameters#setPreviewFrameRate(1)とかやってやると落ちずに動く。
実機でやるとまた違う結果が出るかしら。