Files
MAcode/tcs-iptv/internal/hash/perceptual.go
T
selfrelease a329d4906b init: AIGC-Hub/AVCC 方案文档 + TCS-IPTV 内容可信锁定系统 MVP
- 方案文档: AVCC 体系建设、IPTV TCS 需求(0-req)/PRD(1-prd)/任务(2-task)/二三四期任务
- tcs-iptv: Go 后端(哈希SDK/MA码生成/可信数据空间mock/业务编排/HTTP API+HMAC鉴权)
- web-console: React+AntD 监管大屏(角色工作台/全流程演示/监管片库)
- 一剧一码+集级哈希, 集级下架/恢复, 全栈测试通过
2026-06-14 16:50:31 +08:00

107 lines
2.7 KiB
Go

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
}