前言
Android OpenCV 系列的上一篇文章中,我们学习了 ORB 特征点的暴力匹配方式。复习一下,暴力匹配法会针对查询描述子中的每个描述符在训练描述子中寻找匹配描述子,算法复杂度是 O(
) 级别的,随着特征点数量的增加,运行速度会受到明显的影响。所以,我们今天介绍另外一种匹配方法—— (近似)最近邻快速搜索库(Fast Library for Approximate Nearest Neighbors,FLANN)。它是一个对大数据集和高维特征进行最近邻搜索的算法的集合,而且这些算法都已经被优化过了。在面对大数据集时它的效果要好于BFMatcher。
API
FlannBasedMatcher
FLANN匹配器
代码语言:javascript复制public static FlannBasedMatcher create()
DescriptorMatcher通用匹配器
代码语言:javascript复制public static DescriptorMatcher create(String descriptorMatcherType)
- 参数一:descriptorMatcherType,描述子匹配器类型。支持下列五种匹配类型:
BruteForce
BruteForce-L1
BruteForce-Hamming
BruteForce-Hamming(2)
FlannBased
public static DescriptorMatcher create(int matcherType)
参数一:matcherType,描述子匹配器类型。支持下列几种匹配类型:
代码语言:javascript复制public static final int
FLANNBASED = 1,
BRUTEFORCE = 2,
BRUTEFORCE_L1 = 3,
BRUTEFORCE_HAMMING = 4,
BRUTEFORCE_HAMMINGLUT = 5,
BRUTEFORCE_SL2 = 6;
所以,针对 FLANN 匹配器,如下三种方式均可以完成构建:
代码语言:javascript复制val matcher = FlannBasedMatcher.create() // 方法一
val matcher = DescriptorMatcher.create(DescriptorMatcher.FLANNBASED) // 方法二
val matcher = DescriptorMatcher.create("FlannBased") // 方法三
操作
代码语言:javascript复制class ORBFLANNMatchActivity : AppCompatActivity() {
private val firstBgr by lazy {
Utils.loadResource(this, R.drawable.lena)
}
private val firstGray by lazy { firstBgr.toGray() }
private val secondBgr by lazy {
Utils.loadResource(this, R.drawable.lena_250)
}
private val secondGray by lazy { secondBgr.toGray() }
private val mBinding: ActivityOrbFlannBinding by lazy {
ActivityOrbFlannBinding.inflate(layoutInflater)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(mBinding.root)
wrapCoroutine({ showLoading() }, { doORBFlannMatch() }, { hideLoading() })
}
private fun showLoading() {
mBinding.isLoading = true
}
private fun hideLoading() {
mBinding.isLoading = false
}
private fun doORBFlannMatch() {
val firstKeyPoints = MatOfKeyPoint()
val secondKeyPoints = MatOfKeyPoint()
val firstDescriptor = Mat()
val secondDescriptor = Mat()
orbFeatures(firstGray, firstKeyPoints, firstDescriptor)
orbFeatures(secondGray, secondKeyPoints, secondDescriptor)
if (firstDescriptor.type() != CvType.CV_32F && secondDescriptor.type() != CvType.CV_32F) {
firstDescriptor.convertTo(firstDescriptor, CvType.CV_32F)
secondDescriptor.convertTo(secondDescriptor, CvType.CV_32F)
}
val matches = MatOfDMatch()
val matcher = FlannBasedMatcher.create()
// val matcher = DescriptorMatcher.create(DescriptorMatcher.FLANNBASED)
// val matcher = DescriptorMatcher.create("FlannBased")
matcher.match(firstDescriptor, secondDescriptor, matches)
Log.e(App.TAG, " matchers size = ${matches.size()}")
val list = matches.toList()
list.sortBy { it.distance }
Log.e(App.TAG, "Min = ${list.first().distance}")
val min = list.first().distance
val max = list.last().distance
val goodMatchers = list.filter {
it.distance < max.times(0.4)
}
Log.e(App.TAG, " good matchers size = ${goodMatchers.size}")
val result = Mat()
val matOfDMatch = MatOfDMatch()
matOfDMatch.fromList(goodMatchers)
drawMatches(firstGray, firstKeyPoints, secondGray, secondKeyPoints, matOfDMatch, result)
GlobalScope.launch(Dispatchers.Main) {
mBinding.ivResult.showMat(result)
}
}
private fun orbFeatures(source: Mat, keyPoints: MatOfKeyPoint, descriptor: Mat) {
val orbDetector = ORB.create(
1000,
1.2f
)
orbDetector.detect(source, keyPoints)
orbDetector.compute(source, keyPoints, descriptor)
Log.e(App.TAG, "count = ${keyPoints.size()}")
}
override fun onDestroy() {
firstBgr.release()
secondBgr.release()
firstGray.release()
secondGray.release()
super.onDestroy()
}
}
效果
FLANN匹配结果
源码
https://github.com/onlyloveyd/LearningAndroidOpenCV