实现平台:windows下的Android studio1.4

依赖库:openCV3.1.0

程序安装平台:Android6.0

实现的功能:从手机中选择一张图片,检测图片的基本特征,通过menu菜单选择要检测的特征,包括Canny边缘检测、Harris角点检测、霍夫直线检测

说明:对于检测图像的基本特征的算法就不加以详细说明了,网上的资料很多,现在这里主要介绍算法以及代码的编写

1.在Androidmanifest.xml文件中添加如下代码:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.wangshuailpp.myapplication" >

    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme" >
        <activity
            android:name=".MainActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

这里最重要的是,表示要开启手机内存的读取权限:

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>

2.在布局文件中activity_main.xml文件中添加一个图片控件:

<?xml version="1.0" encoding="utf-8"?>
<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" android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:paddingBottom="@dimen/activity_vertical_margin" tools:context=".MainActivity">

    <ImageView
        android:id="@+id/Picture"
        android:layout_height="fill_parent"
        android:layout_width="fill_parent"
        android:visibility="visible"
        />
</RelativeLayout>

3.菜单menu_main.xml文件中添加成员:

<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools" tools:context=".MainActivity">
   <item
       android:id="@+id/Canny"
       android:title="Canny"
       android:showAsAction="never"
       />
    <item
        android:id="@+id/Harris"
        android:title="Harris"
        android:showAsAction="never"
        />
    <item
        android:id="@+id/Hough"
        android:title="Hough"
        android:showAsAction="never"
        />
</menu>



4.在MainActivity.java主类中的代码:

package com.example.wangshuailpp.myapplication;
/*
功能介绍:深入OpenCV Android应用开发第二章代码,检测图像的基本特征
        包括了Canny边缘检测法Sobel边缘检测法等
实现步骤:1.从手机中取出一张图片作为原始图片,通过点击menu对应的按钮开始选择图片
        2.通过menu按钮选择要对照片进行的图像处理
 */

import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.ImageView;
import android.widget.Toast;

import org.opencv.android.BaseLoaderCallback;
import org.opencv.android.LoaderCallbackInterface;
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.Point;
import org.opencv.core.Scalar;
import org.opencv.imgproc.Imgproc;

import java.io.FileNotFoundException;
import java.io.InputStream;
import java.util.Random;

public class MainActivity extends ActionBarActivity {

    private final static int CANNY = 0;
    private final static int HARRIS = 1;
    private final static int HOUGH = 2;
    private final static String TAG = "infor";

    private Mat src = null;//定义一个Mat型类用于临时存放选择的图片
    private Mat image = null;//用于存放得到的图片
    private Mat des = null;//用于临时存放Mat型类的图片
    private Bitmap resultBitmap;
    private ImageView pictureView = null;//定义一个ImageView类视图用于存放选择的图片

    private BaseLoaderCallback mOpenCVCallBack = new BaseLoaderCallback(this) {
        @Override
        public void onManagerConnected(int status) {

            switch (status){
                case LoaderCallbackInterface.SUCCESS:
                    /*在这里执行自己的语句*/

                    break;
                default:
                    super.onManagerConnected(status);
                    break;
            }
        }
    };

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

