OpenCV4Android之运用手机摄像头获取Canny边缘

Posted 微卡智享

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了OpenCV4Android之运用手机摄像头获取Canny边缘相关的知识,希望对你有一定的参考价值。

在图像中获取主要影响的边缘用处比较广泛,这样就用到了Canny边缘的效果


Canny边缘的提取流程

  1. 高斯模糊

  2. 梯度计算

  3. 非最大信息压制

  4. 高低阈值链接

  5. 显示



还是用我们的Demo

不过这次我们加上新的东西,因为正好学习了OpenCV里面调用手机里的摄像头功能获取图片信息,花了整整一天算是入了个门吧.


首先在原来的MainActivity里面增加一个按钮

OpenCV4Android之运用手机摄像头获取Canny边缘

对应增加的事件

OpenCV4Android之运用手机摄像头获取Canny边缘



照相机页面

OpenCV4Android之运用手机摄像头获取Canny边缘

首先我们增加了一个opencvcarema.xml页面和一个OpenCVCarema的activity


opencvcarema.xml

这里面可以直接增加org.opencv.android.JavaCameraView,然后又在下方增加了一个按钮

OpenCV4Android之运用手机摄像头获取Canny边缘

OpenCV4Android之运用手机摄像头获取Canny边缘


OpenCvCarema

这个class我们需要继承Activity并且引用了

 CameraBridgeViewBase.CvCameraViewListener2

里面几个的方法

按钮获取当前图像提取边缘

OpenCV4Android之运用手机摄像头获取Canny边缘

里面就是根据获取的图像创建一个bitbmp的图像,然后把获取的图像mRgbaF先用高斯模糊后,再转成灰度图,然后再运用Canny边缘提取出新的Mat,再将提取完的转回为bmp图像,因为我们需要返回数据,所以返回时把bmp图像转为byte[]发送回去,原来我们的MainActivity里的onActivityResult里面增加了返回照相机数据

OpenCV4Android之运用手机摄像头获取Canny边缘


照相机获取图像时加入了横竖屏的判断

在OpenCV调用照相机时,默认的都是横屏,所以我们竖屏手机打开时会是个90的旋转样子,非常不友好,经常查找资料和多次测试,我自己修改了onCameraFrame这个函数

标红的这块是就是判断当前手机屏幕是横屏和竖屏,通过宽度和高度进行对比,宽度大于高度时就是横屏,小于时就是竖屏.

如果是竖屏我们就执行以下步骤:

  • 先将水平图像转为垂直.

  • 转完后的图像再缩放为原来的大小.

  • 再将图像沿Y轴进行翻转(如果不翻转的话看到的图像是左右互调的)


下面的我们的视频效果,最后有OpenCVCarema.java整个类的代码




下面的整个OpenCvCarema.java的类

package dem.vaccae.opencvdemo;

import android.app.Activity;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.Camera;
import android.os.Bundle;
import android.os.PersistableBundle;
import android.support.annotation.Nullable;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.WindowManager;
import android.widget.Button;

import org.opencv.android.CameraBridgeViewBase;
import org.opencv.android.OpenCVLoader;
import org.opencv.android.Utils;
import org.opencv.core.Core;
import org.opencv.core.CvType;
import org.opencv.core.Mat;
import org.opencv.core.Size;
import org.opencv.imgproc.Imgproc;

import java.io.ByteArrayOutputStream;

/**
 * Created by Administrator on 2018-01-01.
 */

public class OpenCvCarema extends Activity implements CameraBridgeViewBase.CvCameraViewListener2 {

    private static final String TAG = "OpenCvCameraActivity";

    static {
        OpenCVLoader.initDebug();
    }


    private Mat mRgba;
    private Mat mRgbaF;
    private Mat mFlipRgba;

    private Camera mCamera;

    private CameraBridgeViewBase mOpenCvCameraView;

    private Button btnCanny;

    //是否按下按键
    private boolean isbtn = false;
    private Bitmap bmp;


    public OpenCvCarema() {
        Log.i(TAG, "Instantiated new " + this.getClass());
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        Log.i(TAG, "called onCreate");
        super.onCreate(savedInstanceState);

        getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);

