Android学习九---OpenCV4android org.opencv.feature2d

不管是在识别,配准等应用中,提取图像的特征都是很关键的一环,提取特征是先找出图像的关键点(如角点,边缘点等),然后用描述子来描述这些点,最后整幅图像就可以表示成一个特征向量,特征向量就可以利用在后续识别中。

这个流程在matlab,opencv中都有相应的函数来实现,matlab封装性好,比较简单,只要输入图像和参数,调用函数就能够得到特征,而opencv就稍微复杂点,我们先通过opencv的c++程序来了解这个过程(资料比较好找点),接着通过阅读opencv4android文档来了解提供的API,最后实现在Android找关键点并画图。

一、OpenCV c++图像配准程序来了解

图像配准程序包括1.定义存储关键点和关键点描绘子的数据结构。2.定义要提取的特征,分别对两幅图像进行关键点检测。3.计算关键点的特征描述子。4.计算匹配点数并显示。

#include <opencv2/core/core.hpp>

#include <opencv2/highgui/highgui.hpp>

#include <opencv2/nonfree/features2d.hpp>

#include <opencv2/legacy/legacy.hpp>

using namespace cv;

int main()

{

Mat image1=imread("../b1.png");

Mat image2=imread("../b2.png");

// 检测surf特征点

vector<KeyPoint> keypoints1,keypoints2;

SurfFeatureDetector detector(400);

detector.detect(image1, keypoints1);

detector.detect(image2, keypoints2);

// 描述surf特征点

SurfDescriptorExtractor surfDesc;

Mat descriptros1,descriptros2;

surfDesc.compute(image1,keypoints1,descriptros1);

surfDesc.compute(image2,keypoints2,descriptros2);

// 计算匹配点数

BruteForceMatcher<L2<float>>matcher;

vector<DMatch> matches;

matcher.match(descriptros1,descriptros2,matches);

std::nth_element(matches.begin(),matches.begin()+24,matches.end());

matches.erase(matches.begin()+25,matches.end());

// 画出匹配图

Mat imageMatches;

drawMatches(image1,keypoints1,image2,keypoints2,matches,

imageMatches,Scalar(255,0,0));

namedWindow("image2");

imshow("image2",image2);

waitKey();

return 0;

}
 

二、转换到OpenCV java下

首先了解OpenCV java 特征提取的API和相应的数据结构。有关特征提取的API是存在包Package org.opencv.features2d中,主要包含以下几个类

1.DescriptorExtractor

计算特征点的特征描述子的抽象类。

主要使用两个methods:

1.1 create

创建特征描述子

Usage :public static DescriptorExtractor create(int extractorType)

ExtractorType:

· "SIFT" -- "SIFT"

· "SURF" -- "SURF"

· "BRIEF" -- "BriefDescriptorExtractor"

· "BRISK" -- "BRISK"

· "ORB" -- "ORB"

· "FREAK" -- "FREAK"

Example:

DescriptorExtractor descriptor=DescriptorExtractor.create(DescriptorExtractor.SIFT);

1.2 compute

提取特征描述子

Usage:public void compute(java.util.List<Mat> images,

java.util.List<MatOfKeyPoint> keypoints,

java.util.List<Mat> descriptors)

public void compute(Mat image,

MatOfKeyPoint keypoints,

Mat descriptors)

image – 输入的图像.

keypoints – 输入的关键点,由FeatureDetector得到。

descriptors – 计算出来的特征描述子

Example:descriptor.compute(mRgba, keypoint, mask);

2. FeatureDetector

用来提取二维图像特征点的类

主要是两个Methods

2.1 create

Usage :public static FeatureDetector create(int detectorType)

DetectorType:

"FAST" -- "FastFeatureDetector"

"STAR" -- "StarFeatureDetector"

"SIFT" -- "SIFT" (nonfree module)

"SURF" -- "SURF" (nonfree module)

"ORB" -- "ORB"

"BRISK" -- "BRISK"

"MSER" -- "MSER"

"GFTT" -- "GoodFeaturesToTrackDetector"

