EmguCV学习笔记 C# 7.2 特征点检测

版权声明:本文为博主原创文章,转载请在显著位置标明本文出处以及作者网名,未经作者允许不得用于商业目的。

EmguCV是一个基于OpenCV的开源免费的跨平台计算机视觉库,它向C#和VB.NET开发者提供了OpenCV库的大部分功能。

教程VB.net版本请访问: EmguCV学习笔记 VB.Net 目录-CSDN博客

教程C#版本请访问: EmguCV学习笔记 C# 目录-CSDN博客

笔者的博客网址:https://blog.csdn.net/uruseibest

教程配套文件及相关说明以及如何获得pdf教程和代码,请移步: EmguCV学习笔记

学习VB.Net知识,请移步: vb.net 教程 目录_vb中如何用datagridview-CSDN博客

学习C#知识,请移步: C# 教程 目录_c#教程目录-CSDN博客

7.2 特征点检测

在EmguCV中,特征点通常使用Features2D命名空间下的类进行检测和提取,常用的特征点检测算法包括FAST、SIFT、SURF、ORB等。使用这些算法可以在图像中提取出关键点和特征描述子,进而用于图像匹配、目标跟踪、三维重建等领域。

7.2.1 Features2DToolbox类

Features2DToolbox类提供了静态方法来绘制特征点和特征点连线。由于此类需要依赖于其他方法获得特征点或特征描述子,因此关于本节中介绍的Features2DToolbox类相关方法具体的例子,请看后面的章节。

7.2.1.1 DrawKeypoints

Features2Dtoolbox的DrawKeypoints方法用于在图像上绘制关键点。该函数可以将检测到的关键点绘制在图像上,以便进行可视化和分析。

函数的定义如下:

public static void DrawKeypoints(

IInputArray image,

VectorOfKeyPoint keypoints,

IInputOutputArray outImage,

Bgr color,

Features2DToolbox. KeypointDrawType type = Features2DToolbox.KeypointDrawType.Default

)

参数说明:

  1. image:要绘制关键点的图像。
  2. keypoints:要绘制的关键点,通常是通过SIFT、SURF、ORB等算法检测到的关键点。
  3. outImage:输出图像,通常是一个Mat对象,用于绘制关键点。
  4. color:绘制关键点的颜色,通常是一个MCvScalar对象,表示颜色的BGR值。
  5. type:绘制关键点的方式,通常是一个Features2DToolbox.KeypointDrawType枚举值,可以选择绘制关键点的位置、方向、大小等信息。它包括如下成员:
    1. KeypointDrawType.Default:绘制圆形。
    2. KeypointDrawType.DrawRichKeypoints:绘制带方向和大小信息的关键点。
7.2.1.2 DrawMatches

Features2Dtoolbox的DrawMatches方法用于绘制特征点匹配结果的函数,它可以将两幅图像中的匹配特征点连接起来,并将连接线绘制到一张新的图像中。常用声明如下:

public static void DrawMatches(

IInputArray modelImage,

VectorOfKeyPoint modelKeypoints,

IInputArray observedImage,

VectorOfKeyPoint observedKeyPoints,

VectorOfDMatch matches,

IInputOutputArray result,

MCvScalar matchColor,

MCvScalar singlePointColor,

VectorOfByte mask = null,

Features2DToolbox. KeypointDrawType flags = Features2DToolbox.KeypointDrawType.Default

)

参数说明:

  1. modelImage:模型图像。
  2. modelKeypoints:模型图像的特征点,类型为VectorOfKeyPoint。
  3. observedImage:匹配图像。
  4. observedKeyPoints:匹配图像的特征点,类型为VectorOfKeyPoint。
  5. matches:两幅图像中的匹配特征点,类型为VectorOfDMatch或者VectorOfVectorOfDMatch。
  6. result:输出图像,用于存储绘制结果。
  7. matchColor:用于绘制匹配线的颜色,类型为MCvScalar。
  8. singlePointColor:用于绘制特征点的颜色,类型为MCvScalar。
  9. mask:绘制掩码。
  10. flags:绘制标志,用于控制绘制的方式,类型为Features2DToolbox.DrawMatchesFlags枚举类型。
