深入OpenCV Android应用开发 中文版 – 第二章代码更新

book

本书中文版链接:京东  当当

英文原版链接:亚马逊《Mastering OpenCV Android Application Programming》


        原书所给代码以章为单位,针对的Android版本从API 19到API 21不等,同时使用的OpenCV库版本也有2.4.9和2.4.10两种。本文给出的代码是在原书代码的基础上,针对Android 7.0(API 24)与OpenCV 3.2进行了修改,补充了一些注释,适当地增加了一些预处理操作,以使代码整体上更合理。

        原书的完整代码可以在这里获取:https://www.packtpub.com/lcode_download/22299

        更新后的代码托管在GitHub:https://github.com/johnhany/MOAAP/tree/master/MOAAP-Chp2-r3

        关于在Android Studio上配置OpenCV 3.1开发环境的方法,请参考《在Android Studio上进行OpenCV 3.1开发》。OpenCV 3.2的配置方法与之相似,不过在配置过程中要注意版本上的不同。

        本章利用OpenCV Java API对图像进行一些基本的特征检测操作,包括Canny边缘检测、Sobel算子、Harris角点检测、霍夫直线检测、霍夫圆检测。本章不涉及NDK开发及多线程。


更新记录:

        2017.07.24更新:代码更新到Android Studio 2.3.3。


开发环境:

        Windows 10 x64专业版(Windows 7 x64旗舰版以及Ubuntu 16.04 x64也验证通过)

        Android Studio 2.3.3(Gradle 3.3,Android Plugin 2.3.3)

        Android 7.0(API 24)

        JDK 8u141

        OpenCV 3.2.0 Android SDK


代码及简略解释:

        1.创建Android Studio项目,包命名为net.johnhany.moaap_chp2

        2.app\src\main\java目录中找到net.johnhany.moaap_chp2包,为MainActivity.java添加如下代码:

package net.johnhany.moaap_chp2;

import android.Manifest;
import android.content.pm.PackageManager;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.support.annotation.NonNull;
import android.content.Intent;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;
import android.media.ExifInterface;
import android.net.Uri;
import android.os.Bundle;
import android.provider.MediaStore;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.ImageView;

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.MatOfDouble;
import org.opencv.core.MatOfPoint;
import org.opencv.core.MatOfRect;
import org.opencv.core.Point;
import org.opencv.core.Rect;
import org.opencv.core.Scalar;
import org.opencv.core.Size;
import org.opencv.imgproc.Imgproc;
import org.opencv.objdetect.HOGDescriptor;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;

public class MainActivity extends AppCompatActivity {

    private Mat originalMat;
    private Bitmap currentBitmap;
    private ImageView imageView;
    static int REQUEST_READ_EXTERNAL_STORAGE = 0;
    static boolean read_external_storage_granted = false;
    private final int ACTION_PICK_PHOTO = 1;

    private BaseLoaderCallback mOpenCVCallBack = new BaseLoaderCallback(this) {
        @Override
        public void onManagerConnected(int status) {
            switch (status) {
                case LoaderCallbackInterface.SUCCESS:
                    //DO YOUR WORK/STUFF HERE
                    Log.i("OpenCV", "OpenCV loaded successfully.");
                    break;
                default:
                    super.onManagerConnected(status);
                    break;
            }
        }
    };

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

        imageView = (ImageView)findViewById(R.id.image_view);

        OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION_3_2_0, this, mOpenCVCallBack);

        if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.READ_EXTERNAL_STORAGE)
                != PackageManager.PERMISSION_GRANTED) {
            Log.i("permission", "request READ_EXTERNAL_STORAGE");
            ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE},
                    REQUEST_READ_EXTERNAL_STORAGE);
        }else {
            Log.i("permission", "READ_EXTERNAL_STORAGE already granted");
            read_external_storage_granted = true;
        }
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.menu_main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {

        int id = item.getItemId();

        //noinspection SimplifiableIfStatement
        if (id == R.id.open_gallery) {
            if(read_external_storage_granted) {
                Intent intent = new Intent(Intent.ACTION_PICK);
                intent.setType("image/*");
                startActivityForResult(intent, ACTION_PICK_PHOTO);
            }else {
                return true;
            }
        } else if (id == R.id.DoG) {
            //Apply Difference of Gaussian
            DifferenceOfGaussian();
        } else if (id == R.id.CannyEdges) {
            //Apply Canny Edge Detector
            Canny();
        } else if (id == R.id.SobelFilter) {
            //Apply Sobel Filter
            Sobel();
        } else if (id == R.id.HarrisCorners) {
            //Apply Harris Corners
            HarrisCorner();
        } else if (id == R.id.HoughLines) {
            //Apply Hough Lines
            HoughLines();
        } else if (id == R.id.HoughCircles) {
            //Apply Hough Circles
            HoughCircles();
        } else if (id == R.id.Contours) {
            //Apply contours
            Contours();
        }

        return super.onOptionsItemSelected(item);

    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (requestCode == ACTION_PICK_PHOTO && resultCode == RESULT_OK && null != data && read_external_storage_granted) {

            Uri selectedImage = data.getData();
            String[] filePathColumn = {MediaStore.Images.Media.DATA};
            Cursor cursor = getContentResolver().query(selectedImage, filePathColumn, null, null, null);
            String picturePath;
            if(cursor == null) {
                Log.i("data", "cannot load any image");
                return;
            }else {
                try {
                    cursor.moveToFirst();
                    int columnIndex = cursor.getColumnIndex(filePathColumn[0]);
                    picturePath = cursor.getString(columnIndex);
                }finally {
                    cursor.close();
                }
            }

            BitmapFactory.Options options = new BitmapFactory.Options();
            options.inSampleSize = 2;
            Bitmap temp = BitmapFactory.decodeFile(picturePath, options);

            int orientation = 0;
            try {
                ExifInterface imgParams = new ExifInterface(picturePath);
                orientation = imgParams.getAttributeInt(
                        ExifInterface.TAG_ORIENTATION,
                        ExifInterface.ORIENTATION_UNDEFINED);
            } catch (IOException e) {
                e.printStackTrace();
            }

            Matrix rotate90 = new Matrix();
            rotate90.postRotate(orientation);
            Bitmap originalBitmap = rotateBitmap(temp,orientation);

            if(originalBitmap != null) {
                Bitmap tempBitmap = originalBitmap.copy(Bitmap.Config.ARGB_8888, true);
                originalMat = new Mat(tempBitmap.getHeight(),
                        tempBitmap.getWidth(), CvType.CV_8U);
                Utils.bitmapToMat(tempBitmap, originalMat);
                currentBitmap = originalBitmap.copy(Bitmap.Config.ARGB_8888, false);
                imageView.setImageBitmap(currentBitmap);
            }else {
                Log.i("data", "originalBitmap is empty");
            }
        }
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String permissions[], @NonNull int[] grantResults) {
        if (requestCode == REQUEST_READ_EXTERNAL_STORAGE) {
            // If request is cancelled, the result arrays are empty.
            if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                // permission was granted
                Log.i("permission", "READ_EXTERNAL_STORAGE granted");
                read_external_storage_granted = true;
            } else {
                // permission denied
                Log.i("permission", "READ_EXTERNAL_STORAGE denied");
            }
        }
    }

    public static Bitmap rotateBitmap(Bitmap bitmap, int orientation) {

        Matrix matrix = new Matrix();
        switch (orientation) {
            case ExifInterface.ORIENTATION_NORMAL:
                return bitmap;
            case ExifInterface.ORIENTATION_FLIP_HORIZONTAL:
                matrix.setScale(-1, 1);
                break;
            case ExifInterface.ORIENTATION_ROTATE_180:
                matrix.setRotate(180);
                break;
            case ExifInterface.ORIENTATION_FLIP_VERTICAL:
                matrix.setRotate(180);
                matrix.postScale(-1, 1);
                break;
            case ExifInterface.ORIENTATION_TRANSPOSE:
                matrix.setRotate(90);
                matrix.postScale(-1, 1);
                break;
            case ExifInterface.ORIENTATION_ROTATE_90:
                matrix.setRotate(90);
                break;
            case ExifInterface.ORIENTATION_TRANSVERSE:
                matrix.setRotate(-90);
                matrix.postScale(-1, 1);
                break;
            case ExifInterface.ORIENTATION_ROTATE_270:
                matrix.setRotate(-90);
                break;
            default:
                return bitmap;
        }
        try {
            Bitmap bmRotated = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
            bitmap.recycle();
            return bmRotated;
        } catch (OutOfMemoryError e) {
            e.printStackTrace();
            return null;
        }

    }

    //Difference of Gaussian
    public void DifferenceOfGaussian() {
        Mat grayMat = new Mat();
        Mat blur1 = new Mat();
        Mat blur2 = new Mat();

        //Converting the image to grayscale
        Imgproc.cvtColor(originalMat, grayMat, Imgproc.COLOR_BGR2GRAY);

        Imgproc.GaussianBlur(grayMat, blur1, new Size(15, 15), 5);
        Imgproc.GaussianBlur(grayMat, blur2, new Size(21, 21), 5);

        //Subtracting the two blurred images
        Mat DoG = new Mat();
        Core.absdiff(blur1, blur2, DoG);

        //Inverse Binary Thresholding
        Core.multiply(DoG, new Scalar(100), DoG);
        Imgproc.threshold(DoG, DoG, 50, 255, Imgproc.THRESH_BINARY_INV);

        //Converting Mat back to Bitmap
        Utils.matToBitmap(DoG, currentBitmap);
        imageView.setImageBitmap(currentBitmap);
    }

    //Canny Edge Detection
    void Canny() {
        Mat grayMat = new Mat();
        Mat cannyEdges = new Mat();
        //Converting the image to grayscale
        Imgproc.cvtColor(originalMat, grayMat, Imgproc.COLOR_BGR2GRAY);

        Imgproc.Canny(grayMat, cannyEdges, 10, 100);

        //Converting Mat back to Bitmap
        Utils.matToBitmap(cannyEdges, currentBitmap);
        imageView.setImageBitmap(currentBitmap);
    }

    //Sobel Operator
    void Sobel() {
        Mat grayMat = new Mat();
        Mat sobel = new Mat(); //Mat to store the final result

        //Matrices to store gradient and absolute gradient respectively
        Mat grad_x = new Mat();
        Mat abs_grad_x = new Mat();

        Mat grad_y = new Mat();
        Mat abs_grad_y = new Mat();

        //Converting the image to grayscale
        Imgproc.cvtColor(originalMat, grayMat, Imgproc.COLOR_BGR2GRAY);

        //Calculating gradient in horizontal direction
        Imgproc.Sobel(grayMat, grad_x, CvType.CV_16S, 1, 0, 3, 1, 0);

        //Calculating gradient in vertical direction
        Imgproc.Sobel(grayMat, grad_y, CvType.CV_16S, 0, 1, 3, 1, 0);

        //Calculating absolute value of gradients in both the direction
        Core.convertScaleAbs(grad_x, abs_grad_x);
        Core.convertScaleAbs(grad_y, abs_grad_y);

        //Calculating the resultant gradient
        Core.addWeighted(abs_grad_x, 0.5, abs_grad_y, 0.5, 1, sobel);

        //Converting Mat back to Bitmap
        Utils.matToBitmap(sobel, currentBitmap);
        imageView.setImageBitmap(currentBitmap);
    }

    void HoughLines() {

        Mat grayMat = new Mat();
        Mat cannyEdges = new Mat();
        Mat lines = new Mat();

        //Converting the image to grayscale
        Imgproc.cvtColor(originalMat, grayMat, Imgproc.COLOR_BGR2GRAY);

        Imgproc.Canny(grayMat, cannyEdges, 10, 100);

        Imgproc.HoughLinesP(cannyEdges, lines, 1, Math.PI / 180, 50, 20, 20);

        Mat houghLines = new Mat();
        houghLines.create(cannyEdges.rows(), cannyEdges.cols(), CvType.CV_8UC1);

        //Drawing lines on the image
        for (int i = 0; i < lines.cols(); i++) {
            double[] points = lines.get(0, i);
            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);

            //Drawing lines on an image
            Imgproc.line(houghLines, pt1, pt2, new Scalar(255, 0, 0), 1);
        }

        //Converting Mat back to Bitmap
        Utils.matToBitmap(houghLines, currentBitmap);
        imageView.setImageBitmap(currentBitmap);

    }

    void HoughCircles() {
        Mat grayMat = new Mat();
        Mat cannyEdges = new Mat();
        Mat circles = new Mat();

        //Converting the image to grayscale
        Imgproc.cvtColor(originalMat, grayMat, Imgproc.COLOR_BGR2GRAY);

        Imgproc.Canny(grayMat, cannyEdges, 10, 100);

        Imgproc.HoughCircles(cannyEdges, circles, Imgproc.CV_HOUGH_GRADIENT, 1, cannyEdges.rows() / 15);//, grayMat.rows() / 8);

        Mat houghCircles = new Mat();
        houghCircles.create(cannyEdges.rows(), cannyEdges.cols(), CvType.CV_8UC1);

        //Drawing lines on the image
        for (int i = 0; i < circles.cols(); i++) {
            double[] parameters = circles.get(0, i);
            double x, y;
            int r;

            x = parameters[0];
            y = parameters[1];
            r = (int) parameters[2];

            Point center = new Point(x, y);

            //Drawing circles on an image
            Imgproc.circle(houghCircles, center, r, new Scalar(255, 0, 0), 1);
        }

        //Converting Mat back to Bitmap
        Utils.matToBitmap(houghCircles, currentBitmap);
        imageView.setImageBitmap(currentBitmap);
    }

    void Contours() {
        Mat grayMat = new Mat();
        Mat cannyEdges = new Mat();
        Mat hierarchy = new Mat();

        List<MatOfPoint> contourList = new ArrayList<MatOfPoint>(); //A list to store all the contours

        //Converting the image to grayscale
        Imgproc.cvtColor(originalMat, grayMat, Imgproc.COLOR_BGR2GRAY);

        Imgproc.Canny(originalMat, cannyEdges, 10, 100);

        //finding contours
        Imgproc.findContours(cannyEdges, contourList, hierarchy, Imgproc.RETR_LIST, Imgproc.CHAIN_APPROX_SIMPLE);

        //Drawing contours on a new image
        Mat contours = new Mat();
        contours.create(cannyEdges.rows(), cannyEdges.cols(), CvType.CV_8UC3);
        Random r = new Random();
        for (int i = 0; i < contourList.size(); i++) {
            Imgproc.drawContours(contours, contourList, i, new Scalar(r.nextInt(255), r.nextInt(255), r.nextInt(255)), -1);
        }
        //Converting Mat back to Bitmap
        Utils.matToBitmap(contours, currentBitmap);
        imageView.setImageBitmap(currentBitmap);
    }

    void HarrisCorner() {
        Mat grayMat = new Mat();
        Mat corners = new Mat();

        //Converting the image to grayscale
        Imgproc.cvtColor(originalMat, grayMat, Imgproc.COLOR_BGR2GRAY);

        Mat tempDst = new Mat();
        //finding contours
        Imgproc.cornerHarris(grayMat, tempDst, 2, 3, 0.04);

        //Normalizing harris corner's output
        Mat tempDstNorm = new Mat();
        Core.normalize(tempDst, tempDstNorm, 0, 255, Core.NORM_MINMAX);
        Core.convertScaleAbs(tempDstNorm, corners);

        //Drawing corners on a new image
        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] > 150)
                    Imgproc.circle(corners, new Point(i, j), 5, new Scalar(r.nextInt(255)), 2);
            }
        }

        //Converting Mat back to Bitmap
        Utils.matToBitmap(corners, currentBitmap);
        imageView.setImageBitmap(currentBitmap);
    }

    void HOGDescriptor() {
        Mat grayMat = new Mat();
        Mat people = new Mat();

        //Converting the image to grayscale
        Imgproc.cvtColor(originalMat, grayMat, Imgproc.COLOR_BGR2GRAY);

        HOGDescriptor hog = new HOGDescriptor();
        hog.setSVMDetector(HOGDescriptor.getDefaultPeopleDetector());

        MatOfRect faces = new MatOfRect();
        MatOfDouble weights = new MatOfDouble();

        hog.detectMultiScale(grayMat, faces, weights);
        originalMat.copyTo(people);
        //Draw faces on the image
        Rect[] facesArray = faces.toArray();
        for (int i = 0; i < facesArray.length; i++)
            Imgproc.rectangle(people, facesArray[i].tl(), facesArray[i].br(), new Scalar(100), 3);

        //Converting Mat back to Bitmap
        Utils.matToBitmap(people, currentBitmap);
        imageView.setImageBitmap(currentBitmap);
    }

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

        与原来的代码相比,产生的变化较大,主要集中在:

        (1)去掉了一些无用的import,增加了一些必要的import;

        (2)在onCreate()中增加了动态权限申请机制,并增加了相应变量REQUEST_READ_EXTERNAL_STORAGEread_external_storage_granted,修改了菜单动作、载入图片的处理逻辑。

        (3)在onActivityResult()中增加了对读取图像文件的Cursor是否为空的判断,避免出现Java.Lang.NullPointerException错误。

        (4)修改了选取图片的代码。

        (5)更新了OpenCV的版本,OPENCV_VERSION_2_4_9改为OPENCV_VERSION_3_2_0

        关于动态权限申请机制的补充:

        该机制是于Android 6.0(API Level 23)开始引入的。如果你的应用的targetSdkVersion低于23,则不必考虑这一点。但是对于API 23及更高版本的系统,对于比较关键敏感的权限,不仅需要在AndroidManifest.xml中进行声明,还需要在Java代码中于需要的时候进行动态地申请。

        3.修改res\layout\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"
    tools:context="net.johnhany.moaap_chp2.MainActivity">

    <ImageView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/image_view"
        android:visibility="visible"
        android:contentDescription="@string/image_view_description" />

