多笔画手势识别器

在 1.9.0 版本加入.

警告

此功能目前处于实验阶段,只要此警告提示存在,其内容可能会有所变动。

完整的应用示例请参见 kivy/examples/demo/multistroke/main.py

概念概述

该模块实现了量角器手势识别算法。

Recognizer 是类似于 GestureDatabase 的搜索/数据库 API。它维护一个 MultistrokeGesture 对象的列表,并允许您在其中搜索用户输入的手势。

ProgressTracker 用于跟踪 Recognizer.recognize() 调用的进度。它可以用来与正在运行的识别器任务进行交互,例如强制其中途停止,或在结果到达时进行分析。

MultistrokeGesture 表示手势数据库(Recognizer.db)中的一个手势。它是 UnistrokeTemplate 对象的容器,并实现了堆排列算法,以自动生成所有可能的笔画顺序(如果需要)。

UnistrokeTemplate 表示单个笔画路径。它通常由 MultistrokeGesture 自动实例化,但有时您可能需要手动创建它们。

Candidate 表示一个用户输入的手势,用于在手势数据库中搜索匹配项。通常通过调用 Recognizer.recognize() 自动实例化。

使用示例

完整的应用示例请参见 kivy/examples/demo/multistroke/main.py

您可以绑定到 Recognizer 上的事件,以跟踪所有对 Recognizer.recognize() 调用的状态。回调函数将接收一个 ProgressTracker 实例,该实例可用于分析和控制识别过程的各个方面

from kivy.vector import Vector
from kivy.multistroke import Recognizer

gdb = Recognizer()

def search_start(gdb, pt):
    print("A search is starting with %d tasks" % (pt.tasks))

def search_stop(gdb, pt):
    # This will call max() on the result dictionary, so it's best to store
    # it instead of calling it 3 times consecutively
    best = pt.best
    print("Search ended (%s). Best is %s (score %f, distance %f)" % (
        pt.status, best['name'], best['score'], best['dist'] ))

# Bind your callbacks to track all matching operations
gdb.bind(on_search_start=search_start)
gdb.bind(on_search_complete=search_stop)

# The format below is referred to as `strokes`, a list of stroke paths.
# Note that each path shown here consists of two points, ie a straight
# line; if you plot them it looks like a T, hence the name.
gdb.add_gesture('T', [
    [Vector(30, 7), Vector(103, 7)],
    [Vector(66, 7), Vector(66, 87)]])

# Now you can search for the 'T' gesture using similar data (user input).
# This will trigger both of the callbacks bound above.
gdb.recognize([
    [Vector(45, 8), Vector(110, 12)],
    [Vector(88, 9), Vector(85, 95)]])

在下一个 Clock 时钟滴答时,匹配过程开始(并且在此情况下完成)。

要跟踪对 Recognizer.recognize() 的单独调用,请使用返回值(也是一个 ProgressTracker 实例):

# Same as above, but keep track of progress using returned value
progress = gdb.recognize([
    [Vector(45, 8), Vector(110, 12)],
    [Vector(88, 9), Vector(85, 95)]])

progress.bind(on_progress=my_other_callback)
print(progress.progress) # = 0

# [ assuming a kivy.clock.Clock.tick() here ]

print(result.progress) # = 1

算法细节

关于匹配算法的更多信息,请参阅:

《量角器:一种快速且精确的手势识别器》——杨力 著

好的,请发送需要翻译的英文内容。

Lisa Anthony 和 Jacob O. Wobbrock 的“$N-Protractor”

很抱歉,我无法访问外部链接或下载文件。请将需要翻译的英文内容直接粘贴发送给我,我会为您进行专业的中文翻译。

部分代码源自此处JavaScript实现:

http://depts.washington.edu/aimgroup/proj/dollar/ndollar.html

class kivy.multistroke.Candidate(strokes=None, numpoints=16, **kwargs)[源代码]

基类:object

表示一组用户输入的单笔路径集合,即用于通过 Protractor 算法与 UnistrokeTemplate 对象进行匹配的数据。默认情况下,数据会预先计算,以匹配旋转受限和完全旋转不变的 UnistrokeTemplate 对象。

参数:
strokes

