特征匹配在计算机视觉中常用于图像或视频中检测目标,首先我们先要了解下什么是特征。特征是独特的具体模式,并且易于跟踪和比较。通过一些寻找特征的算法来实现图像的特征检测,但仅仅是特征检测是不够的,还需要能够将一种特征与另一种特征区分开,这时就用到了特征描述来描述检测到的特征。这些描述能帮助我们在其他图像中找到相似的特征,并能够识别目标。
常用的特征检测算法包括SIFT、SURF、BRIEF、FAST、BRISEK和FREAK等。这里说明一点,SIFT和SURF是专利算法,免费使用仅限于学术和研究目的。下面让我们一起学习下上述几种算法原理以及在Android平台上的使用方法。
1.SIFT-尺度不变特征变换
SIFT算法于2004年David Lowe提出,是一种被广泛认可的特征监测算法之一。
SIFT算法的一些特性:
SIFT的原理需要我们对原文章仔细认真的阅读和理解,它遵循匹配稳健局部特征的策略:
在Android上的使用方法:
"1.0" encoding="utf-8"?>
修改activity_main.xml文件内容
"1.0" encoding="utf-8"?>
"http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
"@+id/iv_Image"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"/>
"@+id/tv_Object1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="666"/>
"@+id/tv_Object2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="444"/>
"@+id/tv_Matches"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="777"/>
"@+id/tv_Time"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="333"/>
图像检测是个耗时操作,所以这里使用AsyncTask来执行实际的计算
new AsyncTask() {
@Override
protected void onPreExecute() {
super.onPreExecute();
mStartTime = System.currentTimeMillis();
}
@Override
protected Bitmap doInBackground(Void... params) {
return executeTask();//目标检测(计算)
}
@Override
protected void onPostExecute(Bitmap bitmap) {
super.onPostExecute(bitmap);
mEndTiem = System.currentTimeMillis();
mImage1.setImageBitmap(bitmap);
mObject1.setText("目标1:" + keypointsObject1);
mObject2.setText("目标2:" + keypointsObject2);
mMatches.setText("关键点匹配:" + keypointMatches);
mTime.setText("耗费时间:" + (mEndTiem - mStartTime) + "ms");
}
}.execute();
在doInBackground()方法中,返回executeTask()方法用于目标检测计算
FeatureDetector detector;
MatOfKeyPoint keypoints1, keypoints2;
DescriptorExtractor descriptorExtractor;
Mat descriptors1, descriptors2;
DescriptorMatcher descriptorMatcher;
MatOfDMatch matches = new MatOfDMatch();
keypoints1 = new MatOfKeyPoint();
keypoints2 = new MatOfKeyPoint();
descriptors1 = new Mat();
descriptors2 = new Mat();
//特征匹配算法
detector = FeatureDetector.create(FeatureDetector.SIFT);
descriptorExtractor = DescriptorExtractor.create(DescriptorExtractor.SIFT);
//检测关键点
detector.detect(src2, keypoints2);
detector.detect(src1, keypoints1);
//添加变量,用于显示关键点数量
keypointsObject1 = keypoints1.toArray().length;
keypointsObject2 = keypoints2.toArray().length;
//计算描述子
descriptorExtractor.compute(src1, keypoints1, descriptors1);
descriptorExtractor.compute(src2, keypoints2, descriptors2);
descriptorMatcher = DescriptorMatcher.create(DescriptorMatcher.BRUTEFORCE_SL2);//暴力匹配器
(2)基于FLANN匹配器–快速近似最近邻库
它 包含一个为在大型数据集中快速最近邻搜索和高维度特征而优化的算法的集合,对于大多数据集,其处理速度高于暴力匹配器。用法:
descriptorMatcher = DescriptorMatcher.create(DescriptorMatcher.FLANNBASED);//基于FLANN匹配器
descriptorMatcher.match(descriptors1, descriptors2, matches);
调用drawMatches()方法绘制结果,显示得到的匹配。
static Mat drawMatches(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);
}
Bitmap bmp = Bitmap.createBitmap(out.cols(), out.rows(), Bitmap.Config.ARGB_8888);
Imgproc.cvtColor(out, out, Imgproc.COLOR_BGR2RGB);
Core.putText(out, "FRAME", new org.opencv.core.Point(im1.width()/2,30),Core.FONT_HERSHEY_PLAIN, 2, new Scalar(0, 255, 255), 3);
Core.putText(out, "MATCHED", new org.opencv.core.Point(im1.width()+im2.width()/2,30),Core.FONT_HERSHEY_PLAIN, 2, new Scalar(255, 0, 0), 3);
return out;
}
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
OPENCV_CAMERA_MODULES:=on
OPENCV_INSTALL_MODULES:=on
include E:/OpenCV4Android SDK/OpenCV-2.4.11-android-sdk/OpenCV-android-sdk/native/jni/OpenCV.mk
LOCAL_MODULE := nonfree
LOCAL_SRC_FILES := nonfree_init.cpp \
sift.cpp \
surf.cpp
LOCAL_LDLIBS += -llog -ldl
include $(BUILD_SHARED_LIBRARY)
APP_STL := gnustl_static
APP_CPPFLAGS := -frtti -fexceptions
APP_ABI := armeabi-v7a
APP_PLATFORM := android-8
sourceSets.main {
jniLibs.srcDir 'src/main/libs'
jni.srcDirs = [] //关闭自动调用ndk-build命令
}
完成编译后,在java代码中加载这个库。在 LoaderCallbackInterface.SUCCESS的case下添加代码:
System.loadLibrary("nonfree");
将在下文中给出ORB的一个具体demo
2.SURF-加速稳健特征
该算法由Herbert Bay等人于2006年提出,它针对SIFT算法匹配速度慢并且计算繁琐这个问题而提出。
SURF算法的一些特性:
在Android上的使用方法:
只需要替换掉doInBackground()方法中的以下代码即可
//特征匹配算法
detector = FeatureDetector.create(FeatureDetector.SIFT);
descriptorExtractor = DescriptorExtractor.create(DescriptorExtractor.SIFT);
换成
detector = FeatureDetector.create(FeatureDetector.SURF);
descriptorExtractor = DescriptorExtractor.create(DescriptorExtractor.SURF);
descriptorMatcher = DescriptorMatcher.create(DescriptorMatcher.BRUTEFORCE_SL2);
3.ORB算法
该算法由Ethan Rublee等人于2011年在OpenCV实验室开发,它是一种替代SIFT和SURF的可行而搞笑的描述子。
SURF算法的一些特性:
在Android上的使用方法:
只需要替换掉doInBackground()方法中的以下代码即可
//特征匹配算法
detector = FeatureDetector.create(FeatureDetector.SIFT);
descriptorExtractor = DescriptorExtractor.create(DescriptorExtractor.SIFT);
换成
detector = FeatureDetector.create(FeatureDetector.ORB);
descriptorExtractor = DescriptorExtractor.create(DescriptorExtractor.ORB);
descriptorMatcher = DescriptorMatcher.create(DescriptorMatcher.BRUTEFORCE_HAMMING);
4.BRISK算法
该算法由Leutenegger等人提出的对最先进的特征检测、描述和匹配算法的代替。在某些情况下,它它是一种替代SIFT和SURF的可行而高效的描述子。
SURF算法的一些特性:
在Android上的使用方法:
只需要替换掉doInBackground()方法中的以下代码即可
//特征匹配算法
detector = FeatureDetector.create(FeatureDetector.SIFT);
descriptorExtractor = DescriptorExtractor.create(DescriptorExtractor.SIFT);
换成
detector = FeatureDetector.create(FeatureDetector.BRISK);
descriptorExtractor = DescriptorExtractor.create(DescriptorExtractor.BRISK);
descriptorMatcher = DescriptorMatcher.create(DescriptorMatcher.BRUTEFORCE_HAMMING);
5.FREAK算法
该算法提出一种能唯一识别关键点的稳健描述子,在计算过程中耗费更少的计算时间按和存储空间,其受人类视网膜启发而来。
在Android上的使用方法:
只需要替换掉doInBackground()方法中的以下代码即可
//特征匹配算法
detector = FeatureDetector.create(FeatureDetector.SIFT);
descriptorExtractor = DescriptorExtractor.create(DescriptorExtractor.SIFT);
换成
detector = FeatureDetector.create(FeatureDetector.FAST);
descriptorExtractor = DescriptorExtractor.create(DescriptorExtractor.FREAK);
descriptorMatcher = DescriptorMatcher.create(DescriptorMatcher.BRUTEFORCE_HAMMING);