Boooil's BLOG.

图像特征匹配开发手记Vol_5

2021/01/28 Share

功能整合


至此基本的功能部件已经撰写的差不多了,接下来就是将这些基本功能封装到类中。

  • District类

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    public class District {
    public double Left;
    public double Right;
    public double Top;
    public double Bottom;

    District(double left,double right,double top,double bottom){
    this.Left = left;
    this.Right = right;
    this.Top = top;
    this.Bottom = bottom;
    }

    }

    区域的结构体类。

    四个成员变量分别代表了矩形的边框位置信息。

    通过四个位置来构造district类。

  • AreaSearch类

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    public class AreaSearch {
    //创建区域
    public List<District> Creat_District(){
    List<District> district_list = new ArrayList<District>();
    District district_1 = new District(116.35609462605613,116.35809070137164 ,
    40.006850750760485,40.00527143350979);
    District district_2 = new District(116.35805850227803,116.3610959501106 ,
    40.00690797043768,40.00488486861002);
    District district_3 = new District(116.3561155131877,116.3580581918368 ,
    40.00517270621107,40.00429274172892);
    District district_4 = new District(116.35813332305531,116.36226554007133 ,
    40.00481085303074,40.00314959336152);
    District district_5 = new District(116.35472021913039,116.35821918730497 ,
    40.004169381315634,40.002565675659014);
    District district_6 = new District(116.35477499180625,116.35830615907452 ,
    40.00245168494597,40.00131673013192);
    District district_7 = new District(116.3582954260433,116.3613650729695 ,
    40.00300270692919,40.00159635843175);
    district_list.add(district_1);
    district_list.add(district_2);
    district_list.add(district_3);
    district_list.add(district_4);
    district_list.add(district_5);
    district_list.add(district_6);
    district_list.add(district_7);
    return district_list;
    }
    //当前坐标是否在区域中
    private boolean in_District(LatLng latlng, District district){
    if(latlng.longitude>=district.Left && latlng.longitude<=district.Right){
    if(latlng.latitude>=district.Bottom && latlng.latitude<=district.Top){
    return true;
    }
    return false;
    }
    else
    return false;
    }
    //返回当前坐标所属的区号
    public int getDistrict(LatLng latlng,List<District>district_list){
    int district_id = 0;//要返回的区号
    for(int i=1;i<district_list.size();i++){
    if(in_District(latlng,district_list.get(i-1))){
    district_id =i;
    }
    }
    return district_id;
    }
    }

    区域查找类。

    Creat_District():创建一个district的数组,包含了地图中所有区域的信息。(区域信息是提前写死的)

    in_District(LatLng latlng, District district):判断一个坐标是否在区域中。

    getDistrict(LatLng latlng,List<District>district_list):返回当前坐标所属的区号

  • FeatureMatch类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
