.net6下 OpenCvSharp4的Demon

使用的是OpenCvSharp4版本,这个OpenCv库支持.netCore和Framework

Opencv是一个常用的机器视觉的工具包,常用的有C++和python版本,但是我觉得还是C#版本好用,毕竟winform 比QT和pyQt 友好太多。

版本信息:

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>WinExe</OutputType>
    <TargetFramework>net6.0-windows</TargetFramework>
    <Nullable>enable</Nullable>
    <UseWindowsForms>true</UseWindowsForms>
    <ImplicitUsings>enable</ImplicitUsings>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="OpenCvSharp4" Version="4.6.0.20220608" />
    <PackageReference Include="OpenCvSharp4.Extensions" Version="4.6.0.20220608" />
    <PackageReference Include="OpenCvSharp4.runtime.win" Version="4.6.0.20220608" />
  </ItemGroup>

</Project>

代码:

using OpenCvSharp;
using OpenCvSharp.Extensions;
using System.ComponentModel;

namespace CVSharpDemon
{
    public partial class FrmMain : Form
    {
        private readonly VideoCapture capture;
        private string fileName;
        private Mat mInput;
        private Mat blur;
        private Mat edges;

        public FrmMain()
        {
            InitializeComponent();
            this.capture = new VideoCapture();
            this.Load += FrmMain_Load;
        }
        
        private void FrmMain_Load(object? sender, EventArgs e)
        {
            //Mat src = new Mat(@"C:\Users\14348\Desktop\dd.png", ImreadModes.Grayscale);
             Mat src = Cv2.ImRead("lenna.png", ImreadModes.GrayScale);
            //Mat dst = new Mat();
            //this.pictureBox1.Image = src.ToBitmap();

            //Cv2.Canny(src, dst, 50, 200);
            //this.pictureBox2.Image = dst.ToBitmap();

            this.txtBox.Text = "50";
            this.txtBox2.Text = "100";

        }
        CancellationTokenSource cts = new CancellationTokenSource();
        private void openCameraBtn_Click(object sender, EventArgs e)
        {
            Task.Run(async () =>
            {
                if (capture.IsOpened())
                {
                    closeCamera();
                }
                else
                {
                    openCamera();
                    while (!cts.IsCancellationRequested)
                    {
                        using (var frameMat = capture.RetrieveMat())
                        {
                            var frameBitmap = BitmapConverter.ToBitmap(frameMat);
                            pictureBox1.Image?.Dispose();
                            pictureBox1.Image = frameBitmap;
                        }
                        await Task.Delay(100);
                    }
                }
            }, cts.Token);

        }

        //打开摄像头
        private void openCamera()
        {
            capture.Open(0, VideoCaptureAPIs.ANY);

            //MessageBox.Show("打开摄像头成功");
            Action action = () => { this.openCameraBtn.Text = "关闭摄像头"; };
            this.openCameraBtn.Invoke(action);
            //openCameraBtn.Text = "关闭摄像头";
        }
        //关闭摄像头
        private void closeCamera()
        {
            capture.Release();
            Action action = () => { this.openCameraBtn.Text = "打开摄像头"; };
            openCameraBtn.Invoke(action);
            pictureBox1.Image = null;
        }

        //打开图片
        private void btnOpenPic_Click(object sender, EventArgs e)
        {
            OpenFileDialog openfiledialog = new OpenFileDialog();
            openfiledialog.Filter = "PNG Files (*.png)|*.png|JPG Files (*.jpg)|*.jpg|GIF Files (*.gif)|*.gif";
            openfiledialog.RestoreDirectory = true;
            if (openfiledialog.ShowDialog() == DialogResult.OK)
            {
                Console.WriteLine(openfiledialog.FileName);
                fileName = openfiledialog.FileName;

                //Mat src = new Mat("foo.png", LoadMode.Color);
                Mat src = new Mat(fileName);
                //Mat src = new Mat(fileName, ImreadModes.Color);
                var frameBitmap = BitmapConverter.ToBitmap(src);

                pictureBox1.Image?.Dispose();
                pictureBox1.Image = frameBitmap;
            }
        }

        /*
         Gaussian Blur,高斯模糊
        减少图像噪声以及降低细节层次
        高斯平滑也用于计算机视觉算法中的预先处理阶段,
        以增强图像在不同比例大小下的图像效果(参见尺度空间表示以及尺度空间实现)。 
        从数学的角度来看,图像的高斯模糊过程就是图像与正态分布做卷积。
        由于正态分布又叫作高斯分布,所以这项技术就叫作高斯模糊。
         摘要:
             Blurs an image using a Gaussian filter.

         参数:
           src:
             input image; the image can have any number of channels, which are processed independently,
             but the depth should be CV_8U, CV_16U, CV_16S, CV_32F or CV_64F.

           dst:
             output image of the same size and type as src.

           ksize:
             Gaussian kernel size. ksize.width and ksize.height can differ but they both must
             be positive and odd. Or, they can be zero’s and then they are computed from sigma*
             .

           sigmaX:
             Gaussian kernel standard deviation in X direction.

           sigmaY:
             Gaussian kernel standard deviation in Y direction; if sigmaY is zero, it is set
             to be equal to sigmaX, if both sigmas are zeros, they are computed from ksize.width
             and ksize.height, respectively (see getGaussianKernel() for details); to fully
             control the result regardless of possible future modifications of all this semantics,
             it is recommended to specify all of ksize, sigmaX, and sigmaY.

           borderType:
             pixel extrapolation method
           
            public static void GaussianBlur(InputArray src, OutputArray dst, Size ksize, double sigmaX, double sigmaY = 0, BorderTypes borderType = BorderTypes.Reflect101);
         */

        private void btnGaussianBlur_Click(object sender, EventArgs e)
        {
            if (String.IsNullOrEmpty(fileName))
            {
                MessageBox.Show("请先打开一个图片");
                return;
            }
            mInput = new Mat(fileName);
            blur = new Mat(mInput.Rows, mInput.Cols, MatType.CV_8UC4);

            OpenCvSharp.Size ksize = new OpenCvSharp.Size(7, 7);
            OpenCvSharp.Point anchor = new OpenCvSharp.Point(3, 3);
            BorderTypes borderType = BorderTypes.Constant;

            //Cv2.Blur(mInput, blur, ksize, anchor, borderType);  //模糊
            Cv2.GaussianBlur(mInput, blur, ksize, 0);  //高斯模糊

            pictureBox2.Image = BitmapConverter.ToBitmap(mInput);
            pictureBox3.Image = BitmapConverter.ToBitmap(blur);

        }

        /*
         Canny 的目标是找到一个最优的边缘检测算法,最优边缘检测的含义是:
        (1) 最优检测:算法能够尽可能多地标识出图像中的实际边缘,漏检真实边缘的概率和误检非边缘的概率都尽可能小;
        (2) 最优定位准则:检测到的边缘点的位置距离实际边缘点的位置最近,或者是由于噪声影响引起检测出的边缘偏离物体的真实边缘的程度最小;
        (3) 检测点与边缘点一一对应:算子检测的边缘点与实际边缘点应该是一一对应。

         摘要:
             Finds edges in an image using Canny algorithm.
    
         参数:
           src:
             Single-channel 8-bit input image
    
           edges:
             The output edge map. It will have the same size and the same type as image
    
           threshold1:
             The first threshold for the hysteresis procedure
    
           threshold2:
             The second threshold for the hysteresis procedure
    
           apertureSize:
             Aperture size for the Sobel operator [By default this is ApertureSize.Size3]
    
           L2gradient:
             Indicates, whether the more accurate L2 norm should be used to compute the image
             gradient magnitude (true), or a faster default L1 norm is enough (false). [By
             default this is false]
        public static void Canny(InputArray src, OutputArray edges, double threshold1, double threshold2, int apertureSize = 3, bool L2gradient = false);

         */