</RelativeLayout>

        4.res目录下创建一个名为menu的Res Folder,再在res\menu中创建一个如下的menu_main.xml文件:

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">
    <item android:id="@+id/open_gallery" android:title="@string/open_gallery"
        android:orderInCategory="100" app:showAsAction="never" />
    <item android:id="@+id/DoG" android:title="@string/DoG"
        android:orderInCategory="100" app:showAsAction="never" />
    <item android:id="@+id/CannyEdges" android:title="@string/CannyEdges"
        android:orderInCategory="100" app:showAsAction="never" />
    <item android:id="@+id/SobelFilter" android:title="@string/SobelFilter"
        android:orderInCategory="100" app:showAsAction="never" />
    <item android:id="@+id/HoughLines" android:title="@string/HoughLines"
        android:orderInCategory="100" app:showAsAction="never" />
    <item android:id="@+id/HoughCircles" android:title="@string/HoughCircles"
        android:orderInCategory="100" app:showAsAction="never" />
    <item android:id="@+id/Contours" android:title="@string/Contours"
        android:orderInCategory="100" app:showAsAction="never" />
    <item android:id="@+id/HarrisCorners" android:title="@string/HarrisCorners"
        android:orderInCategory="100" app:showAsAction="never" />
</menu>

        5.打开res\values\strings.xml文件,修改为以下内容:

