探索OpenCvSharp:用C#和Winform构建图像处理世界

OpencvSharp资料,采用C#加Winform编写,包含接近50个Demo,直接运行即可。 例程包含:模板匹配、边缘识别、人脸识别,灰度变化、标定等。

最近在图像处理领域折腾,发现了一个超棒的资源------基于OpenCvSharp,用C# 加Winform编写且包含近50个可直接运行Demo的项目,简直是图像处理爱好者和开发者的福音。今天就来跟大家唠唠这里面的精彩内容。

丰富多样的例程

模板匹配

模板匹配在图像识别里是个常用技术,比如在一张大图里找某个小图标在哪。在这个项目里,实现模板匹配的代码大概长这样:

csharp 复制代码
using OpenCvSharp;

class TemplateMatching
{
    public static void MatchTemplateExample()
    {
        Mat sourceImage = Cv2.ImRead("source.jpg");
        Mat templateImage = Cv2.ImRead("template.jpg");

        Mat result = new Mat();
        Cv2.MatchTemplate(sourceImage, templateImage, result, TemplateMatchModes.CCoeffNormed);

        Core.MinMaxLoc(result, out _, out double maxVal, out _, out Point maxLoc);

        double threshold = 0.8;
        if (maxVal >= threshold)
        {
            int width = templateImage.Width;
            int height = templateImage.Height;
            Rect rect = new Rect(maxLoc, new Size(width, height));
            Cv2.Rectangle(sourceImage, rect, Scalar.Red, 2);
        }

        Cv2.ImShow("Matched Image", sourceImage);
        Cv2.WaitKey(0);
        Cv2.DestroyAllWindows();
    }
}

代码分析:首先,通过Cv2.ImRead读取源图像和模板图像。然后用Cv2.MatchTemplate函数进行模板匹配,这里用的匹配模式是TemplateMatchModes.CCoeffNormed ,它会返回一个匹配结果的矩阵。接着用Core.MinMaxLoc找到矩阵里的最大值,也就是最佳匹配位置。设置一个阈值,如果最大值大于阈值,就认为匹配成功,在源图像上画出匹配区域的矩形框,最后显示图像。

边缘识别

边缘识别能提取图像中物体的轮廓,对于图像分析很关键。下面是简单的边缘识别代码:

csharp 复制代码
using OpenCvSharp;

class EdgeDetection
{
    public static void CannyEdgeDetection()
    {
        Mat image = Cv2.ImRead("input.jpg", ImreadModes.Grayscale);

        Mat edges = new Mat();
        Cv2.Canny(image, edges, 50, 150);

        Cv2.ImShow("Edges", edges);
        Cv2.WaitKey(0);
        Cv2.DestroyAllWindows();
    }
}

代码分析:先把图像以灰度模式读进来,因为Canny边缘检测算法在灰度图上效果更好。Cv2.Canny函数接收图像、输出边缘图像、以及两个阈值,较低阈值用于边缘连接,较高阈值用于检测明显的边缘。最后显示检测到的边缘图像。

人脸识别

人脸识别一直是个热门话题。在这个项目里,利用OpenCvSharp也能轻松实现简单的人脸识别:

csharp 复制代码
using OpenCvSharp;
using OpenCvSharp.Dnn;

class FaceRecognition
{
    public static void FaceRecognitionExample()
    {
        string modelFile = "res10_300x300_ssd_iter_140000_fp16.caffemodel";
        string configFile = "deploy.prototxt";
        Net net = DnnDnn.Net.ReadNetFromCaffe(configFile, modelFile);

        Mat image = Cv2.ImRead("group_photo.jpg");
        Mat blob = DnnBlob.FromImage(image, 1.0, new Size(300, 300), new Scalar(104.0, 177.0, 123.0), false, false);

        net.SetInput(blob);
        Mat detections = net.Forward();

        for (int i = 0; i < detections.Rows; i++)
        {
            float confidence = detections.At<float>(i, 2);
            if (confidence > 0.5)
            {
                int x1 = (int)(detections.At<float>(i, 3) * image.Width);
                int y1 = (int)(detections.At<float>(i, 4) * image.Height);
                int x2 = (int)(detections.At<float>(i, 5) * image.Width);
                int y2 = (int)(detections.At<float>(i, 6) * image.Height);

                Rect rect = new Rect(x1, y1, x2 - x1, y2 - y1);
                Cv2.Rectangle(image, rect, Scalar.Green, 2);
            }
        }

        Cv2.ImShow("Face Detection", image);
        Cv2.WaitKey(0);
        Cv2.DestroyAllWindows();
    }
}

