65.9K
CodeProject 正在变化。 阅读更多。
Home

使用形状上下文算法查找形状之间的相似性和差异

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.62/5 (11投票s)

2009年9月29日

MIT

3分钟阅读

viewsIcon

51128

downloadIcon

2084

使用提供的形状上下文算法和基础设施,可以广泛地匹配形状。

引言

形状上下文算法从两个绘图中采样点,并根据每个点与其他点的角度视图生成直方图。直方图现在代表了这些点,用于匹配第一个绘图和第二个绘图中最高度匹配的点,最初是为了找到完美匹配。为了改进匹配,可以修改以扩大范围(目标点的数量)并保持匹配过程的结果唯一,但这次不是完美匹配。

背景

形状上下文匹配算法是“形状匹配框架”的一部分,该框架旨在为使用 .NET 环境构建绘图 - 相似性/差异软件提供核心支持。该项目仅使用并依赖于“形状匹配框架”解决方案提供的 Matrix 库实现。可以轻松地隔离这两个项目/DLL 以获得该算法本身的功能。该库有两种使用方法:一种是使用包含的函数以形成经典算法(如书籍中所述),另一种是使用提供的实现,该实现经过轻微修改并针对特定需求进行了定制。

Using the Code

在我看来,这个项目可以分为两个主要逻辑

  • 第一个是匹配逻辑——目前从两个绘图中随机抽取点,并根据完美配对原则匹配最佳配对。当目标集更大时,算法可以选择更好的配对,但此时完美配对原则已不复存在。可以选择随机点的部分,通过委托替换为自定义逻辑。
  • 第二部分是尝试将目标形状变为源形状。这里可以应用许多技术,我选择实现薄板样条(TPS)变换,简而言之,这是一种非仿射变换。这意味着我们可以几乎独立控制每个像素。给定源点和目标点,算法就像拉动一张“薄片”(类似于绘图的画布),从源拉到目标。当有很多匹配点时,会向不同方向拉动“薄片”,试图满足这些匹配点。

我必须承认,将这两个部分结合起来时会出现问题,因为匹配是基于邻近度的。存在一些不合理的交叉匹配。这可能会导致 TPS 变得不稳定,并导致结果失控。因此,应该应用一些正则化或纠正,这部分开放征求建议。让我们看一个简单的使用示例

using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
using LiniarAlgebra;
using Adaption;
using System.Drawing;
using System.Reflection;
using ShapeContext;

namespace New_Project
{
    static class Program
    {
        [STAThread]
        static void Main()
        {
            Point[] sources = { 
                                  new Point(1,1),    // We creating something like 
					       // this shape    
                                  new Point(1,3),    //    
                                  new Point(1,6),    //  *--*--*  
                                  new Point(3,6),    //  |     |  
                                  new Point(6,6),    //  *     *  
                                  new Point(6,3),    //  |     |
                                  new Point(6,1),    //  *--*--*
                                  new Point(3,1)     // 
                              };

            Point[] targets = { 
                                  new Point(9,8),    //  We creating something 
					       //  like the previous but
                                  new Point(9,3),    //  with some displacements 
					       //  and order mess   
                                  new Point(4,1),    //           _-*      
                                  new Point(4,3),    //     *---*-  |    
                                  new Point(6,1),    //     |       |       
                                  new Point(6,6),    //     *       *     
                                  new Point(9,1),    //     |       |
                                  new Point(4,6)     //     *---*---* 
                              };

            Size surfaceSize = new Size(15,15); //Choosing big enough 
					  //canvas size to see the result

            //Creating identity function 1 to 1 selection, no reduction is made here
            SelectSamplesDelegate selectionLogic = (points, numOfPoints) => points;

            //Creating a matching object with all necessary data
            ShapeContextMatching matching = 
	     new ShapeContextMatching(sources, targets, surfaceSize, selectionLogic);
            
            //setting euclid restriction for TPS transformation
            matching.DistanceTreshold = 20;
            //Now calculating
            matching.Calculate();

            Point[] resultSamples = matching.LastTargetSamples;
            //We can see that the algorithm placed the points 
            //in matching order corresponding to sources
            //resultSamples(debug print as column)  ,  and the sources (as column)
            //    {X = 4 Y = 1}                                <--(1,1)
            //    {X = 4 Y = 3}                                <--(1,3)
            //    {X = 9 Y = 1}                                <--(1,6)
            //    {X = 6 Y = 1}                                <--(3,6)
            //    {X = 9 Y = 8}                                <--(6,6)
            //    {X = 9 Y = 3}                                <--(6,3)
            //    {X = 6 Y = 6}                                <--(6,1)
            //    {X = 4 Y = 6}                                <--(3,1)
                                        
            //As about TPS based transformation 
            Point[] TransformedResult = matching.ResultPoints;

            //There was some improvement but also unwanted effect of (5,5) 
            //in wrong direction
            //{X = 7 Y = 6}
            //{X = 8 Y = 3}
            //{X = 4 Y = 2}         *
            //{X = 4 Y = 3}      *
            //{X = 6 Y = 2}    *
            //{X = 5 Y = 5}    *     *
            //{X = 8 Y = 2}    *  *  *
            //{X = 4 Y = 4}  
        }
    }
}