        setContentView(R.layout.opencvcarema);


        mOpenCvCameraView = findViewById(R.id.opencvcaremaview);
        mOpenCvCameraView.enableView();
        mOpenCvCameraView.setCvCameraViewListener(this);
        //后置摄像头
        mOpenCvCameraView.setCameraIndex(CameraBridgeViewBase.CAMERA_ID_BACK);

        btnCanny = findViewById(R.id.btnCanny);
        btnCanny.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                isbtn = true;
                bmp = Bitmap.createBitmap(mRgbaF.width(), mRgbaF.height(), Bitmap.Config.ARGB_8888);

                //先进行高斯模糊
                Imgproc.GaussianBlur(mRgbaF, mRgbaF, new Size(3, 3), 0, 0, Imgproc.BORDER_DEFAULT);
                //先改为灰度图像
                Imgproc.cvtColor(mRgbaF, mRgbaF, Imgproc.COLOR_BGRA2GRAY);
                //t代表低阈值,高阈值一般是低阈值的2倍,所以threshold用t*2或t*3即可,
                //apertureSize这里输入3,就是代表3*3的眼膜
                //L2gradient这个是对精度要求,如果是true精度会高,但是会慢,所以一般来说我们都用false就可以了
                Imgproc.Canny(mRgbaF, mFlipRgba, 112, 112 * 2, 3, false);

                Utils.matToBitmap(mFlipRgba, bmp);

                //将图片压缩成byte[]传递回去
                ByteArrayOutputStream baos = new ByteArrayOutputStream();
                bmp.compress(Bitmap.CompressFormat.PNG, 100, baos);
                byte[] bitmapByte = baos.toByteArray();

                Intent rtnintent = new Intent();
                rtnintent.putExtra("bitmap", bitmapByte);
                OpenCvCarema.this.setResult(Activity.RESULT_OK, rtnintent);
                finish();
            }
        });
    }

    @Override
    protected void onPause() {
        super.onPause();
        if (mOpenCvCameraView != null)
            mOpenCvCameraView.disableView();
    }

    @Override
    protected void onResume() {
        super.onResume();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        mOpenCvCameraView.disableView();
    }

    @Override
    public void onCameraViewStarted(int width, int height) {
        mRgba = new Mat();
        mRgbaF = new Mat();
        mFlipRgba = new Mat();
    }

    @Override
    public void onCameraViewStopped() {
        mFlipRgba.release();
        mRgbaF.release();
    }

    @Override
    public Mat onCameraFrame(CameraBridgeViewBase.CvCameraViewFrame inputFrame) {
        if (!isbtn) {
            //注意
            mRgba = inputFrame.rgba();

            //获取屏幕宽和高用于判断是横屏还是竖屏
            DisplayMetrics dm = getResources().getDisplayMetrics();
            if (dm.widthPixels < dm.heightPixels) {
                //转置函数,可以水平的图像变为垂直
                Core.transpose(mRgba, mFlipRgba);
                //将转置后的图像缩放为mRgbaF的大小
                Imgproc.resize(mFlipRgba, mRgbaF, mRgba.size(), 0.0D, 0.0D, 0);
                //flipCode>0将mRgbaF水平翻转(沿Y轴翻转)得到mRgba
                Core.flip(mRgbaF, mRgbaF, 1);
            } else {
                mRgbaF = mRgba;
            }
        }
        return mRgbaF;
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        return true;
    }
}




-END-




以上是关于OpenCV4Android之运用手机摄像头获取Canny边缘的主要内容,如果未能解决你的问题,请参考以下文章

Android基础知识——运用手机多媒体

AI 之 OpenCvSharp 安卓手机摄像头识别人脸

Unity之手机键盘自定义输入栏位置适配&不同手机分辨率适配

Unity之手机键盘自定义输入栏位置适配&不同手机分辨率适配

Unity之手机键盘自定义输入栏位置适配&不同手机分辨率适配

C++ 原生 OpenCV 到 opencv4android 端口太慢?