代码分析:这里用到了深度学习模型,先读取模型文件和配置文件创建网络。把图像转成Blob数据格式输入网络,网络会输出检测结果。遍历检测结果,根据置信度判断是否为有效人脸,如果是就画出人脸的矩形框。

灰度变化

灰度变化能简化图像信息,便于后续处理。

csharp 复制代码
using OpenCvSharp;

class GrayscaleConversion
{
    public static void ConvertToGrayscale()
    {
        Mat colorImage = Cv2.ImRead("color_image.jpg");
        Mat grayImage = new Mat();

        Cv2.CvtColor(colorImage, grayImage, ColorConversionCodes.BGR2GRAY);

        Cv2.ImShow("Color Image", colorImage);
        Cv2.ImShow("Grayscale Image", grayImage);
        Cv2.WaitKey(0);
        Cv2.DestroyAllWindows();
    }
}

代码分析:读取彩色图像后,用Cv2.CvtColor函数把彩色图像转成灰度图像,这里指定了从BGR颜色空间转成灰度的代码ColorConversionCodes.BGR2GRAY,最后分别显示彩色图像和灰度图像。

标定

标定在机器视觉里用于确定相机的参数,以便更准确地测量和识别。虽然代码相对复杂些,但这个项目里也有详细实现。

csharp 复制代码
// 这里只给出简单框架,实际标定代码更复杂
using OpenCvSharp;
using OpenCvSharp.Calib3d;

class CameraCalibration
{
    public static void CalibrateCamera()
    {
        // 准备棋盘格角点数据
        Size patternSize = new Size(9, 6);
        Point3f[] objectPoints = new Point3f[patternSize.Width * patternSize.Height];
        for (int i = 0; i < patternSize.Height; i++)
        {
            for (int j = 0; j < patternSize.Width; j++)
            {
                objectPoints[i * patternSize.Width + j] = new Point3f(j, i, 0);
            }
        }

        // 存储所有图像的角点
        List<Point3f[]> objectPointsList = new List<Point3f[]>();
        List<Point2f[]> imagePointsList = new List<Point2f[]>();

        // 读取图像并寻找角点
        for (int i = 0; i < numImages; i++)
        {
            Mat image = Cv2.ImRead($"image_{i}.jpg");
            Mat gray = new Mat();
            Cv2.CvtColor(image, gray, ColorConversionCodes.BGR2GRAY);

            bool found = Cv2.FindChessboardCorners(gray, patternSize, out Point2f[] corners, ChessboardFlags.AdaptiveThresh | ChessboardFlags.NormalizeImage);
            if (found)
            {
                Cv2.CornerSubPix(gray, corners, new Size(11, 11), new Size(-1, -1), new TermCriteria(TermCriteriaTypes.Eps + TermCriteriaTypes.MaxIter, 30, 0.1));
                objectPointsList.Add(objectPoints);
                imagePointsList.Add(corners);
            }
        }

        // 标定相机
        Mat cameraMatrix = Mat.Zeros(3, 3, MatType.CV_64F);
        Mat distCoeffs = Mat.Zeros(5, 1, MatType.CV_64F);
        Calib3d.CalibrateCamera(objectPointsList, imagePointsList, gray.Size(), cameraMatrix, distCoeffs, out _, out _, CalibrationFlags.None);
    }
}

代码分析:首先定义棋盘格的尺寸,生成棋盘格角点的三维坐标。循环读取图像,把彩色图像转成灰度图后找棋盘格角点,如果找到就优化角点位置并存储。最后用这些角点数据进行相机标定,得到相机矩阵和畸变系数。

这个基于OpenCvSharp,C# 加Winform的项目,通过这一个个精彩的Demo,为我们打开了图像处理的大门,无论是新手学习还是老手拓展思路,都非常有价值,强烈推荐大家下载来亲自运行体验一番!

相关推荐
心疼你的一切5 天前
Unity开发Rokid应用之离线语音指令交互模型
android·开发语言·unity·游戏引擎·交互·lucene
weisian15117 天前
Elasticsearch-3--什么是Lucene?
大数据·elasticsearch·lucene
sniper_fandc1 个月前
Elasticsearch从入门到进阶——搜索优化原理
elasticsearch·搜索引擎·lucene·1024程序员节
酥酥禾1 个月前
C# LINQ常用语法
solr·lucene
cyh男1 个月前
lucene中AutomatonQuery类的作用
lucene
cyh男1 个月前
lucene中的PointRangeQuery和PointInSetQuery有什么区别
lucene
cyh男1 个月前
为什么ES中不推荐使用wildcard查询
elasticsearch·lucene
渣渣盟2 个月前
中文分词技术全解析
搜索引擎·全文检索·lucene
cyh男2 个月前
lucene 8.7.0 版本中的倒排索引、数字、DocValues三种类型的查询性能对比
lucene