<resources>
    <string name="app_name">第二章 - 深入OpenCV Android应用开发</string>
    <string name="title_activity_main">第二章 - 深入OpenCV Android应用开发</string>
    <string name="open_gallery">载入图像</string>
    <string name="image_view_description">Image View</string>
    <string name="DoG">Difference of Gaussians</string>
    <string name="CannyEdges">Canny Edges</string>
    <string name="SobelFilter">Sobel Filter</string>
    <string name="HoughLines">Hough Lines</string>
    <string name="HoughCircles">Hough Circles</string>
    <string name="Contours">Contours</string>
    <string name="HarrisCorners">Harris Corners</string>
</resources>

        6.修改res\AndroidManifest.xml文件为如下内容:

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

    <supports-screens android:resizeable="true"
        android:smallScreens="true"
        android:normalScreens="true"
        android:largeScreens="true"
        android:anyDensity="true" />

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

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme"
        android:screenOrientation="landscape"
        android:exported="true">
        <activity
            android:name="net.johnhany.moaap_chp2.MainActivity"
            android:label="@string/title_activity_main" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

        与原来的代码产生的变化有:

        (1)去掉了uses-sdk标签,因为该属性总是会被build.gradle中设置的值所覆盖。

        (2)删除了多余的权限申请。

        7.app\build.gradle文件修改为以下内容:

