@blog.justoneplanet.info

日々勉強

もっとandroidでカメラを使う

前回のコードを使ってプレビュー画面の画像を保存できるようにした。まぁ、備忘録程度。

■オートフォーカスを使う

以下のように記述するとオートフォーカスを使うことができる。

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;
    }
}

ファイルの保存をメインスレッドで行ってるのは手抜き。

コメントはまだありません»

No comments yet.

RSS feed for comments on this post.TrackBack URL

Leave a comment