        private void btnCanny_Click(object sender, EventArgs e)
        {
            if (String.IsNullOrEmpty(fileName))
            {
                MessageBox.Show("请先打开一个图片");
                return;
            }
            mInput = new Mat(fileName);
            blur = new Mat(mInput.Rows, mInput.Cols, MatType.CV_8UC4);

            OpenCvSharp.Size ksize = new OpenCvSharp.Size(5, 5);
            Cv2.GaussianBlur(mInput, blur, ksize, 0);  //高斯模糊

            edges = new Mat(mInput.Rows, mInput.Cols, MatType.CV_8UC4);
            double threshold1 = double.Parse(this.txtBox.Text.Trim().ToString());
            double threshold2 = double.Parse(this.txtBox2.Text.Trim().ToString());
            int apertureSize = 3;

            Cv2.Canny(blur, edges, threshold1, threshold2, apertureSize);     //边缘检测

            pictureBox1.Image = BitmapConverter.ToBitmap(mInput);
            pictureBox2.Image = BitmapConverter.ToBitmap(blur);
            pictureBox3.Image = BitmapConverter.ToBitmap(edges);
        }
        /*
         MSER = Maximally Stable Extremal Regions
        最大极值稳定区
        业界认为是性能最好的仿射不变区域,MSER是当使用不同的灰度阈值对图像进行二值化时得到的最稳定的区域,特点:
        1.对于图像灰度的仿射变化具有不变性
        2.稳定性,区域的支持集相对灰度变化稳定
        3.可以检测不同精细程度的区域

        流程:
        1,使用一系列灰度阈值对图像进行二值化处理
        2,对于每个阈值得到的二值图像,得到相应的黑色区域与白色区域
        3,在比较宽的灰度阈值范围内保持形状稳定的区域就是MSERs
        评判标准: dA/dt,A: 二值图像区域面积,t: 灰度

        MSER在detect的时候不需要传特别的参数
        只需要在Create的时候传递参数

        
         摘要:
             Creates MSER parameters

         参数:
           delta:
             delta, in the code, it compares (size_{i}-size_{i-delta})/size_{i-delta}

           minArea:
             prune the area which smaller than min_area

           maxArea:
             prune the area which bigger than max_area

           maxVariation:
             prune the area have simliar size to its children

           minDiversity:
             trace back to cut off mser with diversity < min_diversity

           maxEvolution:
             for color image, the evolution steps

           areaThreshold:
             the area threshold to cause re-initialize

           minMargin:
             ignore too small margin

           edgeBlurSize:
             the aperture size for edge blur

        public static MSER Create(int delta = 5, int minArea = 60, int maxArea = 14400, double maxVariation = 0.25, double minDiversity = 0.2, int maxEvolution = 200, double areaThreshold = 1.01, double minMargin = 0.003, int edgeBlurSize = 5);

         */
        private void btnMSER_Click(object sender, EventArgs e)
        {
            if (String.IsNullOrEmpty(fileName))
            {
                MessageBox.Show("请先打开一个图片");
                return;
            }

            Mat src = new Mat(fileName);
            Mat gray = new Mat();
            Mat dst = src.Clone();

            Cv2.CvtColor(src, gray, ColorConversionCodes.BGR2GRAY);

            int delta = 5;
            int minArea = 60;
            int maxArea = 14400;
            //int maxArea = 100000;
            double maxVariation = 0.25;

            MSER mser = MSER.Create(delta, minArea, maxArea, maxVariation);

            DateTime currentTime = DateTime.Now;
            KeyPoint[] contours = mser.Detect(gray, null);
            resultLabel.Text = "Detect耗时:" + (System.DateTime.Now - currentTime).TotalMilliseconds + "ms";
            Console.WriteLine("contours len=" + contours.Length);

            currentTime = DateTime.Now;
            OpenCvSharp.Point[][] outPoint;
            Rect[] bboxes;
            InputArray input = InputArray.Create(gray);
            mser.DetectRegions(input, out outPoint, out bboxes);
            resultLabel.Text += ",  DetectRegions耗时:" + (System.DateTime.Now - currentTime).TotalMilliseconds + "ms";
            Console.WriteLine("bboxes len=" + bboxes.Length);

            gray = src.Clone();
            foreach (KeyPoint pts in contours)
            {
                gray.Circle((int)pts.Pt.X, (int)pts.Pt.Y, (int)pts.Size, Scalar.Black);
                gray.Circle((int)pts.Pt.X, (int)pts.Pt.Y, (int)1, Scalar.Black, 2);
                Console.WriteLine("(" + pts.Pt.X + " , " + pts.Pt.Y + ") Size=" + pts.Size + ", Angle=" + pts.Angle + ", Response=" + pts.Response + ", ClassId=" + pts.ClassId);
            }

            foreach (OpenCvSharp.Point[] pts in outPoint)
            {
                foreach (OpenCvSharp.Point p in pts)
                {
                    dst.Circle(p, 1, Scalar.Black);
                }
            }
            foreach (Rect b in bboxes)
            {
                dst.Rectangle(b, Scalar.Black, 2);
            }

            pictureBox1.Image = BitmapConverter.ToBitmap(src);
            pictureBox2.Image = BitmapConverter.ToBitmap(gray);
            pictureBox3.Image = BitmapConverter.ToBitmap(dst);

        }

        /*
         基于边缘检测算法的轮廊寻找和提取

        算法流程
        1,高斯模糊
        2,Canny边缘检测
        3,FindContours寻找轮廓,轮廊提取

         摘要:
           Finds contours in a binary image.

         参数:
           image:
             Source, an 8-bit single-channel image. Non-zero pixels are treated as 1’s. Zero
             pixels remain 0’s, so the image is treated as binary. The function modifies the
             image while extracting the contours.

           contours:
             Detected contours. Each contour is stored as a vector of points.

           hierarchy:
             Optional output vector, containing information about the image topology. It has
             as many elements as the number of contours. For each i-th contour contours[i],
             the members of the elements hierarchy[i] are set to 0-based indices in contours
             of the next and previous contours at the same hierarchical level, the first child
             contour and the parent contour, respectively. If for the contour i there are
             no next, previous, parent, or nested contours, the corresponding elements of
             hierarchy[i] will be negative.

           mode:
             Contour retrieval mode

           method:
             Contour approximation method

           offset:
             Optional offset by which every contour point is shifted. This is useful if the
             contours are extracted from the image ROI and then they should be analyzed in
             the whole image context.

        public static void FindContours(InputArray image, out Point[][] contours, out HierarchyIndex[] hierarchy, RetrievalModes mode, ContourApproximationModes method, Point? offset = null);

        image,灰度图片输入
        contours,轮廓结果输出
        mode,轮廓检索模式
        External,只检测外层轮廓
        List,提取所有轮廓,并放置在list中,检测的轮廓不建立等级关系
        CComp,提取所有轮廓,并将轮廓组织成双层结构(two-level hierarchy),顶层为连通域的外围边界,次层位内层边界
        Tree,提取所有轮廓并重新建立网状轮廓结构
        FloodFill,官网没有介绍,应该是洪水填充法
        method,轮廓近似方法
        ApproxNone,获取每个轮廓的每个像素,相邻的两个点的像素位置差不超过1
        ApproxSimple,压缩水平方向,垂直方向,对角线方向的元素,值保留该方向的重点坐标,如果一个矩形轮廓只需4个点来保存轮廓信息
        ApproxTC89L1,使用Teh-Chinl链逼近算法中的一种
        ApproxTC89KCOS,使用Teh-Chinl链逼近算法中的一种

         */

