Files
gocv/calib3d_test.go
2023-10-17 21:40:32 +02:00

671 lines
15 KiB
Go

package gocv
import (
"fmt"
"image"
"image/color"
"math"
"testing"
)
func TestFisheyeUndistorImage(t *testing.T) {
img := IMRead("images/fisheye_sample.jpg", IMReadUnchanged)
if img.Empty() {
t.Error("Invalid read of Mat test")
return
}
defer img.Close()
dest := NewMat()
defer dest.Close()
k := NewMatWithSize(3, 3, MatTypeCV64F)
defer k.Close()
k.SetDoubleAt(0, 0, 689.21)
k.SetDoubleAt(0, 1, 0)
k.SetDoubleAt(0, 2, 1295.56)
k.SetDoubleAt(1, 0, 0)
k.SetDoubleAt(1, 1, 690.48)
k.SetDoubleAt(1, 2, 942.17)
k.SetDoubleAt(2, 0, 0)
k.SetDoubleAt(2, 1, 0)
k.SetDoubleAt(2, 2, 1)
d := NewMatWithSize(1, 4, MatTypeCV64F)
defer d.Close()
d.SetDoubleAt(0, 0, 0)
d.SetDoubleAt(0, 1, 0)
d.SetDoubleAt(0, 2, 0)
d.SetDoubleAt(0, 3, 0)
FisheyeUndistortImage(img, &dest, k, d)
if dest.Empty() {
t.Error("final image is empty")
return
}
// IMWrite("images/fisheye_sample-u.jpg", dest)
}
func TestFisheyeUndistorImageWithParams(t *testing.T) {
img := IMRead("images/fisheye_sample.jpg", IMReadUnchanged)
if img.Empty() {
t.Error("Invalid read of Mat test")
return
}
defer img.Close()
dest := NewMat()
defer dest.Close()
k := NewMatWithSize(3, 3, MatTypeCV64F)
defer k.Close()
k.SetDoubleAt(0, 0, 689.21)
k.SetDoubleAt(0, 1, 0)
k.SetDoubleAt(0, 2, 1295.56)
k.SetDoubleAt(1, 0, 0)
k.SetDoubleAt(1, 1, 690.48)
k.SetDoubleAt(1, 2, 942.17)
k.SetDoubleAt(2, 0, 0)
k.SetDoubleAt(2, 1, 0)
k.SetDoubleAt(2, 2, 1)
d := NewMatWithSize(1, 4, MatTypeCV64F)
defer d.Close()
d.SetDoubleAt(0, 0, 0)
d.SetDoubleAt(0, 1, 0)
d.SetDoubleAt(0, 2, 0)
d.SetDoubleAt(0, 3, 0)
knew := NewMat()
defer knew.Close()
k.CopyTo(&knew)
knew.SetDoubleAt(0, 0, 0.4*k.GetDoubleAt(0, 0))
knew.SetDoubleAt(1, 1, 0.4*k.GetDoubleAt(1, 1))
size := image.Point{dest.Rows(), dest.Cols()}
FisheyeUndistortImageWithParams(img, &dest, k, d, knew, size)
if dest.Empty() {
t.Error("final image is empty")
return
}
// IMWrite("images/fisheye_sample-up.jpg", dest)
}
func TestInitUndistortRectifyMap(t *testing.T) {
img := IMRead("images/distortion.jpg", IMReadUnchanged)
if img.Empty() {
t.Error("Invalid read of Mat test")
return
}
defer img.Close()
dest := NewMat()
defer dest.Close()
k := NewMatWithSize(3, 3, MatTypeCV64F)
defer k.Close()
k.SetDoubleAt(0, 0, 842.0261028)
k.SetDoubleAt(0, 1, 0)
k.SetDoubleAt(0, 2, 667.7569792)
k.SetDoubleAt(1, 0, 0)
k.SetDoubleAt(1, 1, 707.3668897)
k.SetDoubleAt(1, 2, 385.56476464)
k.SetDoubleAt(2, 0, 0)
k.SetDoubleAt(2, 1, 0)
k.SetDoubleAt(2, 2, 1)
d := NewMatWithSize(1, 5, MatTypeCV64F)
defer d.Close()
d.SetDoubleAt(0, 0, -3.65584802e-01)
d.SetDoubleAt(0, 1, 1.41555815e-01)
d.SetDoubleAt(0, 2, -2.62985819e-03)
d.SetDoubleAt(0, 3, 2.05841873e-04)
d.SetDoubleAt(0, 4, -2.35021914e-02)
//FisheyeUndistortImage(img, &dest, k, d)
//img.Reshape()
newC, roi := GetOptimalNewCameraMatrixWithParams(k, d, image.Point{X: img.Cols(), Y: img.Rows()}, (float64)(1), image.Point{X: img.Cols(), Y: img.Rows()}, false)
if newC.Empty() {
t.Error("final image is empty")
return
}
fmt.Printf("roi:%+v\n", roi)
defer newC.Close()
r := NewMat()
defer r.Close()
mapx := NewMat()
defer mapx.Close()
mapy := NewMat()
defer mapy.Close()
//dest := NewMat()
InitUndistortRectifyMap(k, d, r, newC, image.Point{X: img.Cols(), Y: img.Rows()}, 5, mapx, mapy)
Remap(img, &dest, &mapx, &mapy, InterpolationDefault, BorderConstant, color.RGBA{0, 0, 0, 0})
flg := IMWrite("images/distortion-correct.jpg", dest)
if !flg {
t.Error("IMWrite failed")
}
}
func TestUndistort(t *testing.T) {
img := IMRead("images/distortion.jpg", IMReadUnchanged)
if img.Empty() {
t.Error("Invalid read of Mat test")
return
}
defer img.Close()
dest := img.Clone()
defer dest.Close()
k := NewMatWithSize(3, 3, MatTypeCV64F)
defer k.Close()
k.SetDoubleAt(0, 0, 689.21)
k.SetDoubleAt(0, 1, 0)
k.SetDoubleAt(0, 2, 1295.56)
k.SetDoubleAt(1, 0, 0)
k.SetDoubleAt(1, 1, 690.48)
k.SetDoubleAt(1, 2, 942.17)
k.SetDoubleAt(2, 0, 0)
k.SetDoubleAt(2, 1, 0)
k.SetDoubleAt(2, 2, 1)
d := NewMatWithSize(1, 4, MatTypeCV64F)
defer d.Close()
d.SetDoubleAt(0, 0, 0)
d.SetDoubleAt(0, 1, 0)
d.SetDoubleAt(0, 2, 0)
d.SetDoubleAt(0, 3, 0)
knew := NewMat()
defer knew.Close()
k.CopyTo(&knew)
knew.SetDoubleAt(0, 0, 0.5*k.GetDoubleAt(0, 0))
knew.SetDoubleAt(1, 1, 0.5*k.GetDoubleAt(1, 1))
Undistort(img, &dest, k, d, knew)
if dest.Empty() {
t.Error("final image is empty")
return
}
//IMWrite("images/distortion_up.jpg", dest)
}
func TestUndistortPoint(t *testing.T) {
k := NewMatWithSize(3, 3, MatTypeCV64F)
defer k.Close()
k.SetDoubleAt(0, 0, 1094.7249578198823)
k.SetDoubleAt(0, 1, 0)
k.SetDoubleAt(0, 2, 959.4907612030962)
k.SetDoubleAt(1, 0, 0)
k.SetDoubleAt(1, 1, 1094.9945708128778)
k.SetDoubleAt(1, 2, 536.4566143451868)
k.SetDoubleAt(2, 0, 0)
k.SetDoubleAt(2, 1, 0)
k.SetDoubleAt(2, 2, 1)
d := NewMatWithSize(1, 4, MatTypeCV64F)
defer d.Close()
d.SetDoubleAt(0, 0, -0.05207412392075069)
d.SetDoubleAt(0, 1, -0.089168300192224)
d.SetDoubleAt(0, 2, 0.10465607695792184)
d.SetDoubleAt(0, 3, -0.045693446831115585)
r := NewMat()
defer r.Close()
// transform 3 points in one go
src := NewMatWithSize(3, 1, MatTypeCV64FC2)
defer src.Close()
dst := NewMatWithSize(3, 1, MatTypeCV64FC2)
defer dst.Close()
// This camera matrix is 1920x1080. Points where x < 960 and y < 540 should move toward the top left (x and y get smaller)
// The centre point should be mostly unchanged
// Points where x > 960 and y > 540 should move toward the bottom right (x and y get bigger)
// The index being used for col here is actually the channel (i.e. the point's x/y dimensions)
// (since there's only 1 column so the formula: (colNumber * numChannels + channelNumber) reduces to
// (0 * 2) + channelNumber
// so col = 0 is the x coordinate and col = 1 is the y coordinate
src.SetDoubleAt(0, 0, 480)
src.SetDoubleAt(0, 1, 270)
src.SetDoubleAt(1, 0, 960)
src.SetDoubleAt(1, 1, 540)
src.SetDoubleAt(2, 0, 1920)
src.SetDoubleAt(2, 1, 1080)
UndistortPoints(src, &dst, k, d, r, k)
if dst.GetDoubleAt(0, 0) >= 480 || dst.GetDoubleAt(0, 1) >= 270 {
t.Error("undistortion expected top left point to move further up and left")
return
}
if math.Round(dst.GetDoubleAt(1, 0)) != 960 || math.Round(dst.GetDoubleAt(1, 1)) != 540 {
t.Error("undistortion expected centre point to be nearly unchanged")
return
}
if dst.GetDoubleAt(2, 0) != 1920 || dst.GetDoubleAt(2, 1) != 1080 {
t.Error("undistortion expected bottom right corner to be unchanged")
return
}
}
func TestFisheyeUndistortPoint(t *testing.T) {
k := NewMatWithSize(3, 3, MatTypeCV64F)
defer k.Close()
k.SetDoubleAt(0, 0, 1094.7249578198823)
k.SetDoubleAt(0, 1, 0)
k.SetDoubleAt(0, 2, 959.4907612030962)
k.SetDoubleAt(1, 0, 0)
k.SetDoubleAt(1, 1, 1094.9945708128778)
k.SetDoubleAt(1, 2, 536.4566143451868)
k.SetDoubleAt(2, 0, 0)
k.SetDoubleAt(2, 1, 0)
k.SetDoubleAt(2, 2, 1)
d := NewMatWithSize(1, 4, MatTypeCV64F)
defer d.Close()
d.SetDoubleAt(0, 0, -0.05207412392075069)
d.SetDoubleAt(0, 1, -0.089168300192224)
d.SetDoubleAt(0, 2, 0.10465607695792184)
d.SetDoubleAt(0, 3, -0.045693446831115585)
r := NewMat()
defer r.Close()
// transform 3 points in one go (X and Y values of points go in each channel)
src := NewMatWithSize(3, 1, MatTypeCV64FC2)
defer src.Close()
dst := NewMatWithSize(3, 1, MatTypeCV64FC2)
defer dst.Close()
// This camera matrix is 1920x1080. Points where x < 960 and y < 540 should move toward the top left (x and y get smaller)
// The centre point should be mostly unchanged
// Points where x > 960 and y > 540 should move toward the bottom right (x and y get bigger)
// The index being used for col here is actually the channel (i.e. the point's x/y dimensions)
// (since there's only 1 column so the formula: (colNumber * numChannels + channelNumber) reduces to
// (0 * 2) + channelNumber
// so col = 0 is the x coordinate and col = 1 is the y coordinate
src.SetDoubleAt(0, 0, 480)
src.SetDoubleAt(0, 1, 270)
src.SetDoubleAt(1, 0, 960)
src.SetDoubleAt(1, 1, 540)
src.SetDoubleAt(2, 0, 1440)
src.SetDoubleAt(2, 1, 810)
kNew := NewMat()
defer kNew.Close()
k.CopyTo(&kNew)
kNew.SetDoubleAt(0, 0, 0.4*k.GetDoubleAt(0, 0))
kNew.SetDoubleAt(1, 1, 0.4*k.GetDoubleAt(1, 1))
imgSize := image.Point{X: 1920, Y: 1080}
EstimateNewCameraMatrixForUndistortRectify(k, d, imgSize, r, &kNew, 1, imgSize, 1)
FisheyeUndistortPoints(src, &dst, k, d, r, kNew)
if dst.GetDoubleAt(0, 0) == 0 {
t.Error("expected destination Mat to be populated")
}
}
func TestFindAndDrawChessboard(t *testing.T) {
img := IMRead("images/chessboard_4x6.png", IMReadUnchanged)
if img.Empty() {
t.Error("Invalid read of chessboard image")
return
}
defer img.Close()
corners := NewMat()
defer corners.Close()
found := FindChessboardCorners(img, image.Point{X: 4, Y: 6}, &corners, 0)
if found == false {
t.Error("chessboard pattern not found")
return
}
if corners.Empty() {
t.Error("chessboard pattern not found")
return
}
img2 := NewMatWithSize(150, 150, MatTypeCV8U)
defer img2.Close()
DrawChessboardCorners(&img2, image.Pt(4, 6), corners, true)
if img2.Empty() {
t.Error("Error in DrawChessboardCorners test")
}
}
func TestFindAndDrawChessboardSB(t *testing.T) {
img := IMRead("images/chessboard_4x6.png", IMReadUnchanged)
if img.Empty() {
t.Error("Invalid read of chessboard image")
return
}
defer img.Close()
corners := NewMat()
defer corners.Close()
found := FindChessboardCornersSB(img, image.Point{X: 4, Y: 6}, &corners, 0)
if found == false {
t.Error("chessboard pattern not found")
return
}
if corners.Empty() {
t.Error("chessboard pattern not found")
return
}
img2 := NewMatWithSize(150, 150, MatTypeCV8U)
defer img2.Close()
DrawChessboardCorners(&img2, image.Pt(4, 6), corners, true)
if img2.Empty() {
t.Error("Error in DrawChessboardCorners test")
}
}
func TestFindChessboardCornersSBWithMeta(t *testing.T) {
img := IMRead("images/chessboard_4x6.png", IMReadUnchanged)
if img.Empty() {
t.Error("Invalid read of chessboard image")
return
}
defer img.Close()
corners := NewMat()
defer corners.Close()
meta := NewMat()
defer meta.Close()
found := FindChessboardCornersSBWithMeta(img, image.Point{X: 4, Y: 6}, &corners, 0, &meta)
if found == false {
t.Error("chessboard pattern not found")
return
}
if corners.Empty() {
t.Error("chessboard pattern not found")
return
}
img2 := NewMatWithSize(150, 150, MatTypeCV8U)
defer img2.Close()
DrawChessboardCorners(&img2, image.Pt(4, 6), corners, true)
if img2.Empty() {
t.Error("Error in DrawChessboardCorners test")
}
}
func TestCalibrateCamera(t *testing.T) {
img := IMRead("images/chessboard_4x6_distort.png", IMReadGrayScale)
if img.Empty() {
t.Error("Invalid read of chessboard image")
return
}
defer img.Close()
corners := NewMat()
defer corners.Close()
size := image.Pt(4, 6)
found := FindChessboardCorners(img, size, &corners, 0)
if !found {
t.Error("chessboard pattern not found")
return
}
if corners.Empty() {
t.Error("chessboard pattern not found")
return
}
imagePoints := NewPoint2fVectorFromMat(corners)
defer imagePoints.Close()
objectPoints := NewPoint3fVector()
defer objectPoints.Close()
for j := 0; j < size.Y; j++ {
for i := 0; i < size.X; i++ {
objectPoints.Append(Point3f{
X: float32(100 * i),
Y: float32(100 * j),
Z: 0,
})
}
}
cameraMatrix := NewMat()
defer cameraMatrix.Close()
distCoeffs := NewMat()
defer distCoeffs.Close()
rvecs := NewMat()
defer rvecs.Close()
tvecs := NewMat()
defer tvecs.Close()
objectPointsVector := NewPoints3fVector()
objectPointsVector.Append(objectPoints)
defer objectPointsVector.Close()
imagePointsVector := NewPoints2fVector()
imagePointsVector.Append(imagePoints)
defer imagePointsVector.Close()
CalibrateCamera(
objectPointsVector, imagePointsVector, image.Pt(img.Cols(), img.Rows()),
&cameraMatrix, &distCoeffs, &rvecs, &tvecs, 0,
)
dest := NewMat()
defer dest.Close()
Undistort(img, &dest, cameraMatrix, distCoeffs, cameraMatrix)
target := IMRead("images/chessboard_4x6_distort_correct.png", IMReadGrayScale)
defer target.Close()
xor := NewMat()
defer xor.Close()
// The method for compare is ugly : different pix number < 0.5%
BitwiseXor(dest, target, &xor)
differentPixelsNumber := xor.Sum().Val1
maxDifferentPixelsNumber := float64(img.Cols()*img.Rows()) * 0.005
if differentPixelsNumber > maxDifferentPixelsNumber {
t.Error("the undisorted image not equal the target one:", differentPixelsNumber, "bigger than", maxDifferentPixelsNumber)
}
}
func TestEstimateAffinePartial2D(t *testing.T) {
src := []Point2f{
{0, 0},
{10, 5},
{10, 10},
{5, 10},
}
dst := []Point2f{
{0, 0},
{10, 0},
{10, 10},
{0, 10},
}
pvsrc := NewPoint2fVectorFromPoints(src)
defer pvsrc.Close()
pvdst := NewPoint2fVectorFromPoints(dst)
defer pvdst.Close()
m := EstimateAffinePartial2D(pvsrc, pvdst)
defer m.Close()
if m.Cols() != 3 {
t.Errorf("TestEstimateAffinePartial2D(): unexpected cols = %v, want = %v", m.Cols(), 3)
}
if m.Rows() != 2 {
t.Errorf("TestEstimateAffinePartial2D(): unexpected rows = %v, want = %v", m.Rows(), 2)
}
}
func TestEstimateAffinePartial2DWithParams(t *testing.T) {
src := []Point2f{
{0, 0},
{10, 5},
{10, 10},
{5, 10},
}
dst := []Point2f{
{0, 0},
{10, 0},
{10, 10},
{0, 10},
}
pvsrc := NewPoint2fVectorFromPoints(src)
defer pvsrc.Close()
pvdst := NewPoint2fVectorFromPoints(dst)
defer pvdst.Close()
inliers := NewMat()
defer inliers.Close()
method := 8
ransacProjThreshold := 3.0
maxiters := uint(2000)
confidence := 0.99
refineIters := uint(10)
m := EstimateAffinePartial2DWithParams(pvsrc, pvdst, inliers, method, ransacProjThreshold, maxiters, confidence, refineIters)
defer m.Close()
if m.Cols() != 3 {
t.Errorf("TestEstimateAffinePartial2D(): unexpected cols = %v, want = %v", m.Cols(), 3)
}
if m.Rows() != 2 {
t.Errorf("TestEstimateAffinePartial2D(): unexpected rows = %v, want = %v", m.Rows(), 2)
}
}
func TestEstimateAffine2D(t *testing.T) {
src := []Point2f{
{0, 0},
{10, 5},
{10, 10},
{5, 10},
}
dst := []Point2f{
{0, 0},
{10, 0},
{10, 10},
{0, 10},
}
pvsrc := NewPoint2fVectorFromPoints(src)
defer pvsrc.Close()
pvdst := NewPoint2fVectorFromPoints(dst)
defer pvdst.Close()
m := EstimateAffine2D(pvsrc, pvdst)
defer m.Close()
if m.Cols() != 3 {
t.Errorf("TestEstimateAffine2D(): unexpected cols = %v, want = %v", m.Cols(), 3)
}
if m.Rows() != 2 {
t.Errorf("TestEstimateAffine2D(): unexpected rows = %v, want = %v", m.Rows(), 2)
}
}
func TestEstimateAffine2DWithParams(t *testing.T) {
src := []Point2f{
{0, 0},
{10, 5},
{10, 10},
{5, 10},
}
dst := []Point2f{
{0, 0},
{10, 0},
{10, 10},
{0, 10},
}
pvsrc := NewPoint2fVectorFromPoints(src)
defer pvsrc.Close()
pvdst := NewPoint2fVectorFromPoints(dst)
defer pvdst.Close()
inliers := NewMat()
defer inliers.Close()
method := 8
ransacProjThreshold := 3.0
maxiters := uint(2000)
confidence := 0.99
refineIters := uint(10)
m := EstimateAffine2DWithParams(pvsrc, pvdst, inliers, method, ransacProjThreshold, maxiters, confidence, refineIters)
defer m.Close()
if m.Cols() != 3 {
t.Errorf("TestEstimateAffine2DWithParams(): unexpected cols = %v, want = %v", m.Cols(), 3)
}
if m.Rows() != 2 {
t.Errorf("TestEstimateAffine2DWithParams(): unexpected rows = %v, want = %v", m.Rows(), 2)
}
}