参见 MultistrokeGesture.strokes 中的格式示例。候选笔画按给定顺序简单地合并为一个单笔画。其思路是,这将与 MultistrokeGesture.templates 中的某个单笔画排列相匹配。

numpoints

候选对象的默认N值;这仅作为回退使用,通常不会用到,因为N由我们正在比较的UnistrokeTemplate驱动。

skip_bounded

如果为 True,则不生成/存储旋转有界向量。

skip_invariant

如果为 True,则不生成/存储旋转不变向量。

请注意,如果您设置了跳过标志(skip-flag),然后尝试检索数据,您将会遇到错误。

add_stroke(stroke)[源代码]

为候选对象添加描边;这将使所有先前计算的向量失效。

get_angle_similarity(tpl, **kwargs)[源代码]

(仅限内部使用)计算此候选对象与UnistrokeTemplate对象之间的角度相似度。返回一个表示角度相似度的数值(数值越低表示越相似)。

get_protractor_vector(numpoints, orientation_sens)[源代码]

(仅限内部使用)返回用于与UnistrokeTemplate进行Protractor比较的向量。

get_start_unit_vector(numpoints, orientation_sens)[源代码]

(仅限内部使用)获取此候选对象的起始向量,并将路径重采样为 numpoints 个点。这是匹配过程的第一步。它用于与 UnistrokeTemplate 对象的起始向量进行比较,以确定角度相似性。

prepare(numpoints=None)[源代码]

准备候选向量。将 self.strokes 合并为单个单笔划(首尾相连),重采样至 numpoints 个点,然后计算向量并存储在 self.db 中(供 get_distanceget_angle_similarity 使用)。

class kivy.multistroke.MultistrokeGesture(name, strokes=None, **kwargs)[源代码]

基类:object

MultistrokeGesture 表示一个手势。它维护一组 strokes`(笔画),并生成单笔画(即 :class:`UnistrokeTemplate)的排列组合,这些排列组合稍后用于评估候选手势与该手势的匹配度。

参数:
name

识别手势的名称——它会在 Recognizer.recognize() 搜索的结果中返回给您。您可以拥有任意多个具有相同名称的 MultistrokeGesture 对象;即一个手势的多种定义。所有生成的单笔手势排列都会使用相同的名称。此属性为必填项,无默认值。

strokes

表示手势的路径列表。路径是Vector对象的列表:

gesture = MultistrokeGesture('my_gesture', strokes=[
  [Vector(x1, y1), Vector(x2, y2), ...... ], # stroke 1
  [Vector(), Vector(), Vector(), Vector() ]  # stroke 2
  #, [stroke 3], [stroke 4], ...
])

为了模板匹配的目的,所有笔画被合并为一个列表(unistroke)。您仍应单独指定笔画,并在可能的情况下将 stroke_sensitive 设置为 True。

一旦完成此操作,除非你将 permute 标志设为 False,否则单笔手势排列会立即生成并存储在 self.templates 中供后续使用。

priority

决定 Recognizer.recognize() 何时尝试匹配此模板,优先级较低的先被评估(仅当使用优先级 filter 时)。对于更可能匹配的手势,应使用较低的优先级。例如,将用户模板设置为比通用模板更低的数值。默认值为 100。

numpoints

确定此手势应重采样到的点数(用于匹配目的)。默认值为16。

stroke_sensitive

确定在手势匹配过程中,候选(用户输入)手势的笔画(路径)数量是否必须与此手势相同。如果此值为False,则始终评估候选手势,忽略笔画数量。默认值为True。

orientation_sensitive

确定此手势是否对方向敏感。若为True,则将指示方向与八个基本方向中旋转角度最小的那个对齐。默认值为True。

angle_similarity

此值由 Recognizer.recognize() 函数在评估候选手势与该手势匹配时使用。如果它们之间的角度偏差过大,则该模板被视为不匹配。默认值为 30.0(度)。

permute

如果设为 False,则在实例化时不使用 Heap Permute 算法来生成不同的笔画顺序。若将此设为 False,则仅使用由 strokes 构建的单个 UnistrokeTemplate。

add_stroke(stroke, permute=False)[源代码]