"HARRIS" -- "GoodFeaturesToTrackDetector" with Harris detector enabled

"Dense" -- "DenseFeatureDetector"

"SimpleBlob" -- "SimpleBlobDetector"

Example:FeatureDetector detector = FeatureDetector.create(FeatureDetector.MSER);

2.2 detect

Usage:

public void detect(java.util.List<Mat> images,

java.util.List<MatOfKeyPoint> keypoints,

java.util.List<Mat> masks)

public void detect(Mat image,

MatOfKeyPoint keypoints,

Mat mask)

public void detect(Mat image,

MatOfKeyPoint keypoints,)

image –输入图像.

keypoints – 检测得到的关键点

mask – 模板,指定要取关键点度的位置. It must be a 8-bit integer matrix with non-zero values in the region of interest.

3. KeyPoint

采用detect检测得到关键点的数据结构

包括:一些构造函数和相应的域,构造函数不介绍,介绍对应的有哪些域。

angle

public float angle

计算特征点的方向.

class_id

public int class_id

物体的标签,可以用来分类关键点属于哪个类。

octave

public int octave

关键点使在金字塔的哪一层被提取的.

pt

public Point pt

关键点的坐标

response

public float response

响应,对应于哪个关键点的位置。.

size

public float size

有用关键点的邻接区域的半径。

4. DescriptorMatcher

同前面的特征提取,需要create和match

4.1 create

public static DescriptorMatcher create(int matcherType)

采用默认参数创建一个特征描述子匹配

matcherType:模式匹配的方式,算法

static int BRUTEFORCE

static int BRUTEFORCE_HAMMING

static int BRUTEFORCE_HAMMINGLUT

static int BRUTEFORCE_L1

static int BRUTEFORCE_SL2

static int FLANNBASED

4.2 Match

match

public void match(Mat queryDescriptors,

Mat trainDescriptors,

MatOfDMatch matches)

Finds the best match for each descriptor from a query set.

给定查询集合中的每个特征描述子,寻找 最佳匹配

Parameters:

queryDescriptors -特征描述子查询集.

trainDescriptors - 待训练(模板)的特征描述子集. 这个集没被加载到类的对象中.

matches –匹配点数. 匹配点数的大小小于待查询的特征描述子的个数。

knnMatch

public void knnMatch(Mat queryDescriptors,

Mat trainDescriptors,

java.util.List<MatOfDMatch> matches,

int k,

Mat mask,

boolean compactResult)

给定查询集合中的每个特征描述子,寻找 k个最佳匹配.

radiusMatch

public void radiusMatch(Mat queryDescriptors,

java.util.List<MatOfDMatch> matches,

float maxDistance)

对于每一个查询特征描述子, 在特定距离范围内寻找特征描述子.

5. DMatch

保存匹配特征的数据结构

float distance

两个特征向量之间的欧氏距离,越小表明匹配度越高。

int imgIdx

训练图像的索引(若有多个)

int queryIdx

此匹配对应的查询图像的特征描述子索引

int trainIdx

此匹配对应的训练(模板)图像的特征描述子索引

三、完整的C++匹配代码

来自http://blog.csdn.net/masibuaa/article/details/8998601

#include "opencv2/highgui/highgui.hpp"

#include "opencv2/imgproc/imgproc.hpp"

#include "opencv2/nonfree/nonfree.hpp"

#include "opencv2/nonfree/features2d.hpp"

#include <iostream>

#include <stdio.h>

#include <stdlib.h>

using namespace cv;

using namespace std;

int main()