apply plugin: 'com.android.application'

android {
    compileSdkVersion 24
    buildToolsVersion "26.0.0"
    defaultConfig {
        applicationId "net.johnhany.moaap_chp2"
        minSdkVersion 16
        targetSdkVersion 24
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    compile fileTree(include: ['*.jar'], dir: 'libs')
    androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
        exclude group: 'com.android.support', module: 'support-annotations'
    })
    compile 'com.android.support:appcompat-v7:24.2.1'
    compile 'com.android.support.constraint:constraint-layout:1.0.2'
    testCompile 'junit:junit:4.12'
    compile project(':openCVLibrary320')
}

        其中,compileSdkVersionbuildToolsVersiontargetSdkVersion三个版本可以自定,com.android.support:appcompat的版本最好与targetSdkVersion相一致,比如同为23或24。

        8.检查openCVLibrary320\build.gradle文件是否为以下内容:

apply plugin: 'com.android.library'

android {
    compileSdkVersion 24
    buildToolsVersion "26.0.0"

    defaultConfig {
        minSdkVersion 16
        targetSdkVersion 24
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
        }
    }
}

运行效果:

Screenshot_20160723

7

avatar
5 Comment threads
2 Thread replies
0 Followers
 
Most reacted comment
Hottest comment thread
5 Comment authors
shadow_wxhJerseyJohn Hany黄欣欣derek Recent comment authors
  Subscribe  
最新 最旧
订阅评论
shadow_wxh
shadow_wxh

            Matrix rotate90 = new Matrix();

            rotate90.postRotate(orientation);

这两行没什么用

Jersey
Jersey

第九章的代码你有运行过吗?运行成功没有,我这有一个问题是检测角点失败,希望大神帮助解决,谢谢!

黄欣欣
黄欣欣

博主,请问一个问题。

在res目录下创建一个名为menu的Res Folder,再在res\menu中创建一个如下的menu_main.xml文件

为什么每个文件我创建了menu之后,添加xml文件,然后就算把 root bag 改好了,xml文件都会跑到layout的文件夹下面去,,这个文件输入代码之后也是错误的,提示 element dosen't require attribute android…好像是这样的提示 出来?

黄欣欣
黄欣欣

您好~~请问是不是每一次创建新的project就要重新配置opencv环境?

derek
derek

代码更新,哪里能下载?