2010年6月2日水曜日

カメラのプレビュー画像をいじってみる(1)

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)とかやってやると落ちずに動く。
実機でやるとまた違う結果が出るかしら。

0 件のコメント:

コメントを投稿