public class FeatureMatch {

public double Score(Bitmap src_bitmap1, Bitmap src_bitmap2){
double score = 0;

Mat test1 = new Mat();
Mat test2 = new Mat();
Mat test1_Gray = new Mat();
Mat test2_Gray = new Mat();
Mat test1_Blurred = new Mat();
Mat test2_Blurred = new Mat();
Mat out = new Mat();

FeatureDetector SIFTdter = FeatureDetector.create(FeatureDetector.SURF);//创建特征监测
DescriptorExtractor descriptorExtractor=DescriptorExtractor.create(DescriptorExtractor.SURF);//描述子提取
DescriptorMatcher descriptorMatcher=DescriptorMatcher.create(DescriptorMatcher.FLANNBASED);//描述子匹配
MatOfDMatch matches=new MatOfDMatch();

Utils.bitmapToMat(src_bitmap1,test1);
Utils.bitmapToMat(src_bitmap2,test2);
Imgproc.cvtColor(test1,test1_Gray,COLOR_BGR2GRAY);
Imgproc.cvtColor(test2,test2_Gray,COLOR_BGR2GRAY);//转化为灰度图
Size Ksize = new Size(5,5);
GaussianBlur(test1_Gray,test1_Blurred,Ksize,0);
GaussianBlur(test2_Gray,test2_Blurred,Ksize,0);
Mat descriptors1=new Mat();
Mat descriptors2=new Mat();
MatOfKeyPoint kp1 = new MatOfKeyPoint();//特征点
MatOfKeyPoint kp2 = new MatOfKeyPoint();//特征点
SIFTdter.detect(test1_Blurred,kp1);
SIFTdter.detect(test2_Blurred,kp2);//监测特征点
descriptorExtractor.compute(test1_Blurred,kp1,descriptors1);//计算描述子
descriptorExtractor.compute(test2_Blurred,kp2,descriptors2);
descriptorMatcher.match(descriptors1,descriptors2,matches);//进行匹配

//-- Quick calculation of max and min distances between keypoints
List<DMatch> matches_list = matches.toList();//将MatOfDMatch转为List,方便操作
Collections.sort(matches_list, new Comparator<DMatch>() {
@Override
public int compare(DMatch o1, DMatch o2) {
return (int) (o1.distance-o2.distance);
}
});

double max_dist = 0; double min_dist = 10000;
for( int i = 0; i < descriptors1.rows(); i++ )
{ double dist = matches_list.get(i).distance;
if( dist < min_dist ) min_dist = dist;
if( dist > max_dist ) max_dist = dist;
}

List<DMatch> good_matches_list = new ArrayList<DMatch>();
for( int i = 0; i < descriptors1.rows(); i++ ){
if( matches_list.get(i).distance <= 2*min_dist){
good_matches_list.add( matches_list.get(i));
}
}
/*--------------------------------------------------------------------------------*/

//RANSAC优化
//根据matches将特征点对齐,将坐标转换为float类型
List<KeyPoint> keypoint_1 = new ArrayList<KeyPoint>();
List<KeyPoint> keypoint_2 = new ArrayList<KeyPoint>();
keypoint_1 = kp1.toList();
keypoint_2 = kp2.toList();
List<KeyPoint> R_keypoint_1 = new ArrayList<KeyPoint>();
List<KeyPoint> R_keypoint_2 = new ArrayList<KeyPoint>();
for (int i=0;i<good_matches_list.size();i++) {
R_keypoint_1.add(keypoint_1.get(good_matches_list.get(i).queryIdx));
R_keypoint_2.add(keypoint_2.get(good_matches_list.get(i).trainIdx));
//这两句话的理解:R_keypoint1是要存储img01中能与img02匹配的特征点,
//matches中存储了这些匹配点对的img01和img02的索引值
}
List<Point> p1 = new ArrayList<Point>();
List<Point> p2 = new ArrayList<Point>();
for (int i=0;i<good_matches_list.size();i++){
p1.add(R_keypoint_1.get(i).pt);
p2.add(R_keypoint_2.get(i).pt);
}
MatOfPoint2f mp1 = new MatOfPoint2f();
mp1.fromList(p1);
MatOfPoint2f mp2 = new MatOfPoint2f();
mp2.fromList(p2);
Mat Fundamental = new Mat();
Mat m_RANSACStatus = new Mat();//用于存储RANSAC后每个点的状态
Fundamental = Calib3d.findFundamentalMat(mp1,mp2,RANSAC,3,0.99,m_RANSACStatus);

List<KeyPoint> RR_kp1_list = new ArrayList<KeyPoint>();
List<KeyPoint> RR_kp2_list = new ArrayList<KeyPoint>();
List<DMatch> RR_match_list = new ArrayList<DMatch>();
int index = 0;
for(int i=0;i<good_matches_list.size();i++){
double[] d = m_RANSACStatus.get(i,0);
if(d[0]!=0){
RR_kp1_list.add(keypoint_1.get(i));
RR_kp2_list.add(keypoint_2.get(i));
good_matches_list.get(i).queryIdx=index;
good_matches_list.get(i).trainIdx=index;
RR_match_list.add(good_matches_list.get(i));
index++;
}
}
Collections.sort(RR_match_list, new Comparator<DMatch>() {
@Override
public int compare(DMatch o1, DMatch o2) {
return (int) (o1.distance-o2.distance);
}
});


score = Ransac_aver(RR_match_list);

return score;
}
private double Ransac_aver(List<DMatch> matches){
double sum = 0;
for(int i=0;i<matches.size();i++){
sum+= matches.get(i).distance;
}
return sum/matches.size();
}
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);
return out;
}
}

​ 图像特征匹配类

​ Score(Bitmap src_bitmap1, Bitmap src_bitmap2):返回传入两张图片的匹配得分(distance)

​ Ransac_aver(List<DMatch> matches):计算RANSAC提纯后的distance平均值

​ Mat drawMatchs(Mat img1,MatOfKeyPoint key1,Mat img2,MatOfKeyPoint key2,MatOfDMatch matches,boolean imageOnly):绘制两张图片对比图,返回类型为矩阵。imageOnly参数:是否画出匹配线条。


最后还有一点小坑

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
OpenCVLoader.initDebug();
mLoaderCallback.onManagerConnected(LoaderCallbackInterface.SUCCESS);
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");
}
break;
default: {
super.onManagerConnected(status);
}
break;
}
}
};

​ 新创建的activity一定要记得加载Opencv,不然一些API函数会报错无法使用。