7.2.1.3 VoteForUniqueness

Feature2DToolbox类的VoteForUniqueness方法实现特征匹配的进一步优化,去除重复匹配和错误匹配,保留唯一匹配结果。该方法声明如下:

public static void VoteForUniqueness(

VectorOfVectorOfDMatch matches,

double uniquenessThreshold,

Mat mask

)

参数说明:

  1. matches:需要筛选的特征点,VectorOfVectorOfDMatch变量。
  2. uniquenessThreshold:阈值,取值范围为0到1之间,一般设置为0.8或0.9。
  3. mask:输入输出矩阵,这是一个Row=matches.Size、Col=1的矩阵。输入的时候,指定哪些特征点需要进行唯一性计算。如果某个特征点的mask值为0,则不进行计算。通常设置为MCvScalar(255)。输出的时候,获得唯一性矩阵,其中唯一的特征点对应的像素值为255,非唯一的特征点对应的像素值为0。
7.2.1.4 VoteForSizeAndOrientation

VoteForSizeAndOrientation方法用于计算匹配的特征点之间的大小和方向的一致性得分,从而去除可能的错误匹配。该方法声明如下:

public static int VoteForSizeAndOrientation(

VectorOfKeyPoint modelKeyPoints,

VectorOfKeyPoint observedKeyPoints,

VectorOfVectorOfDMatch matches,

Mat mask,

double scaleIncrement,

int rotationBins

)

参数说明:

  1. modelKeyPoints:模型图像中的特征点,VectorOfKeyPoint类型。
  2. observedKeyPoints:匹配图像中的特征点,VectorOfKeyPoint类型。
  3. matches:匹配的特征点,VectorOfVectorOfDMatch类型。
  4. mask:蒙版。输入是VoteForUniqueness方法获得的mask。输出为大小和方向一致性得分矩阵,其中唯一的特征点对应的像素值为255,非唯一的特征点对应的像素值为0。
  5. scaleIncrement:缩放值,通常设置为1.5。
  6. rotationBins:匹配可旋转点的数量,通常设置为20。

返回值:

Mask中非0元素的个数。

7.2.1.5 GetHomographyMatrixFromMatchedFeatures

GetHomographyMatrixFromMatchedFeatures方法用于计算两幅图像中匹配的特征点之间的单应性矩阵,从而实现图像的配准和拼接。该方法声明如下:

public static Mat GetHomographyMatrixFromMatchedFeatures(

VectorOfKeyPoint model,

VectorOfKeyPoint observed,

VectorOfVectorOfDMatch matches,

Mat mask,

double ransacReprojThreshold

)

参数说明:

  1. model:模型图像中的特征点,VectorOfKeyPoint类型。
  2. observed:匹配图像中的特征点,VectorOfKeyPoint类型。
  3. matches:匹配的特征点,VectorOfVectorOfDMatch类型。
  4. mask:蒙版。输入通常是VoteForUniqueness方法或者VoteForSizeAndOrientation方法获得的mask,也可以手动设置。
  5. ransacReprojThreshold:RANSAC算法中的二次投影误差阈值,一般为1到10之间。

返回值:

返回单应性矩阵,如果找不到,则返回Nothing。

7.2.2 SIFT类

EmguCV中的SIFT类是使用SIFT (Scale-Invariant Feature Transform)算法进行特征点检测和描述子生成的工具。SIFT算法是一种基于尺度空间极值检测和高斯差分图像描述子的算法,具有较高的特征检测精度和描述子匹配准确性,它可以在不同尺度和旋转角度下检测和描述图像中的特征点,由于其良好的尺度不变性和旋转不变性,被广泛应用于计算机视觉领域。SIFT算法原本申请了专利需要许可才能使用,但现在SIFT算法的专利已经到期。

SIFT类提供了以下方法来检测特征点和获得特征描述:

1 、Detect 方法

Detect方法使用SIFT特征检测器检测了图像中的SIFT特征点,并将检测结果保存在MKeyPoint类型数组变量中。

MKeyPoint类表示一个特征点,包含了该特征点的位置、尺度、方向等信息。

2 、DetecRaw 方法

DetecRaw方法同Detect方法,但是它将检测结果保存在VectorOfKeyPoint类型变量中。

VectorOfKeyPoint类表示一个特征点向量,可以用于管理和操作多个特征点,常用于特征点的提取、保存、加载、匹配等操作。

3 、Compute 方法

Compute方法用于在给定的图像和对应特征点处提取特征描述子,将提取结果保存在Mat类型变量中。

特征描述子包含了描述关键点周围区域的一组数值,可以用于比较、匹配和识别关键点。在使用SIFT算法提取特征描述子时,特征描述子保存在一个Mat类型变量中,该Mat的大小是"关键点的数量×128",对应Mat的"行×列",每个关键点为一行,128表示每个特征描述子包含128个数值,这些数值表示了关键点周围区域的特征信息,可以用于描述和区分不同的关键点。

4 、DetectAndCompute 方法

DetectAndCompute方法是Detect方法和Compute方法的集成,可以返回图像中的SIFT关键点和特征描述子。

参数说明:

  1. image:输入图像,类型为`Mat`。
  2. mask:掩膜图像,用于指定特征点的提取范围。该参数通常设置为Nothing。
  3. keypoints:提取到的特征点,类型为VectorOfKeyPoint。
  4. descriptors:计算得到的特征描述子,类型为Mat。
  5. useProvidedKeypoints:是否使用输入的特征点进行计算。当为False时,既要检测关键点,又要计算特征描述子;否则只获得特征描述子。

【代码位置:frmChapter7】Button6_Click

//获得特征关键点

private void Button6_Click(object sender, EventArgs e)

{

Mat msrc = new Mat("c:\\learnEmgucv\\lena.jpg", ImreadModes.AnyColor);

Emgu.CV.Features2D.SIFT sift = new Emgu.CV.Features2D.SIFT();

VectorOfKeyPoint vkp = new VectorOfKeyPoint();

sift.DetectRaw(msrc, vkp);

//绘制关键点

Mat m1 = msrc.Clone();

for (int i = 0; i < vkp.Size; i++)

CvInvoke.Circle(m1, PointFToPoint(vkp[i].Point), 3, new MCvScalar(0, 0, 255), -1);

ImageBox1.Image = m1;

MKeyPoint[] mkp = sift.Detect(msrc);

//绘制关键点

Mat m2 = msrc.Clone();

for (int i = 0; i < mkp.Length; i++)

CvInvoke.Circle(m2, PointFToPoint(mkp[i].Point), 3, new MCvScalar(0, 0, 255), -1);

ImageBox2.Image = m2;

}

输出结果如下图所示:

图7-6 获得关键点比较

通过以下方法获得特征描述子

【代码位置:frmChapter7】Button7_Click

//获得特征描述子

private void Button7_Click(object sender, EventArgs e)

{

Mat msrc = new Mat("c:\\learnEmgucv\\lena.jpg", ImreadModes.AnyColor);

Emgu.CV.Features2D.SIFT sift = new Emgu.CV.Features2D.SIFT();

//使用DetectRaw方法来获得特征点,返回一个VectorOfKeyPoint

VectorOfKeyPoint vkp1 = new VectorOfKeyPoint();

sift.DetectRaw(msrc, vkp1);

//获得特征描述子

Mat mdst1 = new Mat();

sift.Compute(msrc, vkp1, mdst1);

//或者采用DetectAndCompute方法来获得特征描述子

VectorOfKeyPoint vkp2 = new VectorOfKeyPoint();

Mat mdst2 = new Mat();

sift.DetectAndCompute(msrc, null, vkp2, mdst2, false);

}

输出结果如下图所示:

图7-7 获得特征描述子