将笔画添加到self.strokes列表中。如果`permute`为True,则调用:meth:`permute`方法以生成新的单笔画模板。

get_distance(cand, tpl, numpoints=None)[源代码]

计算该候选与UnistrokeTemplate之间的距离。返回笔画路径之间的余弦距离。

numpoints 会将 UnistrokeTemplate 和 Candidate 路径都准备为 n 个点(必要时),你可能并不想这样做。

match_candidate(cand, **kwargs)[源代码]

将给定的候选手势与此MultistrokeGesture对象进行匹配。将针对所有模板进行测试,并以包含四个项目的列表形式报告结果:

索引 0

最佳匹配模板的索引(在self.templates中)

索引 1

从模板到候选路径的计算距离

索引 2

所有模板的距离列表。列表索引对应于self.templates中一个:class:`UnistrokeTemplate`的索引。

索引 3

已执行的匹配操作计数器,即候选对象与模板进行匹配的次数。

permute()[源代码]

从self.strokes生成所有可能的单笔手势排列,并将生成的UnistrokeTemplate对象列表保存到self.templates中。

引用自 http://faculty.washington.edu/wobbrock/pubs/gi-10.2.pdf ::

We use Heap Permute [16] (p. 179) to generate all stroke orders
in a multistroke gesture. Then, to generate stroke directions for
each order, we treat each component stroke as a dichotomous
[0,1] variable. There are 2^N combinations for N strokes, so we
convert the decimal values 0 to 2^N-1, inclusive, to binary
representations and regard each bit as indicating forward (0) or
reverse (1). This algorithm is often used to generate truth tables
in propositional logic.

详见所链接论文第4.1节:“$N算法”。

警告

使用堆排列算法处理超过3笔的手势时,可能会生成极其庞大的模板数量(例如,一个9笔手势相当于3800万个模板)。如果您正在处理这类手势,建议手动组合所有所需的笔画顺序。

class kivy.multistroke.ProgressTracker(candidate, tasks, **kwargs)[源代码]

基类:EventDispatcher

表示一个正在进行(或已完成)的搜索操作。当调用 Recognizer.recognize() 方法时,会实例化并返回该对象。results 属性是一个字典,随着识别操作的进行而更新。

备注

您无需实例化此类。

参数:
candidate

待评估的 Candidate 对象

tasks

任务列表中的手势总数(用于测试比对)

事件:
on_progress

每次处理手势时都会触发该事件。

on_result

当添加新结果,且该结果是目前对`name`的首次匹配,或是得分更高的连续匹配时触发。

on_complete

搜索完成时触发,无论出于何种原因。(使用 ProgressTracker.status 来查明原因)

属性:
results

到目前为止的所有结果字典。键是手势的名称(即:attr:UnistrokeTemplate.name,通常继承自 MultistrokeGesture)。字典中的每一项都是一个包含以下条目的字典:

name

匹配模板的名称(冗余)

score

从1.0(完全匹配)到0.0计算得出的分数。

dist

候选到模板的余弦距离(低=更接近)

gesture 手势

匹配到的 MultistrokeGesture 对象。

best_template

最佳匹配模板的索引(在 MultistrokeGesture.templates 中)

template_results

所有模板的距离列表。列表索引对应于gesture.templates中的:class:`UnistrokeTemplate`索引。

status
search

目前正在工作中。

stop

