LoginSignup
39
36

More than 5 years have passed since last update.

今更カメラ制御の基礎(STEP4:アスペクト比を調整)

Last updated at Posted at 2014-10-18

やりたいこと

プレビューのアスペクト比を可能な限り正しくする。
このデモでは、プレビューの1フレーム(OneShot)を切り取って保存するので、保存画像のアスペクト比も同時に揃える。

STEP3(3.5)からの追加

・surfaceChangedのstartPreview()実行前に、setPreviewSize()を利用してアスペクト比を計算・設定する。
・計算には、ApiDemoでもあるgetOptimalPreviewSize()を補助ライブラリとして実装

他のステップはこちら

STEP1:基礎
STEP2:オートフォーカスとか
STEP3:保存
STEP4:アスペクト比調整

積み残し

私はPortraitモードしか使わないので、回転の計算をPortraitに固定しているが、画面の回転に対応したい人は、surfaceChengedにそのためのロジックを追加する必要がある。

ソース

MyActivity.java
public class MyActivity extends Activity {

    private SurfaceView mySurfaceView;
    private Camera myCamera; //hardware


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_my);

        //SurfaceView
        mySurfaceView = (SurfaceView)findViewById(R.id.mySurfaceVIew);
        //リスナ追加
        mySurfaceView.setOnClickListener(onSurfaceClickListener);

        //SurfaceHolder(SVの制御に使うInterface)
        SurfaceHolder holder = mySurfaceView.getHolder();
        //コールバックを設定
        holder.addCallback(callback);

    }

    //コールバック
    private SurfaceHolder.Callback callback = new SurfaceHolder.Callback() {
        @Override
        public void surfaceCreated(SurfaceHolder surfaceHolder) {

            //CameraOpen
            myCamera = Camera.open();

            //Portrait対応
            myCamera.setDisplayOrientation(90);

            //出力をSurfaceViewに設定
            try{
                myCamera.setPreviewDisplay(surfaceHolder);
            }catch(Exception e){
                e.printStackTrace();
            }

        }

        @Override
        public void surfaceChanged(SurfaceHolder surfaceHolder, int i, int w, int h) {

            //最適サイズを取得 << STEP4
            Camera.Parameters params = myCamera.getParameters();
            List<Camera.Size> sizes = params.getSupportedPreviewSizes();
            Camera.Size optimalSize = getOptimalPreviewSize(sizes,w,h);
            params.setPreviewSize(optimalSize.width,optimalSize.height);
            myCamera.setParameters(params);

            //プレビュースタート(Changedは最初にも1度は呼ばれる)
            myCamera.startPreview();

        }

        @Override
        public void surfaceDestroyed(SurfaceHolder surfaceHolder) {

            //片付け
            myCamera.release();
            myCamera = null;
        }
    };

    //Surfaceをクリックした時
    private View.OnClickListener onSurfaceClickListener = new View.OnClickListener(){
        @Override
        public void onClick(View view){
            if(myCamera != null){
                //AutoFocusを実行
                myCamera.autoFocus(autoFocusCallback);
            }
        }

    };

    //AutoFocusした時
    private Camera.AutoFocusCallback autoFocusCallback = new Camera.AutoFocusCallback(){
        @Override
        public void onAutoFocus(boolean b, Camera camera) {

            //ただ写真を撮るだけならここにtakePicture()を入れても良い
            //しかし、Pixelを弄りたいのでもうワンクッション(画像処理やバーコード処理をしたいので)
            //Previewを1枚切り取る(そしてイベント発生)
            camera.setOneShotPreviewCallback(previewCallback);
        }
    };

    //切り取った時(ここで撮影、各種画像処理を行う)
    private Camera.PreviewCallback previewCallback = new Camera.PreviewCallback(){

        //OnShotPreview時のbyte[]が渡ってくる
        @Override
        public void onPreviewFrame(byte[] bytes, Camera camera) {

            //プレビューのフォーマットはYUVなので、YUVをBmpに変換する必要がある(ややこしい)
            int w = camera.getParameters().getPreviewSize().width;
            int h = camera.getParameters().getPreviewSize().height;

            //切り取った画像を保存
            //Bitmap bmp = BitmapFactory.decodeByteArray(bytes,0,bytes.length,null);
            Bitmap bmp = getBitmapImageFromYUV(bytes, w, h);

            //回転
            Matrix m = new Matrix();
            m.setRotate(90);

            //保存用 bitmap生成
            Bitmap rotated_bmp = Bitmap.createBitmap(bmp,0,0,bmp.getWidth(),bmp.getHeight(),m,true);

            //保存
            String res = MediaStore.Images.Media.insertImage(getContentResolver(),rotated_bmp,"hoge.jpg",null);
            Log.v("tama","res="+res);

        }
    };

    //YUVをBitmapに変換する関数(参照:http://tech.thecoolblogs.com/2013/02/get-bitmap-image-from-yuv-in-android.html)
    public static Bitmap getBitmapImageFromYUV(byte[] data, int width, int height) {
        YuvImage yuvimage = new YuvImage(data, ImageFormat.NV21, width, height, null);
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        yuvimage.compressToJpeg(new Rect(0, 0, width, height), 80, baos);
        byte[] jdata = baos.toByteArray();
        BitmapFactory.Options bitmapFatoryOptions = new BitmapFactory.Options();
        bitmapFatoryOptions.inPreferredConfig = Bitmap.Config.RGB_565;
        Bitmap bmp = BitmapFactory.decodeByteArray(jdata, 0, jdata.length, bitmapFatoryOptions);
        return bmp;
    }

    //ApiDemoでよく使うgetOptimalPreviewSize << STEP4
    private Camera.Size getOptimalPreviewSize(List<Camera.Size> sizes, int w, int h) {

        final double ASPECT_TOLERANCE = 0.1;
        double targetRatio=(double)h / w;

        if (sizes == null) return null;

        Camera.Size optimalSize = null;
        double minDiff = Double.MAX_VALUE;

        int targetHeight = h;

        for (Camera.Size size : sizes) {
            double ratio = (double) size.width / size.height;
            if (Math.abs(ratio - targetRatio) > ASPECT_TOLERANCE) continue;
            if (Math.abs(size.height - targetHeight) < minDiff) {
                optimalSize = size;
                minDiff = Math.abs(size.height - targetHeight);
            }
        }

        if (optimalSize == null) {
            minDiff = Double.MAX_VALUE;
            for (Camera.Size size : sizes) {
                if (Math.abs(size.height - targetHeight) < minDiff) {
                    optimalSize = size;
                    minDiff = Math.abs(size.height - targetHeight);
                }
            }
        }
        return optimalSize;
    }

}
39
36
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
39
36