From 78f61c8155fc9c7b0be0a39d7783b2aac10b7faa Mon Sep 17 00:00:00 2001 From: l0rem1psum Date: Sat, 27 Jan 2024 15:36:48 +0800 Subject: [PATCH 1/2] feat(imgproc): demosaicing wrapper --- ROADMAP.md | 2 +- cuda/imgproc.cpp | 8 ++++ cuda/imgproc.go | 21 +++++++++++ cuda/imgproc.h | 1 + imgproc.cpp | 4 ++ imgproc.go | 10 +++++ imgproc.h | 1 + imgproc_test.go | 95 ++++++++++++++++++++++++++++++++++++++++++++++++ 8 files changed, 141 insertions(+), 1 deletion(-) diff --git a/ROADMAP.md b/ROADMAP.md index b11e4b85..c048ee08 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -320,7 +320,7 @@ Your pull requests will be greatly appreciated! - [ ] **cudaimgproc. Image Processing - WORK STARTED** The following functions still need implementation: - [X] [cv::cuda::TemplateMatching](https://docs.opencv.org/master/d2/d58/classcv_1_1cuda_1_1TemplateMatching.html) - [ ] [cv::cuda::alphaComp](https://docs.opencv.org/master/db/d8c/group__cudaimgproc__color.html#ga08a698700458d9311390997b57fbf8dc) - - [ ] [cv::cuda::demosaicing](https://docs.opencv.org/master/db/d8c/group__cudaimgproc__color.html#ga7fb153572b573ebd2d7610fcbe64166e) + - [X] [cv::cuda::demosaicing](https://docs.opencv.org/master/db/d8c/group__cudaimgproc__color.html#ga7fb153572b573ebd2d7610fcbe64166e) - [ ] [cv::cuda::gammaCorrection](https://docs.opencv.org/master/db/d8c/group__cudaimgproc__color.html#gaf4195a8409c3b8fbfa37295c2b2c4729) - [ ] [cv::cuda::swapChannels](https://docs.opencv.org/master/db/d8c/group__cudaimgproc__color.html#ga75a29cc4a97cde0d43ea066b01de927e) - [ ] [cv::cuda::calcHist](https://docs.opencv.org/master/d8/d0e/group__cudaimgproc__hist.html#gaaf3944106890947020bb4522a7619c26) diff --git a/cuda/imgproc.cpp b/cuda/imgproc.cpp index b3242a01..c5309cca 100644 --- a/cuda/imgproc.cpp +++ b/cuda/imgproc.cpp @@ -10,6 +10,14 @@ void GpuCvtColor(GpuMat src, GpuMat dst, int code, Stream s) { cv::cuda::cvtColor(*src, *dst, code, 0, *s); } +void GpuDemosaicing(GpuMat src, GpuMat dst, int code, Stream s) { + if (s == NULL) { + cv::cuda::demosaicing(*src, *dst, code); + return; + } + cv::cuda::demosaicing(*src, *dst, code, -1, *s); +} + CannyEdgeDetector CreateCannyEdgeDetector(double lowThresh, double highThresh) { return new cv::Ptr(cv::cuda::createCannyEdgeDetector(lowThresh, highThresh)); } diff --git a/cuda/imgproc.go b/cuda/imgproc.go index 9e949bd7..c8e82366 100644 --- a/cuda/imgproc.go +++ b/cuda/imgproc.go @@ -142,6 +142,27 @@ func CvtColorWithStream(src GpuMat, dst *GpuMat, code gocv.ColorConversionCode, C.GpuCvtColor(src.p, dst.p, C.int(code), s.p) } +// Demosaicing converts an image from Bayer pattern to RGB or grayscale. +// It converts the src Mat image to the dst Mat using the +// code param containing the desired ColorConversionCode color space. +// +// For further details, please see: +// https://docs.opencv.org/master/db/d8c/group__cudaimgproc__color.html#ga7fb153572b573ebd2d7610fcbe64166e +func Demosaicing(src GpuMat, dst *GpuMat, code gocv.ColorConversionCode) { + C.GpuDemosaicing(src.p, dst.p, C.int(code), nil) +} + +// DemosaicingWithStream converts an image from Bayer pattern to RGB or grayscale +// using a Stream for concurrency. +// It converts the src Mat image to the dst Mat using the +// code param containing the desired ColorConversionCode color space. +// +// For further details, please see: +// https://docs.opencv.org/master/db/d8c/group__cudaimgproc__color.html#ga7fb153572b573ebd2d7610fcbe64166e +func DemosaicingWithStream(src GpuMat, dst *GpuMat, code gocv.ColorConversionCode, s Stream) { + C.GpuDemosaicing(src.p, dst.p, C.int(code), s.p) +} + // HoughLinesDetector // // For further details, please see: diff --git a/cuda/imgproc.h b/cuda/imgproc.h index 84b1a4ce..4d743f59 100644 --- a/cuda/imgproc.h +++ b/cuda/imgproc.h @@ -26,6 +26,7 @@ typedef void* TemplateMatching; // standalone functions void GpuCvtColor(GpuMat src, GpuMat dst, int code, Stream s); +void GpuDemosaicing(GpuMat src, GpuMat dst, int code, Stream s); // CannyEdgeDetector CannyEdgeDetector CreateCannyEdgeDetector(double lowThresh, double highThresh); diff --git a/imgproc.cpp b/imgproc.cpp index dd65eac1..45abd7d6 100644 --- a/imgproc.cpp +++ b/imgproc.cpp @@ -15,6 +15,10 @@ void CvtColor(Mat src, Mat dst, int code) { cv::cvtColor(*src, *dst, code); } +void Demosaicing(Mat src, Mat dst, int code) { + cv::demosaicing(*src, *dst, code); +} + void EqualizeHist(Mat src, Mat dst) { cv::equalizeHist(*src, *dst); } diff --git a/imgproc.go b/imgproc.go index 80fb091a..1feeb7ad 100644 --- a/imgproc.go +++ b/imgproc.go @@ -57,6 +57,16 @@ func CvtColor(src Mat, dst *Mat, code ColorConversionCode) { C.CvtColor(src.p, dst.p, C.int(code)) } +// Demosaicing converts an image from Bayer pattern to RGB or grayscale. +// It converts the src Mat image to the dst Mat using the +// code param containing the desired ColorConversionCode color space. +// +// For further details, please see: +// https://docs.opencv.org/master/d7/d1b/group__imgproc__color__conversions.html#ga57261f12fccf872a2b2d66daf29d5bd0 +func Demosaicing(src Mat, dst *Mat, code ColorConversionCode) { + C.Demosaicing(src.p, dst.p, C.int(code)) +} + // EqualizeHist normalizes the brightness and increases the contrast of the image. // // For further details, please see: diff --git a/imgproc.h b/imgproc.h index ec862bd3..68130d0e 100644 --- a/imgproc.h +++ b/imgproc.h @@ -19,6 +19,7 @@ typedef void* CLAHE; double ArcLength(PointVector curve, bool is_closed); PointVector ApproxPolyDP(PointVector curve, double epsilon, bool closed); void CvtColor(Mat src, Mat dst, int code); +void Demosaicing(Mat src, Mat dst, int code); void EqualizeHist(Mat src, Mat dst); void CalcHist(struct Mats mats, IntVector chans, Mat mask, Mat hist, IntVector sz, FloatVector rng, bool acc); void CalcBackProject(struct Mats mats, IntVector chans, Mat hist, Mat backProject, FloatVector rng, bool uniform); diff --git a/imgproc_test.go b/imgproc_test.go index 9b975f18..b51c57d7 100644 --- a/imgproc_test.go +++ b/imgproc_test.go @@ -1,6 +1,7 @@ package gocv import ( + "fmt" "image" "image/color" "image/draw" @@ -119,6 +120,100 @@ func TestCvtColor(t *testing.T) { } } +func NewBayerFromMat(src Mat, pattern string) (Mat, error) { + dest := NewMatWithSize(src.Rows(), src.Cols(), MatTypeCV8UC1) + + switch pattern { + case "bg": + for y := 0; y < src.Rows(); y++ { + for x := 0; x < src.Cols(); x++ { + if (x+y)%2 != 0 { + dest.SetUCharAt(y, x, src.GetVecbAt(y, x)[1]) + } else if (x % 2) != 0 { + dest.SetUCharAt(y, x, src.GetVecbAt(y, x)[0]) + } else { + dest.SetUCharAt(y, x, src.GetVecbAt(y, x)[2]) + } + } + } + case "gb": + for y := 0; y < src.Rows(); y++ { + for x := 0; x < src.Cols(); x++ { + if (x+y)%2 == 0 { + dest.SetUCharAt(y, x, src.GetVecbAt(y, x)[1]) + } else if (x % 2) == 0 { + dest.SetUCharAt(y, x, src.GetVecbAt(y, x)[0]) + } else { + dest.SetUCharAt(y, x, src.GetVecbAt(y, x)[2]) + } + } + } + case "rg": + for y := 0; y < src.Rows(); y++ { + for x := 0; x < src.Cols(); x++ { + if (x+y)%2 != 0 { + dest.SetUCharAt(y, x, src.GetVecbAt(y, x)[1]) + } else if (x % 2) == 0 { + dest.SetUCharAt(y, x, src.GetVecbAt(y, x)[0]) + } else { + dest.SetUCharAt(y, x, src.GetVecbAt(y, x)[2]) + } + } + } + case "gr": + for y := 0; y < src.Rows(); y++ { + for x := 0; x < src.Cols(); x++ { + if (x+y)%2 == 0 { + dest.SetUCharAt(y, x, src.GetVecbAt(y, x)[1]) + } else if (x % 2) != 0 { + dest.SetUCharAt(y, x, src.GetVecbAt(y, x)[0]) + } else { + dest.SetUCharAt(y, x, src.GetVecbAt(y, x)[2]) + } + } + } + default: + return Mat{}, fmt.Errorf("invalid pattern: %s", pattern) + } + + return dest, nil +} + +func TestDemosaicing(t *testing.T) { + img := IMRead("images/face.jpg", IMReadColor) + if img.Empty() { + t.Error("Invalid read of Mat in Demosaicing test") + } + defer img.Close() + + patterns := map[string]ColorConversionCode{ + "bg": ColorBayerBGToBGR, + "gb": ColorBayerGBToBGR, + "rg": ColorBayerRGToBGR, + "gr": ColorBayerGRToBGR, + } + + for pattern, code := range patterns { + bayerImg, err := NewBayerFromMat(img, pattern) + if bayerImg.Empty() { + t.Error("Invalid conversion from Mat to Bayer in Demosaicing test") + } + if err != nil { + t.Error(err) + } + + dest := NewMat() + + Demosaicing(bayerImg, &dest, code) + if dest.Empty() || bayerImg.Rows() != dest.Rows() || bayerImg.Cols() != dest.Cols() { + t.Error("Invalid convert in Demosaicing test") + } + + bayerImg.Close() + dest.Close() + } +} + func TestBilateralFilter(t *testing.T) { img := IMRead("images/face-detect.jpg", IMReadColor) if img.Empty() { From e46a09cdf0cafcfe3098c5de1b57af28280c7a5e Mon Sep 17 00:00:00 2001 From: l0rem1psum Date: Sat, 27 Jan 2024 15:45:52 +0800 Subject: [PATCH 2/2] feat(demosaicing): release mat when conversion to bayer encounters invalid pattern --- imgproc_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/imgproc_test.go b/imgproc_test.go index b51c57d7..e445b4e0 100644 --- a/imgproc_test.go +++ b/imgproc_test.go @@ -173,6 +173,7 @@ func NewBayerFromMat(src Mat, pattern string) (Mat, error) { } } default: + dest.Close() return Mat{}, fmt.Errorf("invalid pattern: %s", pattern) }