Android 使用OPENCV实现图像实时对比

来源:互联网 发布:流量充值软件 编辑:程序博客网 时间:2024/06/14 11:09

原创:转发请注明出处
Demo下载地址:http://download.csdn.net/download/prince_wenzheng/9760658

本文主要介绍使用OpenCV实现相机实时对比图片,得到相似度,可用于实现类似ar红包的功能
首先下载SDK,OpenCV-3.2.0(下载官网:http://opencv.org),点击OpenCV for Android下载,下载完成后解压
SDK中包含很多有趣的demo,可以一并看一下,所以,最好新建一个Eclipse的工作空间

1. 创建demo工程

新建一个Android Application,命名为OpenCVDemo

2. 集成OpenCV SDK

将完整的OpenCV SDK拷贝到工作空间中,将OpenCV_SDK_3.2.0/sdk/java导入到Eclipse中,这个就是我们需要集成的library,将它与我们的demo关联

3. 配置SDK

●因为要用到相机,所以在AndroidManifest.xml添加以下代码

添加权限

<uses-permission android:name="android.permission.CAMERA"/>  <uses-feature android:name="android.hardware.camera" android:required="false"/>    <uses-feature android:name="android.hardware.camera.autofocus" android:required="false"/><uses-feature android:name="android.hardware.camera.front" android:required="false"/><uses-feature android:name="android.hardware.camera.front.autofocus" android:required="false"/>

在application节点下添加

android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
●在MainActivity中继承CvCameraViewListener2接口

CvCameraViewListener2是使用OpenCV相机的核心监听

public class MainActivity extends Activity implements CvCameraViewListener2 {    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);    }    @Override    public void onCameraViewStarted(int width, int height) {        // TODO Auto-generated method stub    }    @Override    public void onCameraViewStopped() {        // TODO Auto-generated method stub    }    @Override    public Mat onCameraFrame(CvCameraViewFrame inputFrame) {        // TODO Auto-generated method stub        return null;    }}

onCameraViewStarted:相机启动时调用
onCameraViewStopped:相机销毁时调用
onCameraFrame: 相机工作时调用,参数是相机每一帧的图像,实时对比就在这个方法中进行

●初始化相机

编写布局文件,添加相机控件,相机控件实质是SurfaceView

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:tools="http://schemas.android.com/tools"    android:layout_width="match_parent"    android:layout_height="match_parent"    tools:context="com.example.opencvtest.MainActivity" >    <org.opencv.android.JavaCameraView        android:id="@+id/cv"        android:layout_width="fill_parent"        android:layout_height="fill_parent" /></RelativeLayout>

在OnCreate中初始化相机,并添加开启相机的回调

    /**     * CV相机     */    private CameraBridgeViewBase mCVCamera;    /**     * 加载OpenCV的回调     */    private BaseLoaderCallback mLoaderCallback;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        // 初始化CV相机        mCVCamera = (CameraBridgeViewBase) findViewById(R.id.cv);        mCVCamera.setVisibility(CameraBridgeViewBase.VISIBLE);        // 设置相机监听        mCVCamera.setCvCameraViewListener(this);        // 连接到OpenCV的回调        mLoaderCallback = new BaseLoaderCallback(this) {            @Override            public void onManagerConnected(int status) {                switch (status) {                case LoaderCallbackInterface.SUCCESS:                    mCVCamera.enableView();                    break;                default:                    break;                }            }        };    }

界面加载完成时说明OpenCV已经初始化完成,所以重写onResume()方法,开启相机

