marclee44 / me

1 stars 0 forks source link

OpenCV初探之C#篇 #19

Open marclee44 opened 2 years ago

marclee44 commented 2 years ago

OpenCV——Open Source Computer Vision Library,是目前最流行的计算机视觉处理库之一。当前的4.x版本基于C++语言开发,这对于主要使用C#作为开发工具的程序员,并不太友好。 不过有一名日本工程师开发并在持续更新OpenCvSharp,这是OpenCV的.NET包装器,非常接近于原始的OpenCV,并且有很多的样例参考。其采用LGPL发行,对商业应用很友好。 今天我们就用Visual Studio 2019配合OpenCvSharp来一探OpenCV。

第一步 创建一个Wpf应用

项目名称、解决方案名称随意,存储位置任意。 但是,框架必须选择.NET Framework 4.6.1或以上,因为OpenCVSharp需要。

第二步 添加OpenCvSharp包

打开NuGet包管理器,搜索OpenCvSharp4,可以找到4个包

这里我们选择添加OpenCvSharp4.Windows,这会自动连带添加其他3个包

第三步 基础功能

        private void ShowPicture(object sender, RoutedEventArgs e)
        {
            //声明Mat实例,读取指定图片
            Mat image = Cv2.ImRead(@"D:\demo.png", ImreadModes.Color);

            //在Image控件中显示原始图片
            imageContainer.Source = image.ToWriteableBitmap();

            //声明各种颜色空间转换的Mat实例
            Mat gray = new Mat();
            Mat HSV = new Mat();
            Mat YUV = new Mat();
            Mat Lab = new Mat();
            Mat img32 = new Mat();

            //进行各种颜色空间转换
            image.ConvertTo(img32, MatType.CV_32F, 1.0 / 255);
            Cv2.CvtColor(img32, HSV, ColorConversionCodes.BGR2HSV);
            Cv2.CvtColor(img32, YUV, ColorConversionCodes.BGR2YUV);
            Cv2.CvtColor(img32, Lab, ColorConversionCodes.BGR2Lab);
            Cv2.CvtColor(img32, gray, ColorConversionCodes.BGR2GRAY);

            //在指定名称的窗口中显示各种颜色空间转换的结果,窗口不存在会新建
            //当然,也可以像原始图片一样,在预设的Image控件中显示
            Cv2.ImShow("HSV", HSV);
            Cv2.ImShow("YUV", YUV);
            Cv2.ImShow("Lab", Lab);
            Cv2.ImShow("gray", gray);
            Cv2.ImShow("img32", img32);

            //让图片窗口一直显示,直到有按键按下
            Cv2.WaitKey(0);
        }

需要说明的是: Mat类是OpenCV用于处理图像而引入的一个封装类。其设计实现十分全面而具体,基本覆盖计算机视觉对于图像处理的基本要求。 Cv2则是静态方法类,包装了绝大多数OpenCV的C++方法。

        private void ShowVideo(object sender, RoutedEventArgs e)
        {
            //声明视频捕获类实例,打开指定视频
            var capture = new VideoCapture(@"D:\demo.mp4");
            //设置每帧显示的时间
            int sleepTime = (int)Math.Round(1000 / capture.Fps);

            //声明Mat实例
            Mat frame = new Mat();
            //循环读取视频每帧
            while (true)
            {
                //播放完毕后退出循环
                if (!capture.Read(frame))
                    break;

                //在Image控件中显示当前帧
                imageContainer.Source = frame.ToWriteableBitmap(); ;

                //让当前帧显示固定时间
                Cv2.WaitKey(sleepTime);
            }
        }

最后 来个高级点的功能

    public partial class MainWindow : System.Windows.Window
    {
        //视频捕获类
        private readonly VideoCapture capture;
        //级联分类器检测类
        private readonly CascadeClassifier cascadeClassifier;
        //后台线程类
        private readonly BackgroundWorker bkgWorker;

        public MainWindow()
        {
            InitializeComponent();

            capture = new VideoCapture();
            //用人脸Haar特征库初始化级联分类器检测类,以便进行人脸识别
            cascadeClassifier = new CascadeClassifier("haarcascade_frontalface_default.xml");

            bkgWorker = new BackgroundWorker { WorkerSupportsCancellation = true };
            bkgWorker.DoWork += Worker_DoWork;

            Loaded += MainWindow_Loaded;
            Closing += MainWindow_Closing;
        }

        private void MainWindow_Loaded(object sender, System.Windows.RoutedEventArgs e)
        {
            //捕获第0个摄像头的图像
            capture.Open(0, VideoCaptureAPIs.ANY);
            if (!capture.IsOpened())
            {
                Close();
                return;
            }
            //运行后台处理线程
            bkgWorker.RunWorkerAsync();
        }

        private void MainWindow_Closing(object sender, CancelEventArgs e)
        {
            bkgWorker.CancelAsync();
            capture.Dispose();
            cascadeClassifier.Dispose();
        }

        private void Worker_DoWork(object sender, DoWorkEventArgs e)
        {
            var worker = (BackgroundWorker)sender;
            while (!worker.CancellationPending)
            {
                //对当前帧进行解码,生成Mat实例(与之前的Read方法效果相似)
                using (var frameMat = capture.RetrieveMat())
                {
                    //检测出当前帧中所有的人脸
                    var rects = cascadeClassifier.DetectMultiScale(frameMat, 1.1, 5, HaarDetectionTypes.ScaleImage, new OpenCvSharp.Size(30, 30));

                    foreach (var rect in rects)
                    {
                        //在当前帧中,为检测到的每个人脸,画一个红色的方框
                        Cv2.Rectangle(frameMat, rect, Scalar.Red);
                    }

                    //显示每帧画面需要回到UI线程
                    Dispatcher.Invoke(() =>
                    {
                        FrameImage.Source = frameMat.ToWriteableBitmap();
                    });
                }

                //每帧显示30ms
                Cv2.WaitKey(30);
            }
        }
    }