質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

88.60%

Androidのcamera2でズームした画像を保存したい

解決済

回答 1

投稿

  • 評価
  • クリップ 2
  • VIEW 1,981

Begi

score 54

前提・実現したいこと

androidのカメラ開発を行っています。
camera2のAPIで基本的な動作は実装済みで、ズーム機能も付加しようと思い開発していますが、ズームした状態でシャッターボタンを押してもズーム前の画像が撮影されてしまいます。
お力をお貸しいただけないでしょうか。

仕様

・横向きで撮影。解像度は1280*960固定
・アプリ自体はwebView
・撮影→プレビュー確認→再撮影又は保存して次の画像を撮影 の連続撮影機能あり

public class TakePictureActivity extends Activity {

    RelativeLayout wrapper;
    static final String TAG = "TakePictureActivity";
    private Size mPreviewSize;

    private TextureView mTextureView;
    private CameraDevice mCameraDevice;
    private CaptureRequest.Builder mPreviewBuilder;
    private CameraCaptureSession mPreviewSession;

    private Button mBtnShot;            //撮影ボタン
    Resources res;
    private TextView mTextViewPictName; //キャプション

    static final int CAM_WIDTH = 1280;
    static final int CAM_HEIGHT = 960;

    boolean zoomFlg = false;
    private int zoomTop = 0;
    private int zoomLeft = 0;
    //画像の順番を管理
    private String mCurrentImageId;
    private String base64img;

