326 lines
7.5 KiB
Go
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)
|
|
}
|
|
}
|