From da3d6d433c33bdc996ade30948d7a5fcc80de167 Mon Sep 17 00:00:00 2001 From: pinb Date: Thu, 23 Nov 2023 00:23:26 +0900 Subject: [PATCH] update --- BlazorApp/BlazorApp.csproj | 4 +- BlazorApp/Pages/Index.razor | 16 +- BlazorApp/Pages/OpenCvSharpSample.razor | 312 +++++++++++++++++++--- BlazorApp/Shared/NavMenu.razor | 8 +- BlazorApp/wwwroot/images/gaborfilter.png | Bin 0 -> 13037 bytes BlazorApp/wwwroot/images/gaborfilter2.png | Bin 0 -> 3872 bytes BlazorApp/wwwroot/images/gaborfilter3.png | Bin 0 -> 3931 bytes BlazorApp/wwwroot/images/lenna.bmp | Bin 0 -> 786486 bytes BlazorApp/wwwroot/images/lenna.jpg | Bin 0 -> 8006 bytes BlazorApp/wwwroot/images/lenna.png | Bin 0 -> 473831 bytes OpenCvSharpBlazorSamples.sln | 15 ++ README.md | 2 +- 12 files changed, 310 insertions(+), 47 deletions(-) create mode 100644 BlazorApp/wwwroot/images/gaborfilter.png create mode 100644 BlazorApp/wwwroot/images/gaborfilter2.png create mode 100644 BlazorApp/wwwroot/images/gaborfilter3.png create mode 100644 BlazorApp/wwwroot/images/lenna.bmp create mode 100644 BlazorApp/wwwroot/images/lenna.jpg create mode 100644 BlazorApp/wwwroot/images/lenna.png diff --git a/BlazorApp/BlazorApp.csproj b/BlazorApp/BlazorApp.csproj index 73a18f0..2614717 100644 --- a/BlazorApp/BlazorApp.csproj +++ b/BlazorApp/BlazorApp.csproj @@ -19,8 +19,8 @@ - - + + diff --git a/BlazorApp/Pages/Index.razor b/BlazorApp/Pages/Index.razor index 64f37f1..168093d 100644 --- a/BlazorApp/Pages/Index.razor +++ b/BlazorApp/Pages/Index.razor @@ -2,12 +2,20 @@ @using OpenCvSharp -Index +PINBlog -

Hello, world!

+

Hello, PINblog!

-Welcome to your new app. +Welcome to PINBlog's Gabor Filter Test Page'. +
+
+	⬅️ 왼쪽의 GaborFilter Test 탭을 클릭하면 해당 페이지로 이동합니다. 🤗🤗
+
+
+ diff --git a/BlazorApp/Pages/OpenCvSharpSample.razor b/BlazorApp/Pages/OpenCvSharpSample.razor index 40db74e..6dfa1c4 100644 --- a/BlazorApp/Pages/OpenCvSharpSample.razor +++ b/BlazorApp/Pages/OpenCvSharpSample.razor @@ -4,10 +4,85 @@ @inject HttpClient httpClient; @implements IDisposable -OpenCvSharp Sample - -

OpenCvSharp on WebAssembly

+Gabor Filter Test +
+

Gabor Filter란?


+

Gabor Filter는 영상처리에서 Bio-inspired라는 키워드가 있으면 빠지지않고 등장한다.

+

외곽선을 검출하는 기능을 하는 필터로, 사람의 시각체계가 반응하는 것과 비슷하다는 이유로 널리 사용되고 있다.

+

Gabor Fiter는 간단히 말해서 사인 함수로 모듈레이션 된 Gaussian Filter라고 생각할 수 있다.

+

파라미터를 조절함에 따라 Edge의 크기나 방향성을 바꿀 수 있으므로 Bio-inspired 영상처리 알고리즘에서 특징점 추출 알고리즘으로 핵심적인 역할을 하고 있다.

+

2D Gabor Filter의 수식은 아래와 같다.

