Files
oko_public/frame/motion.go
T
2026-09-09 21:44:05 -05:00

110 lines
2.7 KiB
Go

package frame
import (
"errors"
"fmt"
"log"
"gocv.io/x/gocv"
)
type Comparison struct {
Guid1, Guid2 []byte
PixelsChanged int
Threshold uint8
}
func (f *Frame) compareTo(next *Frame, threshold uint8) (int, error) {
if f.Width != next.Width || f.Height != next.Height || f.Channels != next.Channels {
return 0, fmt.Errorf("frame dimension mismatch: (%dx%dx%d) vs (%dx%dx%d)",
f.Width, f.Height, f.Channels, next.Width, next.Height, next.Channels)
}
matA, err := f.ToMat()
if err != nil {
return 0, fmt.Errorf("ToMat error: %w", err)
}
defer matA.Close()
matB, err := next.ToMat()
if err != nil {
return 0, fmt.Errorf("ToMat error: %w", err)
}
defer matB.Close()
diff := gocv.NewMat()
defer diff.Close()
gocv.AbsDiff(matA, matB, &diff)
gray := gocv.NewMat()
defer gray.Close()
if diff.Channels() > 1 {
gocv.CvtColor(diff, &gray, gocv.ColorBGRToGray)
} else {
diff.CopyTo(&gray)
}
thresh := gocv.NewMat()
defer thresh.Close()
gocv.Threshold(gray, &thresh, float32(threshold), 255, gocv.ThresholdBinary)
return gocv.CountNonZero(thresh), nil
}
// GetHighestMotion returns the highest percentage of changed pixels between
// any two consecutive frames in the clip. It assumes CountChangedPixels has
// already populated Comparisons (i.e. motion has already been determined).
// The result is an integer percentage in the range [0, 100]. Returns 0 if
// there are no comparisons or the frame dimensions are unknown.
func (c *Clip) GetHighestMotion() int {
totalPixels := int(c.Width) * int(c.Height)
if totalPixels <= 0 || len(c.Comparisons) == 0 {
return 0
}
highest := 0
for _, cmp := range c.Comparisons {
percent := int((int64(cmp.PixelsChanged) * 100) / int64(totalPixels))
if percent > highest {
highest = percent
}
}
return highest
}
func (c *Clip) CountChangedPixels(threshold uint8) error {
c.Comparisons = nil
var errs []error
for i := 0; i < len(c.PixelMats)-1; i++ {
a := Frame{
PixelBytes: c.PixelMats[i],
Width: c.Width,
Height: c.Height,
GocvImageType: c.Types,
Channels: c.Channels,
Guid: c.Guids[i],
}
b := Frame{
PixelBytes: c.PixelMats[i+1],
Width: c.Width,
Height: c.Height,
GocvImageType: c.Types,
Channels: c.Channels,
Guid: c.Guids[i+1],
}
changed, err := a.compareTo(&b, threshold)
if err != nil {
log.Printf("CountChangedPixels: frame pair %d/%d: %v", i, i+1, err)
errs = append(errs, fmt.Errorf("pair %d/%d: %w", i, i+1, err))
continue
}
c.Comparisons = append(c.Comparisons, Comparison{
Guid1: c.Guids[i],
Guid2: c.Guids[i+1],
PixelsChanged: changed,
Threshold: threshold,
})
}
return errors.Join(errs...)
}