    static int camIdx = 0;
    CameraCharacteristics characteristics;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.take_picture_activity);
        res = getResources();

        this.wrapper = (RelativeLayout) findViewById(R.id.wrapper);

        mTextureView = (TextureView) findViewById(R.id.takepicture_texture_view);
        mTextureView.setSurfaceTextureListener(mSurfaceTextureListener);

        //撮影ボタンクリック時の処理
        this.mBtnShot = (Button) findViewById(R.id.takepicture_btn_shoot);
        this.mBtnShot.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                takePicture();
            }
        });

        this.setContentView(this.wrapper);
    }

    /**
     * textureViewのサイズを変更
     */
    private void changeTextureView(int dstWidth, int dstHeight) {
        int tw = mTextureView.getWidth();
        int th = mTextureView.getHeight();
        {
            int tmp = tw;
            tw = th;
            th = tmp;
        }
        double srcRatio = (double) dstHeight / dstWidth;
        double tRatio = (double) th / tw;
        int dw, dh;
        if(tRatio < srcRatio) {
            dw = (int) (th / srcRatio);
            dh = th;
        }
        else {
            dw = tw;
            dh = (int) (dw * srcRatio);
        }
        mTextureView.setScaleY((float) dw / tw);
        mTextureView.setScaleX((float) dh / th);
    }

    private void takePicture() {

        if(mCameraDevice == null){
            Log.e(TAG, "mCameraDevice is null");
            return;
        }

        CameraManager manager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);
        try {
            characteristics = manager.getCameraCharacteristics(mCameraDevice.getId());

            ImageReader reader = ImageReader.newInstance(CAM_WIDTH, CAM_HEIGHT, ImageFormat.JPEG, 1);
            List<Surface> outputSurfaces = new ArrayList<>();
            outputSurfaces.add(reader.getSurface());
            outputSurfaces.add(new Surface(mTextureView.getSurfaceTexture()));

            final CaptureRequest.Builder captureBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
            captureBuilder.addTarget(reader.getSurface());
            captureBuilder.set(CaptureRequest.CONTROL_MODE, CameraMetadata.CONTROL_MODE_AUTO);

            ImageReader.OnImageAvailableListener readerListener = new ImageReader.OnImageAvailableListener() {
                @Override
                public void onImageAvailable(ImageReader reader) {
                    Image image = reader.acquireLatestImage();

                    ByteBuffer buffer = image.getPlanes()[0].getBuffer();
                    byte bytes[] = new byte[buffer.capacity()];
                    buffer.get(bytes);

                    if (!zoomFlg) {
                        zoomTop = 0;
                        zoomLeft = 0;
                    }
                    if (zoomLeft > zoomTop) {
                        int tmp = zoomTop;
                        zoomTop = zoomLeft;
                        zoomLeft = tmp;
                    }

                    //TODO: メンテンス案件: Image からのクロップ 拡大率対応が必要
                    Bitmap bmp = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);

                    // 希望するサイズを画像から得る
                    int srcWidth = image.getWidth();
                    int srcHeight = image.getHeight();

                    Bitmap rotBmp = Bitmap.createBitmap(cropMap, 0, 0, srcWidth, srcHeight, null, true);

                    ByteArrayOutputStream baos = new ByteArrayOutputStream();
                    rotBmp.compress(CompressFormat.JPEG, 90, baos);
                    byte[] dstBytes = baos.toByteArray();

                    String base64Str = Base64.encodeToString(dstBytes, Base64.DEFAULT);
                    base64img = "data:image/jpeg;base64," + base64Str;
                    zoomFlg = false;
                }
            };

            HandlerThread thread = new HandlerThread("CameraCapture");
            thread.start();
            final Handler backgroundHandler = new Handler(thread.getLooper());
            reader.setOnImageAvailableListener(readerListener, backgroundHandler);

            final CameraCaptureSession.CaptureCallback captureListener = new CameraCaptureSession.CaptureCallback() {
                @Override
                public void onCaptureCompleted(CameraCaptureSession session, CaptureRequest request, TotalCaptureResult result) {
                    super.onCaptureCompleted(session, request, result);
                    Log.i(TAG, "onCaptureCompleted");
                    mTextureView.setAlpha(1.0f);
                    closeCamera();
                }

            };

            mCameraDevice.createCaptureSession(outputSurfaces, new CameraCaptureSession.StateCallback() {
                @Override
                public void onConfigured(CameraCaptureSession session) {
                    try {
                        session.capture(captureBuilder.build(), captureListener, backgroundHandler);
                    } catch (CameraAccessException e){
                        e.printStackTrace();
                    }
                }

                @Override
                public void onConfigureFailed(CameraCaptureSession session) {
                }
            }, backgroundHandler);
        } catch (CameraAccessException e){
            e.printStackTrace();
        }
    }

    private TextureView.SurfaceTextureListener mSurfaceTextureListener = new TextureView.SurfaceTextureListener() {
        @Override
        public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
            openCamera();
        }
        @Override
        public void onSurfaceTextureUpdated(SurfaceTexture surface) { }
        @Override
        public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) { }
        @Override
        public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
            return false;
        }
    };

    private void setupZoomHandler(CameraCharacteristics cameraCharacteristics) {
        ActiveArrayZoomHandlerBuilder.forView(mTextureView)
                .setActiveArraySize(cameraCharacteristics.get(CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE))
                .setMaxZoom(cameraCharacteristics.get(CameraCharacteristics.SCALER_AVAILABLE_MAX_DIGITAL_ZOOM ) * 10)
                .setZoomListener(new ActiveArrayZoomHandler.IZoomHandlerListener() {
                    @Override
                    public void onZoomChanged(Rect zoom) {
                        mPreviewBuilder.set(CaptureRequest.SCALER_CROP_REGION, zoom);
                        zoomTop = zoom.top;
                        zoomLeft = zoom.left;
                        zoomFlg = true;
                        updatePreview();
                    }
                }).build();
    }

    private void openCamera() {
        CameraManager manager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);
        try {
            String camId = manager.getCameraIdList()[camIdx % manager.getCameraIdList().length];
            manager.openCamera(camId, mStateCallback, null);
        } catch (CameraAccessException e){
        } catch (Exception e) {
            closeCamera();
        }
    }

    /**
     * カメラ終了時の処理
     */
    private void closeCamera(){
        if(mCameraDevice == null) {
            Log.e(TAG, "mCameraDevice is already closed.");
            return;
        }
        mCameraDevice.close();
    }

    private CameraDevice.StateCallback mStateCallback = new CameraDevice.StateCallback() {
        @Override
        public void onOpened(CameraDevice camera) {
            mCameraDevice = camera;
            Log.i(TAG, "mStateCallback.onOpened");
            startPreview();
        }
        @Override
        public void onError(CameraDevice camera, int error) {
            Log.e(TAG, "error: " + error);
        }
        @Override
        public void onDisconnected(CameraDevice camera) { }
    };

    /**
     * TextureViewに対しサイズを設定
     */
    protected void startPreview() {
        if(mCameraDevice == null || ! mTextureView.isAvailable()){
            Log.e(TAG,
                    "▼▼▼startPreview failed. mCameraDevice: " + mCameraDevice
                    + ", mTextureView.isAvailable: " + mTextureView.isAvailable() + "▼▼▼"
            );
            return;
        }

        SurfaceTexture texture = mTextureView.getSurfaceTexture();
        if(texture == null){
            Log.e(TAG, "texture is null");
            return;
        }

        texture.setDefaultBufferSize(mPreviewSize.getWidth(), mPreviewSize.getHeight());
        Surface surface = new Surface(texture);

        try {
            mPreviewBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
            mPreviewBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
        } catch (CameraAccessException e){
            Log.e(TAG, "createCaptureRequest is failed.");
        }

        mPreviewBuilder.addTarget(surface);
        try {
            mCameraDevice.createCaptureSession(
                    Arrays.asList(surface),
                    new CameraCaptureSession.StateCallback() {
                        @Override
                        public void onConfigured(CameraCaptureSession session) {
                            mPreviewSession = session;
                            mPreviewBuilder.set(CaptureRequest.CONTROL_MODE, CameraMetadata.CONTROL_MODE_AUTO);
                            updatePreview();
                        }
                        @Override
                        public void onConfigureFailed(CameraCaptureSession session) {
                            Toast.makeText(TakePictureActivity.this, "onConfigureFailed", Toast.LENGTH_SHORT).show();
                        }
                    },
                    null
                    );
        } catch (CameraAccessException e){
            Log.e(TAG, "createCaptureSession is failed.");
        }
    }

    private void updatePreview() {

        if(mCameraDevice == null) {
            Log.e(TAG, "mPreviewDevice is null");
            return;
        }

        //mPreviewBuilder.set(CaptureRequest.CONTROL_MODE, CameraMetadata.CONTROL_MODE_AUTO);
        HandlerThread thread = new HandlerThread("CameraPreview");
        thread.start();
        Handler backgroundHandler = new Handler(thread.getLooper());
        try {
            mPreviewSession.setRepeatingRequest(mPreviewBuilder.build(), null, backgroundHandler);
        } catch (CameraAccessException e){
        }
    }

}

