简介
本页是一个教程,展示如何在 TrackMate 中集成您自己的边缘特征分析器算法。专门用于 TrackMate 扩展的系列教程中的第一篇,愿意扩展 TrackMate 的科学家应该首先阅读它。
所有这些教程都假设您熟悉Java开发。您应该熟悉java核心概念,例如面向对象设计、继承、接口等…理想情况下您甚至会知道maven的存在并且它可以帮助您编译软件。除此之外,教程表示提供您需要了解的内容。
边缘特征分析器是一种算法,可以将一个或多个标量数值特征与边缘或 TrackMate 中两个点之间的链接连接起来。例如,瞬时速度是一个边缘特征(您需要计算两个链接点和循环时间间隔),它正好由称为EdgeAnalyzer的算法提供。
TrackMate 模块
TrackMate通过编写模块进行扩展。模块只是为TrackMate提供GUI和API封装的核心功能的基本算法。有7类模块:
- 检测算法
- 贷款融资算法
- 显现的数字特征(例如平均强度等..)
- 链接的数字特征(如速度、方向等)
- 轨道的数字特征(总管道、长度等…)
- 可视化工具
- 后处理操作(导出、数据处理等…)
所有这些模块都实现特定于模块类的接口。例如,边缘分析器算法将实现EdgeSpeedAnalyzer.java接口。因此有7个接口。它们确实有一个共同点,那就是它们都扩展了称为this github repository的母模块接口。
TrackMateModule用于两个基本目的:
-
它本身扩展了
SciJavaPlugin接口,这将促进新模块的自动发现。我们将在最后讨论这一点。 -
它具有GUI集成的基本方法:
getKey()returns a unique string identifier that is used internally to reference the algorithm. For instance:"EDGE_VELOCITY_ANALYZER"getName()returns a string suitable to be displayed in the GUI that named the algorithm. For instance"Edge velocity".getIcon()returns anImageIconto be displayed in the GUI.getInfoText()returns a html string that briefly documents what the algorithm does. Basic html markup is accepted, so you can have something like
"<html>Plot the number of spots in each frame as a function <br>of time. Only the
<u>filtered</u> spots are taken into account. </html>"
这些是用于将模块集成到 GUI 中的方法。根据模块的类别,有些可能会被明显忽略。例如,本教程的边缘分析器主题忽略图标和信息文本,因为它们在 GUI 中默默地使用以提供新功能。
项目基本结构
在我们具体讨论边缘分析器之前,您需要设置一个能够简化 TrackMate 模块开发的开发环境。无需列出要求,只需查看 TrackMateModule,然后克隆它。它包含本教程系列的文件,更重要的是,它被配置为依赖于最新的 TrackMate 版本,这将生成可用于您的代码。
使用maven编译此项目将生成一个jar,您可以将其放入fiji插件文件夹中。然后您的模块将被自动检测并集成到TrackMate中。
但稍后会详细介绍。
让我们开始吧
但让我们回到我们的边缘分析器。
对于本教程,我们将做一些简单的事情,至少在数学上。我们将编写一个边缘分析器,它可以返回 XY 平面中链接的角度(以弧度为单位)。
因此,在我们的项目中为您的新分析器创建一个包,例如fiji.plugin.trackmate.examples.edgeanalyzer。
在这个包中,创建一个类EdgeAngleAnalyzer并让它实现FeatureAnalyzer接口。你应该得到这样的东西:
package plugin.trackmate.examples.edgeanalyzer;
import fiji.plugin.trackmate.features.edges.EdgeAnalyzer;
public class EdgeAngleAnalyzer implements EdgeAnalyzer
{}
值得注意的是,我们提供了一个空白构造函数。这非常:通过我们使用 SciJavaPlugin 集成的方式,我们不能使用构造函数来传递任何对象引用。如果您的分析器需要一些未通过接口方法提供的对象,那么您无法直接使用 TrackMate 进行编码。但是,我们应该用我们拥有的东西来覆盖大多数情况。
特征分析器具体方法
Eclipse 会即时聊天(我猜你正在使用 Eclipse;但是当即时聊天时,一切都趋向于通用)你的类需要实现一些抽象方法。弹出多种方法。
我们看到了上面讨论的通用边界模块方法,以及一些特定于分析器的方法。实际上,大多数新方法对于所有特征分析器(点、形状或边界)都是通用的。这些方法属于EdgeAnalyzer接口,当然,EdgeAnalyzer扩展了该接口。
它们的存在是因为TrackMate需要知道您的特征分析器的作用。由于它计算数字特征,因此它需要知道它计算的特征、它们的名称、短名称(当我们想要在GUI的灭菌部分显示它们时)以及它们的物理尺寸。事实上,TrackMate想要知道您生成的特征的维度,因为它的部分编码是由一位矛盾的物理学家编写的,他不希望将角度和速度均匀在同一个图表上。
这6种方法是:
-
getFeatures()返回一个字符串列表,用于标识分析器生成的特征。可以有多个。这个列表必须包含可在XML文件中使用的字符串。从历史上看,我们使用大写字符串,以java常量的形式,例如DISPLACEMENT。我们称它们为功能键。 -
getFeatureNames()返回一个将功能键链接到功能名称的映射。例如,在 GUI 中,希望我们显示“地质”而不是“地质”,所以这就是这张地图的。重要的是,该映射目的的键是上面列表中定义的键。 -
getFeatureShortNames()返回另一张具有相同规则的地图。当GUI中需要时,我们只是使用它的值来显示功能的短名称。没有关于如何局部功能名称的一般建议;尝试直到合适的状态。 -
getFeatureDimensions()返回最后一张地图,为您的特征提供维度。物理尺寸列于FeatureModel中。 -
getIsIntFeature()只是关于衣。它返回一个映射,说明哪些特征是整数映射的。例如,如果你有一个计算事物的功能糖,例如邻居数量,则应注意功能映射到此处的true。这个实际上不是“真正”有用的;如果设置不正确,不会有问题,也不会损失精度。这只是为了正确的数字。我希望当有2个邻居时,显示的邻居数量是“2”而不是“2.0000000000001”。在我们的示例中,我们测量角度,因此该特征应映射到false。 -
isManualFeature()返回影响此分析器计算的所有特征的单个标志。手动功能是TrackMate v2.3.0中引入的特殊功能。我们暂时把它放在一边。我们的角度特征是由我们即将编写的自动代码计算的。所以这个方法应该返回false。
在本教程中,我们的分析器仅返回一个特征,即角度。所以具体的实现可以是:
package plugin.trackmate.examples.edgeanalyzer;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.swing.ImageIcon;
import fiji.plugin.trackmate.Dimension;
import fiji.plugin.trackmate.features.edges.EdgeAnalyzer;
public class EdgeAngleAnalyzer implements EdgeAnalyzer
{
// The string key that identifies our analyzer.
private static final String KEY = "Edge angle";
// The only feature we compute here.
private static final String EDGE_ANGLE = "EDGE_ANGLE";
private static final List< String > FEATURES = new ArrayList< String >( 1 );
private static final Map< String, Boolean > IS_INT = new HashMap< String, Boolean >( 1 );
public static final Map< String, String > FEATURE_NAMES = new HashMap< String, String >( 1 );
public static final Map< String, String > FEATURE_SHORT_NAMES = new HashMap< String, String >( 1 );
public static final Map< String, Dimension > FEATURE_DIMENSIONS = new HashMap< String, Dimension >( 1 );
// Let's set the feature list, names, short names and dimensions.
static
{
FEATURES.add( EDGE_ANGLE );
IS_INT.put( EDGE_ANGLE, false );
FEATURE_NAMES.put( EDGE_ANGLE, "Link angle" );
FEATURE_SHORT_NAMES.put( EDGE_ANGLE, "Angle" );
FEATURE_DIMENSIONS.put( EDGE_ANGLE, Dimension.ANGLE );
}
private long processingTime;
/*
* TRACKMATEMODULE METHODS
*/
@Override
public String getKey()
{
return KEY;
}
// Return a user-compliant name for this analyzer.
@Override
public String getName()
{
return "Edge angle";
}
// We do not use info texts for any feature actually.
@Override
public String getInfoText()
{
return "";
}
// The same: we don't use icons for features.
@Override
public ImageIcon getIcon()
{
return null;
}
@Override
public List< String > getFeatures()
{
return FEATURES;
}
@Override
public Map< String, String > getFeatureShortNames()
{
return FEATURE_SHORT_NAMES;
}
@Override
public Map< String, String > getFeatureNames()
{
return FEATURE_NAMES;
}
@Override
public Map< String, Dimension > getFeatureDimensions()
{
return FEATURE_DIMENSIONS;
}
@Override
public Map<String, Boolean> getIsIntFeature()
{
return Collections.unmodifiableMap(IS_INT);
}
@Override
public boolean isManualFeature()
{
// This feature is calculated automatically.
return false;
}
多线程和基准测试方法
还有 4 种方法,我们现在将跳过。它们与分析器的多线程方面有关。您可以对分析器进行编码以利用多线程环境,TrackMate 将通过以下方法对其进行配置:
@Override
public void setNumThreads()
{
// We ignore multithreading for this tutorial.
}
@Override
public void setNumThreads( final int numThreads )
{
// We ignore multithreading for this tutorial.
}
@Override
public int getNumThreads()
{
// We ignore multithreading for this tutorial.
return 1;
}
还有
public long getProcessingTime()
返回计算特征所花费的毫秒数。
核心方法
真正重要的是实际执行工作的两种方法:
isLocal()process( final Collection< DefaultWeightedEdge > edges, final Model model )
让我们看看他们如何找到我们的角度分析仪示例。
isLocal()
此方法只有一个布尔值,表明您计算的特征是否是“局部”特征。我所说的局部是指:一条边的特征值是否依赖于其他边?如果不是,那么它是一个局部返回特征:它不会影响其他边缘。如果是,那么它不是局部的。请注意,它适用于分析仪提供的所有功能。
区别促进了 TrackMate 中的一些优化。您知道 TrackMate 可以自动和手动跟踪。事实证明,在同一个软件中执行这两个操作对具有认知的编码来说,特别是当您在手动修改非常大的数据集时提供良好的性能时。当您手动数据修改希望时,TrackMate会实时重新计算所有功能,以便它们始终保持同步。但是,如果您对一条边进行一次准时修改,则您重新重新计算该边的特征,而不是为所有其他边(如果它们不行驶)重新计算特征。如果功能是本地的,TrackMate可以做到这一点。这就是这个方法存在的原因。
边缘局部特征的一个例子是瞬间速度。边的速度只依赖于边,而不取决于其余边。你可能会说,如果你修改一个点的位置,所有接触该点的边缘都会受到影响,所以它不是局部的。但不会:所有接触该点的边都将被修改,因此将被标记为更新,但其他未的边的速度不会受到影响。所以速度是一个局部特征。
非局部边缘特征的一个例子是与最近邻居的距离的边缘。如果移动边,其自身的特征值就会受到影响。但同时也会影响到许多其他边缘的最近距离。所以它是非一条的,我们先验必须为所有边重新计算它。
在我们的例子中,我们正在编写一个边缘分析器,它返回单个的角度,而不考虑其他边缘的角度。因此,它是局部的特色。
process( final Collection< DefaultWeightedEdge > edges, final Model model )
实际执行工作的方法则不太详细。具体实现由edges(要计算其特征的边缘的集合)和model(包含您需要的TrackMate模型的所有信息)提供。
想要知道一件事:计算出特征的数值后,您需要将其存储在 Dimension enum 中。特征模型是主模型的一部分。
它的工作原理类似于2D地图:
final FeatureModel fm = model.getFeatureModel();
Double val = Double.valueOf(3.1451564);
String FEATURE = "MY_AWESOME_EDGE_FEATURE";
fm.putEdgeFeature( edge, FEATURE, val );
对于我们的XY边缘角度,方法内容如下:
@Override
public void process( final Collection< DefaultWeightedEdge > edges, final Model model )
{
final FeatureModel fm = model.getFeatureModel();
for ( final DefaultWeightedEdge edge : edges )
{
final Spot source = model.getTrackModel().getEdgeSource( edge );
final Spot target = model.getTrackModel().getEdgeTarget( edge );
final double x1 = source.getDoublePosition( 0 );
final double y1 = source.getDoublePosition( 1 );
final double x2 = target.getDoublePosition( 0 );
final double y2 = target.getDoublePosition( 1 );
final double angle = Math.atan2( y2 - y1, x2 - x1 );
fm.putEdgeFeature( edge, EDGE_ANGLE, Double.valueOf( angle ) );
}
}
@Override
public boolean isLocal()
{
return true;
}
使用分析器可发现
现在,您的分析仪可以正常工作。它编译并返回预期结果。一切都很好。
只是 TrackMate 甚至不知道它的存在。它的第一个在他孤独的紫色里,毫无用处。
在 TrackMate v2.2.0 中,除了修改或分叉它,然后从头开始重新编译和重新部署之外,没有其他方法可以扩展 TrackMate。在 v2.2.0 中,我们受益于 ImageJ2 团队的插件努力,他们构建了一个非常简单且非常聪明的发现机制,该机制允许简单地将 jar 放在 Fiji 的文件夹中并让 TrackMate 察觉。最重要的是,它简单明了。
请在类声明之前添加以下行:
@Plugin( type = EdgeAnalyzer.class )
public class EdgeAngleAnalyzer implements EdgeAnalyzer
{
...
就是这样。让我重复一遍:
To make a TrackMate module discoverable in TrackMate, just annotate its class with @Plugin( type = TheTrackMateModuleClassYouAreExtending.class ).
@Plugin( type = EdgeAnalyzer.class )行就足够了。还有一些机制允许对优先级、可见性(在GUI菜单中)或启用/禁用进行强度,但稍后会看到。
现在,只需编译您的项目,将生成的 jar 放入 Fiji 插件文件夹中。这是你得到的:

总结
太棒了,不是吗?
您可以找到此示例的完整源代码here。它还可以用于分析仪的模板。
Jean-Yves Tinevez January 2014