        private void btnContours_Click(object sender, EventArgs e)
        {
            if (String.IsNullOrEmpty(fileName))
            {
                MessageBox.Show("请先打开一个图片");
                return;
            }
            btnCanny_Click(sender, e);
            Mat mOutput = new Mat(mInput.Rows, mInput.Cols, MatType.CV_8UC4);
            mInput.CopyTo(mOutput);

            RetrievalModes mode = RetrievalModes.External;
            ContourApproximationModes method = ContourApproximationModes.ApproxNone;
            OpenCvSharp.Point offset = new OpenCvSharp.Point(0, 0);

            Cv2.FindContours(
                image: edges,
                contours: out OpenCvSharp.Point[][] contours,
                hierarchy: out HierarchyIndex[] outputArray,
                mode: mode,
                method: method,
                offset: offset);

            for (int i = 0; i < contours.Length; i++)
            {
                Scalar color = Scalar.RandomColor();
                if (contours[i].Length > 100)
                {
                    Cv2.DrawContours(
                        mOutput,
                        contours,
                        contourIdx: i,
                        color: color,
                        thickness: 2,
                        lineType: LineTypes.Link8,
                        hierarchy: outputArray,
                        maxLevel: 0);
                }
            }

            pictureBox1.Image = BitmapConverter.ToBitmap(mInput);
            pictureBox2.Image = BitmapConverter.ToBitmap(edges);
            pictureBox3.Image = BitmapConverter.ToBitmap(mOutput);

        }

        /*
         Hough变换
        经典的直线检测算法

        算法流程:
        1,高斯模糊
        2,Canny边缘检测
        3,HoughLinesP直线寻找,直线提取

         摘要:
             Finds lines segments in a binary image using probabilistic Hough transform.
        
         参数:
           image:
        
           rho:
             Distance resolution of the accumulator in pixels
        
           theta:
             Angle resolution of the accumulator in radians
        
           threshold:
             The accumulator threshold parameter. Only those lines are returned that get enough
             votes ( > threshold )
        
           minLineLength:
             The minimum line length. Line segments shorter than that will be rejected. [By
             default this is 0]
        
           maxLineGap:
             The maximum allowed gap between points on the same line to link them. [By default
             this is 0]
        
         返回结果:
             The output lines. Each line is represented by a 4-element vector (x1, y1, x2,
             y2)

        public static LineSegmentPoint[] HoughLinesP(InputArray image, double rho, double theta, int threshold, double minLineLength = 0, double maxLineGap = 0);

        image,灰度图片输入
        rho,步长为1的半径
        theta,步长为π/180的角来,来搜索所有可能的直线
        threshold,阈值,大于阈值 threshold 的线段才可以被确认为直线。该值越小,判定出的直线越多;值越大,判定出的直线就越少。
        minLineLength,线段的最短长度,长度小于该值的线段会被忽略
        maxLineGap,两条线段之间的最大间隔,间隔小于该值的两条线会被当成一条线

         */

        private void btnHouhLines_Click(object sender, EventArgs e)
        {
            btnCanny_Click(sender, e);
            Mat mOutput = new Mat(mInput.Rows, mInput.Cols, MatType.CV_8UC4);
            mInput.CopyTo(mOutput);

            double rho = 1;
            double theta = 1;
            int threshold = 10;
            double minLineLength = 0;
            double maxLineGap = 0;

            var res = Cv2.HoughLinesP(edges,
                rho: rho,
                theta: theta / 100.0,
                threshold: threshold,
                minLineLength: minLineLength,
                maxLineGap: maxLineGap);

            for (int i = 0; i < res.Length; i++)
            {
                Scalar color = Scalar.RandomColor();
                Cv2.Line(mOutput, res[i].P1, res[i].P2,
                    color: color,
                    thickness: 2,
                    lineType: LineTypes.Link8);
            }

            pictureBox1.Image = BitmapConverter.ToBitmap(mInput);
            pictureBox2.Image = BitmapConverter.ToBitmap(edges);
            pictureBox3.Image = BitmapConverter.ToBitmap(mOutput);

        }
    }
}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

using OpenCvSharp;
using OpenCvSharp.Extensions;
using System.ComponentModel;

namespace CVSharpDemon
{
    public partial class FrmMain : Form
    {
        private readonly VideoCapture capture;
        private string fileName;
        private Mat mInput;
        private Mat blur;
        private Mat edges;

        public FrmMain()
        {
            InitializeComponent();
            this.capture = new VideoCapture();
            this.Load += FrmMain_Load;
        }
        
        private void FrmMain_Load(object? sender, EventArgs e)
        {
            //Mat src = new Mat(@"C:\Users\14348\Desktop\dd.png", ImreadModes.Grayscale);
             Mat src = Cv2.ImRead("lenna.png", ImreadModes.GrayScale);
            //Mat dst = new Mat();
            //this.pictureBox1.Image = src.ToBitmap();

            //Cv2.Canny(src, dst, 50, 200);
            //this.pictureBox2.Image = dst.ToBitmap();

            this.txtBox.Text = "50";
            this.txtBox2.Text = "100";
            this.trackBar1.Value = 50;
            this.trackBar2.Value = 100;

        }
        CancellationTokenSource cts = new CancellationTokenSource();
        private void openCameraBtn_Click(object sender, EventArgs e)
        {
            Task.Run(async () =>
            {
                if (capture.IsOpened())
                {
                    closeCamera();
                }
                else
                {
                    openCamera();
                    while (!cts.IsCancellationRequested)
                    {
                        using (var frameMat = capture.RetrieveMat())
                        {
                            var frameBitmap = BitmapConverter.ToBitmap(frameMat);
                            pictureBox1.Image?.Dispose();
                            pictureBox1.Image = frameBitmap;
                        }
                        await Task.Delay(100);
                    }
                }
            }, cts.Token);

        }

        //打开摄像头
        private void openCamera()
        {
            capture.Open(0, VideoCaptureAPIs.ANY);

            //MessageBox.Show("打开摄像头成功");
            Action action = () => { this.openCameraBtn.Text = "关闭摄像头"; };
            this.openCameraBtn.Invoke(action);
            //openCameraBtn.Text = "关闭摄像头";
        }
        //关闭摄像头
        private void closeCamera()
        {
            capture.Release();
            Action action = () => { this.openCameraBtn.Text = "打开摄像头"; };
            openCameraBtn.Invoke(action);
            pictureBox1.Image = null;
        }
        Mat src;
        //打开图片
        private void btnOpenPic_Click(object sender, EventArgs e)
        {
            OpenFileDialog openfiledialog = new OpenFileDialog();
            openfiledialog.Filter = "PNG Files (*.png)|*.png|JPG Files (*.jpg)|*.jpg|GIF Files (*.gif)|*.gif";
            openfiledialog.RestoreDirectory = true;
            if (openfiledialog.ShowDialog() == DialogResult.OK)
            {
                Console.WriteLine(openfiledialog.FileName);
                fileName = openfiledialog.FileName;

                //Mat src = new Mat("foo.png", LoadMode.Color);
                src = new Mat(fileName);
                //Mat src = new Mat(fileName, ImreadModes.Color);
                var frameBitmap = BitmapConverter.ToBitmap(src);

                pictureBox1.Image?.Dispose();
                pictureBox1.Image = frameBitmap;
            }
        }

