82 lines
1.7 KiB
Go
82 lines
1.7 KiB
Go
// Package vision 提供帧差指纹、图片编码与(可选的)本地检测接口。
|
|
package vision
|
|
|
|
import (
|
|
"image"
|
|
|
|
"golang.org/x/image/draw"
|
|
)
|
|
|
|
// Fingerprint 是用于判断"画面有没有变化"的感知哈希。
|
|
type Fingerprint struct {
|
|
AHash uint64 // 均值哈希
|
|
DHash uint64 // 差分哈希
|
|
Mean uint8 // 平均亮度
|
|
}
|
|
|
|
// grayGrid 把图像缩放到 w×h 的灰度图。
|
|
func grayGrid(src image.Image, w, h int) *image.Gray {
|
|
dst := image.NewGray(image.Rect(0, 0, w, h))
|
|
draw.ApproxBiLinear.Scale(dst, dst.Bounds(), src, src.Bounds(), draw.Src, nil)
|
|
return dst
|
|
}
|
|
|
|
// Of 计算图像指纹。
|
|
func Of(img image.Image) Fingerprint {
|
|
if img == nil {
|
|
return Fingerprint{}
|
|
}
|
|
small := grayGrid(img, 8, 8)
|
|
|
|
var sum, n int
|
|
for _, v := range small.Pix {
|
|
sum += int(v)
|
|
n++
|
|
}
|
|
avg := sum / max(n, 1)
|
|
mean := uint8(min(avg, 255))
|
|
|
|
var aHash uint64
|
|
for i, v := range small.Pix {
|
|
if int(v) > avg {
|
|
aHash |= 1 << uint(i)
|
|
}
|
|
}
|
|
|
|
wide := grayGrid(img, 9, 8)
|
|
var dHash uint64
|
|
for y := 0; y < 8; y++ {
|
|
for x := 0; x < 8; x++ {
|
|
l := wide.GrayAt(x, y).Y
|
|
r := wide.GrayAt(x+1, y).Y
|
|
if l > r {
|
|
dHash |= 1 << uint(y*8+x)
|
|
}
|
|
}
|
|
}
|
|
|
|
return Fingerprint{AHash: aHash, DHash: dHash, Mean: mean}
|
|
}
|
|
|
|
// Distance 返回两个指纹的汉明距离(取两种哈希的较大值,偏向灵敏)。
|
|
func (f Fingerprint) Distance(o Fingerprint) int {
|
|
return max(bitsSet(f.AHash^o.AHash), bitsSet(f.DHash^o.DHash))
|
|
}
|
|
|
|
// Changed 判断画面是否发生了足以触发提问的变化。
|
|
func (f Fingerprint) Changed(o Fingerprint, threshold int) bool {
|
|
if threshold <= 0 {
|
|
threshold = 5
|
|
}
|
|
return f.Distance(o) > threshold
|
|
}
|
|
|
|
func bitsSet(v uint64) int {
|
|
n := 0
|
|
for v != 0 {
|
|
v &= v - 1
|
|
n++
|
|
}
|
|
return n
|
|
}
|