diff --git a/Makefile b/Makefile index e4c72757..2d83fa50 100644 --- a/Makefile +++ b/Makefile @@ -84,6 +84,16 @@ build_nonfree: $(MAKE) preinstall cd - +# Build OpenCV with cuda. +build_cuda: + cd $(TMP_DIR)opencv/opencv-$(OPENCV_VERSION) + mkdir build + cd build + cmake -j $(shell nproc --all) -D CMAKE_BUILD_TYPE=RELEASE -D CMAKE_INSTALL_PREFIX=/usr/local -D OPENCV_EXTRA_MODULES_PATH=$(TMP_DIR)opencv/opencv_contrib-$(OPENCV_VERSION)/modules -D BUILD_DOCS=OFF -D BUILD_EXAMPLES=OFF -D BUILD_TESTS=OFF -D BUILD_PERF_TESTS=OFF -D BUILD_opencv_java=NO -D BUILD_opencv_python=NO -D BUILD_opencv_python2=NO -D BUILD_opencv_python3=NO -D WITH_JASPER=OFF -DOPENCV_GENERATE_PKGCONFIG=ON -DWITH_CUDA=ON -DENABLE_FAST_MATH=1 -DCUDA_FAST_MATH=1 -DWITH_CUBLAS=1 -DCUDA_TOOLKIT_ROOT_DIR=/usr/local/cuda/ -DBUILD_opencv_cudacodec=OFF .. + $(MAKE) -j $(shell nproc --all) + $(MAKE) preinstall + cd - + # Cleanup temporary build files. clean: rm -rf $(TMP_DIR)opencv @@ -94,6 +104,9 @@ install: deps download build sudo_install clean verify # Do everything on Raspbian. install_raspi: deps download build_raspi sudo_install clean verify +# Do everything with cuda. +install_cuda: deps download build_cuda sudo_install clean verify + # Install system wide. sudo_install: cd $(TMP_DIR)opencv/opencv-$(OPENCV_VERSION)/build diff --git a/cgo.go b/cgo.go index 8d564a56..65dfad26 100644 --- a/cgo.go +++ b/cgo.go @@ -2,7 +2,7 @@ package gocv -// Changes here should be mirrored in contrib/cgo.go. +// Changes here should be mirrored in contrib/cgo.go and cuda/cgo.go. /* #cgo !windows pkg-config: opencv4 diff --git a/cmd/cuda/main.go b/cmd/cuda/main.go new file mode 100644 index 00000000..7891c9ff --- /dev/null +++ b/cmd/cuda/main.go @@ -0,0 +1,28 @@ +// What it does: +// +// This program outputs the current OpenCV library version and CUDA version the console. +// +// How to run: +// +// go run ./cmd/cuda/main.go +// +// +build example + +package main + +import ( + "fmt" + + "gocv.io/x/gocv" + "gocv.io/x/gocv/cuda" +) + +func main() { + fmt.Printf("gocv version: %s\n", gocv.Version()) + fmt.Println("cuda information:") + devices := cuda.GetCudaEnabledDeviceCount() + for i := 0; i < devices; i++ { + fmt.Print(" ") + cuda.PrintShortCudaDeviceInfo(i) + } +} diff --git a/contrib/cgo.go b/contrib/cgo.go index 005b481a..6fbfdedd 100644 --- a/contrib/cgo.go +++ b/contrib/cgo.go @@ -2,7 +2,7 @@ package contrib -// Changes here should be mirrored in gocv/cgo.go. +// Changes here should be mirrored in gocv/cgo.go and cuda/cgo.go /* #cgo !windows pkg-config: opencv4 diff --git a/cuda/README.md b/cuda/README.md new file mode 100644 index 00000000..057cd5b3 --- /dev/null +++ b/cuda/README.md @@ -0,0 +1,24 @@ +# Cuda (Experimental) + +In order to use the cuda package, the cuda toolkit from nvidia needs to be installed on the host system. + +Please see https://docs.nvidia.com/cuda/index.html for more information. + +Furthermore opencv must be compiled with cuda support. + +## Compiling opencv with cuda + +For now we have included the make target `install_cuda` that compiles opencv with cuda. (For more details on the compilation process please see the `Makefile`) + +Simply issue the command `make install_cuda` and you should be good to go. + +Then finally verify that it is all working + + cd $GOPATH/src/gocv.io/x/gocv + go run ./cmd/cuda/main.go + +You should see something along the lines of: + + gocv version: 0.19.0 + cuda information: + Device 0: "GeForce MX150" 2003Mb, sm_61, Driver/Runtime ver.10.0/10.0 diff --git a/cuda/cgo.go b/cuda/cgo.go new file mode 100644 index 00000000..93ab40c9 --- /dev/null +++ b/cuda/cgo.go @@ -0,0 +1,13 @@ +// +build !customenv,!openvino + +package cuda + +// Changes here should be mirrored in gocv/cgo.go and contrib/cgo.go. + +/* +#cgo !windows pkg-config: opencv4 +#cgo CXXFLAGS: --std=c++11 +#cgo windows CPPFLAGS: -IC:/opencv/build/install/include +#cgo windows LDFLAGS: -LC:/opencv/build/install/x64/mingw/lib -lopencv_core401 -lopencv_face401 -lopencv_videoio401 -lopencv_imgproc401 -lopencv_highgui401 -lopencv_imgcodecs401 -lopencv_objdetect401 -lopencv_features2d401 -lopencv_video401 -lopencv_dnn401 -lopencv_xfeatures2d401 -lopencv_plot401 -lopencv_tracking401 -lopencv_img_hash401 -lopencv_calib3d401 +*/ +import "C" diff --git a/cuda/cuda.cpp b/cuda/cuda.cpp new file mode 100644 index 00000000..08056a81 --- /dev/null +++ b/cuda/cuda.cpp @@ -0,0 +1,33 @@ +#include "cuda.h" + +GpuMat GpuMat_New() { + return new cv::cuda::GpuMat(); +} + +void GpuMat_Upload(GpuMat m,Mat data){ + m->upload(*data); +} + +void GpuMat_Download(GpuMat m,Mat dst){ + m->download(*dst); +} + +int GpuMat_Empty(GpuMat m){ + return m->empty(); +} + +void GpuMat_Close(GpuMat m){ + delete m; +} + +void PrintCudaDeviceInfo(int device){ + cv::cuda::printCudaDeviceInfo(device); +} + +void PrintShortCudaDeviceInfo(int device){ + cv::cuda::printShortCudaDeviceInfo(device); +} + +int GetCudaEnabledDeviceCount(){ + return cv::cuda::getCudaEnabledDeviceCount(); +} \ No newline at end of file diff --git a/cuda/cuda.go b/cuda/cuda.go new file mode 100644 index 00000000..6dc74fa5 --- /dev/null +++ b/cuda/cuda.go @@ -0,0 +1,76 @@ +// Package cuda is the GoCV wrapper around OpenCV cuda. +// +// For further details, please see: +// https://github.com/opencv/c +// +// import "gocv.io/x/gocv/cuda" +package cuda + +/* +#include +#include "cuda.h" +*/ +import "C" +import "gocv.io/x/gocv" + +// GpuMat is the GPU version of a Mat +// +// For further details, please see: +// https://docs.opencv.org/master/d0/d60/classcv_1_1cuda_1_1GpuMat.html +type GpuMat struct { + p C.GpuMat +} + +// Upload performs data upload to GpuMat (Blocking call) +// +// For further details, please see: +// https://docs.opencv.org/master/d0/d60/classcv_1_1cuda_1_1GpuMat.html#a00ef5bfe18d14623dcf578a35e40a46b +// +func (g *GpuMat) Upload(data gocv.Mat) { + C.GpuMat_Upload(g.p, C.Mat(data.Ptr())) +} + +// Download performs data download from GpuMat (Blocking call) +// +// For further details, please see: +// https://docs.opencv.org/master/d0/d60/classcv_1_1cuda_1_1GpuMat.html#a027e74e4364ddfd9687b58aa5db8d4e8 +func (g *GpuMat) Download(dst *gocv.Mat) { + C.GpuMat_Download(g.p, C.Mat(dst.Ptr())) +} + +// Empty returns true if GpuMat is empty +func (g *GpuMat) Empty() bool { + return C.GpuMat_Empty(g.p) != 0 +} + +// Close the GpuMat object +func (g *GpuMat) Close() error { + C.GpuMat_Close(g.p) + g.p = nil + return nil +} + +// NewGpuMat Returns a new empty GpuMat +func NewGpuMat() GpuMat { + return newGpuMat(C.GpuMat_New()) +} + +func newGpuMat(p C.GpuMat) GpuMat { + return GpuMat{p: p} +} + +// PrintCudaDeviceInfo prints extensive cuda device information +func PrintCudaDeviceInfo(device int) { + C.PrintCudaDeviceInfo(C.int(device)) +} + +// PrintShortCudaDeviceInfo prints a small amount of cuda device information +func PrintShortCudaDeviceInfo(device int) { + C.PrintShortCudaDeviceInfo(C.int(device)) +} + +// GetCudaEnabledDeviceCount returns the number of cuda enabled devices on the +// system +func GetCudaEnabledDeviceCount() int { + return int(C.GetCudaEnabledDeviceCount()) +} diff --git a/cuda/cuda.h b/cuda/cuda.h new file mode 100644 index 00000000..43dee110 --- /dev/null +++ b/cuda/cuda.h @@ -0,0 +1,32 @@ +#ifndef _OPENCV3_CUDA_H_ +#define _OPENCV3_CUDA_H_ + +#ifdef __cplusplus +#include +#include + +extern "C" { +#endif + +#include "../core.h" + +#ifdef __cplusplus +typedef cv::cuda::GpuMat* GpuMat; +#else +typedef void* GpuMat; +#endif + +GpuMat GpuMat_New(); +void GpuMat_Upload(GpuMat m,Mat data); +void GpuMat_Download(GpuMat m,Mat dst); +void GpuMat_Close(GpuMat m); +int GpuMat_Empty(GpuMat m); + +void PrintCudaDeviceInfo(int device); +void PrintShortCudaDeviceInfo(int device); +int GetCudaEnabledDeviceCount(); +#ifdef __cplusplus +} +#endif + +#endif //_OPENCV3_CUDA_H_ diff --git a/cuda/cuda_test.go b/cuda/cuda_test.go new file mode 100644 index 00000000..166a8631 --- /dev/null +++ b/cuda/cuda_test.go @@ -0,0 +1,20 @@ +package cuda + +import ( + "testing" +) + +func TestNewGpuMat(t *testing.T) { + mat := NewGpuMat() + defer mat.Close() + + if !mat.Empty() { + t.Error("New Mat should be empty") + } +} + +func TestGetCudaEnabledDeviceCount(t *testing.T) { + if GetCudaEnabledDeviceCount() < 1 { + t.Fatal("expected atleast one cuda enabled device") + } +} diff --git a/cuda/cudabgsegm.cpp b/cuda/cudabgsegm.cpp new file mode 100644 index 00000000..f880bee2 --- /dev/null +++ b/cuda/cudabgsegm.cpp @@ -0,0 +1,25 @@ +#include "cudabgsegm.h" + +CudaBackgroundSubtractorMOG2 CudaBackgroundSubtractorMOG2_Create() { + return new cv::Ptr(cv::cuda::createBackgroundSubtractorMOG2()); +} + +void CudaBackgroundSubtractorMOG2_Close(CudaBackgroundSubtractorMOG2 b) { + delete b; +} + +void CudaBackgroundSubtractorMOG2_Apply(CudaBackgroundSubtractorMOG2 b, GpuMat src, GpuMat dst) { + (*b)->apply(*src, *dst); +} + +CudaBackgroundSubtractorMOG CudaBackgroundSubtractorMOG_Create() { + return new cv::Ptr(cv::cuda::createBackgroundSubtractorMOG()); +} + +void CudaBackgroundSubtractorMOG_Close(CudaBackgroundSubtractorMOG b) { + delete b; +} + +void CudaBackgroundSubtractorMOG_Apply(CudaBackgroundSubtractorMOG b, GpuMat src, GpuMat dst) { + (*b)->apply(*src, *dst); +} \ No newline at end of file diff --git a/cuda/cudabgsegm.go b/cuda/cudabgsegm.go new file mode 100644 index 00000000..ba9546be --- /dev/null +++ b/cuda/cudabgsegm.go @@ -0,0 +1,76 @@ +package cuda + +/* +#include +#include "cudabgsegm.h" +*/ +import "C" +import "unsafe" + +// BackgroundSubtractorMOG2 is a wrapper around the cv::cuda::BackgroundSubtractorMOG2. +type BackgroundSubtractorMOG2 struct { + // C.BackgroundSubtractorMOG2 + p unsafe.Pointer +} + +// BackgroundSubtractorMOG is a wrapper around the cv::cuda::BackgroundSubtractorMOG. +type BackgroundSubtractorMOG struct { + // C.BackgroundSubtractorMOG + p unsafe.Pointer +} + +// NewBackgroundSubtractorMOG2 returns a new BackgroundSubtractor algorithm +// of type MOG2. MOG2 is a Gaussian Mixture-based Background/Foreground +// Segmentation Algorithm. +// +// For further details, please see: +// https://docs.opencv.org/master/dc/d3d/cudabgsegm_8hpp.html +// +func NewBackgroundSubtractorMOG2() BackgroundSubtractorMOG2 { + return BackgroundSubtractorMOG2{p: unsafe.Pointer(C.CudaBackgroundSubtractorMOG2_Create())} +} + +// Close BackgroundSubtractorMOG2. +func (b *BackgroundSubtractorMOG2) Close() error { + C.CudaBackgroundSubtractorMOG2_Close((C.CudaBackgroundSubtractorMOG2)(b.p)) + b.p = nil + return nil +} + +// Apply computes a foreground mask using the current BackgroundSubtractorMOG2. +// +// For further details, please see: +// https://docs.opencv.org/master/df/d23/classcv_1_1cuda_1_1BackgroundSubtractorMOG2.html#a92408f07bf1268c1b778cb186b3113b0 +// +func (b *BackgroundSubtractorMOG2) Apply(src GpuMat, dst *GpuMat) { + C.CudaBackgroundSubtractorMOG2_Apply((C.CudaBackgroundSubtractorMOG2)(b.p), src.p, dst.p) + return +} + +// NewBackgroundSubtractorMOG returns a new BackgroundSubtractor algorithm +// of type MOG. MOG is a Gaussian Mixture-based Background/Foreground +// Segmentation Algorithm. +// +// For further details, please see: +// https://docs.opencv.org/master/dc/d3d/cudabgsegm_8hpp.html +// +func NewBackgroundSubtractorMOG() BackgroundSubtractorMOG { + return BackgroundSubtractorMOG{p: unsafe.Pointer(C.CudaBackgroundSubtractorMOG_Create())} +} + +// Close BackgroundSubtractorMOG. +func (b *BackgroundSubtractorMOG) Close() error { + C.CudaBackgroundSubtractorMOG_Close((C.CudaBackgroundSubtractorMOG)(b.p)) + b.p = nil + return nil +} + +// Apply computes a foreground mask using the current BackgroundSubtractorMOG. +// +// For further details, please see: +// https://docs.opencv.org/master/d1/dfe/classcv_1_1cuda_1_1BackgroundSubtractorMOG.html#a8f52d2f7abd1c77c84243efc53972cbf +// +func (b *BackgroundSubtractorMOG) Apply(src GpuMat, dst *GpuMat) { + C.CudaBackgroundSubtractorMOG_Apply((C.CudaBackgroundSubtractorMOG)(b.p), src.p, dst.p) + return +} diff --git a/cuda/cudabgsegm.h b/cuda/cudabgsegm.h new file mode 100644 index 00000000..988b56a0 --- /dev/null +++ b/cuda/cudabgsegm.h @@ -0,0 +1,33 @@ +#ifndef _OPENCV3_CUDABGSEGM_H_ +#define _OPENCV3_CUDABGSEGM_H_ + +#ifdef __cplusplus +#include + +extern "C" { +#endif + +#include "../core.h" +#include "cuda.h" + +#ifdef __cplusplus +typedef cv::Ptr* CudaBackgroundSubtractorMOG2; +typedef cv::Ptr* CudaBackgroundSubtractorMOG; +#else +typedef void* CudaBackgroundSubtractorMOG2; +typedef void* CudaBackgroundSubtractorMOG; +#endif + +CudaBackgroundSubtractorMOG2 CudaBackgroundSubtractorMOG2_Create(); +void CudaBackgroundSubtractorMOG2_Close(CudaBackgroundSubtractorMOG2 b); +void CudaBackgroundSubtractorMOG2_Apply(CudaBackgroundSubtractorMOG2 b, GpuMat src, GpuMat dst); + +CudaBackgroundSubtractorMOG CudaBackgroundSubtractorMOG_Create(); +void CudaBackgroundSubtractorMOG_Close(CudaBackgroundSubtractorMOG b); +void CudaBackgroundSubtractorMOG_Apply(CudaBackgroundSubtractorMOG b, GpuMat src, GpuMat dst); + +#ifdef __cplusplus +} +#endif + +#endif //_OPENCV3_CUDABGSEGM_H_ diff --git a/cuda/cudabgsegm_test.go b/cuda/cudabgsegm_test.go new file mode 100644 index 00000000..91d98602 --- /dev/null +++ b/cuda/cudabgsegm_test.go @@ -0,0 +1,62 @@ +package cuda + +import ( + "gocv.io/x/gocv" + "testing" +) + +func TestCudaMOG2(t *testing.T) { + img := gocv.IMRead("../images/face.jpg", gocv.IMReadColor) + if img.Empty() { + t.Error("Invalid Mat in CudaMOG2 test") + } + defer img.Close() + + var cimg, dimg = NewGpuMat(), NewGpuMat() + defer cimg.Close() + defer dimg.Close() + + cimg.Upload(img) + + dst := gocv.NewMat() + defer dst.Close() + + mog2 := NewBackgroundSubtractorMOG2() + defer mog2.Close() + + mog2.Apply(cimg, &dimg) + + dimg.Download(&dst) + + if dst.Empty() { + t.Error("Error in TestCudaMOG2 test") + } +} + +func TestCudaMOG(t *testing.T) { + img := gocv.IMRead("../images/face.jpg", gocv.IMReadColor) + if img.Empty() { + t.Error("Invalid Mat in CudaMOG test") + } + defer img.Close() + + var cimg, dimg = NewGpuMat(), NewGpuMat() + defer cimg.Close() + defer dimg.Close() + + cimg.Upload(img) + + dst := gocv.NewMat() + defer dst.Close() + + mog2 := NewBackgroundSubtractorMOG() + defer mog2.Close() + + mog2.Apply(cimg, &dimg) + + dimg.Download(&dst) + + if dst.Empty() { + t.Error("Error in TestCudaMOG test") + } +}