查看输出的特征描述子Mat,可以看到lena图像的关键点一共1110(Rows),Mat宽度128。特征描述子具体的使用参看后面。

7.2.3 BFMatcher类

EmguCV中的BFMatcher类是一个Brute-Force Matcher(暴力匹配器),用于在两个特征描述子集合之间进行匹配。BFMatcher类提供了两种匹配方法:暴力匹配和k近邻匹配,可以根据实际需求选择合适的方法。

BFMatcher类的主要方法为Match方法,可以接受两个特征描述子集合并返回它们之间的匹配结果。另外,该类还提供了一些其他的属性和方法,用于设置匹配算法的参数和获取匹配结果。

下面是BFMatcher类的常用方法和属性:

  1. Match方法:在两个特征描述子集合之间进行匹配。

  2. KnnMatch方法:使用k近邻算法在两个特征描述子集合之间进行匹配。

  3. MatchType属性:设置匹配算法的类型。

  4. CrossCheck属性:设置是否使用交叉验证。

7.2.3.1 Match

Match方法是在两个特征描述子集合之间进行匹配的函数,它的声明如下:

public void Match(

IInputArray queryDescriptors,

IInputArray trainDescriptors,

VectorOfDMatch matches,

IInputArray mask = null

)

该函数接受三个参数:

  1. queryDescriptors:查询图像的特征描述子,类型为Mat。
  2. trainDescriptors:训练图像的特征描述子,类型为Mat。
  3. matches:VectorOfDMatch类型。其中每个Dmatch结构表示每个查询描述子和其最佳匹配的训练描述子之间的匹配结果,它有三个主要属性:
    1. QueryIdx:对应查询图像查询描述子的索引。
    2. TrainIdx:对应训练图像描述子的索引。
    3. Distance:两个描述子之间的距离,越小表明匹配度越高。
  4. mask:掩膜图像,用于指定匹配的范围,类型为Mat。

【代码位置:frmChapter7】Button8_Click

//特征点匹配

private void Button8_Click(object sender, EventArgs e)

{

Mat m1 = new Mat("c:\\learnEmgucv\\lena.jpg", ImreadModes.AnyColor);

Mat m2 = new Mat("c:\\learnEmgucv\\lena_keypoint.jpg", ImreadModes.AnyColor);

Emgu.CV.Features2D.SIFT SIFT1 = new Emgu.CV.Features2D.SIFT();

Mat des1 = new Mat();

VectorOfKeyPoint vkp1 = new VectorOfKeyPoint();

SIFT1.DetectAndCompute(m1, null, vkp1, des1, false);

Mat m1c = m1.Clone();

//使用Circle标记特征点

for (int i = 0; i < vkp1.Size; i++)

CvInvoke.Circle(m1c, PointFToPoint(vkp1[i].Point), 3, new MCvScalar(0, 0, 255), -1);

ImageBox1.Image = m1c;

Emgu.CV.Features2D.SIFT SIFT2 = new Emgu.CV.Features2D.SIFT();

Mat des2 = new Mat();

VectorOfKeyPoint vkp2 = new VectorOfKeyPoint();

SIFT2.DetectAndCompute(m2, null, vkp2, des2, false);

Mat m2c = m2.Clone();

//使用DrawKeypoints标记特征点

Features2DToolbox.DrawKeypoints(m2c, vkp2, m2c, new Bgr(0, 0, 255));

ImageBox2.Image = m2c;

VectorOfDMatch matches = new VectorOfDMatch();

BFMatcher matcher = new BFMatcher(DistanceType.L2);

//进行匹配

matcher.Match(des2, des1, matches);

Console.WriteLine("匹配成功特征点数目:" + matches.Size);

Mat outimg = new Mat();

//绘制将特征点连接起来

//需要注意Match和DrawMatches参数的顺序

Emgu.CV.Features2D.Features2DToolbox.DrawMatches(

m1, vkp1, m2, vkp2, matches, outimg, new MCvScalar(0, 0, 255), new MCvScalar(0, 255, 0));

ImageBox3.Image = outimg;

}

