Files
vphash/vphash.go
T

75 lines
1.4 KiB
Go
Raw Normal View History

2026-06-07 00:22:19 +03:00
package vphash
import (
"image"
"time"
"github.com/corona10/goimagehash"
)
// Decoder is the minimal interface VPHash needs.
// Implement this for any video source: MPEG, AV1, H.264, live stream.
type Decoder interface {
Next() (img image.Image, t time.Duration, ok bool)
}
type Entry struct {
Start time.Duration
End time.Duration
Hash *goimagehash.ImageHash
}
// Scan reads frames and emits scene entries as they're detected.
// The channel closes when the video ends.
func Scan(dec Decoder, maxDistance, minFrames int) <-chan Entry {
ch := make(chan Entry)
if maxDistance == 0 {
maxDistance = 10
}
if minFrames == 0 {
minFrames = 24
}
go func() {
defer close(ch)
var (
hashes []*goimagehash.ImageHash
times []time.Duration
sceneStart int
)
for {
img, t, ok := dec.Next()
if !ok {
if len(hashes)-sceneStart >= minFrames {
mid := (sceneStart + len(hashes) - 1) / 2
ch <- Entry{Start: times[sceneStart], End: t, Hash: hashes[mid]}
}
return
}
h, _ := goimagehash.PerceptionHash(img)
hashes = append(hashes, h)
times = append(times, t)
if len(hashes) == 1 {
continue
}
dist, _ := hashes[len(hashes)-2].Distance(h)
if dist > maxDistance {
if len(hashes)-1-sceneStart >= minFrames {
mid := (sceneStart + len(hashes) - 2) / 2
ch <- Entry{Start: times[sceneStart], End: t, Hash: hashes[mid]}
}
sceneStart = len(hashes) - 1
}
}
}()
return ch
}