Files
2026-09-09 21:44:05 -05:00

326 lines
7.5 KiB
Go

package frame
import (
"testing"
"time"
"gocv.io/x/gocv"
)
func frameFromMat(t *testing.T, mat *gocv.Mat) Frame {
t.Helper()
return Frame{
PixelBytes: mat.ToBytes(),
Width: uint(mat.Cols()),
Height: uint(mat.Rows()),
GocvImageType: mat.Type(),
Channels: mat.Channels(),
Guid: []byte("guid"),
Timestamp: uint64(time.Now().UnixNano()),
}
}
func TestCompareTo_IdenticalFrames(t *testing.T) {
mat := gocv.NewMatWithSize(4, 4, gocv.MatTypeCV8UC1)
defer mat.Close()
mat.SetTo(gocv.NewScalar(100, 0, 0, 0))
a := frameFromMat(t, &mat)
b := frameFromMat(t, &mat)
changed, err := a.compareTo(&b, 30)
if err != nil {
t.Fatalf("compareTo error: %v", err)
}
if changed != 0 {
t.Errorf("identical frames: got %d changed pixels, want 0", changed)
}
}
func TestCompareTo_AllPixelsChanged(t *testing.T) {
matA := gocv.NewMatWithSize(4, 4, gocv.MatTypeCV8UC1)
defer matA.Close()
matA.SetTo(gocv.NewScalar(0, 0, 0, 0))
matB := gocv.NewMatWithSize(4, 4, gocv.MatTypeCV8UC1)
defer matB.Close()
matB.SetTo(gocv.NewScalar(255, 0, 0, 0))
a := frameFromMat(t, &matA)
b := frameFromMat(t, &matB)
changed, err := a.compareTo(&b, 30)
if err != nil {
t.Fatalf("compareTo error: %v", err)
}
if changed != 16 {
t.Errorf("all pixels changed: got %d, want 16", changed)
}
}
func TestCompareTo_PartialChange(t *testing.T) {
h := 4
w := 4
matA := gocv.NewMatWithSize(h, w, gocv.MatTypeCV8UC1)
defer matA.Close()
matA.SetTo(gocv.NewScalar(0, 0, 0, 0))
matB := gocv.NewMatWithSize(h, w, gocv.MatTypeCV8UC1)
defer matB.Close()
matB.SetTo(gocv.NewScalar(0, 0, 0, 0))
matB.SetUCharAt(0, 0, 200)
matB.SetUCharAt(0, 1, 200)
matB.SetUCharAt(1, 0, 200)
a := frameFromMat(t, &matA)
b := frameFromMat(t, &matB)
changed, err := a.compareTo(&b, 30)
if err != nil {
t.Fatalf("compareTo error: %v", err)
}
if changed != 3 {
t.Errorf("partial change: got %d, want 3", changed)
}
}
func TestCompareTo_ThresholdFiltersSmallDiffs(t *testing.T) {
h := 2
w := 2
matA := gocv.NewMatWithSize(h, w, gocv.MatTypeCV8UC1)
defer matA.Close()
matA.SetTo(gocv.NewScalar(100, 0, 0, 0))
matB := gocv.NewMatWithSize(h, w, gocv.MatTypeCV8UC1)
defer matB.Close()
matB.SetTo(gocv.NewScalar(100, 0, 0, 0))
matB.SetUCharAt(0, 0, 101)
matB.SetUCharAt(0, 1, 131)
a := frameFromMat(t, &matA)
b := frameFromMat(t, &matB)
changed, err := a.compareTo(&b, 30)
if err != nil {
t.Fatalf("compareTo error: %v", err)
}
if changed != 1 {
t.Errorf("threshold: got %d, want 1 (only pixel diff >30 should count)", changed)
}
}
func TestCompareTo_MultiChannel(t *testing.T) {
matA := gocv.NewMatWithSize(2, 2, gocv.MatTypeCV8UC3)
defer matA.Close()
matA.SetTo(gocv.NewScalar(0, 0, 0, 0))
matB := gocv.NewMatWithSize(2, 2, gocv.MatTypeCV8UC3)
defer matB.Close()
matB.SetTo(gocv.NewScalar(0, 0, 0, 0))
// Set all channels at (0,0) to 255 so grayscale conversion keeps a high value
matB.SetUCharAt(0, 0, 255)
matB.SetUCharAt(0, 1, 255)
matB.SetUCharAt(0, 2, 255)
a := frameFromMat(t, &matA)
b := frameFromMat(t, &matB)
changed, err := a.compareTo(&b, 30)
if err != nil {
t.Fatalf("compareTo error: %v", err)
}
if changed == 0 {
t.Error("multi-channel: expected >0 changed pixels")
}
}
func TestCompareTo_DimensionMismatch(t *testing.T) {
matA := gocv.NewMatWithSize(4, 4, gocv.MatTypeCV8UC1)
defer matA.Close()
matB := gocv.NewMatWithSize(8, 8, gocv.MatTypeCV8UC1)
defer matB.Close()
a := frameFromMat(t, &matA)
b := frameFromMat(t, &matB)
_, err := a.compareTo(&b, 30)
if err == nil {
t.Error("expected error for dimension mismatch, got nil")
}
}
func TestCompareTo_ChannelMismatch(t *testing.T) {
matA := gocv.NewMatWithSize(4, 4, gocv.MatTypeCV8UC1)
defer matA.Close()
matB := gocv.NewMatWithSize(4, 4, gocv.MatTypeCV8UC3)
defer matB.Close()
a := frameFromMat(t, &matA)
b := frameFromMat(t, &matB)
_, err := a.compareTo(&b, 30)
if err == nil {
t.Error("expected error for channel mismatch, got nil")
}
}
func TestCompareTo_IdenticalPixelData(t *testing.T) {
a := Frame{
PixelBytes: []byte{0, 0, 0, 0},
Width: 2,
Height: 2,
GocvImageType: gocv.MatTypeCV8UC1,
Channels: 1,
}
b := Frame{
PixelBytes: []byte{0, 0, 0, 0},
Width: 2,
Height: 2,
GocvImageType: gocv.MatTypeCV8UC1,
Channels: 1,
}
changed, err := a.compareTo(&b, 30)
if err != nil {
t.Fatalf("compareTo error: %v", err)
}
if changed != 0 {
t.Errorf("identical pixel data: got %d changed, want 0", changed)
}
}
func TestCountChangedPixels_Basic(t *testing.T) {
mat := gocv.NewMatWithSize(4, 4, gocv.MatTypeCV8UC1)
defer mat.Close()
mat.SetTo(gocv.NewScalar(0, 0, 0, 0))
bytes0 := mat.ToBytes()
mat.SetTo(gocv.NewScalar(255, 0, 0, 0))
bytes1 := mat.ToBytes()
mat.SetTo(gocv.NewScalar(0, 0, 0, 0))
bytes2 := mat.ToBytes()
clp := Clip{
PixelMats: [][]byte{bytes0, bytes1, bytes2},
Width: 4,
Height: 4,
Types: gocv.MatTypeCV8UC1,
Channels: 1,
Guids: [][]byte{{0}, {1}, {2}},
Timestamps: []uint64{0, 1, 2},
}
clp.CountChangedPixels(30)
if len(clp.Comparisons) != 2 {
t.Fatalf("got %d comparisons, want 2", len(clp.Comparisons))
}
if clp.Comparisons[0].PixelsChanged != 16 {
t.Errorf("pair 0->1: got %d changed, want 16", clp.Comparisons[0].PixelsChanged)
}
if clp.Comparisons[1].PixelsChanged != 16 {
t.Errorf("pair 1->2: got %d changed, want 16", clp.Comparisons[1].PixelsChanged)
}
}
func TestCountChangedPixels_SingleFrame(t *testing.T) {
clp := Clip{
PixelMats: [][]byte{{0, 0, 0, 0}},
Width: 2,
Height: 2,
Types: gocv.MatTypeCV8UC1,
Channels: 1,
Guids: [][]byte{{0}},
Timestamps: []uint64{0},
}
clp.CountChangedPixels(30)
if len(clp.Comparisons) != 0 {
t.Errorf("got %d comparisons, want 0", len(clp.Comparisons))
}
}
func TestCountChangedPixels_EmptyClip(t *testing.T) {
clp := Clip{}
clp.CountChangedPixels(30)
if len(clp.Comparisons) != 0 {
t.Errorf("got %d comparisons, want 0", len(clp.Comparisons))
}
}
func TestCountChangedPixels_ResetsComparisons(t *testing.T) {
clp := Clip{
PixelMats: [][]byte{{0}},
Width: 1,
Height: 1,
Channels: 1,
Guids: [][]byte{{0}},
Timestamps: []uint64{0},
Comparisons: []Comparison{{PixelsChanged: 999}},
}
clp.CountChangedPixels(30)
if len(clp.Comparisons) != 0 {
t.Errorf("expected comparisons to be reset, got %d", len(clp.Comparisons))
}
}
func TestCountChangedPixels_ThresholdParameter(t *testing.T) {
matA := gocv.NewMatWithSize(2, 2, gocv.MatTypeCV8UC1)
defer matA.Close()
matA.SetTo(gocv.NewScalar(100, 0, 0, 0))
matB := gocv.NewMatWithSize(2, 2, gocv.MatTypeCV8UC1)
defer matB.Close()
matB.SetTo(gocv.NewScalar(100, 0, 0, 0))
matB.SetUCharAt(0, 0, 101)
matB.SetUCharAt(0, 1, 161)
clp := Clip{
PixelMats: [][]byte{matA.ToBytes(), matB.ToBytes()},
Width: 2,
Height: 2,
Types: gocv.MatTypeCV8UC1,
Channels: 1,
Guids: [][]byte{{0}, {1}},
Timestamps: []uint64{0, 1},
}
clp.CountChangedPixels(60)
if clp.Comparisons[0].PixelsChanged != 1 {
t.Errorf("threshold 60: got %d, want 1 (only diff >60 counts)", clp.Comparisons[0].PixelsChanged)
}
}
func TestComparison_Fields(t *testing.T) {
cmp := Comparison{
Guid1: []byte("a"),
Guid2: []byte("b"),
PixelsChanged: 42,
Threshold: 30,
}
if string(cmp.Guid1) != "a" || string(cmp.Guid2) != "b" {
t.Error("Guid fields mismatch")
}
if cmp.PixelsChanged != 42 {
t.Errorf("PixelsChanged = %d, want 42", cmp.PixelsChanged)
}
if cmp.Threshold != 30 {
t.Errorf("Threshold = %d, want 30", cmp.Threshold)
}
}