        pictureView = (ImageView)findViewById(R.id.Picture);

    }

    /*启动openCV*/
    @Override
    protected void onResume() {
        super.onResume();
        OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION_3_1_0, this, mOpenCVCallBack);
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.menu_main, menu);
        return true;
    }

    /*在这里选取要进行的操作*/
    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();

        //对应Canny边缘检测的按钮
        if (id == R.id.Canny) {
            /*下面对通过Intent对象得到选择图片的Activity,最后返回图片的信息,得到图片*/
            Intent pictureSelectIntent = new Intent(Intent.ACTION_PICK);//设置Action
            pictureSelectIntent.setType("image/");//设置数据的类型
            startActivityForResult(pictureSelectIntent,CANNY);
            return true;
        }

        //对应Harris边缘检测的按钮
        if (R.id.Harris == id){
            Intent pictureSelectIntent = new Intent(Intent.ACTION_PICK);
            pictureSelectIntent.setType("image/");
            startActivityForResult(pictureSelectIntent,HARRIS);
            return true;
        }
        //对应Hough的直线检测按钮
        if(R.id.Hough == id){
            Intent pictureSelectIntent = new Intent(Intent.ACTION_PICK);
            pictureSelectIntent.setType("image/");
            startActivityForResult(pictureSelectIntent,HOUGH);
            return true;
        }

        return super.onOptionsItemSelected(item);
    }


    /*调用StartActivityForResult后的回调函数
    * 在这个函数里面得到图片然后进行相应的处理
    * */

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if(RESULT_OK == resultCode){
            switch(requestCode){
                case CANNY:
                    try {
                        Log.i(TAG,"onActivityResult00000000000");
                        image = GetPicture(data);
                        Toast.makeText(MainActivity.this, "图片选取成功", Toast.LENGTH_SHORT).show();
                        Log.i(TAG,"onActivityResult11111111111");
                        resultBitmap = MyCanny(image);
                        Log.i(TAG,"onActivityResult22222222222222");
                        pictureView.setImageBitmap(resultBitmap);
                    } catch (FileNotFoundException e) {
                        e.printStackTrace();
                    }
                    break;
                case HARRIS:
                    try {
                        image = GetPicture(data);//得到图片
                        Toast.makeText(MainActivity.this, "图片选取成功", Toast.LENGTH_SHORT).show();
                        Log.i(TAG,"onActivityResult11111111111");
                        resultBitmap = MyHarris(image);//角点检测的图像处理
                        Log.i(TAG,"onActivityResult22222222222222");
                        pictureView.setImageBitmap(resultBitmap);

                    } catch (FileNotFoundException e) {
                        e.printStackTrace();
                    }
                case HOUGH:
                    try {
                        image = GetPicture(data);//得到图片
                        Toast.makeText(MainActivity.this, "图片选取成功", Toast.LENGTH_SHORT).show();
                        Log.i(TAG,"onActivityResult11111111111");
                        resultBitmap = MyHoughLine(image);
                        pictureView.setImageBitmap(resultBitmap);

                    }catch (FileNotFoundException e) {
                        e.printStackTrace();
                    }
            }
        }
    }

    /*得到图片*/
    public Mat GetPicture(Intent data) throws FileNotFoundException {

        /*下面的代码是获得手机内的图片*/
        final Uri imageUri = data.getData();//得到图片的路径
        final InputStream imageStream = getContentResolver().openInputStream(imageUri);//得到基于路径的流文件
        final Bitmap selectImage = BitmapFactory.decodeStream(imageStream);//得到了图片的位图

        /*下面将位图转换成Mat型,可以进行图片的处理*/
        src = new Mat(selectImage.getHeight(),selectImage.getWidth(), CvType.CV_8UC4);
        Utils.bitmapToMat(selectImage,src);

        return src;
    }

    /*下面进行图片的处理
    *
    * */

    /*Canny边缘处理*/
    public Bitmap MyCanny(Mat src){

        Bitmap result;
        Mat grayMat = new Mat();
        Mat cannyEdges = new Mat();
        Log.i(TAG,"MyCanny0000000000");

        /*将图片转换成灰度图*/
        Imgproc.cvtColor(src, grayMat, Imgproc.COLOR_BGR2GRAY);
        Log.i(TAG, "MyCanny1111111111111111");
        /*得到边缘图,这里最后两个参数控制着选择边缘的阀值上限和下限*/
        Imgproc.Canny(grayMat,cannyEdges,50,300);
        Log.i(TAG, "MyCanny222222222222222222222222");
        /*将Mat图转换成位图*/
        result = Bitmap.createBitmap(src.cols(),src.rows(),Bitmap.Config.ARGB_8888);
        Utils.matToBitmap(cannyEdges,result);
        Log.i(TAG, "MyCanny3333333333333333333333");

        return result;
    }

    /*Harris角点检测*/
    public Bitmap MyHarris(Mat src){
        Bitmap resultHarris;

        Mat grayMat = new Mat();
        Mat corners = new Mat();

        Log.i(TAG,"MyHarris00000000000000000000");
        /*将图片转换成灰度图*/
        Imgproc.cvtColor(src,grayMat,Imgproc.COLOR_BGR2GRAY);
        Log.i(TAG, "MyHarris1111111111111111111");
        /*找出角点*/
        Mat tempDst = new Mat();
        Imgproc.cornerHarris(grayMat,tempDst,2,3,0.04);
        Log.i(TAG, "MyHarris2222222222222222222");
        /*归一化Harris角点的输出*/
        Mat tempDstNorm = new Mat();
        Core.normalize(tempDst,tempDstNorm,0,255,Core.NORM_MINMAX);
        Core.convertScaleAbs(tempDstNorm, corners);
        Log.i(TAG, "MyHarris33333333333333333333");
        /*在新的图片上绘制角点*/
        Random r = new Random();
        for(int i = 0; i < tempDstNorm.cols(); i++){
            for (int j = 0;j <tempDstNorm.rows(); j++){
                double[] value = tempDstNorm.get(j,i);
                if(value[0] > 250){//决定了画出哪些角点,值越大选择画出的点就越少。如果程序跑的比较慢,就是由于值选取的太小,导致画的点过多
                    Imgproc.circle(corners, new Point(i,j),5,new Scalar(r.nextInt(255)),2);
                }
            }
        }
        Log.i(TAG,"MyHarris4444444444444444444444444");
        /*将Mat图转换成位图*/
        resultHarris = Bitmap.createBitmap(src.cols(),src.rows(),Bitmap.Config.ARGB_8888);//这一步至关重要,必须初始化Bitmap对象的大小
        Utils.matToBitmap(corners, resultHarris);

        return resultHarris;
    }

    /*Hough直线检测*/
    public Bitmap MyHoughLine(Mat src){
        Bitmap resultHough;

        Mat grayMat = new Mat();
        Mat cannyEdges = new Mat();
        Mat lines = new Mat();
        Mat origination = new Mat(src.size(),CvType.CV_8UC1);
        src.copyTo(origination);//拷贝

        /*通过Canny得到边缘图*/
        Imgproc.cvtColor(origination,grayMat,Imgproc.COLOR_BGR2GRAY);
        Imgproc.Canny(grayMat,cannyEdges,50,300);
        //Mat cannyEdges = new Mat(resultHough.getHeight(),resultHough.getWidth(),CvType.CV_8UC1);


        Log.i(TAG,"MyHoughLine00000000000000");
        /*获得直线图*/
        Imgproc.HoughLinesP(cannyEdges,lines,1,Math.PI/180,10,0,50);
        Log.i(TAG, "MyHoughLine111111111111111");

        Mat houghLines = new Mat();
        houghLines.create(cannyEdges.rows(),cannyEdges.cols(),CvType.CV_8UC1);
        Log.i(TAG, "MyHoughLine2222222222222222222");
        /*在图线的上绘制直线*/
        for(int i = 0;i < lines.rows();i++){
            double[] points = lines.get(i,0);
            if(null != points){
                double x1,y1,x2,y2;

                x1 = points[0];
                y1 = points[1];
                x2 = points[2];
                y2 = points[3];

                Point pt1 = new Point(x1,y1);
                Point pt2 = new Point(x2,y2);
            /*在一幅图像上绘制直线*/
                Imgproc.line(houghLines,pt1,pt2,new Scalar(55,100,195),3);
            }
        }
        Log.i(TAG, "MyHoughLine3333333333333333333333333");
        resultHough = Bitmap.createBitmap(src.cols(),src.rows(),Bitmap.Config.ARGB_8888);
        Utils.matToBitmap(houghLines,resultHough);
        Log.i(TAG, "MyHoughLine44444444444444444444444444444");

        return resultHough;
    }
}


这里需要注意的事项:

1.在霍夫直线检测中有一句代码,很多网上的程序都不对,都写成了


double[] points = lines.get(0,i);

其实是

double[] points = lines.get(i,0);


写成第一种,会导致只会画出一条直线。



其他的都可以在程序的解释中看到,在这里就不都说了,下面直接贴结果,分别是原图,Canny,Harri,霍夫直线。

openCV获取安卓屏幕数据流 android opencv图像识别_openCV获取安卓屏幕数据流

openCV获取安卓屏幕数据流 android opencv图像识别_xml_02


openCV获取安卓屏幕数据流 android opencv图像识别_特征检测_03



openCV获取安卓屏幕数据流 android opencv图像识别_特征检测_04