输出结果如下图所示:

图7-8 图像特征点匹配

【代码位置:frmChapter7】Button9_Click

//特征点匹配 采用一定算法对特征点进行筛选

private void Button9_Click(object sender, EventArgs e)

{

Mat m1 = new Mat("c:\\learnEmgucv\\lena.jpg", ImreadModes.AnyColor);

Mat m2 = new Mat("c:\\learnEmgucv\\lena_keypoint.jpg", ImreadModes.AnyColor);

SIFT SIFT1 = new SIFT();

Mat des1 = new Mat();

VectorOfKeyPoint vkp1 = new VectorOfKeyPoint();

SIFT1.DetectAndCompute(m1, null, vkp1, des1, false);

Mat m1c = m1.Clone();

for (int i = 0; i < vkp1.Size; i++)

CvInvoke.Circle(m1c, PointFToPoint(vkp1[i].Point), 3, new MCvScalar(0, 0, 255), -1);

ImageBox1.Image = m1c;

SIFT SIFT2 = new SIFT();

Mat des2 = new Mat();

VectorOfKeyPoint vkp2 = new VectorOfKeyPoint();

SIFT2.DetectAndCompute(m2, null, vkp2, des2, false);

Mat m2c = m2.Clone();

Features2DToolbox.DrawKeypoints(m2c, vkp2, m2c, new Bgr(0, 0, 255));

ImageBox2.Image = m2c;

VectorOfDMatch matches = new VectorOfDMatch();

BFMatcher matcher = new BFMatcher(DistanceType.L2);

matcher.Match(des2, des1, matches);

Console.WriteLine("匹配成功特征点数目:" + matches.Size);

List<MDMatch> lstGoodMatches = new List<MDMatch>();

VectorOfDMatch goodMatches = new VectorOfDMatch();

//采用一定算法对特征点进行筛选

for (int i = 0; i < matches.Size - 1; i++)

if (matches[i].Distance < 0.7 * matches[i + 1].Distance)

lstGoodMatches.Add(matches[i]);

goodMatches.Push(lstGoodMatches.ToArray());

Mat outimg = new Mat();

Emgu.CV.Features2D.Features2DToolbox.DrawMatches(

m1, vkp1, m2, vkp2, goodMatches, outimg, new MCvScalar(0, 0, 255), new MCvScalar(0, 255, 0));

ImageBox3.Image = outimg;

}

输出结果如下图所示:

图7-9 使用一定算法优化后的图像特征点匹配

7.2.3.2 knnMatch

knnMatch方法实现暴力匹配算法中的K近邻匹配(K-Nearest Neighbor Matching)。它用于在两个特征描述子集合之间进行K近邻匹配,并返回每个查询描述子和其最佳匹配的K个训练描述子之间的匹配结果。该方法声明如下:

public void KnnMatch(

IInputArray queryDescriptors,

IInputArray trainDescriptors,

VectorOfVectorOfDMatch matches,

int k,

IInputArray mask = null,

bool compactResult = false

)

该函数接受五个参数:

  1. queryDescriptors:查询图像的特征描述子,类型为Mat。
  2. trainDescriptors:训练图像的特征描述子,类型为Mat。
  3. matches:VectorOfDMatch类型。
  4. k:每个查询描述子在训练描述子集合中寻找的最佳匹配结果数目,但是实际匹配中获得的数目可能会小于k。
  5. mask:掩膜图像,用于指定匹配的范围,默认所有描述子都将进行匹配。
  6. compactResult:是否输出紧凑格式。False时,输出的匹配结果为DMatch对象列表,每个DMatch对象包含了一个查询特征点和一个训练特征点之间的距离和索引。True时,输出的匹配结果为紧凑格式的匹配结果,它是一个二维整数数组,每个元素为一个查询特征点的k个最近邻训练特征点的索引。默认为False。

【代码位置:frmChapter7】Button10_Click

//特征点匹配 KnnMatch

private void Button10_Click(object sender, EventArgs e)