        /*
         Gaussian Blur,高斯模糊
        减少图像噪声以及降低细节层次
        高斯平滑也用于计算机视觉算法中的预先处理阶段,
        以增强图像在不同比例大小下的图像效果(参见尺度空间表示以及尺度空间实现)。 
        从数学的角度来看,图像的高斯模糊过程就是图像与正态分布做卷积。
        由于正态分布又叫作高斯分布,所以这项技术就叫作高斯模糊。
         摘要:
             Blurs an image using a Gaussian filter.

         参数:
           src:
             input image; the image can have any number of channels, which are processed independently,
             but the depth should be CV_8U, CV_16U, CV_16S, CV_32F or CV_64F.

           dst:
             output image of the same size and type as src.

           ksize:
             Gaussian kernel size. ksize.width and ksize.height can differ but they both must
             be positive and odd. Or, they can be zero’s and then they are computed from sigma*
             .

           sigmaX:
             Gaussian kernel standard deviation in X direction.

           sigmaY:
             Gaussian kernel standard deviation in Y direction; if sigmaY is zero, it is set
             to be equal to sigmaX, if both sigmas are zeros, they are computed from ksize.width
             and ksize.height, respectively (see getGaussianKernel() for details); to fully
             control the result regardless of possible future modifications of all this semantics,
             it is recommended to specify all of ksize, sigmaX, and sigmaY.

           borderType:
             pixel extrapolation method
           
            public static void GaussianBlur(InputArray src, OutputArray dst, Size ksize, double sigmaX, double sigmaY = 0, BorderTypes borderType = BorderTypes.Reflect101);
         */

        private void btnGaussianBlur_Click(object sender, EventArgs e)
        {
            if (String.IsNullOrEmpty(fileName))
            {
                MessageBox.Show("请先打开一个图片");
                return;
            }
            mInput = new Mat(fileName);
            blur = new Mat(mInput.Rows, mInput.Cols, MatType.CV_8UC4);

            OpenCvSharp.Size ksize = new OpenCvSharp.Size(7, 7);
            OpenCvSharp.Point anchor = new OpenCvSharp.Point(3, 3);
            BorderTypes borderType = BorderTypes.Constant;

            //Cv2.Blur(mInput, blur, ksize, anchor, borderType);  //模糊
            Cv2.GaussianBlur(mInput, blur, ksize, 0);  //高斯模糊

            pictureBox2.Image = BitmapConverter.ToBitmap(mInput);
            pictureBox3.Image = BitmapConverter.ToBitmap(blur);

        }

        /*
         Canny 的目标是找到一个最优的边缘检测算法,最优边缘检测的含义是:
        (1) 最优检测:算法能够尽可能多地标识出图像中的实际边缘,漏检真实边缘的概率和误检非边缘的概率都尽可能小;
        (2) 最优定位准则:检测到的边缘点的位置距离实际边缘点的位置最近,或者是由于噪声影响引起检测出的边缘偏离物体的真实边缘的程度最小;
        (3) 检测点与边缘点一一对应:算子检测的边缘点与实际边缘点应该是一一对应。

         摘要:
             Finds edges in an image using Canny algorithm.
    
         参数:
           src:
             Single-channel 8-bit input image
    
           edges:
             The output edge map. It will have the same size and the same type as image
    
           threshold1:
             The first threshold for the hysteresis procedure
    
           threshold2:
             The second threshold for the hysteresis procedure
    
           apertureSize:
             Aperture size for the Sobel operator [By default this is ApertureSize.Size3]
    
           L2gradient:
             Indicates, whether the more accurate L2 norm should be used to compute the image
             gradient magnitude (true), or a faster default L1 norm is enough (false). [By
             default this is false]
        public static void Canny(InputArray src, OutputArray edges, double threshold1, double threshold2, int apertureSize = 3, bool L2gradient = false);

         */

        private void btnCanny_Click(object sender, EventArgs e)
        {
            if (String.IsNullOrEmpty(fileName))
            {
                MessageBox.Show("请先打开一个图片");
                return;
            }
            mInput = new Mat(fileName);
            blur = new Mat(mInput.Rows, mInput.Cols, MatType.CV_8UC4);

            OpenCvSharp.Size ksize = new OpenCvSharp.Size(5, 5);
            Cv2.GaussianBlur(mInput, blur, ksize, 0);  //高斯模糊

            edges = new Mat(mInput.Rows, mInput.Cols, MatType.CV_8UC4);
            double threshold1 = double.Parse(this.trackBar1.Value.ToString());
            double threshold2 = double.Parse(this.trackBar2.Value.ToString());
            int apertureSize = 3;

            Cv2.Canny(blur, edges, threshold1, threshold2, apertureSize);     //边缘检测

            pictureBox1.Image = BitmapConverter.ToBitmap(mInput);
            pictureBox2.Image = BitmapConverter.ToBitmap(blur);
            pictureBox3.Image = BitmapConverter.ToBitmap(edges);
        }
        /*
         MSER = Maximally Stable Extremal Regions
        最大极值稳定区
        业界认为是性能最好的仿射不变区域,MSER是当使用不同的灰度阈值对图像进行二值化时得到的最稳定的区域,特点:
        1.对于图像灰度的仿射变化具有不变性
        2.稳定性,区域的支持集相对灰度变化稳定
        3.可以检测不同精细程度的区域

        流程:
        1,使用一系列灰度阈值对图像进行二值化处理
        2,对于每个阈值得到的二值图像,得到相应的黑色区域与白色区域
        3,在比较宽的灰度阈值范围内保持形状稳定的区域就是MSERs
        评判标准: dA/dt,A: 二值图像区域面积,t: 灰度

        MSER在detect的时候不需要传特别的参数
        只需要在Create的时候传递参数

        
         摘要:
             Creates MSER parameters

         参数:
           delta:
             delta, in the code, it compares (size_{i}-size_{i-delta})/size_{i-delta}

           minArea:
             prune the area which smaller than min_area

           maxArea:
             prune the area which bigger than max_area

           maxVariation:
             prune the area have simliar size to its children

           minDiversity:
             trace back to cut off mser with diversity < min_diversity

           maxEvolution:
             for color image, the evolution steps

           areaThreshold:
             the area threshold to cause re-initialize

           minMargin:
             ignore too small margin

           edgeBlurSize:
             the aperture size for edge blur

        public static MSER Create(int delta = 5, int minArea = 60, int maxArea = 14400, double maxVariation = 0.25, double minDiversity = 0.2, int maxEvolution = 200, double areaThreshold = 1.01, double minMargin = 0.003, int edgeBlurSize = 5);

         */
        private void btnMSER_Click(object sender, EventArgs e)
        {
            if (String.IsNullOrEmpty(fileName))
            {
                MessageBox.Show("请先打开一个图片");
                return;
            }

            Mat src = new Mat(fileName);
            Mat gray = new Mat();
            Mat dst = src.Clone();

            Cv2.CvtColor(src, gray, ColorConversionCodes.BGR2GRAY);

            int delta = 5;
            int minArea = 60;
            int maxArea = 14400;
            //int maxArea = 100000;
            double maxVariation = 0.25;

            MSER mser = MSER.Create(delta, minArea, maxArea, maxVariation);

            DateTime currentTime = DateTime.Now;
            KeyPoint[] contours = mser.Detect(gray, null);
            resultLabel.Text = "Detect耗时:" + (System.DateTime.Now - currentTime).TotalMilliseconds + "ms";
            Console.WriteLine("contours len=" + contours.Length);

            currentTime = DateTime.Now;
            OpenCvSharp.Point[][] outPoint;
            Rect[] bboxes;
            InputArray input = InputArray.Create(gray);
            mser.DetectRegions(input, out outPoint, out bboxes);
            resultLabel.Text += ",  DetectRegions耗时:" + (System.DateTime.Now - currentTime).TotalMilliseconds + "ms";
            Console.WriteLine("bboxes len=" + bboxes.Length);

            gray = src.Clone();
            foreach (KeyPoint pts in contours)
            {
                gray.Circle((int)pts.Pt.X, (int)pts.Pt.Y, (int)pts.Size, Scalar.Black);
                gray.Circle((int)pts.Pt.X, (int)pts.Pt.Y, (int)1, Scalar.Black, 2);
                Console.WriteLine("(" + pts.Pt.X + " , " + pts.Pt.Y + ") Size=" + pts.Size + ", Angle=" + pts.Angle + ", Response=" + pts.Response + ", ClassId=" + pts.ClassId);
            }

            foreach (OpenCvSharp.Point[] pts in outPoint)
            {
                foreach (OpenCvSharp.Point p in pts)
                {
                    dst.Circle(p, 1, Scalar.Black);
                }
            }
            foreach (Rect b in bboxes)
            {
                dst.Rectangle(b, Scalar.Black, 2);
            }

            pictureBox1.Image = BitmapConverter.ToBitmap(src);
            pictureBox2.Image = BitmapConverter.ToBitmap(gray);
            pictureBox3.Image = BitmapConverter.ToBitmap(dst);

        }