ズーム機能自体は、別ファイルに分離しています。
BitmapFactory#decodeByteArrayでbitmapをクロップすればいい気がするのですが、うまく動作せず質問させていただきました。
よろしくお願い致します。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • yukke_

    2018/10/27 14:28

    「うまく動作せず」を具体的に書いてください。

    キャンセル

  • Begi

    2018/10/28 14:51

    コメントありがとうございます。decodeByteArrayの第三引数のlengthを減らしてみたり、などてす。根本的な仕組みの理解が足りておらずすみません。

    キャンセル

  • Wind

    2018/11/15 15:16

    ソースをざっと見ただけですので見当外れかもしれませんが、ZoomLeftやZoomTopに値を入れた後、これらの値を何も使っていないように見えますがどうでしょう?

    キャンセル

回答 1

check解決した方法

0

takePicture()メソッドの
captureBuilder.set(CaptureRequest.CONTROL_MODE, CameraMetadata.CONTROL_MODE_AUTO);
のタイミングでcaptureBuilderに対してSCALER_CROP_REGIONをセットしたら拡大した画像が保存できました。
具体的には、別メソッドで設定したRect型のzoomが設定されていればセットするという処理を行いました。
if (zoom != null) {
captureBuilder.set(CaptureRequest.SCALER_CROP_REGION, zoom);
}

yukke_さん、Windさんコメントありがとうございました。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 88.60%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る