安装
需要安装两个依赖:
- OpenCvSharp4
- OpenCvSharp4.runtime.win
安装
1 | Install-Package OpenCvSharp4 -Version 4.8.0.20230708 |
依赖扩展
- OpenCvSharp4.Extensions
其中
OpenCvSharp4.Extensions 主要是一些辅助的工具 比如Mat和Bitmap的互转。
添加引用
1 | using OpenCvSharp; |
识别步骤
常用操作
Mat和Bitmap互转
1 | //Bitmap转Mat |
读取图片
1 | private void readImg() |
保存
1 | Mat img1 = new Mat("D:\\Pic\\0.jpg", ImreadModes.Color); |
查看效果
方式1
本地保存图片
1 | Cv2.ImWrite("D:\\Pic\\3.jpg", img3); |
方式2
窗口打开图片
1 | private void showImg(Mat img) |
图片模式转换
1 | Mat img10 = new Mat(); |
复制
1 | Mat img2 = new Mat(); |
图片拼接
type表示了矩阵中元素的类型以及矩阵的通道个数,它是一系列的预定义的常量,其命名规则为CV_(位数)+(数据类型)+(通道数),由type()返回,但是返回值是int型,不是OpenCV预定义的宏(CV_8UC1, CV_64FC1…),也就是说你用type函数得到的只是一个int型的数值,比如CV_8UC1返回的值是0,而不是CV_8UC1。
数据类型
U(unsigned integer)表示的是无符号整数,
S(signed integer)是有符号整数,
F(float)是浮点数
方式1
1 | /// <summary> |
调用
1 | List<Mat> mats = new List<Mat>(); |
注意
不同色彩模式的图片不能正常合并,和目标图片的色彩模式也要保持一致,这里使用
matList[0].Type()
设置目标图的模式。默认背景是纯黑色,这里
new Scalar(255, 255, 255)
使图片默认为纯白色。
方式2(不推荐)
使用VConcat()
或HConcat()
拼接则要求待拼接图像有相同的宽度或高度
1 | /// <summary> |
调用方式
1 | List<Mat> mats = new List<Mat>(); |
灰度
1 | /// <summary> |
二值化
1 | /// <summary> |
说明
1 | Cv2.Threshold( |
灰度值小于阈值的都设置为0,大于或等于这个阈值的像素将被赋予 maxValue
。
腐蚀与膨胀
腐蚀与膨胀都是针对白色区域的
- 腐蚀 白色变少 黑色变多
- 膨胀 白色变多 黑色减少
示例
1 | /// <summary> |
高斯模糊
1 | /// <summary> |
缩放
1 | /// <summary> |
旋转
其中方式1和方式2都一样,都只能旋转90的倍数。
方式3可以旋转任意角度,但是如果是长方形就会部分无法显示。
所以
- 旋转
90的倍数
推荐方式1
。 - 旋转
其他角度
推荐方式3
。
方式1
1 | public static Mat rotate90Counter(Mat source) |
其中方向
1 | public enum RotateFlags |
方式2
逆时针90
1 | /// <summary> |
顺时针90
1 | /// <summary> |
旋转180
1 | /// <summary> |
总结一下:
- 需逆时针90°旋转时
Transpose(src,tmp) + Flip(tmp,dst,0)
需顺时针90°旋转时
Transpose(src,tmp) + Flip(tmp,dst,1)
需180°旋转时
Flip(src,dst,-1)
Transpose()简单来说,就相当于数学中的转置,在矩阵中,转置就是把行与列相互调换位置;
相当于将图像逆时针旋转90度,然后再关于x轴对称
枚举
1 | public enum FlipMode |
方式3
旋转任意角度
这种方式如果是长方向旋转90度会导致黑边和遮挡。
1 | /// <summary> |
透视变形
获取黑块顶点
1 | using OpenCvSharp; |
透视变形
1 | using OpenCvSharp; |
调用
1 | //透视变形 |
剪裁
1 | // 截取左上角四分之一区域 |
文件名
1 | public class ZPathUtil |
是否涂卡
1 | /// <summary> |
注意传入的原图一定要二值化。
识别中可能需要采取不同的策略。
比如单选题可以取填涂占比最高的项,并且该项的填涂率要达到20%以上。
这里获取某区域的填涂率(从0到1):
1 | /// <summary> |
绘制边框
1 | Mat img10 = new Mat(); |
注意
黑白图片转为彩色
查找轮廓
实现框选用户选择的选项
1 | /// <summary> |
调用方式
1 | int rows = mat4.Rows; |
获取面积
1 | //获取涂写区域 |
其中Cv2.CountNonZero(matTemp)
是获取非0的像素点个素数,所以在二值化的图片中,用户涂的区域都是0,我们只需要获取涂的百分比就能判断用户是否涂卡。
获取答题卡涂的选项
其中每个选项的坐标区域是在制作答题卡的时候,后台要保存的。
1 | int[][][] ques_select = new int[][][] { |
页码识别
页面我们可以转换为二进制然后进行黑块渲染,识别的时候后在转成数字即可。这里是页码从1开始,所以要减1。
1 | /// <summary> |
Mat转Base64
1 | public static string MatToBase64(Mat mat, bool addPrefix = true) |
绘制
绘制文字
1 | public static void WriteTxt |
位置
其中textOrg
是文字的左下角的坐标。
字体
OpenCV 不能自定义字体样式,但是提供了几种预定义的字体样式,通过 HersheyFonts
枚举进行选择。
以下是一些常用的字体类型:
HersheySimplex
:简单的无衬线字体。HersheyPlain
:更细的字体。HersheyDuplex
:双线条字体。HersheyTriplex
:三线条字体。HersheyComplex
:复杂的字体。HersheyComplexSmall
:小型复杂字体。HersheyScriptSimplex
:手写风格字体。HersheyScriptComplex
:复杂手写风格字体。
字体粗细和大小
- 字体粗细:通过
thickness
参数设置。增加值会使文本变粗。 - 字体大小:通过
fontScale
参数设置。增大fontScale
值会使字体变大。
绘制矩形
1 | Mat img7 = new Mat(); |
绘制轮廓
1 | int rows = mat4.Rows; |
工具类
基本操作
CvCommonUtils
1 | using OpenCvSharp; |
获取边界
代码
CvContoursUtils
1 | using OpenCvSharp; |
BoundingRect 和 MinAreaRect
BoundingRect 和 MinAreaRect的区别?
1 | Rect rect2 = Cv2.BoundingRect(pointArr); |
BoundingRect
和 MinAreaRect
都是 OpenCV 中用于获取轮廓外接矩形的函数,它们之间有一些区别:
BoundingRect
:BoundingRect
函数计算轮廓的最小外接矩形,该外接矩形是与轴对齐的,即矩形的边与图像的 x 和 y 轴平行。- 外接矩形的长和宽是通过轮廓的最小外接矩形的水平(水平间距)和垂直(垂直间距)方向上的最大值和最小值计算而得。
- 返回的外接矩形通常保留了一定程度的“多余”部分,即可能会包含一些轮廓之外的区域。
MinAreaRect
:MinAreaRect
函数计算包围轮廓的最小面积矩形,该矩形是不能旋转的(非水平或垂直)。- 返回的外接矩形通常紧贴着轮廓,不包含过多的“多余”部分,因此更接近于轮廓的实际形状。
MinAreaRect
函数返回一个RotatedRect
对象,包含了最小面积矩形的中心点、宽度、高度和旋转角度等信息。
在实际使用中,如果需要获取紧贴着轮廓的外接矩形且不考虑矩形的旋转,建议使用 BoundingRect
函数;如果需要获取紧贴着轮廓的最小面积矩形(可能具有旋转),则应该使用 MinAreaRect
函数。
希望这个对比能帮助你理解 BoundingRect
和 MinAreaRect
函数的区别。如果有任何其他问题,请随时告诉我。
透视变形
CvPerspectiveUtils
1 | using OpenCvSharp; |
在 C# 中使用 OpenCV 时,GetPerspectiveTransform
和 GetAffineTransform
是两种不同的函数,用于计算两种不同类型的转换矩阵。
GetPerspectiveTransform
:GetPerspectiveTransform
函数用于计算透视变换矩阵,该矩阵可以将一个四边形区域映射到另一个四边形区域,实现透视变换。- 在图像处理中,透视变换可用于校正图像中的透视畸变,如将斜切或倾斜的物体调整为正常视角。
- 透视变换需要至少4个对应的点对来计算透视变换矩阵。
GetAffineTransform
:GetAffineTransform
函数用于计算仿射变换矩阵,该矩阵可以将一个平行四边形区域映射到另一个平行四边形区域,实现平移、旋转和缩放等变换。- 仿射变换通常用于实现简单的图像变换,如图像平移、旋转、缩放等操作。
- 仿射变换需要至少3个对应的点对来计算仿射变换矩阵。
因此,GetPerspectiveTransform
用于计算透视变换矩阵,而 GetAffineTransform
用于计算仿射变换矩阵,它们的主要区别在于变换的类型和所需的点对数量。根据具体的图像处理需求,选择适合的函数来进行变换操作。
简而言之
如果是平行四边形到矩形就用
GetAffineTransform
,如果是梯形到矩形要用GetPerspectiveTransform
。
查看代码执行时间
1 | using System.Diagnostics; |
计时实例可以使用多次
1 | System.Diagnostics.Stopwatch oTime = new System.Diagnostics.Stopwatch(); |