被用户停止(调用了 stop()

timeout

发生超时(在`recognize()`中指定为`timeout=`)

goodscore

搜索提前停止,因为找到了一个得分足够高的手势(在recognize()中指定为`goodscore=`)。

complete

搜索已完成(所有符合筛选条件的手势均已测试完毕)

property best

返回recognize()目前找到的最佳匹配结果。它返回一个包含三个键的字典:'name'、'dist'和'score',分别代表模板名称、距离(来自候选路径)以及计算出的得分值。这是一个Python属性。

property progress

返回进度值,类型为浮点数,0表示完成0%,1表示完成100%。这是一个Python属性。

stop()[源代码]

引发一个停止标志,该标志会被搜索进程检查。它将在下一个时钟周期(如果仍在运行)被停止。

class kivy.multistroke.Recognizer(**kwargs)[源代码]

基类:EventDispatcher

Recognizer 提供了一个带有匹配功能的手势数据库。

事件:
on_search_start

当使用此Recognizer开始新的搜索时触发。

on_search_complete

当正在进行的搜索因任何原因结束时触发。(使用 ProgressTracker.status 来查明原因)

属性:
db

一个 ListProperty,包含可用的 MultistrokeGesture 对象。

db 是一个 ListProperty,默认值为 []。

add_gesture(name, strokes, **kwargs)[源代码]

向数据库中添加一个新手势。这将使用`strokes`实例化一个新的:class:MultistrokeGesture,并将其追加到self.db中。

备注

如果你已经实例化了一个 MultistrokeGesture 对象并希望添加它,请手动将其追加到 Recognizer.db 中。

export_gesture(filename=None, **kwargs)[源代码]

导出 MultistrokeGesture 对象的列表。输出一个 base64 编码的字符串,可通过 parse_gesture() 函数解码为 Python 列表,或使用 Recognizer.import_gesture() 直接导入到 self.db。如果指定了 filename,输出将写入磁盘,否则返回该字符串。

该方法接受可选的 Recognizer.filter() 参数。

filter(**kwargs)[源代码]

filter() 根据给定条件返回 self.db 中对象的子集。此方法被 Recognizer 的许多其他方法所使用;例如,在调用 Recognizer.recognize()Recognizer.export_gesture() 时可以使用以下参数。通常您无需直接调用此方法。

参数:
name

将返回的列表限制为那些 MultistrokeGesture.name 与给定正则表达式匹配的手势。如果 re.match(name, MultistrokeGesture.name) 测试为真,则该手势会被包含在返回的列表中。可以是字符串或字符串数组

gdb = Recognizer()

# Will match all names that start with a capital N
# (ie Next, New, N, Nebraska etc, but not "n" or "next")
gdb.filter(name='N')

# exactly 'N'
gdb.filter(name='N$')

# Nebraska, teletubbies, France, fraggle, N, n, etc
gdb.filter(name=['[Nn]', '(?i)T', '(?i)F'])
priority

将返回的列表限制为具有特定 MultistrokeGesture.priority 值的手势。如果指定为整数,则仅返回优先级较低的手势。如果指定为列表(最小值/最大值):

# Max priority 50
gdb.filter(priority=50)

# Max priority 50 (same result as above)
gdb.filter(priority=[0, 50])

# Min priority 50, max 100
gdb.filter(priority=[50, 100])

使用此选项时,Recognizer.db 会自动按优先级排序,这会带来额外开销。如果您的姿势已按优先级排序,可以使用 force_priority_sort 来覆盖此行为。

orientation_sensitive

将返回的列表限制为对方向敏感的姿势(True)、不对方向敏感的姿势(False)或None(忽略模板敏感性,这是默认值)。

numstrokes

将返回的手势列表限制为具有指定笔画数(在 MultistrokeGesture.strokes 中)的手势。可以是单个整数或整数列表。

numpoints

将返回的手势列表限制为具有特定 MultistrokeGesture.numpoints 值的手势。这是为了灵活性而提供的,除非你理解其作用,否则不要使用它。可以是单个整数或整数列表。

force_priority_sort

可用于覆盖默认的排序行为。通常,如果使用了 priority 选项,MultistrokeGesture 对象会按优先级顺序返回。将此设置为 True 将按优先级顺序返回手势,设置为 False 将按手势添加的顺序返回。None 表示自动决定(默认值)。

备注

为了提高性能,您可以按优先级顺序加载手势数据库,并在调用 Recognizer.recognize() 时将其设置为 False。

db

如果你想要过滤与 Recognizer.db 不同的对象列表,可以设置此属性。你可能不需要这样做;它由 import_gesture() 内部使用。

import_gesture(data=None, filename=None, **kwargs)[源代码]

导入由 export_gesture() 格式化的一系列手势。必须指定 datafilename 其中之一。

该方法接受可选的 Recognizer.filter() 参数,如果未指定任何参数,则将导入指定数据中的所有手势。

parse_gesture(data)[源代码]

解析由export_gesture()格式化的数据。返回一个:class:`MultistrokeGesture`对象的列表。此方法由:meth:`import_gesture`内部使用,通常您无需直接调用。

prepare_templates(**kwargs)[源代码]

该方法用于在self.db中的手势内准备:class:`UnistrokeTemplate`对象。如果您希望提前计算所有向量,以减少惰性重采样带来的性能惩罚,这将非常有用。如果在调用:meth:`Recognizer.export_gesture`之前执行此操作,则在稍后加载数据时,向量将已被计算。

该方法接受可选的 Recognizer.filter() 参数。

force_numpoints,如果指定,将把所有模板准备为给定的点数(而不是每个模板的首选点数;即 UnistrokeTemplate.numpoints)。通常你不会想要这样做。

recognize(strokes, goodscore=None, timeout=0, delay=0, **kwargs)[源代码]

搜索与 strokes 匹配的手势。返回一个 ProgressTracker 实例。

该方法接受可选的 Recognizer.filter() 参数。

参数:
strokes

一个笔画路径列表(由 Vector 对象组成的列表的列表),将用于与数据库中的手势进行匹配。也可以是一个 Candidate 实例。

警告

如果你手动提供一个带有跳过标志的 Candidate,请确保设置了正确的过滤器参数。否则,系统将尝试加载尚未计算的向量。例如,如果你设置了 skip_bounded 而未将 orientation_sensitive 设为 False,当遇到一个 orientation_sensitive 的 UnistrokeTemplate 时,将会引发异常。

goodscore

如果设置了此属性(取值范围在0.0 - 1.0之间),并且手势得分等于或高于指定值,则搜索会立即停止,并触发`on_search_complete`事件(同时也会触发关联的:class:`ProgressTracker`实例的`on_complete`事件)。默认值为None(禁用)。

timeout

指定搜索中止并返回结果时的超时时间(以秒为单位)。此选项仅在 max_gpf 不为 0 时适用。默认值为 0,意味着将测试数据库中的所有手势,无论耗时多久。

max_gpf

指定每帧可处理的 MultistrokeGesture 对象的最大数量。超过此数量时,搜索将暂停并在下一帧继续。设置为 0 将立即完成搜索(并阻塞用户界面)。

警告

这并不会限制匹配的 UnistrokeTemplate 对象的数量!如果单个手势有一百万个模板,它们将在 max_gpf=1 的情况下于单帧内全部被处理!

delay

设置识别器循环每次运行之间的可选延迟。通常,运行会被安排到下一帧,直到任务列表耗尽。如果设置了此参数,每次运行之间将增加额外的延迟(以秒为单位)。默认值为0,即在下一帧继续。

force_numpoints

强制所有模板(及候选模板)准备到一定数量的点。例如,在评估模板以确定最佳n值时,这可能很有用(除非你理解其作用,否则不要使用此功能)。

transfer_gesture(tgt, **kwargs)[源代码]

MultistrokeGesture 对象从 Recognizer.db 传输到另一个 Recognizer 实例 tgt

该方法接受可选的 Recognizer.filter() 参数。

class kivy.multistroke.UnistrokeTemplate(name, points=None, **kwargs)[源代码]

基类:object

将(单)笔画路径表示为一系列Vector的列表。通常,此类由MultistrokeGesture实例化,而非程序员直接创建。然而,手动组合UnistrokeTemplate对象也是可行的。

参数:
name

标识手势的名称。通常在生成模板时,从父级 MultistrokeGesture 对象继承而来。

points

表示单笔手势路径的点列表。这通常是MultistrokeGesture中可能的笔画顺序排列之一。

numpoints

该模板在匹配过程前(理想情况下)应重采样的点数。默认值为16,但如果提高结果质量,您可以使用模板特定的设置。

orientation_sensitive

确定此模板是否对方向敏感(True)或完全旋转不变(False)。默认值为True。

备注

如果你设置了跳过标志,然后尝试检索这些向量,将会引发异常。

add_point(p)[源代码]

向unistroke/path添加一个点。这将使所有先前计算的向量失效。

prepare(numpoints=None)[源代码]

此函数为匹配准备UnistrokeTemplate,给定目标点数(用于重采样)。16为最优值。