From 453df7cb3bf9c27c192141493450a80105d65c26 Mon Sep 17 00:00:00 2001 From: derktam Date: Mon, 23 Dec 2019 17:14:06 +0900 Subject: [PATCH] add functions (singular value decomposition, multiply between matrices, transpose matrix) (#559) * core: add the following: cv::SVD::compute() Mat.MultiplyMatrix(): Multiply between matrices Mat.T(): transpose matrix --- core.cpp | 8 ++++++ core.go | 11 +++++++++ core.h | 3 +++ core_test.go | 39 +++++++++++++++++++++++++++++ svd.cpp | 5 ++++ svd.go | 14 +++++++++++ svd.h | 18 ++++++++++++++ svd_test.go | 69 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 8 files changed, 167 insertions(+) create mode 100644 svd.cpp create mode 100644 svd.go create mode 100644 svd.h create mode 100644 svd_test.go diff --git a/core.cpp b/core.cpp index bbb692ac..2d312f2b 100644 --- a/core.cpp +++ b/core.cpp @@ -323,6 +323,14 @@ void Mat_DivideFloat(Mat m, float val) { *m /= val; } +Mat Mat_MultiplyMatrix(Mat x, Mat y) { + return new cv::Mat((*x) * (*y)); +} + +Mat Mat_T(Mat x) { + return new cv::Mat(x->t()); +} + void Mat_AbsDiff(Mat src1, Mat src2, Mat dst) { cv::absdiff(*src1, *src2, *dst); } diff --git a/core.go b/core.go index 5b22f16a..031be3c8 100644 --- a/core.go +++ b/core.go @@ -728,6 +728,17 @@ func (m *Mat) DivideFloat(val float32) { C.Mat_DivideFloat(m.p, C.float(val)) } +// MultiplyMatrix multiplies matrix (m*x) +func (m *Mat) MultiplyMatrix(x Mat) Mat { + return newMat(C.Mat_MultiplyMatrix(m.p, x.p)) +} + +// T transpose matrix +// https://docs.opencv.org/4.1.2/d3/d63/classcv_1_1Mat.html#aaa428c60ccb6d8ea5de18f63dfac8e11 +func (m *Mat) T() Mat { + return newMat(C.Mat_T(m.p)) +} + // ToImage converts a Mat to a image.Image. func (m *Mat) ToImage() (image.Image, error) { t := m.Type() diff --git a/core.h b/core.h index 21e4d2cf..0a8a50f5 100644 --- a/core.h +++ b/core.h @@ -285,6 +285,9 @@ void Mat_AddFloat(Mat m, float val); void Mat_SubtractFloat(Mat m, float val); void Mat_MultiplyFloat(Mat m, float val); void Mat_DivideFloat(Mat m, float val); +Mat Mat_MultiplyMatrix(Mat x, Mat y); + +Mat Mat_T(Mat x); void LUT(Mat src, Mat lut, Mat dst); diff --git a/core_test.go b/core_test.go index 4d3cecbf..21964671 100644 --- a/core_test.go +++ b/core_test.go @@ -785,6 +785,21 @@ func TestMatMutators(t *testing.T) { } mat.Close() }) + t.Run("MultiplyMatrix", func(t *testing.T) { + mat := NewMatWithSizeFromScalar(NewScalar(30.0, 0, 0, 0), 2, 1, MatTypeCV32F) + mat2 := NewMatWithSizeFromScalar(NewScalar(30.0, 0, 0, 0), 1, 2, MatTypeCV32F) + mat3 := mat.MultiplyMatrix(mat2) + for i := 0; i < mat3.Cols(); i++ { + for j := 0; j < mat3.Rows(); j++ { + if mat3.GetFloatAt(i, j) != 900.0 { + t.Errorf("MultiplyMatrix incorrect value: %v\n", mat3.GetFloatAt(i, j)) + } + } + } + mat.Close() + mat2.Close() + mat3.Close() + }) } func TestMatAbsDiff(t *testing.T) { @@ -2150,6 +2165,30 @@ func TestGetTickFrequencyCount(t *testing.T) { } } +func TestMatT(t *testing.T) { + var q = []float32{1, 3, 2, 4} + src := NewMatWithSize(2, 2, MatTypeCV32F) + defer src.Close() + src.SetFloatAt(0, 0, 1) + src.SetFloatAt(0, 1, 2) + src.SetFloatAt(1, 0, 3) + src.SetFloatAt(1, 1, 4) + + dst := src.T() + defer dst.Close() + + ret, err := dst.DataPtrFloat32() + if err != nil { + t.Error(err) + } + + for i := 0; i < len(ret); i++ { + if ret[i] != q[i] { + t.Errorf("MatT incorrect value: %v\n", ret[i]) + } + } +} + func compareImages(img0, img1 image.Image) bool { bounds0 := img0.Bounds() bounds1 := img1.Bounds() diff --git a/svd.cpp b/svd.cpp new file mode 100644 index 00000000..0e0e82d6 --- /dev/null +++ b/svd.cpp @@ -0,0 +1,5 @@ +#include "svd.h" + +void SVD_Compute(Mat src, Mat w, Mat u, Mat vt) { + cv::SVD::compute(*src, *w, *u, *vt, 0); +} \ No newline at end of file diff --git a/svd.go b/svd.go new file mode 100644 index 00000000..16f26ef4 --- /dev/null +++ b/svd.go @@ -0,0 +1,14 @@ +package gocv + +/* +#include +#include "svd.h" +*/ +import "C" + +// SVDCompute decomposes matrix and stores the results to user-provided matrices +// +// https://docs.opencv.org/4.1.2/df/df7/classcv_1_1SVD.html#a76f0b2044df458160292045a3d3714c6 +func SVDCompute(src Mat, w, u, vt *Mat) { + C.SVD_Compute(src.Ptr(), w.Ptr(), u.Ptr(), vt.Ptr()) +} diff --git a/svd.h b/svd.h new file mode 100644 index 00000000..e3aab12f --- /dev/null +++ b/svd.h @@ -0,0 +1,18 @@ +#ifndef _OPENCV3_SVD_H_ +#define _OPENCV3_SVD_H_ + +#ifdef __cplusplus +#include + +extern "C" { +#endif + +#include "core.h" + +void SVD_Compute(Mat src, Mat w, Mat u, Mat vt); + +#ifdef __cplusplus +} +#endif + +#endif //_OPENCV3_SVD_H \ No newline at end of file diff --git a/svd_test.go b/svd_test.go new file mode 100644 index 00000000..97d79e47 --- /dev/null +++ b/svd_test.go @@ -0,0 +1,69 @@ +package gocv + +import ( + "testing" +) + +func TestSVDCompute(t *testing.T) { + var resultW = []float32{6.167493, 3.8214223} + var resultU = []float32{-0.1346676, -0.99089086, 0.9908908, -0.1346676} + var resultVt = []float32{0.01964448, 0.999807, -0.999807, 0.01964448} + + checkFunc := func(a []float32, b []float32) bool { + if len(a) != len(b) { + return false + } + + for i := range a { + if a[i] != b[i] { + return false + } + } + return true + } + + src := NewMatWithSize(2, 2, MatTypeCV32F) + src.SetFloatAt(0, 0, 3.76956568) + src.SetFloatAt(0, 1, -0.90478725) + src.SetFloatAt(1, 0, 0.634576) + src.SetFloatAt(1, 1, 6.10002347) + defer src.Close() + + w := NewMat() + defer w.Close() + + u := NewMat() + defer u.Close() + + vt := NewMat() + defer vt.Close() + + SVDCompute(src, &w, &u, &vt) + + dataW, err := w.DataPtrFloat32() + if err != nil { + t.Error(err) + } + + if !checkFunc(resultW, dataW) { + t.Error("w value is incorrect") + } + + dataU, err := u.DataPtrFloat32() + if err != nil { + t.Error(err) + } + + if !checkFunc(resultU, dataU) { + t.Error("u value is incorrect") + } + + dataVt, err := vt.DataPtrFloat32() + if err != nil { + t.Error(err) + } + + if !checkFunc(resultVt, dataVt) { + t.Error("vt value is incorrect") + } +}