C# GDAL 数字图像处理Part10 自动配准/半自动配准

        又是选修课,今天讲自动配准和半自动配准。

        自动配准就是我们一个点也不选,程序自动进行角点检测和匹配;

        而半自动配准,以仿射变换六参数为例,我们需要先选3个点,确定最初的参数,然后在角点匹配中 检查一个点 经过该六参数变换后 得到的点 与 检测出来的点的误差 是否合格(误差限制是主观确定的)。接下来我们看实现:

1.安装OpenCVSharp

        C#很方便,我们在Nuget中搜索安装以下三个包即可,这里就不教大家安装了,网上很多教程。

 2.Bitmap to Mat

        首先我们知道,OpenCV中进行运算的类是 Mat (Matrix的缩写),那我们要使用OpenCV进行角点检测,就需要将图片转为Mat。

        我们知道,在Gdal导入图片时,一般是将图片转为Bitmap进行显示。那么,在保持高宽比的情况下,使用仅放大缩小后的图片求出来的仿射变换参数,也可以使用在原始大小的图片上(不知道大家能不能理解)。

        知道了这一点,那么我们就可以使用保持高宽比的显示出来的Bitmap图来进行OpenCV上的操作。

3.代码

        贴代码的方式沿袭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代表参考)。

        那么就可以使用我们的仿射变换六参数来验证啦~

        假设仿射变换后:

a\times Xc+b\times Yc+c = X

d\times Xc+e\times Yc+f=Y

        计算点(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,可以试试它们的效果,我也没试过不知道怎么样。

  • 7
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 9
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值