Files
backend_v2/internal/pkg/phash/phash_test.go
toom1996 3fc8b1f924 chore(publish): 收尾清理陈旧 dump 与 gofmt 偏差
删除 cmd/dbtool/db_dump.sql:它是陈旧的全库 dump 旧产物(仍含 4 张已删草稿表与旧 schema),与权威的根 db_dump.sql 同名易混,且未被 .gitignore 覆盖;确认代码与文档均无引用,删除比再生成更干净。gofmt 修正 4 个文件(仅空白与注释,无语义变化):runway_image.go 结构体字段对齐、phash_test.go 行尾注释对齐、ingest_repository.go 两行多余空行、street_main_detail_integration_test.go 文档注释中的两个单引号会被 gofmt 改写成弯引号(改述为「空串」)。
2026-09-23 14:30:20 +08:00

123 lines
3.5 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package phash
import (
"bytes"
"image"
"image/color"
"image/png"
"testing"
)
// genGradient 生成一张水平灰阶渐变的 PNG 字节流(w 宽、h 高)。
// 用于验证 dHash 对「同一图案」的稳定性,以及对「缩放 / 转码」的鲁棒性。
func genGradient(w, h int) []byte {
img := image.NewRGBA(image.Rect(0, 0, w, h))
for y := 0; y < h; y++ {
for x := 0; x < w; x++ {
v := uint8(x * 255 / w)
img.Set(x, y, color.Gray{Y: v})
}
}
var buf bytes.Buffer
if err := png.Encode(&buf, img); err != nil {
panic(err)
}
return buf.Bytes()
}
// genGradientReverse 生成一张「左亮右暗」的反向渐变 PNG。
// 与 genGradient(左暗右亮)方向相反,dHash 的相邻比较结果完全相反,
// 汉明距离应为 64(最大),用于验证「差异显著」的图能被正确区分。
func genGradientReverse(w, h int) []byte {
img := image.NewRGBA(image.Rect(0, 0, w, h))
for y := 0; y < h; y++ {
for x := 0; x < w; x++ {
v := uint8(255 - x*255/w)
img.Set(x, y, color.Gray{Y: v})
}
}
var buf bytes.Buffer
if err := png.Encode(&buf, img); err != nil {
panic(err)
}
return buf.Bytes()
}
// genSolid 生成一张纯色 PNG。
func genSolid(w, h int, v uint8) []byte {
img := image.NewRGBA(image.Rect(0, 0, w, h))
c := color.Gray{Y: v}
for y := 0; y < h; y++ {
for x := 0; x < w; x++ {
img.Set(x, y, c)
}
}
var buf bytes.Buffer
if err := png.Encode(&buf, img); err != nil {
panic(err)
}
return buf.Bytes()
}
// TestOfIdentical 同一字节流 → 指纹完全一致。
func TestOfIdentical(t *testing.T) {
a := genGradient(64, 64)
b := genGradient(64, 64)
if Of(a) != Of(b) {
t.Fatalf("相同图片应得到相同 dHash,got %016x vs %016x", Of(a), Of(b))
}
}
// TestOfResizeSimilar 同一图案缩放到不同尺寸 → 汉明距离很小(近重复可容忍范围内)。
func TestOfResizeSimilar(t *testing.T) {
a := genGradient(64, 64)
b := genGradient(128, 128)
d := Hamming(Of(a), Of(b))
if d > 20 {
t.Fatalf("同一图案缩放后汉明距离应很小,got %d", d)
}
}
// TestOfDifferent 方向相反的图案(左暗右亮 vs 左亮右暗)→ 汉明距离应显著大于近重复阈值。
func TestOfDifferent(t *testing.T) {
a := genGradient(64, 64)
b := genGradientReverse(64, 64)
d := Hamming(Of(a), Of(b))
if d <= DefaultThreshold {
t.Fatalf("不同图片汉明距离应大于阈值 %d,got %d", DefaultThreshold, d)
}
}
// TestToVectorBits 验证向量串格式与维度(64 位、方括号包裹、0/1 组成)。
func TestToVectorBits(t *testing.T) {
s := ToVectorBits(0b1) // 仅最低位为 1
if len(s) != 2*Size+1 { // 64 个 0/1 + 63 个逗号 + 2 个方括号 = 129
t.Fatalf("向量串长度应为 %d,got %d(%s)", 2*Size+1, len(s), s)
}
if s[0] != '[' || s[len(s)-1] != ']' {
t.Fatalf("向量串应以方括号包裹,got %s", s)
}
if s[1] != '1' { // bit0 = 1
t.Fatalf("第 0 维应为 1,got %c", s[1])
}
if s[3] != '0' { // bit1 = 0([1,0,...] 中第二个元素)
t.Fatalf("第 1 维应为 0,got %c", s[3])
}
if ToVectorBits(0) != "" {
t.Fatalf("解码失败(0)应返回空串,got %q", ToVectorBits(0))
}
}
// TestHamming 验证汉明距离的对称性与已知值。
func TestHamming(t *testing.T) {
if Hamming(0, 0) != 0 {
t.Fatalf("相同指纹汉明距离应为 0")
}
if Hamming(0b1111, 0b0000) != 4 {
t.Fatalf("1111 vs 0000 汉明距离应为 4")
}
if Hamming(0b1111, 0b0000) != Hamming(0b0000, 0b1111) {
t.Fatalf("汉明距离应对称")
}
}