安卓平臺(tái)上實(shí)現(xiàn)SIFT特征點(diǎn)監(jiān)測(cè)

作者:劉衍

最近開始學(xué)習(xí)安卓上的圖像處理,剛開始困難重重妻率。沒有很好的教材,網(wǎng)上的例子也比較少侈询,走了不少彎路舌涨。安卓上使用opencv進(jìn)行圖像處理需要較多方面的知識(shí)糯耍,如NDK扔字、JNI、OPENCV温技、安卓等革为。我會(huì)一步一步走下去,并且不斷更新博客舵鳞。朋友們可以在下面留言交流震檩。

【嵌牛導(dǎo)讀】:安卓上使用opencv實(shí)現(xiàn)SIFT特征點(diǎn)監(jiān)測(cè),由于SIFT是專利算法蜓堕,所以O(shè)PENCV4ADNROID沒有將其打包抛虏,所以我們需要自己將改算法打包成.so動(dòng)態(tài)鏈接庫,加載到程序中套才。本文的軟件版本:android studio3.0.1迂猴,opencv4android 2.4.11,NDK 12b背伴,opencv源碼 2.4.11

【嵌牛鼻子】:opencv4android SIFT NDK

【嵌牛提問】:如何在安卓上實(shí)現(xiàn)SIFT沸毁?

【嵌牛正文】:

1.首先新建一個(gè)工程,不用多說傻寂,大家應(yīng)該都輕車熟路

2.寫好布局文件息尺,很簡單,一個(gè)按鈕疾掰,一個(gè)ImageView就行



3.下載opencv4android? 2.4.11搂誉,解壓文件,apk文件夾里面放的是opencvManager的各個(gè)版本静檬,需要根據(jù)手機(jī)CPU架構(gòu)的不同下載不同版本的opencvManager勒葱。samples里面是例程,sdk里面是我們需要的庫文件巴柿。將SDK文件夾下面的java文件夾作為module引入到工程中凛虽。



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


4.下載NDK广恢,修改gradle.properties凯旋,


修改local.properties,(添加下載的NDK路徑)

修改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。在文件夾下創(chuàng)建兩個(gè).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,復(fù)制到j(luò)ni文件夾下荒椭。修改precomp.cpp文件谐鼎,去掉#include “cvconfig.h”和#include “opencv2/ocl/private/util.cpp”。修改nonfree_init.cpp文件趣惠,去掉從#ifdef HAVE_OPENCV_OCL開始狸棍,直到#endif結(jié)束的代碼行。

7.將之前下載的opencv4android中的sdk文件夾下面的native復(fù)制到與工程中APP同級(jí)的地方味悄。

點(diǎn)擊android studio右側(cè)的gradle,找到ndkbuild草戈,點(diǎn)擊。這時(shí)候可能會(huì)出現(xiàn)錯(cuò)誤


不用著急侍瑟,我們?cè)谙螺d好的opencv源碼中找到nonfree文件夾唐片,復(fù)制到工程中native/jni/include/opencv2下面,然后再次點(diǎn)擊ndkbuild涨颜。

這時(shí)已經(jīng)可以成功生成.so文件了费韭。

8.我們已經(jīng)將SIFT算法打包成.so動(dòng)態(tài)鏈接庫了,下面只需要在程序里加載動(dòng)態(tài)鏈接庫庭瑰,然后處理就行了星持。

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);//創(chuàng)建特征監(jiān)測(cè)

? ? ? ? ? ? ? ? ? ? ? ? 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();//特征點(diǎn)

? ? ? ? ? ? ? ? ? ? ? ? MatOfKeyPoint kp2 = new MatOfKeyPoint();//特征點(diǎn)

? ? ? ? ? ? ? ? ? ? ? ? SIFTdter.detect(test1,kp1);

? ? ? ? ? ? ? ? ? ? ? ? SIFTdter.detect(test2,kp2);//監(jiān)測(cè)特征點(diǎn)

? ? ? ? ? ? ? ? ? ? ? ? descriptorExtractor.compute(test1,kp1,descriptors1);//計(jì)算描述子

? ? ? ? ? ? ? ? ? ? ? ? descriptorExtractor.compute(test2,kp2,descriptors2);

? ? ? ? ? ? ? ? ? ? ? ? descriptorMatcher.match(descriptors1,descriptors2,matchs);//進(jìn)行匹配

? ? ? ? ? ? ? ? ? ? ? ? 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.在工程中加入你想處理的圖片,運(yùn)行一下见擦,哈哈钉汗,是不是很有趣



最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市鲤屡,隨后出現(xiàn)的幾起案子损痰,更是在濱河造成了極大的恐慌,老刑警劉巖酒来,帶你破解...
    沈念sama閱讀 210,978評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件卢未,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡堰汉,警方通過查閱死者的電腦和手機(jī)辽社,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,954評(píng)論 2 384
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來翘鸭,“玉大人滴铅,你說我怎么就攤上這事【团遥” “怎么了汉匙?”我有些...
    開封第一講書人閱讀 156,623評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵拱烁,是天一觀的道長。 經(jīng)常有香客問我噩翠,道長戏自,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,324評(píng)論 1 282
  • 正文 為了忘掉前任伤锚,我火速辦了婚禮擅笔,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘屯援。我一直安慰自己猛们,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,390評(píng)論 5 384
  • 文/花漫 我一把揭開白布玄呛。 她就那樣靜靜地躺著阅懦,像睡著了一般和二。 火紅的嫁衣襯著肌膚如雪徘铝。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,741評(píng)論 1 289
  • 那天惯吕,我揣著相機(jī)與錄音惕它,去河邊找鬼。 笑死废登,一個(gè)胖子當(dāng)著我的面吹牛淹魄,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播堡距,決...
    沈念sama閱讀 38,892評(píng)論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼甲锡,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了羽戒?” 一聲冷哼從身側(cè)響起缤沦,我...
    開封第一講書人閱讀 37,655評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎易稠,沒想到半個(gè)月后缸废,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,104評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡驶社,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,451評(píng)論 2 325
  • 正文 我和宋清朗相戀三年企量,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片亡电。...
    茶點(diǎn)故事閱讀 38,569評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡届巩,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出份乒,到底是詐尸還是另有隱情恕汇,我是刑警寧澤零酪,帶...
    沈念sama閱讀 34,254評(píng)論 4 328
  • 正文 年R本政府宣布,位于F島的核電站拇勃,受9級(jí)特大地震影響四苇,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜方咆,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,834評(píng)論 3 312
  • 文/蒙蒙 一月腋、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧瓣赂,春花似錦榆骚、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,725評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至苫纤,卻和暖如春碉钠,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背卷拘。 一陣腳步聲響...
    開封第一講書人閱讀 31,950評(píng)論 1 264
  • 我被黑心中介騙來泰國打工喊废, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人栗弟。 一個(gè)月前我還...
    沈念sama閱讀 46,260評(píng)論 2 360
  • 正文 我出身青樓污筷,卻偏偏與公主長得像,于是被迫代替她去往敵國和親乍赫。 傳聞我的和親對(duì)象是個(gè)殘疾皇子瓣蛀,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,446評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容