我想仔细研究算法中存在问题的部分,即薄板样条(TPS)。获取最后示例的坐标,尝试将目标坐标变为源坐标

using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
using LiniarAlgebra;
using Adaption;
using System.Drawing;
using System.Reflection;
using ShapeContext;

namespace New_Project
{
    static class Program
    {
        [STAThread]
        static void Main()
        {
            DoubleMatrix sources = new DoubleMatrix(8,2);
            sources[0,0] = 1; sources[0,1] = 1;    //  We creating something 
                                                   //  like this shape
            sources[1,0] = 1; sources[1,1] = 3;    //    
            sources[2,0] = 1; sources[2,1] = 6;    //  *--*--*
            sources[3,0] = 3; sources[3,1] = 6;    //  |     |
            sources[4,0] = 6; sources[4,1] = 6;    //  *     *
            sources[5,0] = 6; sources[5,1] = 3;    //  |     |
            sources[6,0] = 6; sources[6,1] = 1;    //  *--*--*
            sources[7,0] = 3; sources[7,1] = 1;    //     

            DoubleMatrix targets = new DoubleMatrix(8,2);

            targets[0, 0] = 4; targets[0, 1] = 1;    //  We creating something 
                                                     //  like the first but
            targets[1, 0] = 4; targets[1, 1] = 3;    //  with a some displacements 
                                                     //  and order mess   
            targets[2, 0] = 4; targets[2, 1] = 6;    //           _-*      
            targets[3, 0] = 6; targets[3, 1] = 6;    //     *---*-  |    
            targets[4, 0] = 9; targets[4, 1] = 9;    //     |       |       
            targets[5, 0] = 9; targets[5, 1] = 3;    //     *       *     
            targets[6, 0] = 9; targets[6, 1] = 1;    //     |       |
            targets[7, 0] = 6; targets[7, 1] = 1;    //     *---*---* 

            Point[] targetPoints = { 
                      new Point(9,9),
                      new Point(9,3),
                      new Point(4,1),
                      new Point(4,3),
                      new Point(6,1),
                      new Point(6,6),
                      new Point(9,1),
                      new Point(4,6) 
                  };

            Size surfaceSize = new Size(15, 15); //Choosing big enough canvas 
                                                 //size to see the result

            TPS tpsTransform = new TPS(surfaceSize);
            tpsTransform.Calculate(targets, sources);

            tpsTransform.Interpolate(ref targetPoints);
            //The result (targetPoints) is 
            //{X = 6 Y = 6}
            //{X = 8 Y = 0}
            //{X = 4 Y = 0}
            //{X = 5 Y = 0}
            //{X = 6 Y = 0}
            //{X = 6 Y = 3}
            //{X = 8 Y = 0}
            //{X = 4 Y = 3}
            //There is no point to try and draw what we got
            //This is very aggressive transformation, 
            //since there are a lot of points that
            //pull to the left.
        }
    }
}

兴趣点 

这是我目前实现中最复杂的算法。它可能不适用于形状对齐,但它为那些想从 .NET 实现开始的人提供了一个良好的基础。当然,在项目的网站上可以找到更多信息和示例。请参阅下一节。

历史 

这个算法在某些方面拓宽了我的思路。在开始编码之前,我没想到会有一个 O(n) 的匹配算法能完成这项工作。当然,它也有其缺点,但这是一个不错的开始。本文及附带的项目是形状匹配框架的一部分,可以在这里找到。正如您所见,经过一些额外的努力,它可以更好地匹配形状

SCcap.JPG - Click to enlarge image
© . All rights reserved.