LoginSignup
12
10

More than 5 years have passed since last update.

OpenCV for Androidでカメラプレビューをクロッピングする

Last updated at Posted at 2015-08-10

OpenCV4Androidのカメラプレビューをクロッピングします.

ちなみにOpenCV4Androidのカメラの使い方は
http://rest-term.com/archives/3010/
などが参考になります.
この記事でも,こんなコードを想定します.
JavaCameraViewを使います.

問題

OpenCV4androidを使ったandroidアプリでカメラプレビューを正方形にクロッピングしたくなりました.
安直にonCameraFrame()で画像をクロップして返せばいいやと思ってやってみたところ,

    @Override
    public Mat onCameraFrame(CvCameraViewFrame inputFrame) {
        mRgba = inputFrame.rgba();
        return new Mat(mRgba, mROI);
    }

以下のようなエラーを吐き続けてプレビューが表示されなくなりました.

08-10 23:08:14.137  30863-31993/com.example E/cv::error()﹕ OpenCV Error: Assertion failed (src.dims == 2 && info.height == (uint32_t)src.rows && info.width == (uint32_t)src.cols) in void Java_org_opencv_android_Utils_nMatToBitmap2(JNIEnv*, jclass, jlong, jobject, jboolean), file /hdd2/buildbot/slaves/slave_ardbeg1/50-SDK/opencv/modules/java/generator/src/cpp/utils.cpp, line 97
08-10 23:08:14.137  30863-31993/com.example E/org.opencv.android.Utils﹕ nMatToBitmap catched cv::Exception: /hdd2/buildbot/slaves/slave_ardbeg1/50-SDK/opencv/modules/java/generator/src/cpp/utils.cpp:97: error: (-215) src.dims == 2 && info.height == (uint32_t)src.rows && info.width == (uint32_t)src.cols in function void Java_org_opencv_android_Utils_nMatToBitmap2(JNIEnv*, jclass, jlong, jobject, jboolean)
08-10 23:08:14.137  30863-31993/com.example E/CameraBridge﹕ Mat type: Mat [ 480*480*CV_8UC4, isCont=false, isSubmat=true, nativeObj=0x78cfb440, dataAddr=0x7a91a1f0 ]
08-10 23:08:14.137  30863-31993/com.example E/CameraBridge﹕ Bitmap type: 720*480
08-10 23:08:14.137  30863-31993/com.example E/CameraBridge﹕ Utils.matToBitmap() throws an exception: /hdd2/buildbot/slaves/slave_ardbeg1/50-SDK/opencv/modules/java/generator/src/cpp/utils.cpp:97: error: (-215) src.dims == 2 && info.height == (uint32_t)src.rows && info.width == (uint32_t)src.cols in function void Java_org_opencv_android_Utils_nMatToBitmap2(JNIEnv*, jclass, jlong, jobject, jboolean)

JavaCameraViewの親クラスCameraBridgeViewBaseは表示用バッファとしてBitmap mCacheBitmapを持っていて,初期化時にこれをカメラのフレームサイズで確保します.
そしてonCameraFrame()から返るMatUtils.matToBitmap()mCacheBitmapに入れてプレビュー表示処理に回します.
しかし,onCameraFrame()からサイズを変えたMatを返すと,Utils.matToBitmap()で変換元と変換先のサイズが合わずに上記のエラーになります.

対策

JavaCameraViewを継承したビュークラスを作り,クロッピングを行いました.

CameraView.java
package com.example;

import android.content.Context;
import android.util.AttributeSet;

import org.opencv.android.JavaCameraView;
import org.opencv.core.Mat;
import org.opencv.core.Rect;

public class CameraView extends JavaCameraView {
    private Rect mROI;
    private int mSquareFrameWidth;

