マイペースなプログラミング日記

DTMやプログラミングにお熱なd-kamiがマイペースに書くブログ

カメラのプレビューを自分で描画してみる

カメラのプレビューを自分で描画するプログラムを作った。使ってるうクラスの関係でAndroid 2.1以降で動くと思われる。必要なPermissionは

android.permission.CAMERA

となっている。あとandroid:screenOrientation="landscape"を忘れずに。今回はプレビューで取得した画像をモノクロ画像に変換して描画してみた

package camera.test;

import java.util.List;

import android.hardware.Camera;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;

import android.view.SurfaceHolder;
import android.view.SurfaceView;

public class CameraView extends SurfaceView implements SurfaceHolder.Callback, Camera.PreviewCallback{
    
    private Camera camera;
    private int width;
    private int height;
    
    private Bitmap bitmap;
    private int[] src;
    private int[] monoRGBs;
    
    public CameraView(Context context) {
        super(context);
        
        SurfaceHolder holder = getHolder();
        holder.addCallback(this);
        holder.setType(SurfaceHolder.SURFACE_TYPE_NORMAL);
    }
    
    public void surfaceCreated(SurfaceHolder holder) {
        camera = Camera.open();

        try{
            camera.setPreviewCallback(this);
        }catch(Exception e){
            android.util.Log.e("", e.getMessage());
        }
    }
    
    public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
        camera.stopPreview();

        Camera.Parameters parameters = camera.getParameters();
        
        List<Camera.Size> sizes = parameters.getSupportedPreviewSizes();
        Camera.Size optimalSize = CameraView.getOptimalPreviewSize(sizes, w, h);

        width = optimalSize.width;
        height = optimalSize.height;

        parameters.setPreviewSize(width, height);
        
        //効果が無い?
        //parameters.setColorEffect(Camera.Parameters.EFFECT_MONO);
        
        bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
        src = new int[width * height];
        monoRGBs = new int[width * height];
        
        camera.setParameters(parameters);
        camera.startPreview();
    }
    
    public void surfaceDestroyed(SurfaceHolder holder) {
        camera.setPreviewCallback(null);
        camera.stopPreview();
        camera.release();
    }
    
    public void onPreviewFrame(final byte[] data, Camera camera){        
        byte[] dataE = new byte[(int)(width * height * 1.5)];
        
        for(int i = 0; i < data.length; i++){
            dataE[i] = data[i];
        }
        
        CameraView.decodeYUV420SP(src, dataE, width, height);
        CameraView.monoFilter(monoRGBs, src, width, height);
        bitmap.setPixels(monoRGBs, 0, width, 0, 0, width, height);
        
        SurfaceHolder holder = getHolder();
        Canvas canvas = holder.lockCanvas();
        canvas.drawBitmap(bitmap, 0, 0, new Paint());
        holder.unlockCanvasAndPost(canvas);
    }
    
    private static Camera.Size getOptimalPreviewSize(List<Camera.Size> sizes, int w, int h) {
        int min = Integer.MAX_VALUE;
        Camera.Size optimalSize = sizes.get(0);
        
        for(Camera.Size size : sizes){
            if(size .width < w || size.height < h){
                continue;                
            }
            
            int dw = Math.abs(w - size.width);
            int dh = Math.abs(h - size.height);
            
            if(min > dw + dh){
                min = dw + dh;
                optimalSize = size;
            }
        }
        
        return optimalSize;
    }
    
    private static 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);
            }
        }
    }
    
    private static void monoFilter(int[] to, int[] from, int width, int height){
        int index, rgb, red, green, blue, mono, newRGB;
        
        for(int y = 0; y < height; y++){
            for(int x = 0; x < width; x++){
                index = y * width + x;
                rgb = from[index];
                red = (rgb >> 16) & 0xFF;
                green = (rgb >> 8) & 0xFF;
                blue = rgb & 0xFF;

                mono = (red + green + blue) / 3;
                newRGB = 0xFF << 24 | mono << 16 | mono << 8 | mono;
                to[index] = newRGB;
            }
        }
    }
}