        /*
         基于边缘检测算法的轮廊寻找和提取

        算法流程
        1,高斯模糊
        2,Canny边缘检测
        3,FindContours寻找轮廓,轮廊提取

         摘要:
           Finds contours in a binary image.

         参数:
           image:
             Source, an 8-bit single-channel image. Non-zero pixels are treated as 1’s. Zero
             pixels remain 0’s, so the image is treated as binary. The function modifies the
             image while extracting the contours.

           contours:
             Detected contours. Each contour is stored as a vector of points.

           hierarchy:
             Optional output vector, containing information about the image topology. It has
             as many elements as the number of contours. For each i-th contour contours[i],
             the members of the elements hierarchy[i] are set to 0-based indices in contours
             of the next and previous contours at the same hierarchical level, the first child
             contour and the parent contour, respectively. If for the contour i there are
             no next, previous, parent, or nested contours, the corresponding elements of
             hierarchy[i] will be negative.

           mode:
             Contour retrieval mode

           method:
             Contour approximation method

           offset:
             Optional offset by which every contour point is shifted. This is useful if the
             contours are extracted from the image ROI and then they should be analyzed in
             the whole image context.

        public static void FindContours(InputArray image, out Point[][] contours, out HierarchyIndex[] hierarchy, RetrievalModes mode, ContourApproximationModes method, Point? offset = null);

        image,灰度图片输入
        contours,轮廓结果输出
        mode,轮廓检索模式
        External,只检测外层轮廓
        List,提取所有轮廓,并放置在list中,检测的轮廓不建立等级关系
        CComp,提取所有轮廓,并将轮廓组织成双层结构(two-level hierarchy),顶层为连通域的外围边界,次层位内层边界
        Tree,提取所有轮廓并重新建立网状轮廓结构
        FloodFill,官网没有介绍,应该是洪水填充法
        method,轮廓近似方法
        ApproxNone,获取每个轮廓的每个像素,相邻的两个点的像素位置差不超过1
        ApproxSimple,压缩水平方向,垂直方向,对角线方向的元素,值保留该方向的重点坐标,如果一个矩形轮廓只需4个点来保存轮廓信息
        ApproxTC89L1,使用Teh-Chinl链逼近算法中的一种
        ApproxTC89KCOS,使用Teh-Chinl链逼近算法中的一种

         */

        private void btnContours_Click(object sender, EventArgs e)
        {
            if (String.IsNullOrEmpty(fileName))
            {
                MessageBox.Show("请先打开一个图片");
                return;
            }
            btnCanny_Click(sender, e);
            Mat mOutput = new Mat(mInput.Rows, mInput.Cols, MatType.CV_8UC4);
            mInput.CopyTo(mOutput);

            RetrievalModes mode = RetrievalModes.External;
            ContourApproximationModes method = ContourApproximationModes.ApproxNone;
            OpenCvSharp.Point offset = new OpenCvSharp.Point(0, 0);

            Cv2.FindContours(
                image: edges,
                contours: out OpenCvSharp.Point[][] contours,
                hierarchy: out HierarchyIndex[] outputArray,
                mode: mode,
                method: method,
                offset: offset);

            for (int i = 0; i < contours.Length; i++)
            {
                Scalar color = Scalar.RandomColor();
                if (contours[i].Length > 100)
                {
                    Cv2.DrawContours(
                        mOutput,
                        contours,
                        contourIdx: i,
                        color: color,
                        thickness: 2,
                        lineType: LineTypes.Link8,
                        hierarchy: outputArray,
                        maxLevel: 0);
                }
            }

            pictureBox1.Image = BitmapConverter.ToBitmap(mInput);
            pictureBox2.Image = BitmapConverter.ToBitmap(edges);
            pictureBox3.Image = BitmapConverter.ToBitmap(mOutput);

        }

        /*
         Hough变换
        经典的直线检测算法

        算法流程:
        1,高斯模糊
        2,Canny边缘检测
        3,HoughLinesP直线寻找,直线提取

         摘要:
             Finds lines segments in a binary image using probabilistic Hough transform.
        
         参数:
           image:
        
           rho:
             Distance resolution of the accumulator in pixels
        
           theta:
             Angle resolution of the accumulator in radians
        
           threshold:
             The accumulator threshold parameter. Only those lines are returned that get enough
             votes ( > threshold )
        
           minLineLength:
             The minimum line length. Line segments shorter than that will be rejected. [By
             default this is 0]
        
           maxLineGap:
             The maximum allowed gap between points on the same line to link them. [By default
             this is 0]
        
         返回结果:
             The output lines. Each line is represented by a 4-element vector (x1, y1, x2,
             y2)

        public static LineSegmentPoint[] HoughLinesP(InputArray image, double rho, double theta, int threshold, double minLineLength = 0, double maxLineGap = 0);

        image,灰度图片输入
        rho,步长为1的半径
        theta,步长为π/180的角来,来搜索所有可能的直线
        threshold,阈值,大于阈值 threshold 的线段才可以被确认为直线。该值越小,判定出的直线越多;值越大,判定出的直线就越少。
        minLineLength,线段的最短长度,长度小于该值的线段会被忽略
        maxLineGap,两条线段之间的最大间隔,间隔小于该值的两条线会被当成一条线

         */

        private void btnHouhLines_Click(object sender, EventArgs e)
        {
            btnCanny_Click(sender, e);
            Mat mOutput = new Mat(mInput.Rows, mInput.Cols, MatType.CV_8UC4);
            mInput.CopyTo(mOutput);

            double rho = 1;
            double theta = 1;
            int threshold = 10;
            double minLineLength = 0;
            double maxLineGap = 0;

            var res = Cv2.HoughLinesP(edges,
                rho: rho,
                theta: theta / 100.0,
                threshold: threshold,
                minLineLength: minLineLength,
                maxLineGap: maxLineGap);

            for (int i = 0; i < res.Length; i++)
            {
                Scalar color = Scalar.RandomColor();
                Cv2.Line(mOutput, res[i].P1, res[i].P2,
                    color: color,
                    thickness: 2,
                    lineType: LineTypes.Link8);
            }

            pictureBox1.Image = BitmapConverter.ToBitmap(mInput);
            pictureBox2.Image = BitmapConverter.ToBitmap(edges);
            pictureBox3.Image = BitmapConverter.ToBitmap(mOutput);

        }

