外挂三部曲(三)

您所在的位置:网站首页 相似度app 外挂三部曲(三)

外挂三部曲(三)

2024-06-29 11:55| 来源: 网络整理| 查看: 265

本文已授权郭霖公众号独家发布

一、第一种对比方式

第一种对比方式是:取出两张 bitmap 中的所有像素,然后一一进行对比。匹配的点除以总点数就能得到一个相似度。代码如下:

object SimilarityUtils { fun similarity(bitmap1: Bitmap, bitmap2: Bitmap): Double { // 获取图片所有的像素 val pixels1 = getPixels(bitmap1) val pixels2 = getPixels(bitmap2) // 总的像素点数以较大图片为准 val totalCount = pixels1.size.coerceAtLeast(pixels2.size) if (totalCount == 0) return 0.00 var matchCount = 0 var i = 0 while (i < pixels1.size && i < pixels2.size) { if (pixels1[i] == pixels2[i]) { // 统计相同的像素点数量 matchCount++ } i++ } // 相同的像素点数量除以总的像素点数,得到相似比例。 return String.format("%.2f", matchCount.toDouble() / totalCount).toDouble() } private fun getPixels(bitmap: Bitmap): IntArray { val pixels = IntArray(bitmap.width * bitmap.height) // 获取每个像素的 RGB 值 bitmap.getPixels(pixels, 0, bitmap.width, 0, 0, bitmap.width, bitmap.height) return pixels } }

可以看到,similarity 函数接收两个 Bitmap,返回一个 Double 值,这个值的取值范围是 0.00~1.00,表示相似度。 首先通过 bitmap.getPixels 取出所有的像素点,以其中较多的像素点作为总点数。 然后通过 pixels1[i] == pixels2[i] 对比每个像素点,如果相同则 matchCount 加一,最后用 matchCount / totalCount 计算出相似度。 这种比较方式特别直观,容易理解,通过每个像素点依次比较得出相似度。我们也很容易想到它的缺点:如果第二张图片是由第一张图片缩放、变形、旋转等变换得来的,那么每个像素点可能都无法匹配上,所以相似度会很低很低。

也就是说,这个算法几乎只能用于比较图片是否一模一样,只要两张图的像素点有细微的错位,比较结果就会完全不准确。 不过其实这种算法已经能够满足我们的需求了,只要我们每次都取一样的 Bitmap 进行比较就可以了。只要保证整张图都一样,或者从 Bitmap 裁剪出的固定区域一样就可以了。此时比较结果可以供我们正常使用。

但更好的做法是通过 SIFT 算法计算相似度。

二、通过 SIFT 算法计算相似度

SIFT 算法指的是尺度不变特征转换 (Scale Invariant Feature Transform)。它是计算机视觉领域中描述图片特征的一种算法,应用非常广泛。

这个算法是由一些大神们研究出来的,由于本文不是在写论文,所以我也不会对这个算法进行深究,简单介绍一下它的大概原理:

先将图片映射为空间中的坐标:

再从所有坐标中过滤出其中的特征点:

再为特征点分配一个方向值,使得图片变形后仍然能够正确匹配:

将这些信息转换成数学描述:

注:算法原理的这段内容,只是我个人一点粗浅的理解,可能和算法的实际实现有出入。但这个算法的实现不是本文的重点,重点在于这个算法可以用于对比两张图片的相似度。所以于我而言,我愿将其称之为魔法。

这个算法被封装在 OpenCV 库中,所以使用前需要导入 OpenCV 库。

OpenCV 官方没有提供 gradle 导入的方式,所以网上有许多导入 OpenCV 库的教程,讲的都是去下载 OpenCV 的源码,再通过 Module 的方式加入项目中。

所以现在我们可以直接在 build.gradle 中直接导入 OpenCV 库:

implementation 'com.quickbirdstudios:opencv:4.5.3.0'

需要注意的是,OpenCV 库非常大,导入这个库会让 apk 的体积增加 100 多 M,所以要慎用。 有了 OpenCV 库,就可以编写图片相似度对比工具类了:

object SIFTUtils { // SIFT detector private val siftDetector by lazy { SIFT.create() } fun similarity(bitmap1: Bitmap, bitmap2: Bitmap): Double { // 计算每张图片的特征点 val descriptors1 = computeDescriptors(bitmap1) val descriptors2 = computeDescriptors(bitmap2) // 比较两张图片的特征点 val descriptorMatcher = DescriptorMatcher.create(DescriptorMatcher.FLANNBASED) val matches: List = ArrayList() // 计算大图中包含多少小图的特征点。 // 如果计算小图中包含多少大图的特征点,结果会不准确。 // 比如:若小图中的 50 个点都包含在大图中的 100 个特征点中,则计算出的相似度为 100%,显然不符合我们的预期 if (bitmap1.byteCount > bitmap2.byteCount) { descriptorMatcher.knnMatch(descriptors1, descriptors2, matches, 2) } else { descriptorMatcher.knnMatch(descriptors2, descriptors1, matches, 2) } Log.i("~~~", "matches.size: ${matches.size}") if (matches.isEmpty()) return 0.00 // 获取匹配的特征点数量 var matchCount = 0 // 邻近距离阀值,这里设置为 0.7,该值可自行调整 val nndrRatio = 0.7f matches.forEach { match -> val array = match.toArray() // 用邻近距离比值法(NNDR)计算匹配点数 if (array[0].distance


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3