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...) }