简介
关于粒子链接模块的最后一部分总结了扩展§§1§§§的系列教程。最难创建的模块是点布雷,这是previous tutorial的主题。粒子链接模块或跟踪器稍微简单一些。
然而,您仍然需要了解我们如何在 TrackMate 中存储和操作链接,这意味着非常简单地介绍数学图表。
简单的无向图
TrackMate 将检测步骤的结果存储为 SpotCollection 中的点。跟踪结果主要是这些点之间的链接,因此我们需要一个结构来保存它们。我们选择了最通用的一个,并选择了一张数学图表。
Mathematical graphs are mathematical structures that hold objects (vertices) and links between them (edges, we will use the two terms interchangeably). TrackMate relies specifically on a specialization: it uses an undirected, simple weighted graph.
- 无向意味着 A 和 B 之间的链接与 B 和 A 之间的链接相同。没有特定方向,因此无法被利用。但是,您会看到 API 提供了格式方向的特定工具。确实,由于我们主要处理延时数据,因此我们希望可以说我们沿着时间方向迭代图形。
- 简单与掌握这个数学领域必须付出的努力无关,而是与两个点之间只能有 1 个链接或没有链接这一事实有关,并且我们不授权从一个点到同一点的链接(无循环)。
- 权重意味着每个链接都有一个关联的数值,称为权重。我们用它只是为了存储一些跟踪算法的结果,但它对 TrackMate 没有真正的影响。
这些限制不会损害您在生命科学中可以表示的普遍性。您仍然可以拥有在典型的延迟实验中找到的经典链接:
-
随着时间的推移跟踪单个对象:
A0 - A1 - A2 - A3 - …
-
细胞分裂:
A0 - A1 -+- B2 - B3 - … | +- C2 - C3 - … -但也包括任何融合、三极分裂以及同一模型中所有内容的混合。
TrackMate 中的图表
顺便说一句,如果您计划为 TrackMate 结果构建分析工具,这一点非常重要。TrackMate 的理念是提供管理最常见的情况(当涉及到链接时),但您的分析工具可能需要特殊的情况。
- 例如,当您跟踪既不聚合也不分裂的泡泡时,您只有一个线性数据结构(每个粒子的对象数据库)。
- 当您追踪细胞谱系时,您就能拥有Mathematical graphs。
- 如果所有细胞分裂成两个女儿,那么你就得到了rooted binary tree。
它们都是简单的图表专业化,并提供非常有用的特殊工具。但TrackMate 不预设这些专业化。它存储和操作图表。
由于我们是 Java 程序员,因此我们使用 Java 库来操作这些图,因此我们依赖于优秀的 JGraphT库。为什么是图表?为什么不为每个位置存储一个后任者列表和一个前任列表呢?或者直接拥有一个轨道对象,可以节省一些时间来确定轨道是什么?嗯,因为在创建链接时图表非常方便且易于使用。当您编写自己的跟踪时跟踪器并找到要添加模型的链接时,你唯一要做的就是:graph.addEdge(A, B)。你不必介意是否属于某个轨道,如果是属于哪个轨道,你不需要全局查看整个图,你只需要关注本地链接即可。在代码中添加链接总是非常简单。
当然,您仍然是一种方法来了解模型中有多少条轨道以及它们是由什么组成的。但这是 TrackMate 的工作。它提供了隐藏图表并跟踪交易的 API。这是通过模型的一个组件 TrackModel 完成的。但在跟踪器中我们不会需要使用这个。我们将一个简单的图表,并且必须使用点和这些点之间的链接来东京它。当跟踪器完成时,TrackMate 将构建并维护一个列表跟踪。
简单性所用的代价是——在跟踪时——获取全局信息并不容易。例如,很容易查询两个点之间是否存在链接,但图表不能直接看到报表。如果您需要它们,或者必须从图表构建它们,或者必须在本地维护它们。但下面有更多介绍。
TrackMate 中的粒子链接算法
从本系列开始,我们就使用了术语“跟踪器”,但我们现在要构建的正确术语是粒子链接算法。我们的粒子是检测步骤结合产生的可见点,链接将是图的边缘。跟踪器可以定义将粒子检测算法与粒子链接算法相的完整应用程序。
在 TrackMate 中,粒子链接算法实现 random linking 接口。这很简单。正如文档中所解释的,SpotTracker 算法只是期望从给定的 SpotCollection 创建一个新的 SimpleWeightedGraph(当然只能使用可见点)。我们使用一个简单的加权图:
- 虽然权重本身不用于后续步骤,但建议使用边权重来报告链接的成本。
- 该图是无向的,但是稍后可以使用
Spot.FRAME功能搜索某些链接方向。SpotTracker 实现不一定处理这个问题;仅创建无向边。 - 两个地点之间不允许有多个链接。
- 允许来源和目标具有相同位置的链接。
- 不允许将源点和目标点链接在同一帧中。这必须通过实施来强制执行。
还有一个额外的方法来提交 Logger 的实例来记录跟踪过程的详细信息。就这样。
一个虚拟例子:醉酒细胞分裂
网上已经有一个例子可以实现SpotTracker.java。 让我们做点其他事情,并构建一个跟踪器,将一个点链接到下一帧中的任意两个点(如果存在),就要求它会像快速进行细胞分裂一样。
创建该类会产生以下零件:
package plugin.trackmate.examples.tracker;
import org.jgrapht.graph.DefaultWeightedEdge;
import org.jgrapht.graph.SimpleWeightedGraph;
import fiji.plugin.trackmate.Logger;
import fiji.plugin.trackmate.Spot;
import fiji.plugin.trackmate.tracking.SpotTracker;
public class DrunkenCellDivisionTracker implements SpotTracker
{
private SimpleWeightedGraph< Spot, DefaultWeightedEdge > graph;
private String errorMessage;
private Logger logger = Logger.VOID_LOGGER;
@Override
public SimpleWeightedGraph< Spot, DefaultWeightedEdge > getResult()
{
return graph;
}
@Override
public boolean checkInput()
{
return true;
}
@Override
public boolean process()
{
graph = new SimpleWeightedGraph< Spot, DefaultWeightedEdge >( DefaultWeightedEdge.class );
return true;
}
@Override
public String getErrorMessage()
{
return errorMessage;
}
@Override
public void setNumThreads()
{
// Ignored. We do not multithreading here.
}
@Override
public void setNumThreads( final int numThreads )
{
// Ignored.
}
@Override
public int getNumThreads()
{
return 1;
}
@Override
public void setLogger( final Logger logger )
{
// Just store the instance for later use.
this.logger = logger;
}
}
参数需要通过类的构造函数传递给类。至于装载,我们后面要建立的工厂将负责获取这些参数。当然,最重要的是跟踪的SpotCollection。在我们的例子中,这将是唯一的一个,因为我们的虚拟跟踪器没有任何设置。所以我们可以有一个像这样的构造函数:
public DrunkenCellDivisionTracker( final SpotCollection spots )
{
this.spots = spots;
}
然后我们在process()方法中利用SpotCollection。我们的策略是循环遍历所有具有内容的帧,每个点链接到下一帧中的两个点(它们无论在哪里),直到没有源点或目标点。该方法如下图所示:
@Override
public boolean process()
{
graph = new SimpleWeightedGraph< Spot, DefaultWeightedEdge >( DefaultWeightedEdge.class );
// Get the frames in order.
final NavigableSet< Integer > frames = spots.keySet();
final Iterator< Integer > frameIterator = frames.iterator();
// Get all the visible spots in the first frame, and put them in a new
// collection.
final Iterable< Spot > iterable = spots.iterable( frameIterator.next(), true );
final Collection< Spot > sourceSpots = new ArrayList< Spot >();
for ( final Spot spot : iterable )
{
sourceSpots.add( spot );
}
// Loop over frames, and link the source spots to spots in the next
// frame.
double progress = 0;
while ( frameIterator.hasNext() )
{
final Integer frame = frameIterator.next();
final Iterator< Spot > it = spots.iterator( frame, true );
SOURCE_LOOP: for ( final Spot source : sourceSpots )
{
/*
* Add the source to the graph, if it is not already done (doing
* it several time is not a problem: it's backed up by a Set).
*/
graph.addVertex( source );
// Finds 2 targets.
for ( int i = 0; i < 2; i++ )
{
if ( it.hasNext() )
{
final Spot target = it.next();
// You must add it as vertex before creating the link.
graph.addVertex( target );
// This is how we create a link.
final DefaultWeightedEdge edge = graph.addEdge( source, target );
// We get the edge back, and set its weight through:
if ( null != edge )
{
graph.setEdgeWeight( edge, 3.14 );
/*
* Edge can be null if a link already exists between
* the two spots.
*/
}
}
else
{
break SOURCE_LOOP;
}
}
}
// Regenerate source list for next frame.
sourceSpots.clear();
for ( final Spot spot : spots.iterable( frame, true ) )
{
sourceSpots.add( spot );
}
progress += 1;
logger.setProgress( progress / frames.size() );
}
return true;
}
所以这并不复杂。这很好,因为复杂的部分(这里完全简洁)是您必须确定要创建哪些链接的部分。这就是您应该发挥科学作用的位置。
工厂类
现在我们已经有了代码的巧妙部分(执行实际链接的部分),我们需要 TrackMate 集成。与检测模块一样,这是通过通过一个名为Tracker code的工厂类完成的。 它与我们在第previous tutorial中看到的SpotDetectorFactory完全对应,因此我不再详细介绍常用方法。
跟踪器特有的方法有:
public SpotTracker create( final SpotCollection spots, final Map< String, Object > settings );
该方法实例化实际的跟踪器类。您可以看到它收到了 SpotCollection 和设置图。该方法预计将解压该地图并提取实例化跟踪器所需的实际参数。
请注意,与检测器实际上相反,TrackMate 在跟踪过程中仅调用此方法一次。它不会为每帧生成跟踪器。所以它实际上比检测更简单:跟踪器实例能否解决整个模型的跟踪问题。因此,不需要像以前那样使用 setTarget() 方法。
public ConfigurationPanel getTrackerConfigurationPanel( final Model model );
此方法应生成一个 GUI 面板,以向用户请求跟踪参数。并检测模块相似。
marshall和unmarshall
与以前一样,保存到 XML 并从 XML 中检索。
public String toString( final Map< String, Object > sm );
用于漂亮地打印特定于该跟踪器的设置图。
剩下的都是经典。这是我们的跟踪器的样子:

TrackMate 识别出有两个曲目。你不必担心这一点。
使用 SciJava visible 参数控制跟踪器的可见性
我们的跟踪器是一个很好的虚拟示例,但它不是那么有用。在某些情况下,您可能希望 TrackMate 模块在 GUI 中不可见。有一种方法可以做到这一点,只需调整 SciJava 注释即可:
To make a TrackMate module available in TrackMate, but not visible in the GUI menus, use the annotation parameter visible = false
因此,编辑我们的跟踪器工厂的标头,设置外观如下:
@Plugin( type = SpotTrackerFactory.class, visible = false )
public class DrunkenCellDivisionTrackerFactory implements SpotTrackerFactory
足以将其隐藏在菜单中。这与我们在 one the previous tutorial 中看到的 enabled 参数不同。该工厂已在 TrackMate 中实例化并可用;它只是在菜单中不显示。
总结
完整的代码以及另一个跟踪器示例的代码可以在 GitHub 上找到: Tracker code. 我们关于如何扩展 TrackMate 的系列教程到此结束。现在就出发吧,满足您的需求;它是您的工具。
Jean-Yves Tinevez 09:26, 5 September 2014 (CDT)