        private void trackBar1_ValueChanged(object sender, EventArgs e)
        {
            this.label1.Text = this.trackBar1.Value.ToString();
        }

        private void trackBar2_ValueChanged(object sender, EventArgs e)
        {
            this.label2.Text = this.trackBar2.Value.ToString();
        }

        private void trackBar3_ValueChanged(object sender, EventArgs e)
        {
            if (String.IsNullOrEmpty(fileName))
            {
                MessageBox.Show("请先打开一个图片");
                return;
            }
            Mat dst1 = new Mat();
            src = new Mat(fileName);
            //Mat src = new Mat(fileName, ImreadModes.Color);
            

            Mat element = Cv2.GetStructuringElement(MorphShapes.Rect, new OpenCvSharp.Size(5, 5), new OpenCvSharp.Point(-1, -1));
            Cv2.Erode(src, dst1, element, new OpenCvSharp.Point(-1, -1), this.trackBar3.Value); //先腐蚀 再膨胀 可以去噪声
            //Cv2.Dilate(dst2, dst1, element, new OpenCvSharp.Point(-1, -1), 2);
            var frameBitmap = BitmapConverter.ToBitmap(dst1);
            pictureBox1.Image?.Dispose();
            pictureBox1.Image = frameBitmap;

        }

        private void trackBar4_ValueChanged(object sender, EventArgs e)
        {
            if (String.IsNullOrEmpty(fileName))
            {
                MessageBox.Show("请先打开一个图片");
                return;
            }
            Mat dst1 = new Mat();
            src = new Mat(fileName);
            //Mat src = new Mat(fileName, ImreadModes.Color);


            Mat element = Cv2.GetStructuringElement(MorphShapes.Rect, new OpenCvSharp.Size(5, 5), new OpenCvSharp.Point(-1, -1));
            Cv2.Dilate(src, dst1, element, new OpenCvSharp.Point(-1, -1), this.trackBar4.Value); //先腐蚀 再膨胀 可以去噪声

            var frameBitmap = BitmapConverter.ToBitmap(dst1);
            pictureBox1.Image?.Dispose();
            pictureBox1.Image = frameBitmap;
        }
    }
}
// See https://aka.ms/new-console-template for more information
using OpenCvSharp;
using NumSharp;

//1,Mat 练习

/*
* 参数:
*     1:图片路径(一定要正确,找不到会报错)
*     2:指定加载图像的颜色和深度(枚举类型,可以指定多个类型),这里指定的任何颜色和任何深度
*/

string path = @"C:\Users\14348\Desktop\Picture\Lena.jpg";
using (Mat src = new Mat(path, ImreadModes.Grayscale | ImreadModes.AnyDepth))
{
    
    /*
     *显示灰度图
    * new Window :新建一个窗口显示窗口
    *  参数:
    *      1:窗口名称
    *      2:指定要显示的Mat对象。
    *      3:窗口显示的模式(枚举类型)-- 可选参数,可以不指定
    */
    //using (new Window("Pic Window", src, WindowFlags.AutoSize))
    //{
    //    Cv2.WaitKey(0);//等于readykey
    //}

    /*
     自定义图像

            Mat m = new Mat(100, 100, MatType.CV_8UC3, s); 
             * 参数:
             *      1: 行 --指定图像的像素行数,多个行组成图片的高度
             *      2: 列 --指定图像的像素列数,多个列组成图片的宽度
             *      3: 对象类型,结构体类型 矩阵数据类型(深度和通道数) MatType
             *      4: 图像颜色
    */
    Scalar s = new Scalar(55, 77, 88);//创建一个颜色对象 Scalar(B,G,R,A)其中A 为透明度。255为不透明,0为全透。
    Mat m = new Mat(224,224,MatType.CV_8UC3,s);//CV_8UC3  意思是:每个像素点 8 Bit ,U:无符号整数(short,float) 3 通道
    //using (new Window("自定义图像",m,WindowFlags.AutoSize))
    //{
    //    Cv2.WaitKey();
    //}

    /*
     创建一个已知图像一样大小和类型的图片
    using (var src = new Mat(imagePath, ImreadModes.AnyColor | ImreadModes.AnyDepth))
    using (var dst = new Mat(src.Size(), src.Type(), Scalar.All(0)))   //与原图大小类型一致 ,黑色
    {
        //dst.Create();//分配新的数据和类型
        using (new Window("src", WindowMode.FreeRatio, src))
        using (new Window("dst", WindowMode.FreeRatio, dst))
        {
            Cv2.WaitKey(0);
        }
    }
     */
}

//2, 使用指针,非安全模式
unsafe void Function1(string path)
{
    using (Mat src = new Mat(path, ImreadModes.AnyColor))
    {
        Mat dst;
        //dst = new Mat(src.Size(), src.Type(), new Scalar(127, 0, 255));
        dst = src.Clone(); //克隆:得到一个完全拷贝的对象
        //Mat dst = new Mat();
        //src.CopyTo(dst);  //拷贝一个与克隆类似,但是dst必须先new出来
        //Cv2.CvtColor(src, dst, ColorConversionCodes.BGRA2GRAY); //转为灰度图
        Console.WriteLine("InputImage 颜色通道:{0}", src.Channels()); //颜色通道数
        Console.WriteLine("Create 颜色通道:{0}", dst.Channels()); //颜色通道数

        //指针获取值(根据下标),第一个像素值
        IntPtr c = dst.Ptr(0); //Mat对象的 Ptr方法:返回指向指定矩阵行的指针。
        byte* c1 = (byte*)c.ToPointer(); //像素值在C#中是byte类型 ,在C++中是 uchar类型
                             //byte* cc = (byte*)dst.Ptr(0);
        Console.WriteLine($"第一行第一个像素值:{*c1}" ); //53

        

        IntPtr ptr = dst.Ptr(1,0);
        byte* c2 = (byte*)ptr;
        Console.WriteLine($"第2行第1列的像素值:{*c2}"); //57

        int row = dst.Rows; //行
        int clo = dst.Cols; //列
        Console.WriteLine($"共有Rows :{row} ;Cols :{clo}");

        //for (int i = 0; i < dst.Rows; i++)
        //{
        //    for (int j = 0; j < dst.Cols; j++)
        //    {
        //        //IntPtr ptr1 = dst.Ptr(i, j);
        //        //byte* c3 = (byte*)dst.Ptr(i, j);

        //        Console.WriteLine($"第{i}行,第{j}列的像素值是{*(byte*)dst.Ptr(i, j)}");
        //    }
        //}
    }
}

Function1(path);

//3,兴趣区域

Mat Lena = new Mat(path, ImreadModes.AnyColor);
Rect roi = new Rect(180, 80, 100, 100);//x,y 矩形起点坐标。矩形宽度和高度 
Mat ImageROI = new Mat(Lena, roi);//新建一个mat,把roi内的图像加载到里面去。
                                  //Cv2.ImShow("兴趣区域", ImageROI);
                                  //Cv2.ImShow("Lena", Lena);
                                  //Cv2.WaitKey();


//4,OpenCv中向量模板类Vec的表示