    public CameraView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    protected void AllocateCache()
    {
        int frameWidth = mFrameWidth, frameHeight = mFrameHeight;
        mSquareFrameWidth = Math.min(mFrameWidth, mFrameHeight);
        mFrameWidth = mSquareFrameWidth;
        mFrameHeight = mSquareFrameWidth;
        super.AllocateCache();
        mFrameWidth = frameWidth;
        mFrameHeight = frameHeight;

        mROI = new Rect((mFrameWidth - mSquareFrameWidth)/2, (mFrameHeight - mSquareFrameWidth)/2, mSquareFrameWidth, mSquareFrameWidth);
    }


    @Override
    protected void deliverAndDrawFrame(CvCameraViewFrame frame) {
        super.deliverAndDrawFrame(new CroppedCameraViewFrame(frame, mROI));
    }

    private class CroppedCameraViewFrame implements CvCameraViewFrame {
        private Rect mROI;
        private CvCameraViewFrame mOriginalFrame;

        @Override
        public Mat gray() {
            return new Mat(mOriginalFrame.gray(), mROI);
        }

        @Override
        public Mat rgba() {
            return new Mat(mOriginalFrame.rgba(), mROI);
        }

        public CroppedCameraViewFrame(CvCameraViewFrame original, Rect ROI) {
            mOriginalFrame = original;
            mROI = ROI;
        }
    }
}

そして,レイアウトファイルで使うビューをこれに差し替えます

activity_main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:opencv="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <com.example.CameraView
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:id="@+id/camera_preview"
        opencv:camera_id="any" />

</LinearLayout>

説明

上述したとおり,カメラのフレームサイズでBitmap mCacheBitmapが初期化されますが,それを行っているのがJavaCameraView.AllocateCache()です.
継承したクラスでは,これを呼ぶ前にmFrameWidth,mFrameHeightを書き換えて初期化するサイズをごまかします.ここでは正方形にクロッピングするようにしました.
AllocateCache()を呼んだあとは元の値に戻します.

また,クロッピングに使うRectオブジェクトmROIを作っておきます.

    @Override
    protected void AllocateCache()
    {
        int frameWidth = mFrameWidth, frameHeight = mFrameHeight;
        mSquareFrameWidth = Math.min(mFrameWidth, mFrameHeight);
        mFrameWidth = mSquareFrameWidth;
        mFrameHeight = mSquareFrameWidth;
        super.AllocateCache();
        mFrameWidth = frameWidth;
        mFrameHeight = frameHeight;

        mROI = new Rect((mFrameWidth - mSquareFrameWidth)/2, (mFrameHeight - mSquareFrameWidth)/2, mSquareFrameWidth, mSquareFrameWidth);
    }

JavaCameraView.deliverAndDrawFrame()onCameraFrame()を呼び,返ってきたMatUtils.matToBitmap()mCacheBitmapに入れています.
そこで,子クラスでdeliverAndDrawFrame()をオーバーライドし,JavaCameraView.deliverAndDrawFrame()に渡るCvCameraViewFrameを実装したオブジェクトをCroppedCameraViewFrameに差し替えています.
CroppedCameraViewFrameは元々のCvCameraViewFrameオブジェクトをラップし,rgba()gray()で元のオブジェクトのrgba(),gray()の返り値をクロッピングして返すことでクロッピングを実現しています.


    @Override
    protected void deliverAndDrawFrame(CvCameraViewFrame frame) {
        super.deliverAndDrawFrame(new CroppedCameraViewFrame(frame, mROI));
    }

    private class CroppedCameraViewFrame implements CvCameraViewFrame {
        private Rect mROI;
        private CvCameraViewFrame mOriginalFrame;

        @Override
        public Mat gray() {
            return new Mat(mOriginalFrame.gray(), mROI);
        }

        @Override
        public Mat rgba() {
            return new Mat(mOriginalFrame.rgba(), mROI);
        }

        public CroppedCameraViewFrame(CvCameraViewFrame original, Rect ROI) {
            mOriginalFrame = original;
            mROI = ROI;
        }
    }
12
10
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
12
10