赞
踩
又是选修课,今天讲自动配准和半自动配准。
自动配准就是我们一个点也不选,程序自动进行角点检测和匹配;
而半自动配准,以仿射变换六参数为例,我们需要先选3个点,确定最初的参数,然后在角点匹配中 检查一个点 经过该六参数变换后 得到的点 与 检测出来的点的误差 是否合格(误差限制是主观确定的)。接下来我们看实现:
C#很方便,我们在Nuget中搜索安装以下三个包即可,这里就不教大家安装了,网上很多教程。
首先我们知道,OpenCV中进行运算的类是 Mat (Matrix的缩写),那我们要使用OpenCV进行角点检测,就需要将图片转为Mat。
我们知道,在Gdal导入图片时,一般是将图片转为Bitmap进行显示。那么,在保持高宽比的情况下,使用仅放大缩小后的图片求出来的仿射变换参数,也可以使用在原始大小的图片上(不知道大家能不能理解)。
知道了这一点,那么我们就可以使用保持高宽比的显示出来的Bitmap图来进行OpenCV上的操作。
贴代码的方式沿袭Part9,会将每一句的作用,当然,仅供参考。大家知道了各个函数的作用可以更灵活的放到自己的代码中。
首先,我们讲半自动配准。
半自动配准是在参数已经求出来的情况下进行配准。
注意,以下的代码中有这么几种我的命名习惯:
TsF:Transform的缩写,代表仿射变换六参数,值为 double[6]{a, b, c, d, e, f}
Cor:前后缀,代表Corrected,待矫正的,用来指待矫正的图像,或者待矫正的图像上的点
Ref:前后缀,代表Reference,参考,用来指参考图像,或者待参考图像上的点
接下来,看一下Bitmap转为Mat的代码:
- OpenCvSharp.Mat Cor_mat = OpenCvSharp.Extensions.BitmapConverter.ToMat(待矫正影像Bitmap变量名);
- OpenCvSharp.Mat Ref_mat = OpenCvSharp.Extensions.BitmapConverter.ToMat(参考影像Bitmap变量名);
然后,我们需要一个容器存储找出的对应点,这个容器中每一个元素应该是 一对 对应点,那我们就来创造这个容器:
var points = new System.Collections.Generic.List<System.Drawing.Point[]>();
我们可以看到创造了一个容器,容器里每一个元素都是Point[ ],所以我们就可以将 每一对 对应点 组成一个 Point数组,它的长度应该是2,一个待矫正点,一个参考点。
创造完了装载结果的容器,我们接下来创造特征提取的模型,这里用的是SIFT算法:
var model = OpenCvSharp.Features2D.SIFT.Create();
这样,我们就实例化了一个SIFT模型。接下来我们看一下这个模型检测特征点和提取特征点描述符的函数,源代码里是这样的:
public virtual void DetectAndCompute(
InputArray image, //需要检测的图像
InputArray? mask, //掩膜,一般为null
out KeyPoint[] keypoints, //存储关键点的数组
OutputArray descriptors, //存储描述符的Mat对象
bool useProvidedKeypoints = false); //一般不选
接下来就是引用:
- Mat Desc_Cor = new Mat();//接收特征描述符的Mat
- Mat Desc_Ref = new Mat();//接收特征描述符的Mat
- KeyPoint[] KP_Cor, KP_Ref;//接收keypoints的对象
-
-
- //进行检测与特征描述符提取
- model.DetectAndCompute(Cor_mat, null, out KP_Cor, Desc_Cor);
- model.DetectAndCompute(Ref_mat, null, out KP_Ref, Desc_Ref);
到此,我们拥有了两幅图片的特征点(KP_Cor,KP_Ref),
及其特征描述(Descriptor)符(Desc_Cor,Desc_Ref)。
于是就可以进行匹配:
- var FBmatcher = new OpenCvSharp.FlannBasedMatcher();//实例化匹配器
- var matches = FBmatcher.Match(Desc_Cor, Desc_Ref);//对特征描述符进行匹配
这下,匹配结果就全都放在了matches这一变量中。
要如何访问 matches 里某一对点的信息呢?我们假设需要访问 第i对点 的信息:
通过以下属性获得 第i个点对中的 待矫正点在待矫正点集的位置 与 参考点在参考集的位置
matches[i].QueryIdx //第i个点对的待矫正点在待矫正点集的索引
matches[i].TrainIdx //第i个点对的参考点在参考点集的索引
我们知道,待矫正点集的变量名为KP_Cor,参考点集的变量名为KP_Ref,那么我们就可以用下面的代码访问 第i对点的 待矫正点的XY坐标 与 参考点的XY坐标:
KP_Cor[matches[i].QueryIdx].Pt.X
KP_Cor[matches[i].QueryIdx].Pt.Y //第i对点的 待矫正点的XY坐标
KP_Ref[matches[i].TrainIdx].Pt.X
KP_Ref[matches[i].TrainIdx].Pt.Y //第i对点的 参考点的XY坐标
获得了这对点的坐标,我们假设为Xc,Yc,Xr,Yr(c代表待矫正,r代表参考)。
那么就可以使用我们的仿射变换六参数来验证啦~
假设仿射变换后:
计算点(X,Y)与点(Xr,Yr)的距离(一般使用欧氏距离)是否符合条件(如:5,8,10),若符合条件的就把这一对match中的两个XY坐标放入我们定义的接受结果的容器中即可。
假设 matches 中 第i个点对 符合条件:
- System.Drawing.Point[] p = new System.Drawing.Point[2];
- p[0].X = (int)Math.Round(KP_Cor[matches[i].QueryIdx].Pt.X);
- p[0].Y = (int)Math.Round(KP_Cor[matches[i].QueryIdx].Pt.Y);
-
- p[1].X = (int)Math.Round(KP_Ref[matches[i].TrainIdx].Pt.X);
- p[1].Y = (int)Math.Round(KP_Ref[matches[i].TrainIdx].Pt.Y);
- points.Add(p);
如此,最后 return points 即可得到符合条件的点对,自动配准的任务也就完成了,返回了一个装载着点对的数组。数组中的元素访问方式如下:
points[1] //第2个点对
points[50][0] //第51个点对的待矫正点
points[32][1].X //第33个点对的参考点的X坐标
接下来就是全自动配准,其实全自动配准的结果往往很难说,不过我在这儿也给大家讲一讲。
全自动配准呢,到上边的获得matches,以及往上的部分,都是一样的。
- var FBmatcher = new OpenCvSharp.FlannBasedMatcher();//实例化匹配器
- var matches = FBmatcher.Match(Desc_Cor, Desc_Ref);//对特征描述符进行匹配
我们获得了matches,在半自动配准中我们有 TsF 这个参考的系数,依据已知的参数来定夺点的取舍;而全自动配准中没有。
我们的想法是,在 matches 中找到差异最小的点对,点对的差异可以通过以下函数访问:
double dist = matches[i].Distance;
我们把 最小差异值的 n(人为定义,可以是2,3...) 倍 作为阈值,差异小于这个值的我们称之为好的匹配点,再将这些好的匹配点像上面的代码一样放到points里即可。
- double max_dist = 0, min_dist = 1000;
- for (int i = 1; i < matches.Length; ++i)
- {
- double dist = matches[i].Distance;
- if (dist > max_dist)
- max_dist = dist;
- if (dist < min_dist)
- min_dist = dist;
- }
获取了 最小差异值:min_dist,接下来就是for遍历matches,当点对的差异值小于阈值,就像上面的代码一样,将这个点对纳入其中:
- System.Drawing.Point[] p = new System.Drawing.Point[2];
- p[0].X = (int)Math.Round(KP_Cor[matches[i].QueryIdx].Pt.X);
- p[0].Y = (int)Math.Round(KP_Cor[matches[i].QueryIdx].Pt.Y);
-
- p[1].X = (int)Math.Round(KP_Ref[matches[i].TrainIdx].Pt.X);
- p[1].Y = (int)Math.Round(KP_Ref[matches[i].TrainIdx].Pt.Y);
- points.Add(p);
好!自动配准的内容就到此。
PS:大家也可以选择不同的检测模型,如:Surf,可以试试它们的效果,我也没试过不知道怎么样。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。