Commit 28d11d6f authored by wanglei's avatar wanglei

...

parent 5791b1ab
...@@ -3,6 +3,7 @@ package com.base.filerecoveryrecyclebin.utils ...@@ -3,6 +3,7 @@ package com.base.filerecoveryrecyclebin.utils
import android.graphics.BitmapFactory import android.graphics.BitmapFactory
import android.provider.MediaStore.Audio.Radio import android.provider.MediaStore.Audio.Radio
import com.base.filerecoveryrecyclebin.bean.MediaBean import com.base.filerecoveryrecyclebin.bean.MediaBean
import com.base.filerecoveryrecyclebin.utils.TestSimilar.testSimilar
//import com.base.filerecoveryrecyclebin.utils.OpencvImageHelp.opencvCompareSimilar //import com.base.filerecoveryrecyclebin.utils.OpencvImageHelp.opencvCompareSimilar
import java.io.File import java.io.File
import kotlin.random.Random import kotlin.random.Random
...@@ -37,8 +38,10 @@ object SimilarHelper { ...@@ -37,8 +38,10 @@ object SimilarHelper {
if (item.path != compareItem.path) { if (item.path != compareItem.path) {
// val percent = opencvCompareSimilar(item.path, compareItem.path) // val percent = opencvCompareSimilar(item.path, compareItem.path)
val percent = similarPercent(File(item.path), File(compareItem.path)) // val percent = similarPercent(File(item.path), File(compareItem.path))
if (percent > 60) { val isSimilar = testSimilar(item.path, compareItem.path)
LogEx.logDebug(TAG, "isSimilar=$isSimilar")
if (isSimilar) {
if (result[item.path] == null) { if (result[item.path] == null) {
LogEx.logDebug(TAG, "item=$item") LogEx.logDebug(TAG, "item=$item")
result[item.path] = arrayListOf() result[item.path] = arrayListOf()
...@@ -47,8 +50,6 @@ object SimilarHelper { ...@@ -47,8 +50,6 @@ object SimilarHelper {
result[item.path]?.add(item) result[item.path]?.add(item)
} }
result[item.path]?.add(compareItem) result[item.path]?.add(compareItem)
LogEx.logDebug(TAG, "percent=$percent ${item.path} vs ${compareItem.path}")
LogEx.logDebug(TAG, "compareItem=$compareItem")
haveComperedList.add(compareItem.path) haveComperedList.add(compareItem.path)
compareIterator.remove() compareIterator.remove()
} }
......
package com.base.filerecoveryrecyclebin.utils
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.graphics.Color
import kotlin.math.sqrt
object TestSimilar {
private val TAG = "TestSimilar"
fun testSimilar(srcPath: String, comparePath: String): Boolean {
//低内存加载
val srcLowBitmap: Bitmap? = loadBitmapWithLowMemory(srcPath)
val compareLowBitmap: Bitmap? = loadBitmapWithLowMemory(comparePath)
if (srcLowBitmap == null || compareLowBitmap == null) {
return false
}
try {
//缩放
val srcScaledBitmap = Bitmap.createScaledBitmap(srcLowBitmap, 16, 16, true)
val compareScaledBitmap = Bitmap.createScaledBitmap(compareLowBitmap, 16, 16, true)
LogEx.logDebug(TAG, "finish to Scaled with=${srcScaledBitmap.width} height=${srcScaledBitmap.height}")
//转数组
val srcBitmapArray = bitmapToFeatureIntArray(srcScaledBitmap)
val compareBitmapArray = bitmapToFeatureIntArray(compareScaledBitmap)
srcBitmapArray.forEach {
LogEx.logDebug(TAG, "finish to IntArray $it")
}
//dct转换
val srcDctArray = dct16x16(srcBitmapArray)
val compareDctArray = dct16x16(compareBitmapArray)
LogEx.logDebug(TAG, "finish to DctArray")
//感知hash转换
val srcHash = generatePerceptualHash(srcDctArray)
val compareHash = generatePerceptualHash(compareDctArray)
LogEx.logDebug(TAG, "srcHash=$srcHash compareHash=$compareHash")
//对比
return isImagesSimilar(srcHash, compareHash, 5)
} catch (e: Exception) {
LogEx.logDebug(TAG, "$e")
} finally {
srcLowBitmap.recycle()
srcLowBitmap.recycle()
}
return false
}
private fun loadBitmapWithLowMemory(filePath: String, reqWidth: Int = 16, reqHeight: Int = 16): Bitmap? {
// 设置BitmapFactory.Options的inJustDecodeBounds为true
val options = BitmapFactory.Options().apply {
inJustDecodeBounds = true
BitmapFactory.decodeFile(filePath, this)
inJustDecodeBounds = false
// 计算inSampleSize值
// inSampleSize = calculateInSampleSize(this, reqWidth, reqHeight)
// 使用RGB_565配置以减少内存使用
inPreferredConfig = Bitmap.Config.RGB_565
}
// 使用计算得到的options加载Bitmap
return BitmapFactory.decodeFile(filePath)
}
private fun calculateInSampleSize(options: BitmapFactory.Options, reqWidth: Int, reqHeight: Int): Int {
// 原始图像的宽度和高度
val height = options.outHeight
val width = options.outWidth
var inSampleSize = 1
// 计算出需要的inSampleSize值
while (height / inSampleSize > reqHeight || width / inSampleSize > reqWidth) {
inSampleSize *= 2
}
return inSampleSize
}
private fun isImagesSimilar(hash1: String, hash2: String, threshold: Int): Boolean {
return calculateHammingDistance(hash1, hash2) <= threshold
}
private fun calculateHammingDistance(hash1: String, hash2: String): Int {
var distance = 0
for (i in hash1.indices) {
if (hash1[i] != hash2[i]) distance++
}
return distance
}
fun bitmapToFeatureIntArray(bitmap: Bitmap): IntArray {
// 创建一个整型数组来存储特征,这里我们假设每个像素一个特征值
val featureSize = bitmap.width * bitmap.height
val featureIntArray = IntArray(featureSize)
// 遍历Bitmap的每个像素
var index = 0
for (x in 0 until bitmap.width) {
for (y in 0 until bitmap.height) {
val pixel = bitmap.getPixel(x, y)
// 计算灰度值
val grayValue = getGrayscaleValue(pixel)
// 存储灰度值到整型数组中
featureIntArray[index++] = grayValue
}
}
return featureIntArray
}
// 辅助函数,用于计算给定像素的灰度值
fun getGrayscaleValue(color: Int): Int {
val red = Color.red(color)
val green = Color.green(color)
val blue = Color.blue(color)
// 使用加权和来计算灰度值
return ((0.299 * red + 0.587 * green + 0.114 * blue).toInt() shl 24) or (color and 0x00FFFFFF)
}
fun dct16x16(block: IntArray): DoubleArray {
val N = 16 // 变换的尺寸
val scaledBlock = DoubleArray(N * N)
for (u in 0 until N) {
for (v in 0 until N) {
var sum = 0.0
for (x in 0 until N) {
for (y in 0 until N) {
// 应用DCT变换公式
sum += block[x * N + y] *
Math.cos((2.0 * x + 1.0) * u * Math.PI / (2.0 * N)) *
Math.cos((2.0 * y + 1.0) * v * Math.PI / (2.0 * N))
}
}
// 标准化因子,当u或v为0时,使用1/sqrt(N),否则使用1/N
val norm = if (u == 0 && v == 0) (1.0 / sqrt(N.toDouble())) else (1.0 / N)
scaledBlock[u * N + v] = sum * norm
}
}
return scaledBlock
}
private fun generatePerceptualHash(dctCoefficients: DoubleArray): String {
// 1. 截断DCT系数,假设dctCoefficients已经是16x16的DCT系数
// 保留8x8的系数块,这里简化处理,只取前64个系数
val truncatedDctCoefficients = dctCoefficients.take(64)
// 2. 量化DCT系数,将系数映射到0-1范围,然后四舍五入到最近的整数
val quantizedCoefficients = truncatedDctCoefficients.map {
((it + 128) / 255 * 0.5).coerceIn(0.0, 1.0).toInt()
}
// 3. 生成哈希值,将量化后的系数转换为二进制字符串
val hashBuilder = StringBuilder()
quantizedCoefficients.forEach {
// 将每个量化系数转换为二进制表示,并添加到哈希构建器中
hashBuilder.append(it.toString(2))
}
// 返回最终的哈希值字符串
return hashBuilder.toString()
}
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment