Files
gocv/video_test.go
2025-07-06 19:07:46 +02:00

446 lines
10 KiB
Go

//go:build !gocv_specific_modules || (gocv_specific_modules && gocv_video)
package gocv
import (
"image"
"image/color"
"math"
"os"
"testing"
)
func TestMOG2(t *testing.T) {
img := IMRead("images/face.jpg", IMReadColor)
if img.Empty() {
t.Error("Invalid Mat in MOG2 test")
}
defer img.Close()
dst := NewMat()
defer dst.Close()
mog2 := NewBackgroundSubtractorMOG2()
defer mog2.Close()
mog2.Apply(img, &dst)
if dst.Empty() {
t.Error("Error in TestMOG2 test")
}
}
func TestMOG2ApplyWithParams(t *testing.T) {
img := IMRead("images/face.jpg", IMReadColor)
if img.Empty() {
t.Error("Invalid Mat in MOG2 test")
}
defer img.Close()
dst := NewMat()
defer dst.Close()
mog2 := NewBackgroundSubtractorMOG2()
defer mog2.Close()
mog2.ApplyWithLearningRate(img, &dst, 0.01)
if dst.Empty() {
t.Error("Error in TestMOG2 test")
}
}
func TestMOG2WithParams(t *testing.T) {
img := IMRead("images/face.jpg", IMReadColor)
if img.Empty() {
t.Error("Invalid Mat in MOG2 test")
}
defer img.Close()
dst := NewMat()
defer dst.Close()
mog2 := NewBackgroundSubtractorMOG2WithParams(250, 8, false)
defer mog2.Close()
mog2.Apply(img, &dst)
if dst.Empty() {
t.Error("Error in TestMOG2WithParams test")
}
}
func TestMOG2WithParamsWithLR(t *testing.T) {
img := IMRead("images/face.jpg", IMReadColor)
if img.Empty() {
t.Error("Invalid Mat in MOG2 test")
}
defer img.Close()
dst := NewMat()
defer dst.Close()
mog2 := NewBackgroundSubtractorMOG2WithParams(250, 8, false)
defer mog2.Close()
mog2.ApplyWithLearningRate(img, &dst, 0.0)
if dst.Empty() {
t.Error("Error in TestMOG2WithParams test")
}
}
func TestKNN(t *testing.T) {
img := IMRead("images/face.jpg", IMReadColor)
if img.Empty() {
t.Error("Invalid Mat in KNN test")
}
defer img.Close()
dst := NewMat()
defer dst.Close()
knn := NewBackgroundSubtractorKNN()
defer knn.Close()
knn.Apply(img, &dst)
if dst.Empty() {
t.Error("Error in TestKNN test")
}
}
func TestKNNWithParams(t *testing.T) {
img := IMRead("images/face.jpg", IMReadColor)
if img.Empty() {
t.Error("Invalid Mat in KNN test")
}
defer img.Close()
dst := NewMat()
defer dst.Close()
knn := NewBackgroundSubtractorKNNWithParams(250, 200, false)
defer knn.Close()
knn.Apply(img, &dst)
if dst.Empty() {
t.Error("Error in TestKNNWithParams test")
}
}
func TestCalcOpticalFlowFarneback(t *testing.T) {
img1 := IMRead("images/face.jpg", IMReadColor)
if img1.Empty() {
t.Error("Invalid Mat in CalcOpticalFlowFarneback test")
}
defer img1.Close()
dest := NewMat()
defer dest.Close()
CvtColor(img1, &dest, ColorBGRAToGray)
img2 := dest.Clone()
defer img2.Close()
flow := NewMat()
defer flow.Close()
CalcOpticalFlowFarneback(dest, img2, &flow, 0.4, 1, 12, 2, 8, 1.2, 0)
if flow.Empty() {
t.Error("Error in CalcOpticalFlowFarneback test")
}
if flow.Rows() != 480 {
t.Errorf("Invalid CalcOpticalFlowFarneback test rows: %v", flow.Rows())
}
if flow.Cols() != 640 {
t.Errorf("Invalid CalcOpticalFlowFarneback test cols: %v", flow.Cols())
}
}
func TestCalcOpticalFlowPyrLK(t *testing.T) {
img1 := IMRead("images/face.jpg", IMReadColor)
if img1.Empty() {
t.Error("Invalid Mat in CalcOpticalFlowPyrLK test")
}
defer img1.Close()
dest := NewMat()
defer dest.Close()
CvtColor(img1, &dest, ColorBGRAToGray)
img2 := dest.Clone()
defer img2.Close()
prevPts := NewMat()
defer prevPts.Close()
nextPts := NewMat()
defer nextPts.Close()
status := NewMat()
defer status.Close()
err := NewMat()
defer err.Close()
corners := NewMat()
defer corners.Close()
GoodFeaturesToTrack(dest, &corners, 500, 0.01, 10)
tc := NewTermCriteria(Count|EPS, 20, 0.03)
CornerSubPix(dest, &corners, image.Pt(10, 10), image.Pt(-1, -1), tc)
CalcOpticalFlowPyrLK(dest, img2, corners, nextPts, &status, &err)
if status.Empty() {
t.Error("Error in CalcOpticalFlowPyrLK test")
}
if status.Rows() != 323 {
t.Errorf("Invalid CalcOpticalFlowPyrLK test rows: %v", status.Rows())
}
if status.Cols() != 1 {
t.Errorf("Invalid CalcOpticalFlowPyrLK test cols: %v", status.Cols())
}
}
func TestCalcOpticalFlowPyrLKWithParams(t *testing.T) {
img1 := IMRead("images/face.jpg", IMReadColor)
if img1.Empty() {
t.Error("Invalid Mat in CalcOpticalFlowPyrLK test")
}
defer img1.Close()
dest := NewMat()
defer dest.Close()
CvtColor(img1, &dest, ColorBGRAToGray)
img2 := dest.Clone()
defer img2.Close()
prevPts := NewMat()
defer prevPts.Close()
nextPts := NewMat()
defer nextPts.Close()
status := NewMat()
defer status.Close()
err := NewMat()
defer err.Close()
corners := NewMat()
defer corners.Close()
GoodFeaturesToTrack(dest, &corners, 500, 0.01, 10)
tc := NewTermCriteria(Count|EPS, 30, 0.03)
CornerSubPix(dest, &corners, image.Pt(10, 10), image.Pt(-1, -1), tc)
CalcOpticalFlowPyrLKWithParams(dest, img2, corners, nextPts, &status, &err, image.Pt(21, 21), 3, tc, 0, 0.0001)
if status.Empty() {
t.Error("Error in CalcOpticalFlowPyrLK test")
}
if status.Rows() != 323 {
t.Errorf("Invalid CalcOpticalFlowPyrLK test rows: %v", status.Rows())
}
if status.Cols() != 1 {
t.Errorf("Invalid CalcOpticalFlowPyrLK test cols: %v", status.Cols())
}
}
func computeRMS(mat1 Mat, mat2 Mat) float64 {
var rms float64
for y := 0; y < mat1.Rows(); y++ {
for x := 0; x < mat1.Cols(); x++ {
diff := float64(mat1.GetFloatAt(y, x) - mat2.GetFloatAt(y, x))
rms += diff * diff
}
}
rms /= float64(mat1.Rows() * mat1.Cols())
return math.Sqrt(rms)
}
func TestFindTransformECC(t *testing.T) {
img := IMRead("images/face.jpg", IMReadGrayScale)
if img.Empty() {
t.Error("Invalid Mat in FindTransformECC test")
}
defer img.Close()
testImg := NewMat()
defer testImg.Close()
Resize(img, &testImg, image.Point{216, 216}, 0, 0, InterpolationLinear)
translationGround := Eye(2, 3, MatTypeCV32F)
defer translationGround.Close()
translationGround.SetFloatAt(0, 2, 11.4159)
translationGround.SetFloatAt(1, 2, 17.1828)
warpedImage := NewMat()
defer warpedImage.Close()
WarpAffineWithParams(testImg, &warpedImage, translationGround, image.Point{200, 200}, InterpolationLinear+WarpInverseMap, BorderConstant, color.RGBA{})
mapTranslation := Eye(2, 3, MatTypeCV32F)
defer mapTranslation.Close()
eecIterations := 50
// Negative value means that ECC_Iterations will be executed.
var eecEpsilon float64 = -1
criteria := NewTermCriteria(Count+EPS, eecIterations, eecEpsilon)
inputMask := NewMat()
defer inputMask.Close()
gaussFiltSize := 5
FindTransformECC(warpedImage, testImg, &mapTranslation, MotionTranslation, criteria, inputMask, gaussFiltSize)
maxRMSECC := 0.1
rms := computeRMS(mapTranslation, translationGround)
if rms > maxRMSECC {
t.Errorf("FindTransformECC RMS = %f", rms)
}
}
func BaseTestTracker(t *testing.T, tracker Tracker, name string) {
if tracker == nil {
t.Error("TestTracker " + name + " should not be nil")
}
img := IMRead("./images/face.jpg", 1)
if img.Empty() {
t.Error("TestTracker " + name + " input img failed to load")
}
defer img.Close()
rect := image.Rect(250, 150, 250+200, 150+250)
init := tracker.Init(img, rect)
if !init {
t.Error("TestTracker " + name + " failed in Init")
}
_, ok := tracker.Update(img)
if !ok {
t.Error("TestTracker " + name + " lost object in Update")
}
}
func TestSingleTrackers(t *testing.T) {
goturnPath := os.Getenv("GOCV_TRACKER_GOTURN_TEST_FILES")
if goturnPath == "" {
goturnPath = "./testdata"
}
vitPath := os.Getenv("GOCV_ONNX_TEST_FILES")
if vitPath == "" {
vitPath = "./testdata"
}
tab := []struct {
name string
tracker Tracker
}{
{"MIL", NewTrackerMIL()},
{"GOTURN", NewTrackerGOTURNWithParams(goturnPath+"/goturn.caffemodel", goturnPath+"/goturn.prototxt")},
{"Vit", NewTrackerVitWithParams(vitPath + "/object_tracking_vittrack_2023sep.onnx")},
}
for _, test := range tab {
func() {
defer test.tracker.Close()
BaseTestTracker(t, test.tracker, test.name)
}()
}
}
func TestKalmanFilter(t *testing.T) {
// Basic test with default constructor.
kf := NewKalmanFilter(2, 1)
kf.Init(2, 1)
measurement := Zeros(1, 1, MatTypeCV32F)
prediction := kf.Predict()
statePost := kf.Correct(measurement)
statePost.Close()
prediction.Close()
measurement.Close()
kf.Close()
// Basic test with param constructor.
kf = NewKalmanFilterWithParams(2, 1, 1, MatTypeCV32F)
control := Ones(1, 1, MatTypeCV32F)
measurement = Ones(1, 1, MatTypeCV32F)
prediction = kf.PredictWithParams(control)
statePost = kf.Correct(measurement)
statePost.Close()
prediction.Close()
measurement.Close()
control.Close()
kf.Close()
}
func TestKalmanFilter_Getters(t *testing.T) {
kf := NewKalmanFilterWithParams(2, 1, 1, MatTypeCV32F)
getterTests := []struct {
desc string
f func() Mat
}{
{desc: "GetStatePre()", f: kf.GetStatePre},
{desc: "GetStatePost()", f: kf.GetStatePost},
{desc: "GetTransitionMatrix()", f: kf.GetTransitionMatrix},
{desc: "GetControlMatrix()", f: kf.GetControlMatrix},
{desc: "GetMeasurementMatrix()", f: kf.GetMeasurementMatrix},
{desc: "GetProcessNoiseCov()", f: kf.GetProcessNoiseCov},
{desc: "GetMeasurementNoiseCov()", f: kf.GetMeasurementNoiseCov},
{desc: "GetErrorCovPre()", f: kf.GetErrorCovPre},
{desc: "GetGain()", f: kf.GetGain},
{desc: "GetErrorCovPost()", f: kf.GetErrorCovPost},
{desc: "GetTemp1()", f: kf.GetTemp1},
{desc: "GetTemp2()", f: kf.GetTemp2},
{desc: "GetTemp3()", f: kf.GetTemp3},
{desc: "GetTemp4()", f: kf.GetTemp4},
{desc: "GetTemp5()", f: kf.GetTemp5},
}
for _, test := range getterTests {
t.Run(test.desc, func(t *testing.T) {
if got := test.f(); got.Empty() {
t.Errorf("%v: returned empty, want non-Empty", test.desc)
} else {
got.Close()
}
})
}
kf.Close()
}
func TestKalmanFilter_Setters(t *testing.T) {
kf := NewKalmanFilter(2, 1)
tests := []struct {
desc string
f func(Mat)
}{
{desc: "SetStatePre()", f: kf.SetStatePre},
{desc: "SetStatePost()", f: kf.SetStatePost},
{desc: "SetTransitionMatrix()", f: kf.SetTransitionMatrix},
{desc: "SetControlMatrix()", f: kf.SetControlMatrix},
{desc: "SetMeasurementMatrix()", f: kf.SetMeasurementMatrix},
{desc: "SetProcessNoiseCov()", f: kf.SetProcessNoiseCov},
{desc: "SetMeasurementNoiseCov()", f: kf.SetMeasurementNoiseCov},
{desc: "SetErrorCovPre()", f: kf.SetErrorCovPre},
{desc: "SetGain()", f: kf.SetGain},
{desc: "SetErrorCovPost()", f: kf.SetErrorCovPost},
}
for _, test := range tests {
t.Run(test.desc, func(t *testing.T) {
testMat := Ones(2, 1, MatTypeCV32F)
// Just run this to make sure the execution doesn't fail.
test.f(testMat)
testMat.Close()
})
}
kf.Close()
}