/*
 Vec的表示 ---- Vec2b,Vec3b,Vec2s,Vec3s
//【1】Vec2b---表示每个Vec2b对象中,存储2个char(字符型)数据
typedef Vec<uchar, 2> Vec2b; 、
 //【2】Vec3b---表示每一个Vec3b对象中,存储3个char(字符型)数据,比如用这样的对象,去存储RGB图像中的一个像素点
typedef Vec<uchar, 3> Vec3b;
//【3】Vec4b---表示每一个Vec4b对象中,存储4个字符型数据,用这样的类对象去存储---4通道RGB+Alpha的图像中的像素点
typedef Vec<uchar, 4> Vec4b;
 //【4】Vec2s---表示这个类的每一个类对象,存储2个short int(短整型)的数据
typedef Vec<short, 2> Vec2s;
typedef Vec<short, 3> Vec3s;
 typedef Vec<short, 4> Vec4s;

Vec的表示---Vec3b

例如 8U 类型的 RGB 彩色图像可以使用 <Vec3b>

3 通道 float 类型的矩阵可以使用 <Vec3f>。

对于 Vec 对象,可以使用 [ ] 符号如操作数组般读写其元素,

Vec3b color;  //用 color 变量描述一种 RGB 颜色
color[0] = 255;  //0通道的B 分量
color[1] = 0;  //1通道的G 分量
color[2] = 0;  //2通道的R 分量 
 

两种表示的用处:

①、已知一幅图像img的数据类型为 unsigned char型灰度图(单通道),要对坐标为(0,0)的像素重新赋值为0

src.at<uchar>(0,0) = 0;
②已知一幅图片img的数据类型为unsigned char的彩色图片,再次要求将坐标(0,0)的像素赋值为0。

Opencv中图像三原色在内存中的排列顺序为B-G-R

 
src.at<Vec3b>(0, 0) [0] = 0;  //B  
src.at<Vec3b>(0, 0) [1] = 0;  //G  
src.at<Vec3b>(0, 0)[2] = 0;  //R 
 

Vec的表示---Vec3f

Vec3f表示的是3通道float类型的 Vect,就相当于3通道float类型的图像(这是其中一个具体化),解释可以从源代码中看出来。

Vec3f point = Vec3f(10, 10, 3.2); //Float, 3 components
cv::Mat mat(3, 3, CV_32FC3); //3 channel matrix
Vec3f v3f = mat.at<Vec3f>(y, x); //read color values for pixel (y,x)

PS:Vec3f是一种数据类型,其是3通道的float,后面的mat.at<Vec3f>(y, x)是访问图像的一种方式。

mat.at<Vec3f>(y, x)[0];mat.at<Vec3f>(y, x)[1];mat.at<Vec3f>(y, x)[2];

对于mat的理解,可以认为mat.at<Vec3f>是mat的一种访问形式,其有点类似vector。

 */


//using (Mat src = new Mat(path,ImreadModes.Color))
//{
//    Mat outImage = new Mat(); //声明一个容器,装载改变后的图像
//    Cv2.CvtColor(src,outImage,ColorConversionCodes.RGB2GRAY);
//    Cv2.ImWrite(@"C:\Users\14348\Desktop\Picture\LenaGrey.jpg",outImage);
//    using (new Window("src", src, WindowFlags.Normal))
//    using (new Window("out", outImage, WindowFlags.Normal ))
//    {
//        Cv2.WaitKey();
//    }
//}

//自定义一张全红色的图片
Mat src1 = new Mat(100, 100, MatType.CV_8UC3, new Scalar(0, 0, 255));
Vec3b vec3B = new Vec3b();
//获取第一个像素的三通道像素值 3通道,byte类型
vec3B.Item0 = src1.At<Vec3b>(0, 0)[0]; //第0行,第0列,第0通道的像素
vec3B.Item1 = src1.At<Vec3b>(0, 0)[1];//第0行,第0列,第1通道的像素
vec3B.Item2 = src1.At<Vec3b>(0, 0)[2];//第0行,第0列,第2通道的像素
Console.WriteLine("0 :" + vec3B.Item0); //控制台输出
Console.WriteLine("1 :" + vec3B.Item1);
Console.WriteLine("2 :" + vec3B.Item2);

//using (new Window("src image", src1, WindowFlags.FreeRatio)) //创建一个新窗口显示图像
//{
//    Cv2.WaitKey();
//}

//5,掩膜操作

using (Mat src = new Mat(path, ImreadModes.AnyColor | ImreadModes.AnyDepth))
using (Mat dst = new Mat())
{
    //定义一个掩膜矩阵
    InputArray kernel = InputArray.Create<int>(new int[3, 3] { { 0, -1, 0 }, { -1, 5, -1 }, { 0, -1, 0 } });

    //进行掩膜操作,提高图片亮度
    Cv2.Filter2D(src, dst, -1, kernel, new Point(-1, 1), 0, BorderTypes.Default);

    //using (new Window("OutputImage", dst, WindowFlags.Normal ))
    //using (new Window("InputImage", src,WindowFlags.Normal))
    //{
    //    Cv2.WaitKey(0);
    //}
}

//指针操作增加饱和度


unsafe void Pointer()
{
    using (Mat src = new Mat(path, ImreadModes.AnyColor | ImreadModes.AnyDepth))
    {
        int clos = (src.Cols - 1) * src.Channels(); //RGB有三个通道,(图像的列(长度) * 图像的通道数)
        int rows = src.Rows; //行(高度)
        int offsetx = src.Channels();

        Mat dst = new Mat(src.Size(), src.Type()); //初始化

        for (int row = 1; row < rows - 1; row++)
        {
            //IntPtr c = dst.Ptr(0); //Mat对象的 Ptr方法:返回指向指定矩阵行的指针。
            //byte* c1 = (byte*)c; //像素值在C#中是byte类型 ,在C++中是 uchar类型
            IntPtr current = src.Ptr(row); //当前行
            byte* curr = (byte*)current.ToPointer();

            IntPtr upRow = src.Ptr(row - 1);//上一行
            byte* up = (byte*)upRow.ToPointer();

            IntPtr nextRow = src.Ptr(row + 1);//下一行
            byte* next = (byte*)nextRow.ToPointer();

            IntPtr outPut = dst.Ptr(row); //输出
            byte* opt = (byte*)outPut.ToPointer();

            for (int col = offsetx; col < clos; col++)
            {
                opt[col] = (byte)Saturate.Saturate_cast((5 * curr[col] - (curr[col - offsetx] + curr[col + offsetx] + up[col] + next[col])));
            }
        }
        using (new Window("OutputImage", dst, WindowFlags.FreeRatio))
        using (new Window("InputImage", src, WindowFlags.FreeRatio))
        {
            Cv2.WaitKey(0);
        }
    }
}

//Pointer();

public class Saturate
{
    //要确保运算后的像素值在正确的范围内
    public static int Saturate_cast(int n)
    {
        if (n <= 0)
        {
            return 0;
        }
        else if (n > 255)
        {
            return 255;
        }
        else
        {
            return n;
        }
    }
}





using OpenCvSharp;
using NumSharp;

string path = @"C:\Users\14348\Desktop\Picture\Lena.jpg";
string path1 = @"C:\Users\14348\Desktop\Picture\Lena.jpg";
string path2 = @"C:\Users\14348\Desktop\Picture\LenaGrey.jpg";


Mat src = new Mat(path, ImreadModes.AnyColor | ImreadModes.AnyDepth);
// At 的用法 读取元素
//using (Mat dst = new Mat())
//{

//    //ColorConversionCodes 色彩空间转换枚举类型 BGRA2GRAY 转为灰度单通道
//    Cv2.CvtColor(src, dst, ColorConversionCodes.BGR2GRAY);
//    Mat gray = new Mat();
//    dst.CopyTo(gray);