{

Mat m1 = new Mat("c:\\learnEmgucv\\lena.jpg", ImreadModes.AnyColor);

Mat m2 = new Mat("c:\\learnEmgucv\\lena_keypoint.jpg", ImreadModes.AnyColor);

SIFT SIFT1 = new SIFT();

Mat des1 = new Mat();

VectorOfKeyPoint vkp1 = new VectorOfKeyPoint();

SIFT1.DetectAndCompute(m1, null, vkp1, des1, false);

Mat m1c = m1.Clone();

for (int i = 0; i < vkp1.Size; i++)

CvInvoke.Circle(m1c, PointFToPoint(vkp1[i].Point), 3, new MCvScalar(0, 0, 255), -1);

ImageBox1.Image = m1c;

SIFT SIFT2 = new SIFT();

Mat des2 = new Mat();

VectorOfKeyPoint vkp2 = new VectorOfKeyPoint();

SIFT2.DetectAndCompute(m2, null, vkp2, des2, false);

Mat m2c = m2.Clone();

Features2DToolbox.DrawKeypoints(m2c, vkp2, m2c, new Bgr(0, 0, 255));

ImageBox2.Image = m2c;

VectorOfVectorOfDMatch matches = new VectorOfVectorOfDMatch();

BFMatcher matcher = new BFMatcher(DistanceType.L2);

//使用KnnMatch匹配

matcher.KnnMatch(des2, des1, matches, 3);

Mat outimg = new Mat();

Emgu.CV.Features2D.Features2DToolbox.DrawMatches(

m1, vkp1, m2, vkp2, matches, outimg, new MCvScalar(0, 0, 255), new MCvScalar(0, 255, 0), null);

ImageBox3.Image = outimg;

}

输出结果如下图所示:

图7-10 图像特征点匹配

【代码位置:frmChapter7】Button11_Click

//特征点匹配 采用一定算法对特征点进行筛选

private void Button11_Click(object sender, EventArgs e)

{

Mat m1 = new Mat("c:\\learnEmgucv\\lena.jpg", ImreadModes.AnyColor);

Mat m2 = new Mat("c:\\learnEmgucv\\lena_keypoint.jpg", ImreadModes.AnyColor);

SIFT SIFT1 = new SIFT();

Mat des1 = new Mat();

VectorOfKeyPoint vkp1 = new VectorOfKeyPoint();

SIFT1.DetectAndCompute(m1, null, vkp1, des1, false);

Mat m1c = m1.Clone();

for (int i = 0; i < vkp1.Size; i++)

CvInvoke.Circle(m1c, PointFToPoint(vkp1[i].Point), 3, new MCvScalar(0, 0, 255), -1);

ImageBox1.Image = m1c;

SIFT SIFT2 = new SIFT();

Mat des2 = new Mat();

VectorOfKeyPoint vkp2 = new VectorOfKeyPoint();

SIFT2.DetectAndCompute(m2, null, vkp2, des2, false);

Mat m2c = m2.Clone();

Features2DToolbox.DrawKeypoints(m2c, vkp2, m2c, new Bgr(0, 0, 255));

ImageBox2.Image = m2c;

VectorOfVectorOfDMatch matches = new VectorOfVectorOfDMatch();

BFMatcher matcher = new BFMatcher(DistanceType.L2);

matcher.KnnMatch(des2, des1, matches, 3);

//获得最小距离

List<MDMatch> lstLastMatches = new List<MDMatch>();

Double min_dist = 100;

for (int i = 0; i < matches.Size; i++)

if (matches[i][0].Distance < min_dist)

min_dist = matches[i][0].Distance;

VectorOfVectorOfDMatch goodMatches = new VectorOfVectorOfDMatch();

//采用一定算法对特征点进行筛选

for (int i = 0; i < matches.Size; i++)

if (matches[i][0].Distance < 2 * min_dist)

goodMatches.Push(matches[i]);

Mat outimg = new Mat();

Emgu.CV.Features2D.Features2DToolbox.DrawMatches(

m1, vkp1, m2, vkp2, goodMatches, outimg, new MCvScalar(0, 0, 255), new MCvScalar(0, 255, 0), null);

ImageBox3.Image = outimg;

}

