package hash import ( "fmt" "image" "image/color" "math/bits" ) // 感知哈希用于跨格式/转码识别同一内容(需求1-AC3)。 // MVP 实现 aHash(均值哈希)与 dHash(差值哈希),输入为已解码的视频代表帧图像。 // 真实视频抽帧由上层(ffmpeg)完成,本包专注哈希算法以便独立测试。 const phashDim = 8 // 8x8 → 64-bit 哈希 // grayResize 将图像缩放为 w×h 的灰度矩阵(最近邻,零依赖)。 func grayResize(img image.Image, w, h int) [][]float64 { b := img.Bounds() srcW, srcH := b.Dx(), b.Dy() out := make([][]float64, h) for y := 0; y < h; y++ { out[y] = make([]float64, w) for x := 0; x < w; x++ { sx := b.Min.X + x*srcW/w sy := b.Min.Y + y*srcH/h r, g, bb, _ := img.At(sx, sy).RGBA() // 转 8 位灰度(ITU-R 601 亮度) gray := 0.299*float64(r>>8) + 0.587*float64(g>>8) + 0.114*float64(bb>>8) out[y][x] = gray } } return out } // AHash 计算均值哈希(64-bit,十六进制 16 字符)。 func AHash(img image.Image) string { m := grayResize(img, phashDim, phashDim) var sum float64 for y := 0; y < phashDim; y++ { for x := 0; x < phashDim; x++ { sum += m[y][x] } } avg := sum / float64(phashDim*phashDim) var hash uint64 var bit uint for y := 0; y < phashDim; y++ { for x := 0; x < phashDim; x++ { if m[y][x] >= avg { hash |= 1 << bit } bit++ } } return fmt.Sprintf("%016x", hash) } // DHash 计算差值哈希(64-bit)。对水平相邻像素比较亮度。 func DHash(img image.Image) string { // 需要 (phashDim+1) 列以产生 phashDim 个差值 m := grayResize(img, phashDim+1, phashDim) var hash uint64 var bit uint for y := 0; y < phashDim; y++ { for x := 0; x < phashDim; x++ { if m[y][x] < m[y][x+1] { hash |= 1 << bit } bit++ } } return fmt.Sprintf("%016x", hash) } // HammingDistance 计算两个等长十六进制哈希的汉明距离。 // 距离越小越相似;用于版权比对与跨版本识别。 func HammingDistance(a, b string) (int, error) { if len(a) != len(b) { return 0, fmt.Errorf("hash: length mismatch %d vs %d", len(a), len(b)) } var va, vb uint64 if _, err := fmt.Sscanf(a, "%x", &va); err != nil { return 0, err } if _, err := fmt.Sscanf(b, "%x", &vb); err != nil { return 0, err } return bits.OnesCount64(va ^ vb), nil } // newGrayTestImage 是测试辅助:由灰度矩阵生成图像。 func newGrayTestImage(gray [][]uint8) image.Image { h := len(gray) w := 0 if h > 0 { w = len(gray[0]) } img := image.NewGray(image.Rect(0, 0, w, h)) for y := 0; y < h; y++ { for x := 0; x < w; x++ { img.SetGray(x, y, color.Gray{Y: gray[y][x]}) } } return img }