目录
前言:
一、函数讲解:
图像阈值处理:Cv2.Threshold()
查找轮廓 Cv2.FindContours()
最小外接矩形 Cv2.BoundingRect();
绘制轮廓 Cv2.DrawContours()
计算轮廓相似度 Cv2.MatchShapes()
二、代码(教学注释详细,仔细阅读)
三、代码过程总结:
前言:
轮廓匹配是一种计算机视觉技术,用于在图像中查找和比较目标轮廓与待匹配轮廓之间的相似度
一、函数讲解:
图像阈值处理:Cv2.Threshold()
用于将图像中的像素值根据阈值分成两个不同的区域,可以用来实现图像的分割、边缘检测等任务。
Cv2.Threahold(Mat src, Mat dst ,double thresh, double maxval, ThresholdTypes type)
-
src
: 输入的源图像,通常是一个单通道灰度图像。 -
dst
: 输出的目标图像,与源图像具有相同的尺寸和类型。 -
thresh
: 阈值,用于将图像中的像素值进行分类。 -
maxval
: 阈值之后的像素值要设置的新值。 -
type
: 阈值处理的类型,有不同的处理方式,比如二进制阈值、反二进制阈值、截断阈值等。
常见的阈值处理类型包括:
-
ThresholdTypes.Binary
: 如果像素值大于等于阈值,则设置为maxval
,否则为 0。 -
ThresholdTypes.BinaryInv
: 如果像素值小于阈值,则设置为maxval
,否则为 0。 -
ThresholdTypes.Trunc
: 如果像素值大于等于阈值,则保持原值,否则截断为阈值。 -
ThresholdTypes.ToZero
: 如果像素值大于等于阈值,则保持原值,否则设置为 0。 -
ThresholdTypes.ToZeroInv
: 如果像素值小于阈值,则保持原值,否则设置为 0。 -
ThresholdTypes.Otsu:
大津法,是一种自动确定图像二值化阈值的方法,它能够将图像的像素分成两个类别,使得类别内方差最小、类别间方差最大。这个方法非常适用于背景与前景之间对比明显的图像。 -
ThresholdTypes.Triangle:三角法,
基于图像直方图的阈值选择方法,通过寻找直方图中的一条直线,将直方图分成两个部分,使得两个部分之间的面积最小,从而得到阈值。
Point[][] contours:二维数组表示多个轮廓,每个轮廓由一组点坐标组成,因此 contours变量
存储了多个轮廓。
HierarchyIndex[] hierarchy :用hierarchy变量存储轮廓之间拓扑关系的数据结构
查找轮廓 Cv2.FindContours()
返回一组轮廓数组 contours和轮廓的拓扑结构hierarchy
Point[][] contours; // 存储轮廓的点坐标数组
HierarchyIndex[] hierarchy; // 存储轮廓之间的拓扑关系
Cv2.FindContours(
input: binaryImage, // 二值化图像,白色像素表示物体,黑色像素表示背景
contours: out contours, // 存储找到的轮廓点坐标
hierarchy: out hierarchy, // 存储轮廓之间的拓扑关系
mode: RetrievalModes.External, // 轮廓检索模式,这里表示只检索外部轮廓
method: ContourApproximationModes.ApproxNone // 轮廓逼近方法,这里表示不逼近轮廓
);
-
input
: 二值化图像,即图像中的物体被标记为白色,背景被标记为黑色。 -
contours
: 用于存储找到的轮廓的点坐标数组。每个轮廓由一组点构成。 -
hierarchy
: 用于存储轮廓之间的拓扑关系,例如父子关系、前后关系等。 -
mode
: 轮廓检索模式,可以是RetrievalModes.List
(检索所有轮廓)或RetrievalModes.External
(只检索外部轮廓)等。 -
method
: 轮廓逼近方法,可以是ContourApproximationModes.ApproxNone
(不逼近轮廓)、ContourApproximationModes.ApproxSimple
(简化轮廓)等。
最小外接矩形 Cv2.BoundingRect();
Rect 变量名 = Cv2.BoundingRect(InputArray points);
points
是输入的轮廓点集,可以是一个 Point[]
或 Mat
。
返回一个 Rect
对象,表示计算得到的最小外接矩形。矩形由左上角坐标 (x, y)
和矩形的宽度和 高度 (width, height)
组成。
绘制轮廓 Cv2.DrawContours()
void Cv2.DrawContours(Mat image, IEnumerable<IEnumerable<Point>> contours, int contourIdx, Scalar color, int thickness = 1, LineTypes lineType = LineTypes.Link8, Mat? hierarchy = null, int maxLevel = int.MaxValue, Point? offset = null);
-
image
:目标图像,轮廓将绘制在这个图像上。 -
contours
:要绘制的轮廓列表,可以是一个包含Point
数组的集合。 -
contourIdx
:要绘制的轮廓的索引。
正数值(0、1、2..):表示你要绘制的具体轮廓的索引。如果有多个轮廓,你可以选择其中一个来绘制;
负数(-1):表示绘制所有的轮廓。这时会绘制contours
列表中的所有轮廓。 -
color
:绘制轮廓的颜色。 -
thickness
:轮廓线的宽度,其余为默认
: 绘制细线,通常是轮廓的默认值。这是最常见的用法。
= 1> 1
: 绘制较粗的线。增加线宽度可以使轮廓更加突出。= -1
: 填充轮廓内部。这样可以创建一个实心的轮廓。
Cv2.MatchShapes()形状匹配
double Cv2.MatchShapes(InputArray contour1, InputArray contour2, ContourMatchModes method, double parameter = 0);
-
contour1
:第一个轮廓的点集,可以是一个Point[]
或Mat
。 -
contour2
:第二个轮廓的点集,可以是一个Point[]
或Mat
。 -
method
:计算相似性的方法,可以是ContourMatchModes
枚举中的一个选项。 -
parameter
:计算方法所需的参数。 - 用于选择形状匹配的方法:
-
ShapeMatchModes.I1
:使用I1方法计算形状相似性。 -
ShapeMatchModes.I2
:使用I2方法计算形状相似性。 -
ShapeMatchModes.I3
:使用I3方法计算形状相似性。
返回值:该方法返回一个 double
类型的相似性度量值,用于表示两个轮廓之间的相似性。值越小,表示形状越相似。
二、代码(教学注释详细,仔细阅读)
using OpenCvSharp;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace 轮廓匹配查找模板
{
class Program
{
static void Main(string[] args)
{
//读取图像
Mat template = Cv2.ImRead("C:/Users/CGW/source/repos/轮廓匹配查找模板_Color Space/轮廓匹配查找模板_Color Space/imgs/0.bmp"); //读取模板图像
Mat imgTest = Cv2.ImRead("C:/Users/CGW/source/repos/轮廓匹配查找模板_Color Space/轮廓匹配查找模板_Color Space/imgs/4.bmp"); //读取测试图像
Mat grayTemp = new Mat(); // 模板图像
Mat grayTest = new Mat(); // 测试图像
//转换为灰度图
Cv2.CvtColor(template, grayTemp, ColorConversionCodes.BGR2GRAY);
Cv2.CvtColor(imgTest, grayTest, ColorConversionCodes.BGR2GRAY);
//二值化--分割轮廓区域
Cv2.Threshold(grayTemp, grayTemp, 90, 255, ThresholdTypes.Binary);
Cv2.Threshold(grayTest, grayTest, 90, 255, ThresholdTypes.Binary);
Point[][] contours; // 存储轮廓的变量
HierarchyIndex[] hierarchy; // 存储轮廓拓扑结构的变量
// 使用 FindContours 函数查找图像中的轮廓,返回一组轮廓数组 contours,同时还可以获得轮廓的拓扑结构。
Cv2.FindContours(grayTemp, out contours, out hierarchy, RetrievalModes.External, ContourApproximationModes.ApproxNone);
int index = 0; // 模板中十字轮廓的轮廓序号
//遍历每个轮廓
for (int i = 0; i < contours.Length; i++)
{
// 获取轮廓的外接矩形
Rect rect = Cv2.BoundingRect(contours[i]);
// 判断轮廓是否满足条件,比如宽度和高度都大于80像素
if (rect.Width > 80 && rect.Height > 80)
{
index = i; // 记录找到的目标轮廓的索引
// 在模板图像上绘制找到的目标轮廓,标记为红色
Cv2.DrawContours(template, contours, i, new Scalar(0, 0, 255), -1);
break; // 找到目标轮廓后跳出循环
}
}
Cv2.ImShow("template", template);
//遍历测试图像中的轮廓做轮廓匹配
Point[][] contours2; //轮廓查找结果变量
HierarchyIndex[] hierarchy2; //轮廓拓扑结构变量
Cv2.FindContours(grayTest, out contours2, out hierarchy2, RetrievalModes.External,
ContourApproximationModes.ApproxNone);
//Console.WriteLine("contour2_size = {0}", contours2.Length); //输出轮廓个数
for (int i = 0; i < contours2.Length; i++)
{
Rect rect = Cv2.BoundingRect(contours2[i]);
//宽高与模板轮廓宽高相符的才做轮廓匹配,剔除干扰轮廓
if (rect.Width >= 80 && rect.Width <= 100 && rect.Height > 80 && rect.Height <= 100) //找到目标轮廓,跳出循环
{
// 计算轮廓匹配值
double matchValue = Cv2.MatchShapes(contours[index], contours2[i], ShapeMatchModes.I3);
// 输出匹配值
Console.WriteLine("matchRate = {0}", matchValue);
// 如果匹配值在一定阈值内,绘制匹配的轮廓
if (matchValue <= 2)
Cv2.DrawContours(imgTest, contours2, i, new Scalar(0, 255, 0), -1);
}
}
Cv2.ImShow("match-result", imgTest);
Cv2.WaitKey(0);
}
}
}
三、代码过程总结:
- 读取模板图像和测试图像。
- 创建存储灰度模板和测试图像的变量
grayTemp
和grayTest
。 - 将图像转换为灰度图像。
- 对模板图像和测试图像进行二值化处理,将图像中的轮廓区域分割出来。
- 定义存储轮廓结果和拓扑结构的变量
contours
和hierarchy
。 - 使用
Cv2.FindContours
在模板图像中查找轮廓,存储在contours
中,同时也获取轮廓的拓扑结构。 - 遍历
contours
数组,找到满足条件的目标轮廓(这里是宽度和高度都大于80像素),在模板图像上绘制目标轮廓。 - 使用
Cv2.ImShow
显示带有标记的模板图像。