//    int height = dst.Rows;
//    int width = dst.Cols;
//    int cn = src.Channels();
//    IntPtr ptr = dst.Ptr(1, 0);


//    //int row = dst.Rows; //行
//    //int clo = dst.Cols; //列
//    //for (int i = 0; i < dst.Rows; i++)
//    //{
//    //    for (int j = 0; j < dst.Cols; j++)
//    //    {
//    //        //IntPtr ptr1 = dst.Ptr(i, j);
//    //        //byte* c3 = (byte*)dst.Ptr(i, j);

//    //        Console.WriteLine($"第{i}行,第{j}列的像素值是{*(byte*)dst.Ptr(i, j)}");
//    //    }
//    //}
//    for (int row = 0; row < height; row++)
//    {
//        for (int col = 0; col < width; col++)
//        {
//            if (cn == 1)
//            {
//                //读取源图的像素
//                byte p = dst.At<byte>(row, col);
//                byte value = byte.Parse((255 - p).ToString()); //像素反转
//                dst.Set(row,col,value); //赋值
//            }
//            else if (cn == 3)
//            {
//                //读取源图的像素
//                int b = src.At<Vec3b>(row, col)[0];
//                int g = src.At<Vec3b>(row, col)[1];
//                int r = src.At<Vec3b>(row, col)[2];

//                Vec3b color = new Vec3b
//                {
//                    Item0 = (byte)(255 - b),
//                    Item1 = (byte)(255 - g),
//                    Item2 = (byte)(255 - r)
//                };

//                dst.Set<Vec3b>(row,col,color);
//            }
//        }
//    }
//    //using (new Window("gray", gray, WindowFlags.FreeRatio)) //单通道图像
//    //using (new Window("dst", dst, WindowFlags.FreeRatio)) //反转后的单通道图像
//    //using (new Window("src", src, WindowFlags.FreeRatio)) //源图
//    //{
//    //    Cv2.WaitKey(0);
//    //}

//    //Cv2.BitwiseNot(src, dst); //反转像素函数,不需要操作像素,达到的效果一样

//    //using (new Window("dst", ~src, WindowFlags.FreeRatio)) //反转
//    //using (new Window("src", src, WindowFlags.FreeRatio)) //源图
//    //{
//    //    Cv2.WaitKey(0);
//    //}



//}

/// <summary>
/// 图像混合
/// </summary>
/// <param name="path1">图像1加载路径</param>
/// <param name="path2">图像2加载路径</param>
void GraphicalMix(string path1, string path2)
{
    using (Mat src1 = new Mat(path1, ImreadModes.AnyColor | ImreadModes.AnyDepth))
    using (Mat src2 = new Mat(path2, ImreadModes.AnyColor | ImreadModes.AnyDepth))
    using (Mat dst = new Mat()) 
    {
        double alpha = 0.5;
        if (src1.Rows == src2.Rows && src1.Cols == src2.Cols && src1.Type() == src2.Type())
        {
            Cv2.AddWeighted(src1, alpha, src2, (1 - alpha), 0, dst);
            using (new Window("dst", dst, WindowFlags.FreeRatio))
            using (new Window("src1", src1, WindowFlags.FreeRatio))
            using (new Window("src2", src2, WindowFlags.FreeRatio))
            {
                Cv2.WaitKey(0);
            }
        }
        else
        {
            Console.WriteLine("图片大小类型不一致!!!");
        }
    }
}

//设置椒盐噪声
Mat src1 = new Mat(path,ImreadModes.Color);
Mat outImage = new Mat(); //声明一个容器,装载改变后的图像
Random random = new Random();
for (int i = 0; i < 3000; i++)
{
    int row = random.Next(src1.Rows);
    int col = random.Next(src1.Cols);

    //char white = (char)255; 黑白
    Vec3b white = new Vec3b(255,255,255);
    src1.Set(row, col, white);
}
//Cv2.ImShow("椒盐噪声", src1);
//Cv2.WaitKey();
//Cv2.CvtColor(src, outImage, ColorConversionCodes.RGB2GRAY);
//Cv2.ImWrite(@"C:\Users\14348\Desktop\Picture\LenaNoise.jpg", src1);


//膨胀腐蚀
/*
 膨胀dilate()
函数原型:

public static void Dilate(InputArray src, OutputArray dst, InputArray? element, Point? anchor = null, int iterations = 1, BorderTypes borderType = BorderTypes.Constant, Scalar? borderValue = null);
1
src:输入图像(建议二值图)
dst:输出图像
element:用于膨胀的结构单元。如果element=new Mat()[为空的意思],则使用一个3x3的矩形结构单元
anchor :锚点位置,默认为(-1,-1)表示位于中心
iterations :膨胀次数
borderType :边界模式,一般使用默认值
borderValue :边界值,一般采用默认值
腐蚀erode()

public static void Erode(InputArray src, OutputArray dst, InputArray? element, Point? anchor = null, int iterations =

 */

Mat dst1 = new Mat();
Mat dst2 = new Mat();
Mat element = Cv2.GetStructuringElement(MorphShapes.Rect, new Size(5, 5), new Point(-1, -1));
Cv2.Erode(src1, dst2, element, new Point(-1, -1), 1); //先腐蚀 再膨胀 可以去噪声
Cv2.Dilate(dst2, dst1, element, new Point(-1, -1), 1);

Window srcWin = new Window("src", src1, WindowFlags.AutoSize);
Window dstWin = new Window("膨胀", dst1, WindowFlags.AutoSize);
Window dstWin1 = new Window("腐蚀", dst2, WindowFlags.AutoSize);

Cv2.WaitKey(0);

//部分代码
//CvTrackbar

#region  滚动条操作演示 -- 调整图片亮度

Mat Temp_Mat;
 Mat output_Mat;
 int Lightness = 50;


Temp_Mat = new Mat(src.Height, src.Width, src.Type(), new Scalar(0, 0, 0));
output_Mat = new Mat(src.Height, src.Width, src.Type(), new Scalar(0, 0, 0));

if (src.Empty())
{
    Console.WriteLine("图像未成功加载...");
    return;
}
Cv2.ImShow("src image", src);

TrackbarCallbackNative OnTrack = new TrackbarCallbackNative(Trackbar);

//Cv2.NamedWindow("亮度调整", WindowFlags.AutoSize);
//Cv2.CreateTrackbar("亮度调整", "亮度调整", ref Lightness, 100, OnTrack);

//Cv2.WaitKey();
//Cv2.DestroyAllWindows();

void Trackbar(int pos, IntPtr userData)
{
    for (int row = 0; row < Temp_Mat.Height; row++)
    {
        for (int col = 0; col < Temp_Mat.Width; col++)
        {

            if (Temp_Mat.Channels() == 1)//灰色图像
            {
                Temp_Mat.Set<byte>(row, col, (byte)pos);//这里用参数Pos或者全局变量Lightness 都是可以的
            }
            else//彩色图像
            {
                Vec3b color = Temp_Mat.Get<Vec3b>(row, col);

                color.Item0 = (byte)(pos);//这里用参数Pos或者全局变量Lightness 都是可以的
                color.Item1 = (byte)(pos);
                color.Item2 = (byte)(pos);
                Temp_Mat.Set<Vec3b>(row, col, color);
            }
        }
    }

    Cv2.Add(src, Temp_Mat, output_Mat);
    Cv2.ImShow("亮度调整", output_Mat);
}
#endregion





  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

潘诺西亚的火山

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值