/// <summary> /// Special threshold algorithm based on https://stackoverflow.com/questions/13391073/adaptive-threshold-of-blurry-image /// </summary> /// <param name="otsuRegions">Amount of splits for the image to perform Otsu threshold</param> /// <returns></returns> public static Mat MorphThreshold(this Mat matGray, int otsuRegions = 3) { Mat image = matGray.Clone(); // Divide the image by its morphologically closed counterpart Mat kernel = Cv2.GetStructuringElement(MorphShapes.Ellipse, new Size(19, 19)); Mat closed = new Mat(); Cv2.MorphologyEx(image, closed, MorphTypes.Close, kernel); //image.ConvertTo(image, MatType.CV_32F); Cv2.Divide(image, closed, image); Cv2.Normalize(image, image, 0, 255, NormTypes.MinMax); //image.ConvertTo(image, MatType.CV_8UC1); // Threshold each block (3x3 grid) of the image separately to // correct for minor differences in contrast across the image int tw = image.Width / otsuRegions, th = image.Height / otsuRegions; for (int i = 0; i < otsuRegions; i++) { for (int j = 0; j < otsuRegions; j++) { Mat block = image.SubMat(i * th, (i + 1) * th, j * tw, (j + 1) * tw); Cv2.Threshold(block, block, -1, 255, ThresholdTypes.Binary | ThresholdTypes.Otsu); } } return(image); }
public void Divide() { using var mat1 = new Mat(3, 1, MatType.CV_8UC1, new byte[] { 64, 128, 192 }); using var mat2 = new Mat(3, 1, MatType.CV_8UC1, new byte[] { 2, 4, 8 }); using var dst = new Mat(); // default Cv2.Divide(mat1, mat2, dst, 1, -1); Assert.Equal(MatType.CV_8UC1, dst.Type()); Assert.Equal(3, dst.Total()); Assert.Equal(32, dst.Get <byte>(0)); Assert.Equal(32, dst.Get <byte>(1)); Assert.Equal(24, dst.Get <byte>(2)); // scale Cv2.Divide(mat1, mat2, dst, 2, -1); Assert.Equal(MatType.CV_8UC1, dst.Type()); Assert.Equal(3, dst.Total()); Assert.Equal(64, dst.Get <byte>(0)); Assert.Equal(64, dst.Get <byte>(1)); Assert.Equal(48, dst.Get <byte>(2)); // scale & dtype Cv2.Divide(mat1, mat2, dst, 2, MatType.CV_32SC1); Assert.Equal(MatType.CV_32SC1, dst.Type()); Assert.Equal(3, dst.Total()); Assert.Equal(64, dst.Get <int>(0)); Assert.Equal(64, dst.Get <int>(1)); Assert.Equal(48, dst.Get <int>(2)); }
/// <summary> /// SSIM (structual similarity, SSIM) 结构相似性,用来判断图片相似度 /// 参考 wikipedia理解什么是SSIM https://zh.wikipedia.org/wiki/%E7%B5%90%E6%A7%8B%E7%9B%B8%E4%BC%BC%E6%80%A7 /// </summary> /// <param name="i1"></param> /// <param name="i2"></param> /// <returns></returns> public static Scalar CalculateSSIM(Mat i1, Mat i2) { const double C1 = 6.5025, C2 = 58.5225; /***************************** INITS **********************************/ MatType d = MatType.CV_32F; Mat I1 = new Mat(), I2 = new Mat(); i1.ConvertTo(I1, d); // cannot calculate on one byte large values i2.ConvertTo(I2, d); Mat I2_2 = I2.Mul(I2); // I2^2 Mat I1_2 = I1.Mul(I1); // I1^2 Mat I1_I2 = I1.Mul(I2); // I1 * I2 /***********************PRELIMINARY COMPUTING ******************************/ Mat mu1 = new Mat(), mu2 = new Mat(); // Cv2.GaussianBlur(I1, mu1, new OpenCvSharp.Size(11, 11), 1.5); Cv2.GaussianBlur(I2, mu2, new OpenCvSharp.Size(11, 11), 1.5); Mat mu1_2 = mu1.Mul(mu1); Mat mu2_2 = mu2.Mul(mu2); Mat mu1_mu2 = mu1.Mul(mu2); Mat sigma1_2 = new Mat(), sigma2_2 = new Mat(), sigma12 = new Mat(); Cv2.GaussianBlur(I1_2, sigma1_2, new OpenCvSharp.Size(11, 11), 1.5); sigma1_2 -= mu1_2; Cv2.GaussianBlur(I2_2, sigma2_2, new OpenCvSharp.Size(11, 11), 1.5); sigma2_2 -= mu2_2; Cv2.GaussianBlur(I1_I2, sigma12, new OpenCvSharp.Size(11, 11), 1.5); sigma12 -= mu1_mu2; ///////////////////////////////// FORMULA //////////////////////////////// Mat t1, t2, t3; t1 = 2 * mu1_mu2 + C1; t2 = 2 * sigma12 + C2; t3 = t1.Mul(t2); // t3 = ((2*mu1_mu2 + C1).*(2*sigma12 + C2)) t1 = mu1_2 + mu2_2 + C1; t2 = sigma1_2 + sigma2_2 + C2; t1 = t1.Mul(t2); // t1 =((mu1_2 + mu2_2 + C1).*(sigma1_2 + sigma2_2 + C2)) Mat ssim_map = new Mat(); Cv2.Divide(t3, t1, ssim_map); // ssim_map = t3./t1; Scalar mssim = Cv2.Mean(ssim_map); // mssim = average of ssim map return(mssim); }
//计算两张图片的相似度SSIM指标 private double countSsim(Mat i1, Mat i2) { const double C1 = 6.5025, C2 = 58.5225; int d = MatType.CV_32F; var I1 = new Mat(); var I2 = new Mat(); i1.ConvertTo(I1, d); i2.ConvertTo(I2, d); Mat I1_2 = I1.Mul(I1); Mat I2_2 = I2.Mul(I2); Mat I1_I2 = I1.Mul(I2); var mu1 = new Mat(); var mu2 = new Mat(); Cv2.GaussianBlur(I1, mu1, new Size(11, 11), 1.5); Cv2.GaussianBlur(I2, mu2, new Size(11, 11), 1.5); Mat mu1_2 = mu1.Mul(mu1); Mat mu2_2 = mu2.Mul(mu2); Mat mu1_mu2 = mu1.Mul(mu2); var sigam1_2 = new Mat(); var sigam2_2 = new Mat(); var sigam12 = new Mat(); Cv2.GaussianBlur(I1_2, sigam1_2, new Size(11, 11), 1.5); sigam1_2 -= mu1_2; Cv2.GaussianBlur(I2_2, sigam2_2, new Size(11, 11), 1.5); sigam2_2 -= mu2_2; Cv2.GaussianBlur(I1_I2, sigam12, new Size(11, 11), 1.5); sigam12 -= mu1_mu2; Mat t1, t2, t3; t1 = 2 * mu1_mu2 + C1; t2 = 2 * sigam12 + C2; t3 = t1.Mul(t2); t1 = mu1_2 + mu2_2 + C1; t2 = sigam1_2 + sigam2_2 + C2; t1 = t1.Mul(t2); Mat ssim_map = new Mat(); Cv2.Divide(t3, t1, ssim_map); Scalar mssim = Cv2.Mean(ssim_map); double ssim = (mssim.Val0 + mssim.Val1 + mssim.Val2) / 3; return(ssim); }
public void ScalarOperations() { var values = new[] { -1f }; using var mat = new Mat(1, 1, MatType.CV_32FC1, values); Assert.Equal(values[0], mat.Get <float>(0, 0)); Cv2.Subtract(mat, 1, mat); Assert.Equal(-2, mat.Get <float>(0, 0)); Cv2.Multiply(mat, 2.0, mat); Assert.Equal(-4, mat.Get <float>(0, 0)); Cv2.Divide(mat, 2.0, mat); Assert.Equal(-2, mat.Get <float>(0, 0)); Cv2.Add(mat, 1, mat); Assert.Equal(-1, mat.Get <float>(0, 0)); }
public void cuda_divide() { Mat mat1 = Image("lenna.png", ImreadModes.Grayscale); Size size = mat1.Size(); Mat mat2 = new Mat(size, mat1.Type(), new Scalar(2)); using (GpuMat g_mat1 = new GpuMat(size, mat1.Type())) using (GpuMat dst = new GpuMat()) { GpuMat g_mat2 = new GpuMat(size, mat2.Type()); g_mat2.Upload(mat2); g_mat1.Upload(mat1); Cuda.cuda.divide(g_mat1, g_mat2, dst, 5.3); Mat dst_gold = new Mat(size, mat1.Type(), Scalar.Black); Cv2.Divide(mat1, mat2, dst_gold, 5.3); ImageEquals(dst_gold, dst); ShowImagesWhenDebugMode(g_mat1, dst); } }
public static SoftwareBitmap AutoColorEnhancement(SoftwareBitmap Input) { using (Mat inputMat = Input.SoftwareBitmapToMat()) using (Mat outputMat = new Mat(inputMat.Rows, inputMat.Cols, MatType.CV_8UC4)) using (Mat Temp = new Mat(inputMat.Rows, inputMat.Cols, MatType.CV_32FC3)) using (Mat Gray = new Mat(inputMat.Rows, inputMat.Cols, MatType.CV_32FC3)) { inputMat.ConvertTo(Temp, MatType.CV_32FC3); Cv2.CvtColor(Temp, Gray, ColorConversionCodes.BGR2GRAY); Cv2.MinMaxLoc(Gray, out _, out double LwMax); float LwAver; int Num; using (Mat Lw_ = new Mat(inputMat.Rows, inputMat.Cols, MatType.CV_32FC3)) { Num = inputMat.Rows * inputMat.Cols; //计算每个数组元素绝对值的自然对数 Cv2.Log(Gray + 1e-3f, Lw_); //矩阵自然指数 LwAver = Convert.ToSingle(Math.Exp(Cv2.Sum(Lw_)[0] / Num)); } using (Mat Lg = new Mat(inputMat.Rows, inputMat.Cols, MatType.CV_32FC3)) { Cv2.Log(Gray / LwAver + 1f, Lg); Cv2.Divide(Lg, Math.Log(LwMax / LwAver + 1f), Lg); //局部自适应 using (Mat Lout = new Mat(inputMat.Rows, inputMat.Cols, MatType.CV_32FC3)) { int kernelSize = Convert.ToInt32(Math.Floor(Convert.ToDouble(Math.Max(3, Math.Max(inputMat.Rows / 100, inputMat.Cols / 100))))); using (Mat Lp = new Mat(inputMat.Rows, inputMat.Cols, MatType.CV_32FC3)) { using (Mat kernel = Cv2.GetStructuringElement(MorphShapes.Rect, new Size(kernelSize, kernelSize))) { Cv2.Dilate(Lg, Lp, kernel); } using (Mat Hg = new Mat(inputMat.Rows, inputMat.Cols, MatType.CV_32FC3)) { CvXImgProc.GuidedFilter(Lg, Lp, Hg, 10, 0.01); Cv2.MinMaxLoc(Lg, out _, out double LgMax); using (Mat alpha = 1.0f + Lg * (36 / LgMax)) using (Mat Lg_ = new Mat(inputMat.Rows, inputMat.Cols, MatType.CV_32FC3)) { Cv2.Log(Lg + 1e-3f, Lg_); float beta = 10 * Convert.ToSingle(Math.Exp(Cv2.Sum(Lg_)[0] / Num)); Cv2.Log(Lg / Hg + beta, Lout); Cv2.Multiply(alpha, Lout, Lout); Cv2.Normalize(Lout, Lout, 0, 255, NormTypes.MinMax); } } } using (Mat gain = new Mat(inputMat.Rows, inputMat.Cols, MatType.CV_32F)) { for (int i = 0; i < inputMat.Rows; i++) { for (int j = 0; j < inputMat.Cols; j++) { float x = Gray.At <float>(i, j); float y = Lout.At <float>(i, j); gain.At <float>(i, j) = 0 == x ? y : y / x; } } Mat[] BGRChannel = Cv2.Split(Temp); try { BGRChannel[0] = (gain.Mul(BGRChannel[0] + Gray) + BGRChannel[0] - Gray) * 0.5f; BGRChannel[1] = (gain.Mul(BGRChannel[1] + Gray) + BGRChannel[1] - Gray) * 0.5f; BGRChannel[2] = (gain.Mul(BGRChannel[2] + Gray) + BGRChannel[2] - Gray) * 0.5f; Cv2.Merge(BGRChannel, Temp); Temp.ConvertTo(outputMat, MatType.CV_8UC4); } finally { Array.ForEach(BGRChannel, (Channel) => Channel.Dispose()); } } } } return(outputMat.MatToSoftwareBitmap()); } }
/// <summary> /// OpenCV4Window 4.4.0 Required /// </summary> /// <param name="bmp1"></param> /// <param name="bmp2"></param> /// <param name="DiffMax"></param> /// <returns></returns> public static bool CompareBitmapsSSIM(Bitmap bmp1, Bitmap bmp2, Double percent) { if (bmp1 == null || bmp2 == null) { return(false); } if (object.Equals(bmp1, bmp2)) { return(false); } if (!bmp1.Size.Equals(bmp2.Size) || !bmp1.PixelFormat.Equals(bmp2.PixelFormat)) { return(false); } Mat img1 = OpenCvSharp.Extensions.BitmapConverter.ToMat(bmp1); Mat img2 = OpenCvSharp.Extensions.BitmapConverter.ToMat(bmp2); const double C1 = 6.5025, C2 = 58.5225; /***************************** INITS **********************************/ MatType d = MatType.CV_32F; Mat I1 = new Mat(), I2 = new Mat(); // cannot calculate on one byte large values img1.ConvertTo(I1, d); img2.ConvertTo(I2, d); Mat I2_2 = I2.Mul(I2); // I2^2 Mat I1_2 = I1.Mul(I1); // I1^2 Mat I1_I2 = I1.Mul(I2); // I1 * I2 /***********************PRELIMINARY COMPUTING ******************************/ Mat mu1 = new Mat(), mu2 = new Mat(); // Cv2.GaussianBlur(I1, mu1, new OpenCvSharp.Size(11, 11), 1.5); Cv2.GaussianBlur(I2, mu2, new OpenCvSharp.Size(11, 11), 1.5); Mat mu1_2 = mu1.Mul(mu1); Mat mu2_2 = mu2.Mul(mu2); Mat mu1_mu2 = mu1.Mul(mu2); Mat sigma1_2 = new Mat(), sigma2_2 = new Mat(), sigma12 = new Mat(); Cv2.GaussianBlur(I1_2, sigma1_2, new OpenCvSharp.Size(11, 11), 1.5); sigma1_2 -= mu1_2; Cv2.GaussianBlur(I2_2, sigma2_2, new OpenCvSharp.Size(11, 11), 1.5); sigma2_2 -= mu2_2; Cv2.GaussianBlur(I1_I2, sigma12, new OpenCvSharp.Size(11, 11), 1.5); sigma12 -= mu1_mu2; ///////////////////////////////// FORMULA //////////////////////////////// Mat t1, t2, t3; t1 = 2 * mu1_mu2 + C1; t2 = 2 * sigma12 + C2; t3 = t1.Mul(t2); // t3 = ((2*mu1_mu2 + C1).*(2*sigma12 + C2)) t1 = mu1_2 + mu2_2 + C1; t2 = sigma1_2 + sigma2_2 + C2; t1 = t1.Mul(t2); // t1 =((mu1_2 + mu2_2 + C1).*(sigma1_2 + sigma2_2 + C2)) Mat ssim_map = new Mat(); Cv2.Divide(t3, t1, ssim_map); // ssim_map = t3./t1; Scalar mssim = Cv2.Mean(ssim_map); // mssim = average of ssim map var ssimValue = (mssim[0] + mssim[1] + mssim[2]) / 3; // Dispose List <IDisposable> listDisposable = new List <IDisposable>(); listDisposable.Add(img1); listDisposable.Add(img2); listDisposable.Add(I1); listDisposable.Add(I2); listDisposable.Add(I2_2); listDisposable.Add(I1_2); listDisposable.Add(I1_I2); listDisposable.Add(mu1); listDisposable.Add(mu2); listDisposable.Add(mu1_2); listDisposable.Add(mu2_2); listDisposable.Add(mu1_mu2); listDisposable.Add(sigma1_2); listDisposable.Add(sigma2_2); listDisposable.Add(sigma12); listDisposable.Add(t1); listDisposable.Add(t2); listDisposable.Add(t3); listDisposable.Add(ssim_map); foreach (var iDispose in listDisposable) { if (iDispose != null) { iDispose.Dispose(); } } return(ssimValue > percent); }
private async Task <List <double> > CalculateBrisqueFeatures(Mat image) { Cv2.SetNumThreads(0); var brisqueFeatures = new List <double>(); Mat originalImage = new Mat(image.Size(), MatType.CV_64FC1, 1); Cv2.CvtColor(image, originalImage, ColorConversionCodes.BGR2GRAY); image.Dispose(); Mat originalImageGrayScale = new Mat(originalImage.Size(), MatType.CV_64FC1, 1); originalImage.ConvertTo(originalImageGrayScale, MatType.CV_64FC1, 1); originalImage.Dispose(); int scaleNum = 2; for (int itr_scale = 1; itr_scale <= scaleNum; itr_scale++) { Size newImageSize = new Size((originalImageGrayScale.Cols / Math.Pow(2, itr_scale - 1)), originalImageGrayScale.Rows / Math.Pow(2, itr_scale - 1)); Mat imagescaledDistance = new Mat(); Cv2.Resize(originalImageGrayScale, imagescaledDistance, newImageSize, 0, 0, InterpolationFlags.Cubic); imagescaledDistance.ConvertTo(imagescaledDistance, MatType.CV_64FC1, 1.0 / 255.0); Mat meanImage = new Mat(imagescaledDistance.Size(), MatType.CV_64FC1, 1.0 / 255.0); Cv2.GaussianBlur(imagescaledDistance, meanImage, new Size(7, 7), 1.166); Mat meanSquareImage = new Mat(); Cv2.Pow(meanImage, 2.0, meanSquareImage); Mat sigma = new Mat(); Cv2.Multiply(imagescaledDistance, imagescaledDistance, sigma); Cv2.GaussianBlur(sigma, sigma, new Size(7, 7), 1.166); Cv2.Subtract(sigma, meanSquareImage, sigma); Cv2.Pow(sigma, 0.5, sigma); Cv2.Add(sigma, new Scalar(1.0 / 255), sigma); Mat structuredDistance = new Mat(imagescaledDistance.Size(), MatType.CV_64FC1, 1); Cv2.Subtract(imagescaledDistance, meanImage, structuredDistance); Cv2.Divide(structuredDistance, sigma, structuredDistance); double lsigma_best = 0, rsigma_best = 0, gamma_best = 0.0; structuredDistance = AGGDFit(structuredDistance, ref lsigma_best, ref rsigma_best, ref gamma_best); brisqueFeatures.Add(gamma_best); brisqueFeatures.Add((lsigma_best * lsigma_best + rsigma_best * rsigma_best) / 2.0); int[,] shifts = new int[, ] { { 0, 1 }, { 1, 0 }, { 1, 1 }, { 1, -1 } }; for (int itr_shift = 1; itr_shift <= 4; itr_shift++) { Mat shiftestructuredDistance = new Mat(imagescaledDistance.Size(), MatType.CV_64FC1, 1); double[,] originalArray = structuredDistance.ToDoubleArray(); double[,] shiftedArray = shiftestructuredDistance.ToDoubleArray(); List <int> Rows = Enumerable.Range(0, structuredDistance.Rows).ToList(); Parallel.ForEach(Rows, i => { for (int j = 0; j < structuredDistance.Cols; j++) { if (i + shifts[itr_shift - 1, 0] >= 0 && i + shifts[itr_shift - 1, 0] < structuredDistance.Rows && j + shifts[itr_shift - 1, 1] >= 0 && j + shifts[itr_shift - 1, 1] < structuredDistance.Cols) { shiftedArray[i, j] = originalArray[i + shifts[itr_shift - 1, 0], j + shifts[itr_shift - 1, 1]]; } else { shiftedArray[i, j] = 0; } } }); shiftestructuredDistance = shiftestructuredDistance.ToMat(shiftedArray); Cv2.Multiply(structuredDistance, shiftestructuredDistance, shiftestructuredDistance); shiftestructuredDistance = AGGDFit(shiftestructuredDistance, ref lsigma_best, ref rsigma_best, ref gamma_best); double constant = Math.Sqrt((SpecialFunctions.Gamma(1 / gamma_best)) / (SpecialFunctions.Gamma(3 / gamma_best))); double meanparam = (rsigma_best - lsigma_best) * (SpecialFunctions.Gamma(2 / gamma_best) / SpecialFunctions.Gamma(1 / gamma_best)) * constant; //feature parameters brisqueFeatures.Add(gamma_best); brisqueFeatures.Add(meanparam); brisqueFeatures.Add(Math.Pow(lsigma_best, 2)); brisqueFeatures.Add(Math.Pow(rsigma_best, 2)); } } return(brisqueFeatures); }
private static void DetectBallsForArticle() { var g_src = new Mat("0.png", LoadMode.GrayScale); var src = new Mat("0.png"); var hsv_src = new Mat(); Cv2.CvtColor(src, hsv_src, ColorConversion.RgbToHsv); var background = new Mat("background.png"); var g_background = new Mat("background.png", LoadMode.GrayScale); var hsv_background = new Mat(); Cv2.CvtColor(background, hsv_background, ColorConversion.RgbToHsv); var canny = new Mat(); var dst2 = new Mat(); Cv2.Canny(g_src, canny, 50, 200); Cv2.Threshold(src, dst2, 50, 200, OpenCvSharp.ThresholdType.Binary); //Cv2.Subtract(g_src, g_background, dst2); //Cv2.Absdiff(g_src, g_background, dst2); //Cv2.Subtract(src, background, dst2); Cv2.Absdiff(src, background, dst2); //dst2.ImWrite("diff.bmp"); Mat[] dst2_channels; Cv2.Split(dst2, out dst2_channels); Mat[] background_channels; Cv2.Split(background, out background_channels); Mat[] hsv_background_channels; Cv2.Split(hsv_background, out hsv_background_channels); Mat[] hsv_src_channels; Cv2.Split(hsv_src, out hsv_src_channels); var div_0 = new Mat(); //Cv2.Divide(dst2_channels[1], background_channels[1], div_0, scale:50); Cv2.Divide(background_channels[1], dst2_channels[1], div_0, scale: 40); Mat dst2_01 = new Mat(); Mat dst2_12 = new Mat(); Mat dst2_012 = new Mat(); Cv2.Absdiff(dst2_channels[0], dst2_channels[1], dst2_01); Cv2.Absdiff(dst2_channels[1], dst2_channels[2], dst2_12); Cv2.Add(dst2_01, dst2_12, dst2_012); var hsv_diff = Enumerable.Range(0, 3).Select(i => new Mat()).ToArray(); for (var i = 0; i < 3; ++i) { Cv2.Absdiff(hsv_src_channels[i], hsv_background_channels[i], hsv_diff[i]); } //Cv2.Compare(dst2_channels[2], t_dst, t_dst); var dst3 = new Mat(); Cv2.Threshold(dst2_012, dst3, 60, 255, ThresholdType.Binary); //OpenCvSharp.CPlusPlus.Cv2.CvtColor(dst2, dst3, OpenCvSharp.ColorConversion.RgbToGray); //var circles = OpenCvSharp.CPlusPlus.Cv2.HoughCircles(dst3, OpenCvSharp.HoughCirclesMethod.Gradient, 1, 10, minRadius:10, maxRadius: 80); //foreach (var circle in circles) // Console.WriteLine(circle); //Console.WriteLine(hsv_diff[0]); //hsv_diff[1].ImWrite("hsv_diff_s.bmp"); DetectBallView(hsv_diff[1], hsv_diff[0]); return; //using (new Window("src image", src)) //using (new Window("dst image", background)) ////using (new Window("canny", canny)) //using (new Window("dst2 image", dst2)) //using (new Window("diff0", dst2_channels[1])) //using (new Window("bg0", background_channels[1])) //using (new Window("dst3 image", div_0)) using (new Window("src h", hsv_src_channels[0])) using (new Window("bg h", hsv_background_channels[0])) using (new Window("d h", hsv_diff[0])) using (new Window("src s", hsv_src_channels[1])) using (new Window("bg s", hsv_background_channels[1])) using (new Window("d s", hsv_diff[1])) using (new Window("src v", hsv_src_channels[2])) using (new Window("bg v", hsv_background_channels[2])) using (new Window("d v", hsv_diff[2])) { Cv2.WaitKey(); } }