Exemple #1
0
        /// <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);
        }
Exemple #2
0
        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));
        }
Exemple #3
0
        /// <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);
        }
Exemple #5
0
        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());
                        }
        }
Exemple #8
0
        /// <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);
        }
Exemple #10
0
        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();
                                            }
        }