+

+ gaborfilter
+ gaborfilter
+ gaborfilter
+



+

함수 원형

+
+        
+  cv::Mat cv::getGaborKernel(cv::Size ksize, double sigma, double theta, double lambd, double gamma, double psi = CV_PI*0.5, int ktype = CV_64F)  
+        
+    

+

cv::getGaborKernel 함수는 OpenCV에서 가버필터(Gabor filter)를 생성하는 데 사용된다.

+

가버필터는 이미지 처리와 컴퓨터 비전에서 특정 방향성과 주파수의 특징을 강조하는 데 사용되는 선형 필터이다.



+

Parameters


+ +
+
+
+

Gabor Filter 실행 (속도 느림 주의)

+ + Your browser does not support the HTML5 canvas tag. + + + Your browser does not support the HTML5 canvas tag. + +
+
+

Kernel Size(0 이상 홀수)

+ +
+

Sigma (0.0 이상)

+ +
+

Theta (0도 ~ 180도)

+ +
+

Lambda (0.0 이상 이미지 사이즈 미만(256.0))

+ +
+

Gamma (0.0 ~ 1.0)

+ +
+

Psi (0도 ~ 360도)

+ +
+ +
+
Your browser does not support the HTML5 canvas tag. @@ -17,23 +92,38 @@
+

Threshold (0 ~ 255)