输出结果如下图所示:

图7-11 使用一定算法优化后的图像特征点匹配

【代码位置:frmChapter7】Button12_Click

//图像匹配

private void Button12_Click(object sender, EventArgs e)

{

Mat m1 = new Mat("c:\\learnEmgucv\\lena.jpg", ImreadModes.AnyColor);

Mat m2 = new Mat("c:\\learnEmgucv\\lena_keypoint.jpg", ImreadModes.AnyColor);

SIFT SIFT1 = new SIFT();

Mat des1 = new Mat();

VectorOfKeyPoint vkp1 = new VectorOfKeyPoint();

SIFT1.DetectAndCompute(m1, null, vkp1, des1, false);

Mat m1c = m1.Clone();

for (int i = 0; i < vkp1.Size; i++)

CvInvoke.Circle(m1c, PointFToPoint(vkp1[i].Point), 3, new MCvScalar(0, 0, 255), -1);

ImageBox1.Image = m1c;

SIFT SIFT2 = new SIFT();

Mat des2 = new Mat();

VectorOfKeyPoint vkp2 = new VectorOfKeyPoint();

SIFT2.DetectAndCompute(m2, null, vkp2, des2, false);

Mat m2c = m2.Clone();

Features2DToolbox.DrawKeypoints(m2c, vkp2, m2c, new Bgr(0, 0, 255));

ImageBox2.Image = m2c;

VectorOfVectorOfDMatch matches = new VectorOfVectorOfDMatch();

BFMatcher matcher = new BFMatcher(DistanceType.L2);

matcher.KnnMatch(des2, des1, matches, 3);

Mat mask = new Mat(matches.Size, 1, DepthType.Cv8U, 1);

mask.SetTo(new MCvScalar(255));

//进一步优化特征匹配

Features2DToolbox.VoteForUniqueness(matches, 0.8, mask);

//去除可能的错误匹配

Features2DToolbox.VoteForSizeAndOrientation(vkp1, vkp2, matches, mask, 1.5, 20);

//得到模板和匹配图片的仿射矩阵

Mat mtrans = new Mat();

mtrans = Features2DToolbox.GetHomographyMatrixFromMatchedFeatures(vkp1, vkp2, matches, mask, 3);

//得到模板图像矩形区域

Rectangle rec = new Rectangle(Point.Empty, m1.Size);

//提取模板图像矩形区域的四个顶点,为后面定位匹配图像做准备

PointF[] pts = new PointF[4];

pts[0] = new PointF(rec.Left, rec.Bottom);

pts[1] = new PointF(rec.Right, rec.Bottom);

pts[2] = new PointF(rec.Right, rec.Top);

pts[3] = new PointF(rec.Left, rec.Top);

//定位匹配图像

pts = CvInvoke.PerspectiveTransform(pts, mtrans);

//PointF() to Point()

Point[] points = Array.ConvertAll(pts, new Converter<PointF, Point>(PointFToPoint));

//绘制

VectorOfPoint vp = new VectorOfPoint(points);

for (int i = 0; i < vp.Size; i++)

CvInvoke.Polylines(m2, vp, true, new MCvScalar(255, 0, 0), 4);

CvInvoke.Imshow("m2", m2);

}

输出结果如下图所示:

图7-12 图像匹配

7.2.4 Feature2D命名空间

Feature2D命名空间是EmguCV中用于处理图像特征的命名空间。该命名空间包含了多个类和方法,可以用于检测、提取和匹配图像特征。该命名空间中有两个重要的基类:

1、Feature2D类

Feature2D类是一个抽象类,用于定义检测和提取图像特征的接口。该类包含了多个方法,包括Detect方法、Compute方法和ComputeDescriptors方法等。具体的特征检测和提取算法都是通过Feature2D类的派生类来实现的,例如SIFT、ORB等。