{

initModule_nonfree();//初始化模块,使用SIFT或SURF时用到

Ptr<FeatureDetector> detector = FeatureDetector::create( "SIFT" );//创建SIFT特征检测器

Ptr<DescriptorExtractor> descriptor_extractor = DescriptorExtractor::create( "SIFT" );//创建特征向量生成器

Ptr<DescriptorMatcher> descriptor_matcher = DescriptorMatcher::create( "BruteForce" );//创建特征匹配器

if( detector.empty() || descriptor_extractor.empty() )

cout<<"fail to create detector!";

//读入图像

Mat img1 = imread("desk.jpg");

Mat img2 = imread("desk_glue.jpg");

//特征点检测

double t = getTickCount();//当前滴答数

vector<KeyPoint> keypoints1,keypoints2;

detector->detect( img1, keypoints1 );//检测img1中的SIFT特征点,存储到keypoints1中

detector->detect( img2, keypoints2 );

cout<<"图像1特征点个数:"<<keypoints1.size()<<endl;

cout<<"图像2特征点个数:"<<keypoints2.size()<<endl;

//根据特征点计算特征描述子矩阵,即特征向量矩阵

Mat descriptors1,descriptors2;

descriptor_extractor->compute( img1, keypoints1, descriptors1 );

descriptor_extractor->compute( img2, keypoints2, descriptors2 );

t = ((double)getTickCount() - t)/getTickFrequency();

cout<<"SIFT算法用时:"<<t<<"秒"<<endl;

cout<<"图像1特征描述矩阵大小:"<<descriptors1.size()

<<",特征向量个数:"<<descriptors1.rows<<",维数:"<<descriptors1.cols<<endl;

cout<<"图像2特征描述矩阵大小:"<<descriptors2.size()

<<",特征向量个数:"<<descriptors2.rows<<",维数:"<<descriptors2.cols<<endl;

//画出特征点

Mat img_keypoints1,img_keypoints2;

drawKeypoints(img1,keypoints1,img_keypoints1,Scalar::all(-1),0);

drawKeypoints(img2,keypoints2,img_keypoints2,Scalar::all(-1),0);

//imshow("Src1",img_keypoints1);

//imshow("Src2",img_keypoints2);

//特征匹配

vector<DMatch> matches;//匹配结果

descriptor_matcher->match( descriptors1, descriptors2, matches );//匹配两个图像的特征矩阵

cout<<"Match个数:"<<matches.size()<<endl;

//计算匹配结果中距离的最大和最小值

//距离是指两个特征向量间的欧式距离,表明两个特征的差异,值越小表明两个特征点越接近

double max_dist = 0;

double min_dist = 100;

for(int i=0; i<matches.size(); i++)

{

double dist = matches[i].distance;

if(dist < min_dist) min_dist = dist;

if(dist > max_dist) max_dist = dist;

}

cout<<"最大距离:"<<max_dist<<endl;

cout<<"最小距离:"<<min_dist<<endl;

//筛选出较好的匹配点

vector<DMatch> goodMatches;

for(int i=0; i<matches.size(); i++)

{

if(matches[i].distance < 0.31 * max_dist)

{

goodMatches.push_back(matches[i]);

}

}

cout<<"goodMatch个数:"<<goodMatches.size()<<endl;

//画出匹配结果

Mat img_matches;

//红色连接的是匹配的特征点对,绿色是未匹配的特征点

drawMatches(img1,keypoints1,img2,keypoints2,goodMatches,img_matches,

Scalar::all(-1)/*CV_RGB(255,0,0)*/,CV_RGB(0,255,0),Mat(),2);

imshow("MatchSIFT",img_matches);

waitKey(0);

return 0;

}

四、Android实现

待更新

时间: 2024-10-19 06:59:52

Android学习九---OpenCV4android org.opencv.feature2d的相关文章

Android学习九:屏幕自适应

android中不同手机分辨率适配问题 在项目开发的过程中,同一个布局对应不同的手机会显示出不同的效果.导致这个现象产生的原因是不同手机的分辨率不同.在android sdk提供的帮助文档中,我们可以看到各种手机的分辨率和对应的屏大小.QVGA (240x320),WQVGA400(240x400),WQVGA432 (240x432),HVGA (320x480),WVGA800 (480x800),WVGA854 (480x854). 目前android手机的分辨率大致就是帮助文档中描述的几

android学习九(android的广播)