    @Override    protected void onResume() {        // 界面加载完成的时候向OpenCV的连接回调发送连接成功的信号        if (OpenCVLoader.initDebug()) {            mLoaderCallback.onManagerConnected(LoaderCallbackInterface.SUCCESS);        }        super.onResume();    }

重写onPause方法,及时销毁相机

    @Override    protected void onPause() {        super.onPause();        // 销毁OpenCV相机        if (mCVCamera != null)            mCVCamera.disableView();    }

编辑onCameraFrame方法,让相机的每一帧展示在界面上

    @Override    public Mat onCameraFrame(CvCameraViewFrame inputFrame) {        return inputFrame.rgba();    }

到这开启相机的代码已经完成了,但是此时的工程需要依赖SDK中提供的OpenCV_3.2.0_Manager才能运行,这显然是不满足多数项目需求的,需要进行以下操作:
为项目添加C/C++编译支持,右键项目,选择Android Tools–>Add Native Support,点击finish
打开jni/Android.mk

在红框处添加以下代码

OPENCV_CAMERA_MODULES:=onOPENCV_INSTALL_MODULES:=onOPENCV_LIB_TYPE:=SHAREDifdef OPENCV_ANDROID_SDK  ifneq ("","$(wildcard $(OPENCV_ANDROID_SDK)/OpenCV.mk)")    include ${OPENCV_ANDROID_SDK}/OpenCV.mk  else    include ${OPENCV_ANDROID_SDK}/sdk/native/jni/OpenCV.mk  endifelse  include ../OpenCV-android-sdk/sdk/native/jni/OpenCV.mkendif

因为include使用的是相对路径,所以最好将OpenCVDemo和OpenCV-android-sdk放在同一个目录下,否则需要根据自己目录结构修改相对路径

● 运行

4. 编写图片对比的方法

新建一个HistUtils.java,添加下面两个方法

    /**     * 比较来个矩阵的相似度     *      * @param mBitmap1     * @param mBitmap2     * @return     */    public static double comPareHist(Bitmap mBitmap1, Bitmap mBitmap2) {        Mat mat1 = new Mat();        Mat mat2 = new Mat();        Utils.bitmapToMat(mBitmap1, mat1);        Utils.bitmapToMat(mBitmap2, mat2);        return comPareHist(mat1, mat2);    }    /**     * 比较来个矩阵的相似度     *      * @param mat1     * @param mat2     * @return     */    public static double comPareHist(Mat mat1, Mat mat2) {        Mat srcMat = new Mat();        Mat desMat = new Mat();        Imgproc.cvtColor(mat1, srcMat, Imgproc.COLOR_BGR2GRAY);        Imgproc.cvtColor(mat2, desMat, Imgproc.COLOR_BGR2GRAY);        srcMat.convertTo(srcMat, CvType.CV_32F);        desMat.convertTo(desMat, CvType.CV_32F);        double target = Imgproc.compareHist(srcMat, desMat,                Imgproc.CV_COMP_CORREL);        return target;    }

Imgproc.compareHist(Mat H1, Mat H2, int method)这个方法是OpenCV提供的通过直方图算法对比两张图片的相似度,完全相同的两张图片相似度为1。

添加对比按钮,实现主动对比和自动对比

核心的代码上面已经写完了,剩下的就是添加按钮和触发对比,就不详细介绍了,直接把完整的代码附上,备注很详细

MainActivity.java

package com.example.opencvdemo;import org.opencv.android.BaseLoaderCallback;import org.opencv.android.CameraBridgeViewBase;import org.opencv.android.CameraBridgeViewBase.CvCameraViewFrame;import org.opencv.android.CameraBridgeViewBase.CvCameraViewListener2;import org.opencv.android.LoaderCallbackInterface;import org.opencv.android.OpenCVLoader;import org.opencv.android.Utils;import org.opencv.core.Mat;import android.annotation.SuppressLint;import android.app.Activity;import android.graphics.Bitmap;import android.graphics.Bitmap.Config;import android.os.Bundle;import android.os.Handler;import android.os.Message;import android.view.View;import android.view.View.OnClickListener;import android.widget.Button;import android.widget.ImageView;import android.widget.TextView;public class MainActivity extends Activity implements CvCameraViewListener2 {    /**     * 展示原图和对比图     */    private ImageView iv1, iv2;    /**     * 原图和对比图     */    private Bitmap bmp1, bmp2;    /**     * 拍摄原图、拍摄对比图、对比、自动对比     */    private Button pz1, pz2, db, zddb;    /**     * 显示相似度(完全相同值为1)     */    private TextView tv;    /**     * CV相机     */    private CameraBridgeViewBase mCVCamera;    /**     * 加载OpenCV的回调     */    private BaseLoaderCallback mLoaderCallback;    /**     * 拍照状态 0:不拍照 ,1:拍原图,2:拍对比图,3:拍对比图并自动对比     */    private int isTakePhoto = 0;    /**     * 用于定时执行图片对比     */    private Handler handler;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        // 初始化        init();    }    @SuppressLint("HandlerLeak")    private void init() {        // 初始化        iv1 = (ImageView) findViewById(R.id.iv1);        iv2 = (ImageView) findViewById(R.id.iv2);        pz1 = (Button) findViewById(R.id.pz1);        pz2 = (Button) findViewById(R.id.pz2);        db = (Button) findViewById(R.id.db);        zddb = (Button) findViewById(R.id.zddb);        tv = (TextView) findViewById(R.id.tv);        handler = new Handler() {            @Override            public void handleMessage(Message msg) {                super.handleMessage(msg);                // 自动拍照并对比                isTakePhoto = 3;            }        };        OnClickListener btnOnClick = new OnClickListener() {            @Override            public void onClick(View v) {                switch (v.getId()) {                case R.id.pz1:                    // 拍原图                    isTakePhoto = 1;                    break;                case R.id.pz2:                    // 拍对比图                    isTakePhoto = 2;                    break;                case R.id.db:                    hist();                    break;                case R.id.zddb:                    // 拍对比图并自动对比                    isTakePhoto = 3;                    break;                }            }        };        // 设置点击事件        pz1.setOnClickListener(btnOnClick);        pz2.setOnClickListener(btnOnClick);        db.setOnClickListener(btnOnClick);        zddb.setOnClickListener(btnOnClick);        // 初始化CV相机        mCVCamera = (CameraBridgeViewBase) findViewById(R.id.cv);        mCVCamera.setVisibility(CameraBridgeViewBase.VISIBLE);        // 设置相机监听        mCVCamera.setCvCameraViewListener(this);        // 连接到OpenCV的回调        mLoaderCallback = new BaseLoaderCallback(this) {            @Override            public void onManagerConnected(int status) {                switch (status) {                case LoaderCallbackInterface.SUCCESS:                    mCVCamera.enableView();                    break;                default:                    break;                }            }        };    }    private void hist() {        // 对比算法会耗时,导致页面卡顿,所以新开线程进行对比        new Thread(new Runnable() {            @Override            public void run() {                // 对比                final double target = HistUtils.comPareHist(bmp1, bmp2);                MainActivity.this.runOnUiThread(new Runnable() {                    @Override                    public void run() {                        // 将相似度显示在左上角                        tv.setText("相似度:" + target);                    }                });            }        }).start();    }    @Override    public void onCameraViewStarted(int width, int height) {    }    @Override    public void onCameraViewStopped() {    }    @Override    public Mat onCameraFrame(CvCameraViewFrame inputFrame) {// 相机拍摄每一帧的图像,都在此处理        // 获取相机中的图像        final Mat rgba = inputFrame.rgba();        if (isTakePhoto != 0) {            // 记录拍照状态            final int who = isTakePhoto;            // 重置拍照状态            isTakePhoto = 0;            // 要把Mat对象转换成Bitmap对象,需要创建一个宽高相同的Bitmap对象昨晚参数            final Bitmap bmp = Bitmap.createBitmap(rgba.cols(), rgba.rows(),                    Config.RGB_565);            // 记录要展示图片的ImageView            ImageView iv = null;            // Mat >>> Bitmap            Utils.matToBitmap(rgba, bmp);            if (who == 1) {                // 展示原图                iv = iv1;                bmp1 = bmp;            } else if (who == 2) {                // 展示对比图                iv = iv2;                bmp2 = bmp;            } else {                // 展示对比图                iv = iv2;                bmp2 = bmp;                // 对比                hist();                // 每隔0.5秒对比一次                handler.sendEmptyMessageDelayed(1, 500);            }            // 记录要展示图片的ImageView            final ImageView image = iv;            runOnUiThread(new Runnable() {                @Override                public void run() {                    // 展示拍到的图片                    image.setImageBitmap(bmp);                    if (bmp1 != null) {                        // 如果原图已经拍好了,那么可以进行自动对比,将自动对比按钮设置为可用                        zddb.setEnabled(true);                        if (bmp2 != null) {                            // 如果原图和对比图都已经拍好了,那么可以进行对比,将对比按钮设置为可用                            db.setEnabled(true);                        }                    }                }            });        }        // 将每一帧的图像展示在界面上        return inputFrame.rgba();    }    @Override    protected void onResume() {        // 界面加载完成的时候向OpenCV的连接回调发送连接成功的信号        if (OpenCVLoader.initDebug()) {            mLoaderCallback.onManagerConnected(LoaderCallbackInterface.SUCCESS);        }        super.onResume();    }    @Override    protected void onPause() {        super.onPause();        // 销毁OpenCV相机        if (mCVCamera != null)            mCVCamera.disableView();    }}
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:tools="http://schemas.android.com/tools"    android:layout_width="match_parent"    android:layout_height="match_parent"    tools:context="com.example.opencvdemo.MainActivity" >    <RelativeLayout        android:id="@+id/rl"        android:layout_width="match_parent"        android:layout_height="match_parent" >        <org.opencv.android.JavaCameraView            android:id="@+id/cv"            android:layout_width="fill_parent"            android:layout_height="fill_parent" />        <TextView            android:id="@+id/tv"            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:background="#ffffff"            android:textColor="#000000"            android:textSize="13sp" />    </RelativeLayout>    <LinearLayout        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:layout_centerInParent="true"        android:orientation="horizontal" >        <ImageView            android:id="@+id/iv1"            android:layout_width="150dp"            android:layout_height="wrap_content"            android:src="@drawable/red" />        <ImageView            android:id="@+id/iv2"            android:layout_width="150dp"            android:layout_height="wrap_content"            android:src="@drawable/red" />    </LinearLayout>    <LinearLayout        android:id="@+id/ll"        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:layout_alignParentBottom="true"        android:orientation="horizontal" >        <Button            android:id="@+id/pz1"            android:layout_width="0dp"            android:layout_height="wrap_content"            android:layout_weight="1"            android:text="拍照1" />        <Button            android:id="@+id/pz2"            android:layout_width="0dp"            android:layout_height="wrap_content"            android:layout_weight="1"            android:text="拍照2" />        <Button            android:id="@+id/db"            android:layout_width="0dp"            android:layout_height="wrap_content"            android:layout_weight="1"            android:enabled="false"            android:text="对比" />        <Button            android:id="@+id/zddb"            android:layout_width="0dp"            android:layout_height="wrap_content"            android:layout_weight="1"            android:enabled="false"            android:text="自动对比" />    </LinearLayout></RelativeLayout>

red.png(自己做的图片,有点丑)
drawable-hdpi/red.png

● 运行

这里写图片描述

● 完成,欢迎吐槽
1 0
原创粉丝点击