+ +
- - - +
+ @code { private Mat? srcMat; + private Mat? srcMat2; private ElementReference srcCanvas; private ElementReference dstCanvas; + private ElementReference srcCanvas2; + private ElementReference dstCanvas2; private CanvasClient? srcCanvasClient; private CanvasClient? dstCanvasClient; + private CanvasClient? srcCanvasClient2; + private CanvasClient? dstCanvasClient2; + private int valKernelSize = 3; + private double valSigma = 0.0; + private double valTheta = 0.0; + private double valLambd = 0.0; + private double valGamma = 0.0; + private double valPsi = 0.0; + private int valthreshold = 127; public void Dispose() { srcMat?.Dispose(); + srcMat2?.Dispose(); } protected override async Task OnInitializedAsync() @@ -43,19 +133,35 @@ protected override async Task OnAfterRenderAsync(bool firstRender) { - //if (!firstRender) - // return; + if (!firstRender) + return; await base.OnAfterRenderAsync(firstRender); - var imageBytes = await httpClient.GetByteArrayAsync("images/Mandrill.bmp"); + var imageBytes = await httpClient.GetByteArrayAsync("images/mandrill.bmp"); srcMat ??= Mat.FromImageData(imageBytes); - + if(srcMat.Width < 256 || srcMat.Height < 256) + { + Cv2.PyrUp(srcMat, srcMat, new Size(256, 256)); + } srcCanvasClient ??= new CanvasClient(jsRuntime, srcCanvas); dstCanvasClient ??= new CanvasClient(jsRuntime, dstCanvas); await srcCanvasClient.DrawMatAsync(srcMat); + + + var imageBytes2 = await httpClient.GetByteArrayAsync("images/lenna.bmp"); + srcMat2 ??= Mat.FromImageData(imageBytes2); + if(srcMat2.Width > 256 || srcMat2.Height > 256) + { + Cv2.PyrDown(srcMat2, srcMat2, new Size(256, 256)); + } + + srcCanvasClient2 ??= new CanvasClient(jsRuntime, srcCanvas2); + dstCanvasClient2 ??= new CanvasClient(jsRuntime, dstCanvas2); + + await srcCanvasClient2.DrawMatAsync(srcMat2); } - + private async Task Grayscale() { if (srcMat is null) @@ -68,7 +174,7 @@ await dstCanvasClient.DrawMatAsync(grayMat); } - + private async Task PseudoColor() { if (srcMat is null) @@ -83,7 +189,7 @@ await dstCanvasClient.DrawMatAsync(dstMat); } - + private async Task Threshold() { if (srcMat is null) @@ -92,45 +198,175 @@ throw new InvalidOperationException($"{nameof(dstCanvasClient)} is null"); using var grayMat = new Mat(); - using var dstMat = new Mat(); + using var dstMat = new Mat(srcMat.Size(), MatType.CV_8UC1); Cv2.CvtColor(srcMat, grayMat, ColorConversionCodes.BGR2GRAY); - Cv2.Threshold(grayMat, dstMat, 0, 255, ThresholdTypes.Binary | ThresholdTypes.Otsu); + //Cv2.Threshold(grayMat, dstMat, 127, 255, ThresholdTypes.Binary); + + for(int i=0; i(i, j); + byte newValue = (Convert.ToInt32(pixelValue) > valthreshold) ? (byte)255 : (byte)0; + dstMat.Set(i, j, newValue); + } + } await dstCanvasClient.DrawMatAsync(dstMat); } - private async Task Canny() + private async Task GaborFilter() { - if (srcMat is null) - throw new InvalidOperationException($"{nameof(srcMat)} is null"); - if (dstCanvasClient is null) - throw new InvalidOperationException($"{nameof(dstCanvasClient)} is null"); + if (srcMat2 is null) + throw new InvalidOperationException($"{nameof(srcMat2)} is null"); + if (dstCanvasClient2 is null) + throw new InvalidOperationException($"{nameof(dstCanvasClient2)} is null"); - using var grayMat = new Mat(); - using var dstMat = new Mat(); - Cv2.CvtColor(srcMat, grayMat, ColorConversionCodes.BGR2GRAY); - Cv2.Canny(grayMat, dstMat, 32, 128); + using var grayMat = new Mat(srcMat2.Size(), MatType.CV_32F); + Cv2.CvtColor(srcMat2, grayMat, ColorConversionCodes.BGR2GRAY); - await dstCanvasClient.DrawMatAsync(dstMat); + int kernel_size = valKernelSize; + double sigma = valSigma; + double theta = valTheta * Cv2.PI / 180.0; + double lambd = valLambd; + double gamma = valGamma; + double psi = valPsi * Cv2.PI / 180.0; + + + Mat rst = new Mat(); + try + { + //Mat gabor_filter = Cv2.GetGaborKernel(new Size(kernel_size, kernel_size), sigma, theta, lambd, gamma, psi, ktype); + //Mat kenel = new Mat(5, 5, MatType.CV_8UC1); + //Cv2.Filter2D(grayMat, rst, grayMat.Type(), kenel); + + Mat gabor_filter = Gabor2DFilter.CreateGaborKernel(kernel_size, sigma, theta, lambd, gamma, psi); + rst = Gabor2DFilter.Filter2D(grayMat, gabor_filter); + } + catch(Exception e) + { + String log = e.Message; + } + await dstCanvasClient2.DrawMatAsync(rst); } - private async Task Akaze() + public class Gabor2DFilter { - if (srcMat is null) - throw new InvalidOperationException($"{nameof(srcMat)} is null"); - if (dstCanvasClient is null) - throw new InvalidOperationException($"{nameof(dstCanvasClient)} is null"); + public static Mat CreateGaborKernel(int ksize, double sigma, double theta, double lambd, double gamma, double psi) + { + int center = ksize / 2; + Mat kernel = new Mat(ksize, ksize, MatType.CV_32F); + double sigmaX = sigma; + double sigmaY = sigma / gamma; - using var grayMat = new Mat(); - Cv2.CvtColor(srcMat, grayMat, ColorConversionCodes.BGR2GRAY); + //for (int y = -center; y <= center; y++) + //{ + // for (int x = -center; x <= center; x++) + // { + // double xPrime = x * Math.Cos(theta) + y * Math.Sin(theta); + // double yPrime = -x * Math.Sin(theta) + y * Math.Cos(theta); + // double value = Math.Exp(-0.5 * (xPrime * xPrime / (sigmaX * sigmaX) + yPrime * yPrime / (sigmaY * sigmaY))); + // value *= Math.Cos(2 * Math.PI * xPrime / lambd + psi); + // kernel.Set(center + y, center + x, Convert.ToSingle(value)); + // } + //} + unsafe + { + for (int y = -center; y <= center; y++) + { + for (int x = -center; x <= center; x++) + { + double xPrime = x * Math.Cos(theta) + y * Math.Sin(theta); + double yPrime = -x * Math.Sin(theta) + y * Math.Cos(theta); + double value = Math.Exp(-0.5 * (xPrime * xPrime / (sigmaX * sigmaX) + yPrime * yPrime / (sigmaY * sigmaY))); + value *= Math.Cos(2 * Math.PI * xPrime / lambd + psi); - using var akaze = AKAZE.Create(); - using var descriptors = new Mat(); - akaze.DetectAndCompute(grayMat, null, out var keypoints, descriptors); + float* kernelPtr = (float*)(kernel.DataPointer + (center + y) * kernel.Step() + (center + x) * sizeof(float)); + *kernelPtr = Convert.ToSingle(value); + } + } + } + return kernel; + } + + public static Mat Filter2D(Mat src, Mat kernel) + { + int kernelRows = kernel.Rows; + int kernelCols = kernel.Cols; + int kernelCenterX = kernelCols / 2; + int kernelCenterY = kernelRows / 2; + MatType type = src.Type(); + Mat dst = new Mat(src.Size(), type); + + try + { + //for (int y = 0; y < src.Rows; y++) + //{ + // for (int x = 0; x < src.Cols; x++) + // { + // double sum = 0.0; + // + // for (int kRow = 0; kRow < kernelRows; kRow++) + // { + // int yy = y + kRow - kernelCenterY; + // + // for (int kCol = 0; kCol < kernelCols; kCol++) + // { + // int xx = x + kCol - kernelCenterX; + // + // if (xx >= 0 && xx < src.Cols && yy >= 0 && yy < src.Rows) + // { + // byte pixelValue = src.At(yy, xx); + // double kernelValue = kernel.At(kRow, kCol); + // sum += Convert.ToDouble(pixelValue) * kernelValue; + // } + // } + // } + // + // byte resultValue = dst.At(y, x); + // resultValue = (byte)Math.Clamp(sum, 0, 255); + // dst.Set(y, x, resultValue); + // } + //} + unsafe + { + for (int y = 0; y < src.Rows; y++) + { + for (int x = 0; x < src.Cols; x++) + { + double sum = 0.0; + + for (int kRow = 0; kRow < kernelRows; kRow++) + { + int yy = y + kRow - kernelCenterY; + for (int kCol = 0; kCol < kernelCols; kCol++) + { + int xx = x + kCol - kernelCenterX; + + if (xx >= 0 && xx < src.Cols && yy >= 0 && yy < src.Rows) + { + byte* pixelValue = src.DataPointer + yy * src.Step() + xx * src.ElemSize(); + double kernelValue = kernel.At(kRow, kCol); + sum += Convert.ToDouble(*pixelValue) * kernelValue; + } + } + } + + byte* resultValue = dst.DataPointer + y * dst.Step() + x * dst.ElemSize(); + *resultValue = (byte)Math.Clamp(sum, 0, 255); + } + } + } + } + catch(Exception ex) + { + String log = ex.Message; + } + + return dst; + } + - using var dstMat = srcMat.Clone(); - Cv2.DrawKeypoints(srcMat, keypoints, dstMat); - await dstCanvasClient.DrawMatAsync(dstMat); } } \ No newline at end of file diff --git a/BlazorApp/Shared/NavMenu.razor b/BlazorApp/Shared/NavMenu.razor index bc44b33..71cc6d7 100644 --- a/BlazorApp/Shared/NavMenu.razor +++ b/BlazorApp/Shared/NavMenu.razor @@ -1,6 +1,6 @@