Skip to content

Commit 9d7b390

Browse files
Downsample images before hashing
1 parent 042fbd7 commit 9d7b390

File tree

1 file changed

+48
-22
lines changed

1 file changed

+48
-22
lines changed
Lines changed: 48 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,61 @@
11
package com.d4rk.cleaner.app.images.utils
22

3+
import android.graphics.Bitmap
34
import android.graphics.BitmapFactory
45
import androidx.core.graphics.scale
56
import java.io.File
67

78
object ImageHashUtils {
89
fun perceptualHash(file: File): String? = runCatching {
9-
val bitmap = BitmapFactory.decodeFile(file.absolutePath) ?: return null
10-
val resized = bitmap.scale(8, 8)
11-
val pixels = IntArray(64)
12-
resized.getPixels(pixels, 0, 8, 0, 0, 8, 8)
13-
var sum = 0
14-
val gray = IntArray(64)
15-
pixels.forEachIndexed { index, pixel ->
16-
val r = (pixel shr 16) and 0xff
17-
val g = (pixel shr 8) and 0xff
18-
val b = pixel and 0xff
19-
val lum = (r + g + b) / 3
20-
gray[index] = lum
21-
sum += lum
22-
}
23-
val avg = sum / 64
24-
var hash = 0L
25-
gray.forEachIndexed { index, lum ->
26-
if (lum >= avg) {
27-
hash = hash or (1L shl (63 - index))
10+
val options = BitmapFactory.Options().apply { inJustDecodeBounds = true }
11+
BitmapFactory.decodeFile(file.absolutePath, options)
12+
options.inSampleSize = calculateInSampleSize(options, 8, 8)
13+
options.inJustDecodeBounds = false
14+
BitmapFactory.decodeFile(file.absolutePath, options)?.use { bitmap ->
15+
bitmap.scale(8, 8).use { resized ->
16+
val pixels = IntArray(64)
17+
resized.getPixels(pixels, 0, 8, 0, 0, 8, 8)
18+
var sum = 0
19+
val gray = IntArray(64)
20+
pixels.forEachIndexed { index, pixel ->
21+
val r = (pixel shr 16) and 0xff
22+
val g = (pixel shr 8) and 0xff
23+
val b = pixel and 0xff
24+
val lum = (r + g + b) / 3
25+
gray[index] = lum
26+
sum += lum
27+
}
28+
val avg = sum / 64
29+
var hash = 0L
30+
gray.forEachIndexed { index, lum ->
31+
if (lum >= avg) {
32+
hash = hash or (1L shl (63 - index))
33+
}
34+
}
35+
java.lang.Long.toHexString(hash)
2836
}
2937
}
30-
resized.recycle()
31-
bitmap.recycle()
32-
java.lang.Long.toHexString(hash)
3338
}.getOrNull()
39+
40+
private fun calculateInSampleSize(options: BitmapFactory.Options, reqWidth: Int, reqHeight: Int): Int {
41+
val (height, width) = options.run { outHeight to outWidth }
42+
var inSampleSize = 1
43+
if (height > reqHeight || width > reqWidth) {
44+
var halfHeight = height / 2
45+
var halfWidth = width / 2
46+
while ((halfHeight / inSampleSize) >= reqHeight && (halfWidth / inSampleSize) >= reqWidth) {
47+
inSampleSize *= 2
48+
}
49+
}
50+
return inSampleSize
51+
}
52+
53+
private inline fun <T> Bitmap.use(block: (Bitmap) -> T): T {
54+
return try {
55+
block(this)
56+
} finally {
57+
recycle()
58+
}
59+
}
3460
}
3561

0 commit comments

Comments
 (0)