2、XFeature2D类

XFeature2D类是EmguCV中用于实现新型特征算法的类,它包含了多种高级特征算法,如FREAK、LATCH和DAISY等。XFeature2D类的特征算法通常比feature2d类更高级、更复杂,具有更好的性能和效果。

Feature2D命名空间提供的图像特征类的使用请参考7.2.2 【SIFT类】

这里以AKAZE算法为例

【代码位置:frmChapter7】Button13_Click

//AKAZE

private void Button13_Click(object sender, EventArgs e)

{

Mat m1 = new Mat("c:\\learnEmgucv\\lena.jpg", ImreadModes.AnyColor);

Mat m2 = new Mat("c:\\learnEmgucv\\lena_keypoint.jpg", ImreadModes.AnyColor);

AKAZE AKAZE1 = new AKAZE();

Mat des1 = new Mat();

VectorOfKeyPoint vkp1 = new VectorOfKeyPoint();

AKAZE1.DetectAndCompute(m1, null, vkp1, des1, false);

Mat m1c = m1.Clone();

//使用Circle标记特征点

for (int i = 0; i < vkp1.Size; i++)

CvInvoke.Circle(m1c, PointFToPoint(vkp1[i].Point), 3, new MCvScalar(0, 0, 255), -1);

ImageBox1.Image = m1c;

AKAZE AKAZE2 = new AKAZE();

Mat des2 = new Mat();

VectorOfKeyPoint vkp2 = new VectorOfKeyPoint();

AKAZE2.DetectAndCompute(m2, null, vkp2, des2, false);

Mat m2c = m2.Clone();

//使用DrawKeypoints标记特征点

Features2DToolbox.DrawKeypoints(m2c, vkp2, m2c, new Bgr(0, 0, 255));

ImageBox2.Image = m2c;

VectorOfDMatch matches = new VectorOfDMatch();

BFMatcher matcher = new BFMatcher(DistanceType.L2);

//进行匹配

matcher.Match(des2, des1, matches);

Console.WriteLine("匹配成功特征点数目:" + matches.Size);

Mat outimg = new Mat();

//绘制将特征点连接起来

//需要注意Match和DrawMatches参数的顺序

Emgu.CV.Features2D.Features2DToolbox.DrawMatches(

m1, vkp1, m2, vkp2, matches, outimg, new MCvScalar(0, 0, 255), new MCvScalar(0, 255, 0));

ImageBox3.Image = outimg;

}

输出结果如下图所示:

图7-13 AKAZE算法实现的特征点匹配

相关推荐
华清远见IT开放实验室1 小时前
【每天学点AI】实战图像增强技术在人工智能图像处理中的应用
图像处理·人工智能·python·opencv·计算机视觉
只怕自己不够好1 小时前
《OpenCV 图像缩放、翻转与变换全攻略:从基础操作到高级应用实战》
人工智能·opencv·计算机视觉
HPC_fac130520678164 小时前
以科学计算为切入点:剖析英伟达服务器过热难题
服务器·人工智能·深度学习·机器学习·计算机视觉·数据挖掘·gpu算力
安静读书7 小时前
Python解析视频FPS(帧率)、分辨率信息
python·opencv·音视频
小陈phd7 小时前
OpenCV从入门到精通实战(九)——基于dlib的疲劳监测 ear计算
人工智能·opencv·计算机视觉
向宇it9 小时前
【unity小技巧】unity 什么是反射?反射的作用?反射的使用场景?反射的缺点?常用的反射操作?反射常见示例
开发语言·游戏·unity·c#·游戏引擎
九鼎科技-Leo9 小时前
什么是 WPF 中的依赖属性?有什么作用?
windows·c#·.net·wpf
Heaphaestus,RC10 小时前
【Unity3D】获取 GameObject 的完整层级结构
unity·c#
baivfhpwxf202311 小时前
C# 5000 转16进制 字节(激光器串口通讯生成指定格式命令)
开发语言·c#
直裾11 小时前
Scala全文单词统计
开发语言·c#·scala