安卓平台上实现SIFT特征点监测

作者:刘衍

最近开始学习安卓上的图像处理,刚开始困难重重。没有很好的教材,网上的例子也比较少,走了不少弯路。安卓上使用opencv进行图像处理需要较多方面的知识,如NDK、JNI、OPENCV、安卓等。我会一步一步走下去,并且不断更新博客。朋友们可以在下面留言交流。

【嵌牛导读】:安卓上使用opencv实现SIFT特征点监测,由于SIFT是专利算法,所以OPENCV4ADNROID没有将其打包,所以我们需要自己将改算法打包成.so动态链接库,加载到程序中。本文的软件版本:android studio3.0.1,opencv4android 2.4.11,NDK 12b,opencv源码 2.4.11

【嵌牛鼻子】:opencv4android SIFT NDK

【嵌牛提问】:如何在安卓上实现SIFT?

【嵌牛正文】:

1.首先新建一个工程,不用多说,大家应该都轻车熟路

安卓平台上实现SIFT特征点监测_第1张图片

2.写好布局文件,很简单,一个按钮,一个ImageView就行


安卓平台上实现SIFT特征点监测_第2张图片


安卓平台上实现SIFT特征点监测_第3张图片

3.下载opencv4android  2.4.11,解压文件,apk文件夹里面放的是opencvManager的各个版本,需要根据手机CPU架构的不同下载不同版本的opencvManager。samples里面是例程,sdk里面是我们需要的库文件。将SDK文件夹下面的java文件夹作为module引入到工程中。


安卓平台上实现SIFT特征点监测_第4张图片


安卓平台上实现SIFT特征点监测_第5张图片

引入的module的build.gradle中的sdk版本可能与APPmodule中的不一致,需要将引入的module的build.gradle中的sdk版本修改


安卓平台上实现SIFT特征点监测_第6张图片

4.下载NDK,修改gradle.properties,


安卓平台上实现SIFT特征点监测_第7张图片

修改local.properties,(添加下载的NDK路径)

安卓平台上实现SIFT特征点监测_第8张图片

修改APP下的build.gradle,在android{}中添加

sourceSets.main.jni.srcDirs= []

//禁止自带的ndk功能

sourceSets.main.jniLibs.srcDirs= ['src/main/libs','src/main/jniLibs']

//重定向so目录为src/main/libs和src/main/jniLibs,原来为src/main/jniLibs

task ndkBuild(type:Exec,description:'Compile JNI source with NDK') {

    Properties properties = new Properties()

    properties.load(project.rootProject.file('local.properties').newDataInputStream())

    def ndkDir = properties.getProperty('ndk.dir')

    if (org.apache.tools.ant.taskdefs.condition.Os.isFamily(org.apache.tools.ant.taskdefs.condition.Os.FAMILY_WINDOWS)) {

        commandLine "$ndkDir/ndk-build.cmd",'-C',file('src/main/jni').absolutePath

} else {

        commandLine "$ndkDir/ndk-build",'-C',file('src/main/jni').absolutePath

}

}

tasks.withType(JavaCompile) {

    compileTask ->compileTask.dependsOn ndkBuild

}

task ndkClean(type:Exec,description:'Clean NDK Binaries') {

    Properties properties = new Properties()

    properties.load(project.rootProject.file('local.properties').newDataInputStream())

    def ndkDir = properties.getProperty('ndk.dir')

    if (org.apache.tools.ant.taskdefs.condition.Os.isFamily(org.apache.tools.ant.taskdefs.condition.Os.FAMILY_WINDOWS)) {

        commandLine "$ndkDir/ndk-build.cmd",'clean','-C',file('src/main/jni').absolutePath

} else {

        commandLine "$ndkDir/ndk-build",'clean','-C',file('src/main/jni').absolutePath

}

}

clean.dependsOn 'ndkClean'

5.在app/main下面新建文件夹jni。在文件夹下创建两个.mk文件---Android.mk和Application.mk。

在Android.mk中添加如下代码

LOCAL_PATH:= $(call my-dir)

include $(CLEAR_VARS)

OPENCV_INSTALL_MODULES:=on

OPENCV_CAMERA_MODULES:=off

include ..\..\..\..\native\jni\OpenCV.mk

LOCAL_MODULE    := nonfree

LOCAL_LDLIBS    += -llog

LOCAL_SRC_FILES := nonfree_init.cpp \

precomp.hpp \

sift.cpp \

surf.cpp

include $(BUILD_SHARED_LIBRARY)

在Application.mk中添加如下代码

APP_STL := gnustl_static

APP_CPPFLAGS := -frtti -fexceptions

APP_ABI := armeabi armeabi-v7a x86

6.下载opencv源码。找到nonfree_init.cpp,precomp.cpp,sift.cpp,surf.cpp,复制到jni文件夹下。修改precomp.cpp文件,去掉#include “cvconfig.h”和#include “opencv2/ocl/private/util.cpp”。修改nonfree_init.cpp文件,去掉从#ifdef HAVE_OPENCV_OCL开始,直到#endif结束的代码行。

7.将之前下载的opencv4android中的sdk文件夹下面的native复制到与工程中APP同级的地方。

点击android studio右侧的gradle,找到ndkbuild,点击。这时候可能会出现错误


安卓平台上实现SIFT特征点监测_第9张图片

不用着急,我们在下载好的opencv源码中找到nonfree文件夹,复制到工程中native/jni/include/opencv2下面,然后再次点击ndkbuild。

安卓平台上实现SIFT特征点监测_第10张图片

这时已经可以成功生成.so文件了。

安卓平台上实现SIFT特征点监测_第11张图片

8.我们已经将SIFT算法打包成.so动态链接库了,下面只需要在程序里加载动态链接库,然后处理就行了。

MainActivity.class中添加如下代码

package com.tinymonster.opencvstudy1;

import android.graphics.Bitmap;

import android.graphics.BitmapFactory;

import android.graphics.drawable.Drawable;

import android.os.AsyncTask;

import android.support.v7.app.AppCompatActivity;

import android.os.Bundle;

import android.util.Log;

import android.view.View;

import android.widget.Button;

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.Mat;

import org.opencv.core.MatOfByte;

import org.opencv.core.MatOfDMatch;

import org.opencv.core.MatOfKeyPoint;

import org.opencv.core.MatOfPoint;

import org.opencv.core.Point;

import org.opencv.core.Scalar;

import org.opencv.features2d.DescriptorExtractor;

import org.opencv.features2d.DescriptorMatcher;

import org.opencv.features2d.FeatureDetector;

import org.opencv.features2d.Features2d;

import org.opencv.features2d.KeyPoint;

import org.opencv.imgproc.Imgproc;

import java.util.ArrayList;

import java.util.List;

public class MainActivity extends AppCompatActivity {

    private Button button1;

    private ImageView imageView1;

    private ImageView imageView2;

    private static String TAG="MainActivity";