在这里 篇文章里面我将总结广播接收器(Broadcast Receiver)方面的知识.首先我们来了解下andorid中广播的类型,android中的广播可以分为2种,标准广播和有序广播. 标准广播:是一种完全异步执行的广播,在广播发出后,所有广播接收器几乎都会同一时刻接收到这条广播消息,因此它们之间没有任何先后顺序可言.这种广播的效率比较高,但同时也意味着它无法被截断的. 有序广播:则是一种同步机制的广播,在广播发出之后,同一时刻只会有一个广播接收器能够接收到这条广播信息,当这个广播接收器中逻

android学习九 对话框碎片

1.android的对话框是异步的,对话框创建后马上执行下面的代码.好处: a.通过实现对话框的回调方法反馈用户与对话框的交互. b.能够在代码中清楚对话框. 2.碎片对话框基类DialogFragment继承自Fragment.显示对话框的3个步骤 2.1创建一个对话框碎片实例 2.2获取碎片事务 2.3使用对话框碎片实例的show方法显示,show方法有两个版本,第一个版本接收FragmentManager参数,第二个版本接收FragmentTrancstion参数. 第二个版本可以有机会将

Android学习笔记二十九之SwipeRefreshLayout、RecyclerView和CardView

Android学习笔记二十九之SwipeRefreshLayout.RecyclerView和CardView 前面我们介绍了AlertDialog和几个常用的Dialog,ProgressDialog进度条提示框.DatePickerDialog日期选择对话框和TimePickerDialog时间选择对话框.这一节我们介绍几个新的API控件SwipeRefreshLayout.RecyclerView和CardView,这几个API控件都是google在Android5.0推出的.下面我们来学

九、Android学习笔记_ Android开发中使用软引用和弱引用防止内存溢出

在<Effective Java 2nd Edition>中,第6条"消除过期的对象引用"提到,虽然Java有 垃圾回收机制,但是只要是自己管理的内存,就应该警惕内存泄露的问题,例如的对象池.缓存中的过期对象都有可能引发内存泄露的问题.书中还提到可以用 WeakHashMap来作为缓存的容器可以有效解决这一问题.之前也确实遇到过类似问题,但是没有接触过"弱引用"相关的问题,于是查阅了一些资料. <Java 理论与实践: 用弱引用堵住内存泄漏>

Android学习——在Android中使用OpenCV的第一个程序

刚开始学习Android,由于之前比较熟悉OpenCV,于是就想先在Android上运行OpenCV试试 =================================================================================== 1.环境配置 JDK Eclipse ADT CDT Android SDK Android NDK cygwin OpenCV for Android 2.4.9 这部分网上很多,我就不再赘述了,可以参考:http://bl

Android学习路线(九)为Action Bar添加Style

这里先贴出原文,下次再来翻译::p 原文地址:http://developer.android.com/training/basics/actionbar/styling.html The action bar provides your users a familiar and predictable way to perform actions and navigate your app, but that doesn't mean it needs to look exactly the

九、Android学习第八天——广播机制与WIFI网络操作(转)

(转自:http://wenku.baidu.com/view/af39b3164431b90d6c85c72f.html) 九.Android学习第八天——广播机制与WIFI网络操作 今天熟悉了Android中的广播机制与WIFI网络的一些基本操作,总结如下: Android的广播机制 我们知道广播机制中,发送方不会关心接收方时候接收到数据或者如何去处理数据. 这里总结下Android中BroadcastReceiver的注册方法: (一)在应用程序中进行注册 (二)在Manifest.xml

Android学习路线(十九)支持不同设备——支持不同(Android)平台版本

当最新的Android版本为你的应用提供着很棒的APIs时,你却要在更多的设备更新之前继续支持老的系统版本.这篇课程如何在继续支持低版本的系统的情况下使用新版本的高级API. Platform Versions 仪表板展示了最新的活跃设备上运行的Android系统版本的分布,基于设备访问Google Play商店的次数.通常情况下,支持90%的活跃设备同时使用最新版本作为target是一个好习惯. 贴士: 为了在不同的Android版本上提供最好的特性和功能,你应该在你的应用中使用Android