48 lines
1.2 KiB
Go
48 lines
1.2 KiB
Go
package common
|
||
|
||
import (
|
||
"bytes"
|
||
"image"
|
||
"image/color"
|
||
_ "image/jpeg" // 注册解码器(dHash 全量解码用)
|
||
_ "image/png"
|
||
)
|
||
|
||
// ImageDHash64 整图 64 位 dHash(9x8 灰度采样,与 ffmpeg scale=9:8 同语义)。
|
||
// 两图相似度 = 哈希异或的位 1 数(汉明距离),≤ CleanHashHamming 阈值视为近重复
|
||
// (数据清洗桶内多样性保留用,见 consts.CleanBuckets)。
|
||
func ImageDHash64(data []byte) (uint64, error) {
|
||
src, _, err := image.Decode(bytes.NewReader(data))
|
||
if err != nil {
|
||
return 0, err
|
||
}
|
||
return hashOf(src)
|
||
}
|
||
|
||
// hashOf 图像 dHash:采样 9 列 x 8 行(全域拉伸),逐行比较相邻列明暗得 64 位
|
||
func hashOf(src image.Image) (uint64, error) {
|
||
b := src.Bounds()
|
||
w, h := b.Dx(), b.Dy()
|
||
if w < 1 || h < 1 {
|
||
return 0, image.ErrFormat
|
||
}
|
||
var gray color.Gray
|
||
shade := func(x, y int) uint32 {
|
||
gray = color.GrayModel.Convert(src.At(b.Min.X+x, b.Min.Y+y)).(color.Gray)
|
||
return uint32(gray.Y)
|
||
}
|
||
var h64 uint64
|
||
for r := 0; r < 8; r++ {
|
||
y := (h - 1) * r / 7
|
||
for c := 0; c < 8; c++ {
|
||
xl := (w - 1) * c / 8
|
||
xr := (w - 1) * (c + 1) / 8
|
||
h64 <<= 1
|
||
if shade(xl, y) < shade(xr, y) {
|
||
h64 |= 1
|
||
}
|
||
}
|
||
}
|
||
return h64, nil
|
||
}
|