作者:劉衍
最近開始學(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)行一下见擦,哈哈钉汗,是不是很有趣