中间光流部分省略了,可以直接看中文版一
与跟踪,对象检测和一些相关主题的两个基本概念是关键点和描述符。我们的第一个任务是了解这者,以及它们之间的差异。
在最高抽象层次上,关键点是图像的一小部分,应该或者至少是独一无二的,我们认为可能将其能够定位在另一个相关图像中。描述符是一些数学结构,通常(但不总是)是浮点值的向量,它以某种方式描述单个关键点,并且可以用于确定在某些上下文中两个关键点是否“相同”。
历史上,第一个重要的关键点类型之一是我们在本章开头提到的Harris角点。回想一下背后的基本概念,Harris角点是图像中在两个不同方向维度上具有强强度变化的点,是可以被用来匹配另一个相关图像(例如,视频流的后续帧中的图像)的良好候选。
如图16-10中的图像,图像是一本书的文字。你可以看到在组成单个文本字符的行开始或结束的地方找到Harris角点,或者有交叉的行(例如h的中间)或者b。你会注意到“角”不会出现在字符的长行边缘,只有在结尾处,这是因为在这样一个边上发现的一个特征非常像在该边缘任何地方发现的其他特征。
这是理所当然的,如果某些东西在当前图像中不是唯一的,那就不会在另一个图像中是独一无二的,所以这样的像素不符合好的特征。
图16-10 两幅文字的图片,右边这幅以白色圆点显示了Horris角点。注意在没有对焦的区域,没有找到任何Horris角点
对于我们人类的眼睛,每个特征看起来都将会和其他特征有所不同。该特征描述符的问题是解决了如何使电脑处理这样的协会。我们提到出现的三叉路口的一个h或者一个b是好的“特征”,但是电脑该怎么描述这些特征描述符之间的差异呢?
我们可以用任何我们想要的方式构造一个特征描述符——例如我们可能从关键点周围的3×3区域的强度值形成一个向量。这种方法的一个问题是,如果从略有不同的角度看关键点,特征描述符可以具有不同的值。通常,旋转不变性是特征描述符的期望属性。当然,您是否需要旋转不变的描述符取决于您的应用程序。检测和追踪人的时候,重力在创造一个不对称的世界中发挥了很大的作用,在这个世界里,人们的头脑通常处于顶端,脚下通常处于底部。在这种应用中,缺乏旋转对称性的描述符可能不是问题。相比之下,当飞机在不同方向行驶时,地面的空中图像“旋转”,因此图像可能出现在似乎是随机取向的位置。
光流,跟踪和识别
在上一节中,我们在Lucas Kanade的背景下讨论了光流算法。在这种情况下,OpenCV为我们提供了一个单一的高级工具获取关键点列表,并尝试找到最新的匹配项帧。这些要点没有很多的结构或成分,仅仅使它们足够可以从两个非常相似的帧中的一帧定位到另一帧。通常关键点可以具有非常强大的相关描述符,并允许这些描述符指向不仅在连续的视频帧中匹配,甚至完全匹配不同的图像。这可以让我们在新环境中找到已知对象,或者在复杂的场景变化中追踪对象,以及其他许多应用。
关键点(及其描述符)的三大任务类别是的是跟踪,对象识别和立体视觉。跟踪是随着场景演变中的某些动作的跟随顺序图像流。跟踪出现在两个主要的子类别中:第一个是在静态场景中跟踪对象,另一个则跟踪场景本身来估计摄像机的运动。通常,术语跟踪单独指前者,后者称为视觉几何。当然很多应用经常的同时做这两件事情。
第二个任务类别是对象识别。在这种情况下,人们正在看场景并尝试识别一个或多个已知对象的存在。这个想法在这里是将某些关键点描述符与每个对象相关联,并与推理相关联。
如果你看到与特定对象相关联的足够的关键点,那么你可以合理地得出结论,这个对象存在于现场。
最后还有立体视觉。在这种情况下,我们有兴趣定位相应的指向相同场景或对象的两个或多个摄像机视图。结合这些有关相机位置和光学性质信息的相机位置本身,我们可以根据单独能够匹配的各个点来计算三维的位置。
OpenCV提供了处理许多类型的关键点和多种类型和方法关键点描述符。它还提供了在两帧之间(以稀疏光流的方式,跟踪和视觉几何,或立体视觉)或者图像的帧和数据之间(用于对象识别)匹配它们的方法。
通常OpenCV怎么处理关键点和描述符?
当您进行跟踪时,以及利用关键点及其描述符进行其他类型的有用分析,通常需要三件事情,第一个是根据一些关键点的定义搜索并找到图像中所有关键点,二是根据你找到的每个关键点创建一个描述符,第三个是比较你发现的关键点,根据这些描述符的意义和现有的一些现有的描述符比较,并看看是否可以找到任何匹配。在跟踪应用程序中,最后一步涉及查找序列一帧中的特征,并尝试将其与前一帧中的特征进行匹配。在对象检测应用程序中,通常在搜索特征“已知”特征的一些(潜在的广泛)数据库“已知”与单个对象或对象类相关联。
对于这些层中的每一层,OpenCV都提供了一个遵循的一般化机制“class that do stuff”(functor)模型。 因此,对于每个阶段,都有一个抽象基类,定义了一系列对象的公共接口从它衍生出来,每个派生类都实现一个特定的算法。
cv::KeyPoint类
当然,如果我们要找到关键点,我们需要一些方法来表示他们。 回想一下,关键点不是特征描述符,所以大部分我们需要的知道什么时候我们有一个关键点是它在哪里。 之后,还有一些关键点具有的次要特征,有些则不具有。这是cv :: KeyPoint类的实际定义:
class cv::KeyPoint {
public:
cv::Point2f pt; // 关键点的坐标
float size; //有意义的关键点邻域的直径
float angle; // 计算出的关键点的角度 (-1 ifnone)
float response; // 选择的关键点的响应
int octave; //八度(金字塔层)中关键点被提取的点
int class_id; //对象id,可用于按对象聚集关键点
};
cv::KeyPoint(
float x,
float y,
float _size,
float _angle=-1,
float _response=0,
int _octave=0,
int _class_id=-1
);
...
};
你可以看到,每个关键点都有一个cv :: Point2f成员,这只是告诉我们它位于哪里。size告诉我们关于关键点周围的区域,无论是在某种程度上都包含关键点存在的确定,或者在这个关键点的描述符中扮演一个角色。Angle仅对某些关键点有意义,许多关键点实现旋转对称,而不是在最严格的意义上实际上是不变的,而是通过在比较两个描述符时可以考虑某种自然方向(这不是一个复杂的想法,如果你正在看铅笔的两个图像,旋转显然很重要,但是如果你想比较它们,你可以在进行比较之前以相同的方向轻松地将它们形象化)。
response可以用于更强烈地响应一个关键点的探测器。在某些情况下,这可以被解释为该特征实际存在的概率。Octave被用来当在图像金字塔中找到关键点时,在这些情况下,重要的是知道哪个标尺是关键点,因为在大多数情况下,我们希望在新图像中以相同或相似的比例找到匹配。最后,关于类ID,在构造关键点数据库时,使用class_id来区分与一个对象相关联的关键点和与另一个对象相关联的关键点(当我们在“摘要”关键点匹配类别中讨论关键点匹配接口时,我们将返回到这一点: cv :: DescriptorMatcher)。
cv :: KeyPoint对象有两个构造函数,它们基本相同;其唯一的区别是您可以设置关键点的位置与两个浮点数或单个cv :: Point2f对象。 其实呢,除非你正在编写自己的关键点查找器,你不会倾向于使用这些功能。如果您正在使用库中可用的检测,描述符构造和比较功能,通常您甚至不会看到关键点对象。