    @Override

    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_main);

        if (!OpenCVLoader.initDebug()) {

            Log.d(TAG, "Internal OpenCV library not found. Using OpenCV Manager for initialization");

            OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION_2_4_11, this, mLoaderCallback);

        } else {

            Log.d(TAG, "OpenCV library found inside package. Using it!");

            mLoaderCallback.onManagerConnected(LoaderCallbackInterface.SUCCESS);

        }

        initView();

    }

    private void initView(){

        button1=(Button)findViewById(R.id.button1);

        imageView1=(ImageView) findViewById(R.id.imageView1);

        button1.setOnClickListener(new View.OnClickListener() {

            @Override

            public void onClick(View view) {

                new AsyncTask(){

                    @Override

                    protected void onPreExecute() {

                        super.onPreExecute();

                    }

                    @Override

                    protected Bitmap doInBackground(Void...voids) {

                        Mat test1=new Mat();

                        Mat test2=new Mat();

                        Mat out;

                        FeatureDetector SIFTdter = FeatureDetector.create(FeatureDetector.SIFT);//创建特征监测

                        DescriptorExtractor descriptorExtractor=DescriptorExtractor.create(DescriptorExtractor.SIFT);//描述子提取

                        DescriptorMatcher descriptorMatcher=DescriptorMatcher.create(DescriptorMatcher.FLANNBASED);//描述子匹配 暴力匹配器

                        MatOfDMatch matchs=new MatOfDMatch();

                        Bitmap src_bitmap1= BitmapFactory.decodeResource(getResources(),R.drawable.photo7);

                        Bitmap src_bitmap2= BitmapFactory.decodeResource(getResources(),R.drawable.photo6);

                        Utils.bitmapToMat(src_bitmap1,test1);

                        Utils.bitmapToMat(src_bitmap2,test2);

                        Mat descriptors1=new Mat();

                        Mat descriptors2=new Mat();

                        MatOfKeyPoint kp1 = new MatOfKeyPoint();//特征点

                        MatOfKeyPoint kp2 = new MatOfKeyPoint();//特征点

                        SIFTdter.detect(test1,kp1);

                        SIFTdter.detect(test2,kp2);//监测特征点

                        descriptorExtractor.compute(test1,kp1,descriptors1);//计算描述子

                        descriptorExtractor.compute(test2,kp2,descriptors2);

                        descriptorMatcher.match(descriptors1,descriptors2,matchs);//进行匹配

                        out=drawMatchs(test1,kp1,test2,kp2,matchs,false);

                        Bitmap out_drawable =Bitmap.createBitmap(out.cols(),out.rows(),Bitmap.Config.ARGB_8888);

                        Utils.matToBitmap(out,out_drawable);

                        return out_drawable;

                    }

                    @Override

                    protected void onPostExecute(Bitmap bitmap) {

                        super.onPostExecute(bitmap);

                        imageView1.setImageBitmap(bitmap);

                    }

                }.execute();

            }

        });

    }

    Mat drawMatchs(Mat img1,MatOfKeyPoint key1,Mat img2,MatOfKeyPoint key2,MatOfDMatch matches,boolean imageOnly){

        Mat out =new Mat();

        Mat im1 =new Mat();

        Mat im2 =new Mat();

        Imgproc.cvtColor(img1,im1,Imgproc.COLOR_BGR2RGB);

        Imgproc.cvtColor(img2,im2,Imgproc.COLOR_BGR2RGB);

        if(imageOnly){

            MatOfDMatch emptyMatch=new MatOfDMatch();

            MatOfKeyPoint emptyKey1=new MatOfKeyPoint();

            MatOfKeyPoint emptyKey2=new MatOfKeyPoint();

            Features2d.drawMatches(im1,emptyKey1,im2,emptyKey2,emptyMatch,out);

        }else {

            Features2d.drawMatches(im1,key1,im2,key2,matches,out);

        }

        Imgproc.cvtColor(out,out, Imgproc.COLOR_BGR2RGB);

        Core.putText(out,"src",new Point(img1.width()/2,30),Core.FONT_HERSHEY_PLAIN,2,new Scalar(0,255,255),3);

        Core.putText(out,"matched",new Point((img1.width()+img2.width())/2,30),Core.FONT_HERSHEY_PLAIN,2,new Scalar(255,0,0),3);

        return out;

    }

    private BaseLoaderCallback mLoaderCallback = new BaseLoaderCallback(this) {

        @Override

        public void onManagerConnected(int status) {

            switch (status) {

                case LoaderCallbackInterface.SUCCESS: {

                    Log.i(TAG, "OpenCV loaded successfully");

                    System.loadLibrary("nonfree");

//                    mOpenCvCameraView.enableView();

//                    mOpenCvCameraView.setOnTouchListener(ColorBlobDetectionActivity.this);

                }

                break;

                default: {

                    super.onManagerConnected(status);

                }

                break;

            }

}

    };

}

9.在工程中加入你想处理的图片,运行一下,哈哈,是不是很有趣


安卓平台上实现SIFT特征点监测_第12张图片


安卓平台上实现SIFT特征点监测_第13张图片

你可能感兴趣的:(安卓平台上实现SIFT特征点监测)