##介绍
第 7 版TrackMate,检测算法仅限于返回阵列的位置及其半径。所有检测均由 frame, x, y, z, radius, quality 形状的元组表示。这非常适合实现“检测算法”,该算法返回对象的位置,但忽略其形状。本教程系列中的第 previous page 展示了如何使用基本代码来实现作为 TrackMate 检测器的算法。
在版本 7 中,我们重写了 TrackMate 的很大一部分,以消除这个限制,至少在 2D 方面是这样。我们更改了数据模型,方便 TrackMate 中的§§0§§§可能_存储形状,同时可以不影响现有的检测器。我们制作了一个新的 API 来促进在 TrackMate 中实现_重构算法,它返回可以对象的形状。它们的形状和形状用于计算形态特征或测量对象构造内部的强度。我们使用这个 API在 TrackMate 中实现7 new segmentation algorithms,集成了 Java 中可用的一些最佳分段工具。本页介绍了如何使用此 API 自行实现自己的分段算法,并使其成为 TrackMate 的一等公民,就像我们在本系列中介绍的其他 TrackMate 模块一样。
我们首先需要回顾一下新的API本身,它基本上可以归结为一个提供静态方法的类。,对于其他检测器,我们需要在检测器工厂中添加一些标志来告诉TrackMate,我们构建的是返回对象形状的分割算法。最后,我们似乎看到对检测器的多线程进行一些调整,以适应您可能需要集成到TrackMate中的各种现有分割工具。我们将使用上述7种分割算法的示例来作为本教程的基础。
创建存储对象目的点。
Spot课程中发生的变化
从版本 7 开始,TrackMate 中的 SpotRoi 类有一个新字段:Spot 对象。
它基本上由存储相对于点中心(x, y, z元组)的对象创建的2D搬运组成。它唯一的字段是两个double[]仓库,用于表示的xp和yp坐标,沿着排序。最后一点与第一点相连。
检测算法返回的Spot对象的SpotRoi设置为null。 TrackMate 中分段算法的唯一区别在于,它返回 Spot 对象和非null SpotRoi 对象。
限制
让我们从坏消息开始:我们只能处理 2D 图像的对象。关键是我们还没有一个好的方法来存储斐济的 3D 效果。这可能会改变,但目前您将看到的所有内容仅适用于 2D 效果,即加工。
另外,对象形状必须由 simple polygon 表示。即:不能有孔,操作的线不能互相相交。如果创建具有相交的点,则结果是不确定的。如果您的对象指定了孔,并且尝试为它们创建点,则这些孔将被忽略。
从运营创建Spot
xp和yp的两个double[]存储必须相对于点X和Y位置进行设置。您自己可以计算它们,但使用静态方法createSpot()物理更容易,它返回坐标中的x和y中的一点:
double[] xp = ...;
double[] yp = ...;
double quality = ...;
Spot spotWithShape = SpotRoi.createSpot(xp, yp, quality);
对于Spot中存储的所有坐标,X和Y必须采用物理单位(例如,如果图像以微米为单位布局,则为微米)。此方法将负责计算点x和y中心,计算硬盘的读出点,并正确地对应同一点。
示例:如何在 StarDist 中创建灯光
Fiji StarDist implementation将找到的对象返回为作业。因此,我们只需使用此方法将其桥接到TrackMate。在MaskUtils中,您将找到以下几行:
/*
* We received the 'polygons' object from the StarDist runner.
* As for all the other detectors, this instance have to return
* a list of Spot 'spots' containing all the objects segmented in
* the current time-point.
*/
// Create spots from output.
for ( final Integer polygonID : polygons.getWinner() )
{
// Collect quality = max of proba.
final PolygonRoi roi = polygons.getPolygonRoi( polygonID );
proba.setRoi( roi );
final double quality = proba.getStatistics( Measurements.MIN_MAX ).max;
// Create ROI.
final Polygon polygon = roi.getPolygon();
final double[] xpoly = new double[ polygon.npoints ];
final double[] ypoly = new double[ polygon.npoints ];
for ( int i = 0; i < polygon.npoints; i++ )
{
/*
* Here we convert the polygon points in pixel coordinates to
* physical coordinates (multiplication by the calibration).
* We also need to offset them by the interval top-left corner
* in case the user ask to perform segmentation in a sub-region
* of the source image.
*/
xpoly[ i ] = calibration[ 0 ] * ( interval.min( 0 ) + polygon.xpoints[ i ] );
ypoly[ i ] = calibration[ 1 ] * ( interval.min( 1 ) + polygon.ypoints[ i ] );
}
Spot spot = SpotRoi.createSpot( xpoly, ypoly, quality );
// 'spots' is the list of Spot this detector will return.
spots.add( spot );
}
如您所见,这相当简单。它说明了如何在 TrackMate 中插入任何返回作业的东西并总计创建一个新的检测器。如果您想集成返回图像掩码、概率图或标签的技术,我们还为这些情况制作了实用方法。
从计算器图像或阈值图像创建演示集合
MaskUtils.fromThresholdWithROI()方法
您可以使用许多上述方法来实现您自己的分割算法。我们还提供了一个简单的 API 来促进在 TrackMate 中集成现有的分割算法。现有算法或控制器图像、标签图像,或返回某种阈值概率图来生成对象。
该 API 主要由类 StarDistDetector.java 中的实用方法组成,这些方法接受此类输入并输出包含对象和 Spot 集合。
让我们从如何输入蒙版图像开始。
为了获得最大的灵活性,TrackMate 中的控件图像可以是任何类型,只要它使用标量、像素类型。我们简单地说,对象是通过连接所有值严格大于 0 的像素而形成的。 因此,这样的模具制作溶液的方法就是导入阈值图像的方法:
MaskUtils.fromThresholdWithROI()
/**
* Creates spots <b>with their ROIs</b> from a <b>2D</b> grayscale image,
* thresholded to create a mask. A spot is created for each
* connected-component of the mask, with a size that matches the mask size.
* The quality of the spots is read from another image, by taking the max
* pixel value of this image with the ROI.
*
* @param <T>
* the type of the input image. Must be real, scalar.
* @param <S>
* the type of the quality image. Must be real, scalar.
* @param input
* the input image. Must be 2D.
* @param interval
* the interval in the input image to analyze.
* @param calibration
* the physical calibration.
* @param threshold
* the threshold to apply to the input image.
* @param simplify
* if <code>true</code> the polygon will be post-processed to be
* smoother and contain less points.
* @param numThreads
* how many threads to use for multithreaded computation.
* @param qualityImage
* the image in which to read the quality value.
* @return a list of spots, with ROI.
*/
public static final < T extends RealType< T >, S extends NumericType< S > > List< Spot > fromThresholdWithROI(
final RandomAccessible< T > input,
final Interval interval,
final double[] calibration,
final double threshold,
final boolean simplify,
final int numThreads,
final RandomAccessibleInterval< S > qualityImage )
该方法适用于二维图像。将创建具有对象的点。让我们回顾一下它的输入:
input是空码输入。它必须是T类型的RandomAccessible,这是 TrackMate 自动提供给其检测器的经典框架。interval是要分析的输入中的间隔。与所有其他检测器一样,TrackMate 将像素信息作为无界RandomAccessible返回,我们需要指定要分析图像的哪一部分。同样,这对于所有检测器都是通用的,并由 TrackMate 提供。calibration是double[]3个元素的阵列,其中包含图像的像素大小(像素宽度、高度和深度)。它可以从您在斐济设置的输入布局中读取的。同样,所有这些对于所有都是通用的。threshold是一个双精度值,该值的强度将被视为对象的标准。这是特定于该检测器的并且由用户设置。对于控制器图像,它是 0。simplify是一个布尔标志,表明用户是否平滑和简化创建。这对于正确的测量形态特征非常重要,我们在其他地方完整记录了它。numThreads。以这种方式创建点是多线程的,您可以在此处设置要使用的线程数。同样,如果您将帐篷声明为Multithreaded,则 TrackMate 将自动设置帐篷的numThreads值,并且您可以在此处使用它。如果您的检测器不是多线程的,请使用值 1。qualityImage是一张图像,我们表格中读取所创建显示的质量值。它必须在与interval参数相同的间隔上定义,并且像素必须为NumericType和标量。如果您无法设置通道或图像的质量(如蒙版),只需将null传递给此参数,对象的质量值将设置区域。否则,这将是质量图像中对象内部的最大像素值。
示例:面罩检测器
我们来看看它是如何用在口罩检测器上的。由于模拟器图像被简单地视为阈值为 0 的灰度图像,因此模拟器检测器实际上是在 MaskUtils.fromThresholdWithROI() 类中实现的。(this line返回值阈值设置为 0 的ThresholdDetector。参见MaskDetectorFactory。)
以下是process()方法的内容:
@Override
public boolean process()
{
final long start = System.currentTimeMillis();
if ( input.numDimensions() == 2 )
{
/*
* 2D: we compute and store the contour.
*/
spots = MaskUtils.fromThresholdWithROI( input, interval, calibration, threshold, simplify, numThreads, null );
}
else if ( input.numDimensions() == 3 )
{
/*
* 3D: We create spots of the same volume that of the region.
*/
spots = MaskUtils.fromThreshold( input, interval, calibration, threshold, numThreads );
}
else
{
errorMessage = baseErrorMessage + "Required a 2D or 3D input, got " + input.numDimensions() + "D.";
return false;
}
final long end = System.currentTimeMillis();
this.processingTime = end - start;
return true;
}
请注意,对 2D 和 3D 图像的处理方式不同。如上所述,新 API 仅支持 2D 图像的对象。方法§§§3图像§§§是对 3D 的MaskUtils.fromThresholdWithROI()的补充,但返回没有的Spot对象。这里创建具有一个半径的点,使得具有该半径的球体具有与分段对象相同的体积。
使用此 API 可以使检测器代码变得非常短。您可以采用相同的方法来集成分割器来输出代理图像或阈值图像。例如,这就是我们集成_Traininable Weka 分段_插件和_ilastik_像素分类器所做的事情。
示例:Weka 检测器
Weka 的义务不是很复杂。调用 Weka 的大部分工作是在 WekaRunner.computeProbabilities() 类中完成的。 运行Weka是在WekaRunner方法中完成的。它返回指定输入和指定类别的概率分类。我们就不详细说了。 但根据这个概率创建点很简单。方法IlastikRunner方法,类似于上一段中描述的方法:
public List< Spot > getSpots( final RandomAccessibleInterval< T > proba, final double[] calibration, final double threshold, final boolean simplify )
{
final List< Spot > spots;
if ( isProcessing3D )
{
spots = MaskUtils.fromThreshold(
proba,
proba,
calibration,
threshold,
numThreads,
proba );
}
else
{
spots = MaskUtils.fromThresholdWithROI(
proba,
proba,
calibration,
threshold,
simplify,
numThreads,
proba );
}
return spots;
}
这里分割概率图的阈值由用户设置。 由于我们有概率图,因此我们可以用它来计算概率导出的质量值。
示例:ilastik 芭
ilastik 的工作原理完全相同。它有一个 WekaRunner.getSpots() 类,负责调用 ilastik 放置结果转换为现货集合。 ilastik 检测器只是对其进行简单的调用。
然而,我们的算法使用了特殊的时间点片段。事实上,ilastik 运行程序需要立即接收_所有_要处理的时间点,在它们上运行 ilastik,然后返回。我们将在下一节讨论这个问题。
以下是对运行程序代码的一些解释:
/*
* This corresponds roughyl to the lines 94-110 of the IlastikRunner class.
*/
// Path to the ilastik project, provided by the users.
final File projectFile = new File( projectFilePath );
// Create an ilastik pixel classifier, configured with the classifier in the specified project.
final PixelClassification classifier = new PixelClassification(
executableFilePath,
projectFile,
logService,
statusService,
numThreads,
maxRamMb );
final PixelPredictionType predictionType = PixelPredictionType.Probabilities;
// Run the classifier on the 'cropped' source image. This will result in the PixelClassification
// Actually RUNNING ilastik in the background, passing input and output images as files saved
// in a temp folder. But this is transparent to us.
final ImgPlus< T > output = classifier.classifyPixels( cropped, predictionType );
// The output has one channel per class in the classifier, so we need to get the channel that
// contains the probability for our object of interest only (specified by the user via the classID
// parameter).
final ImgPlus< T > proba = ImgPlusViews.hyperSlice( output, output.dimensionIndex( Axes.CHANNEL ), classId );
// Etc.
...
// Not we just have to import this probability map as TrackMate ROIs. Since we received the proba
// for ALL time-points at once, we need to process it time-point by time-point:
for ( int t = 0; t < proba.dimension( timeIndex ); t++ )
{
final List< Spot > spotsThisFrame;
final ImgPlus< T > probaThisFrame = TMUtils.hyperSlice( proba, 0, t );
if ( DetectionUtils.is2D( probaThisFrame ) )
{
/*
* 2D: we compute and store the contour.
*/
final boolean simplify = true;
// In 2D we again use the MaskUtils utilities. Note that we use the '...WithROI()'
// method version. In 2D we can import objects with their contours.
spotsThisFrame = MaskUtils.fromThresholdWithROI(
probaThisFrame,
probaThisFrame,
calibration,
probaThreshold,
simplify,
numThreads,
probaThisFrame );
}
else
{
/*
* 3D: We create spots of the same volume that of the region.
* So we use the methods without the '...WithROI()'.
*/
spotsThisFrame = MaskUtils.fromThreshold(
probaThisFrame,
probaThisFrame,
calibration,
probaThreshold,
numThreads,
probaThisFrame );
}
// etc...
控制时间点的切片
通常,TrackMate 会自动执行多个时间点的多线程处理。当您开发检测器时,第 SpotDetector 实例应该仅在一个时间点运行。TrackMate 中的处理逻辑将负责向该检测器提供单个时间点图像,并将检测结果合并到 SpotCollection 中的正确位置。
但是因为我们想要与ilastik等算法和工具集成,所以我们需要另外提供一种处理时间点的方法。例如,ilastik希望立即接收其所有时间点,进行处理,并返回所有时间点的概率。这比多次调用ilastik(每个时间点调用一次)要快的部分。
因此,从 v7 开始,TrackMate 中的SpotDetectorFactory 有一个新的层次结构:
SpotDetectorFactory
SpotDetectorFactory is the initial interface for spot detector factories that generate one detector per time-point. It is will suited to detection and segmentation algorithms that can run concurrently on several time-points at once without a penalty too large. All detectors I know of, except the ilastik detector, derive from this interface.
注意其唯一的具体方法如下:
/**
* Returns a new {@link SpotDetector} configured to operate on the given
* target frame. This factory must be first given the <code>ImgPlus</code>
* and the settings map, through the <code>#setTarget(ImgPlus, Map)</code>
* method.
*
* @param interval
* the interval that determines the region in the source image to
* operate on. This must <b>not</b> have a dimension for time
* (<i>e.g.</i> if the source image is 2D+T (3D), then the
* interval must be 2D; if the source image is 3D without time,
* then the interval must be 3D).
* @param frame
* the frame index in the source image to operate on
*/
public SpotDetector< T > getDetector( final Interval interval, int frame );
因此将在每个时间点生成一个SpotDetector实例。这样的检测器具有用于输出的§§2§§§,预计该检测器仅包含在其配置运行的帧中找到的点。TrackMate将负责构成SpotCollection中不同时间点的所有List< Spot >。
SpotGlobalDetectorFactory
SpotGlobalDetectorFactory is a new interface that does not slice time-points. Its unique specific method returns a SpotGlobalDetector that is expected to process all time-points at once. Such a detector is instantiated only once per detector run.
/**
* Returns a new {@link SpotDetector} configured to operate on all the
* time-points. This factory must be first given the <code>ImgPlus</code>
* and the settings map, through the <code>#setTarget(ImgPlus, Map)</code>
* method.
*
* @param interval
* the interval that determines the region in the source image to
* operate on. This must <b>not</b> have a dimension for time
* (<i>e.g.</i> if the source image is 2D+T (3D), then the
* interval must be 2D; if the source image is 3D without time,
* then the interval must be 3D).
*/
public SpotGlobalDetector< T > getDetector( final Interval interval );
SpotGlobalDetector输出SpotCollection,包含电影所有时间点的所有付费。
基本SpotDetectorFactoryBase接口和has2Dsegmentation标志
上面的两个接口派生自包含公共方法的母接口:SpotDetectorFactoryBase。如果正在为 TrackMate 构建分段算法,它包含一个非常重要的方法:
/**
* Return <code>true</code> for the detectors that can provide a spot with a
* 2D <code>SpotRoi</code> when they operate on 2D images.
* <p>
* This flag may be used by clients to exploit the fact that the spots
* created with this detector will have a contour that can be used
* <i>e.g.</i> to compute morphological features. The default is
* <code>false</code>, indicating that this detector provides spots as a X,
* Y, Z, radius tuple.
*
* @return <code>true</code> if the spots created by this detector have a 2D
* contour.
*/
public default boolean has2Dsegmentation()
{
return false;
}
如果您的支架可以返回 2D 的图像光斑形状,请重写此方法以使其返回 true。这对于让 TrackMate 知道它在检测器创建的位置计算形态特征非常重要。如果您在 GUI(区域,…)中看不到形态特征,这很可能是由于此方法造成的。
正如您在本页上面的示例中看到的,新检测器的方法返回 true。