Repository 'points_association_nn'
hg clone https://toolshed.g2.bx.psu.edu/repos/imgteam/points_association_nn

Changeset 0:04e692ee53a8 (2021-07-22)
Next changeset 1:fd4293bee0dc (2021-07-22)
Commit message:
"planemo upload for repository https://github.com/BMCV/galaxy-image-analysis/tree/master/tools/points_association_nn/ commit db4c2a87a21f32e5d12d11e68f32773bfc06fcfd"
added:
points_association_nn.py
points_association_nn.xml
test-data/spots_detected.tsv
test-data/spots_linked.xlsx
b
diff -r 000000000000 -r 04e692ee53a8 points_association_nn.py
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/points_association_nn.py Thu Jul 22 22:29:47 2021 +0000
[
@@ -0,0 +1,168 @@
+"""
+Copyright 2021 Biomedical Computer Vision Group, Heidelberg University.
+Author: Qi Gao (qi.gao@bioquant.uni-heidelberg.de)
+
+Distributed under the MIT license.
+See file LICENSE for detail or copy at https://opensource.org/licenses/MIT
+
+"""
+
+import argparse
+
+import numpy as np
+import openpyxl  # noqa: F401
+import pandas as pd
+import skimage.util
+
+
+def disk_mask(imsz, ir, ic, nbpx):
+    ys, xs = np.ogrid[-nbpx:nbpx + 1, -nbpx:nbpx + 1]
+    se = xs ** 2 + ys ** 2 <= nbpx ** 2
+    mask = np.zeros(imsz, dtype=int)
+    if ir - nbpx < 0 or ic - nbpx < 0 or ir + nbpx + 1 > imsz[0] or ic + nbpx + 1 > imsz[1]:
+        mask = skimage.util.pad(mask, nbpx)
+        mask[ir:ir + 2 * nbpx + 1, ic:ic + 2 * nbpx + 1] = se
+        mask = skimage.util.crop(mask, nbpx)
+    else:
+        mask[ir - nbpx:ir + nbpx + 1, ic - nbpx:ic + nbpx + 1] = se
+    return mask
+
+
+def find_nn(cim, icy, icx, nim, nbpx):
+    mask = disk_mask(cim.shape, icy, icx, nbpx)
+    iys_nim, ixs_nim = np.where(nim * mask)
+    if iys_nim.size == 0:
+        return np.NaN, np.NaN
+
+    d2 = (icy - iys_nim) ** 2 + (icx - ixs_nim) ** 2
+    I1 = np.argsort(d2)
+    iy_nim = iys_nim[I1[0]]
+    ix_nim = ixs_nim[I1[0]]
+
+    mask = disk_mask(cim.shape, iy_nim, ix_nim, nbpx)
+    iys_cim, ixs_cim = np.where(cim * mask)
+    d2 = (iy_nim - iys_cim) ** 2 + (ix_nim - ixs_cim) ** 2
+    I2 = np.argsort(d2)
+    if not iys_cim[I2[0]] == icy or not ixs_cim[I2[0]] == icx:
+        return np.NaN, np.NaN
+
+    return iy_nim, ix_nim
+
+
+def points_linking(fn_in, fn_out, nbpx=6, th=25, minlen=50):
+    data = pd.read_csv(fn_in, delimiter="\t")
+    all_data = np.array(data)
+    assert all_data.shape[1] in [3, 4], 'unknow collum(s) in input data!'
+
+    coords = all_data[:, :3].astype('int64')
+
+    frame_1st = np.min(coords[:, 0])
+    frame_end = np.max(coords[:, 0])
+    assert set([i for i in range(frame_1st, frame_end + 1)]).issubset(set(coords[:, 0].tolist())), "spots missing at some time point!"
+
+    nSlices = frame_end
+    stack_h = np.max(coords[:, 2]) + nbpx
+    stack_w = np.max(coords[:, 1]) + nbpx
+    stack = np.zeros((stack_h, stack_w, nSlices), dtype='int8')
+    stack_r = np.zeros((stack_h, stack_w, nSlices), dtype='float64')
+
+    for i in range(all_data.shape[0]):
+        iyxz = tuple(coords[i, ::-1] - 1)
+        stack[iyxz] = 1
+        stack_r[iyxz] = all_data[i, -1]
+
+    tracks_all = np.array([], dtype=float).reshape(0, nSlices, 4)
+    maxv = np.max(stack_r)
+    br_max = maxv
+    idx_max = np.argmax(stack_r)
+    while 1:
+        iyxz = np.unravel_index(idx_max, stack.shape)
+
+        spot_br = np.empty((nSlices, 1))
+        track = np.empty((nSlices, 3))
+        for i in range(nSlices):
+            spot_br[i] = np.NaN
+            track[i, :] = np.array((np.NaN, np.NaN, np.NaN))
+
+        spot_br[iyxz[2]] = maxv
+        track[iyxz[2], :] = np.array(iyxz[::-1]) + 1
+
+        # forward
+        icy = iyxz[0]
+        icx = iyxz[1]
+        for inz in range(iyxz[2] + 1, nSlices):
+            iny, inx = find_nn(stack[:, :, inz - 1], icy, icx, stack[:, :, inz], nbpx)
+            if np.isnan(iny) and not inz == nSlices - 1:
+                iny, inx = find_nn(stack[:, :, inz - 1], icy, icx, stack[:, :, inz + 1], nbpx)
+                if np.isnan(iny):
+                    break
+                else:
+                    iny = icy
+                    inx = icx
+                    stack[iny, inx, inz] = 1
+                    stack_r[iny, inx, inz] = stack_r[iny, inx, inz - 1]
+            elif np.isnan(iny) and inz == nSlices - 1:
+                break
+
+            track[inz, :] = np.array((inz, inx, iny)) + 1
+            spot_br[inz] = stack_r[iny, inx, inz]
+            icy = iny
+            icx = inx
+
+        # backward
+        icy = iyxz[0]
+        icx = iyxz[1]
+        for inz in range(iyxz[2] - 1, -1, -1):
+            iny, inx = find_nn(stack[:, :, inz + 1], icy, icx, stack[:, :, inz], nbpx)
+            if np.isnan(iny) and not inz == 0:
+                iny, inx = find_nn(stack[:, :, inz + 1], icy, icx, stack[:, :, inz - 1], nbpx)
+                if np.isnan(iny):
+                    break
+                else:
+                    iny = icy
+                    inx = icx
+                    stack[iny, inx, inz] = 1
+                    stack_r[iny, inx, inz] = stack_r[iny, inx, inz + 1]
+            elif np.isnan(iny) and inz == 0:
+                break
+
+            track[inz, :] = np.array((inz, inx, iny)) + 1
+            spot_br[inz] = stack_r[iny, inx, inz]
+            icy = iny
+            icx = inx
+
+        for iz in range(nSlices):
+            if not np.isnan(track[iz, 0]):
+                stack[track[iz, 2].astype(int) - 1, track[iz, 1].astype(int) - 1, iz] = 0
+                stack_r[track[iz, 2].astype(int) - 1, track[iz, 1].astype(int) - 1, iz] = 0
+
+        # discard short trajectories
+        if np.count_nonzero(~np.isnan(spot_br)) > minlen * (frame_end - frame_1st) / 100:
+            tmp = np.concatenate((track, spot_br), axis=1)
+            tracks_all = np.concatenate((tracks_all, tmp.reshape(1, -1, 4)), axis=0)
+
+        maxv = np.max(stack_r)
+        idx_max = np.argmax(stack_r)
+        if maxv < th * br_max / 100:
+            break
+
+    with pd.ExcelWriter(fn_out, engine="openpyxl") as writer:
+        for i in range(tracks_all.shape[0]):
+            df = pd.DataFrame()
+            df['FRAME'] = tracks_all[i, :, 0]
+            df['POS_X'] = tracks_all[i, :, 1]
+            df['POS_Y'] = tracks_all[i, :, 2]
+            df['INTENSITY'] = tracks_all[i, :, 3]
+            df.to_excel(writer, sheet_name='spot%s' % (i + 1), index=False, float_format='%.2f')
+        writer.save()
+
+
+if __name__ == "__main__":
+    parser = argparse.ArgumentParser(description="Association of points in consecutive frames using the nearest neighbor algorithm")
+    parser.add_argument("fn_in", help="Name of input file (tsv tabular)")
+    parser.add_argument("fn_out", help="Name of output file (xlsx)")
+    parser.add_argument("nbpx", type=int, help="Neighborhood size in pixel")
+    parser.add_argument("thres", type=float, help="Percentage of the global maximal intensity for thresholding some event")
+    parser.add_argument("minlen", type=float, help="Minimum length of tracks (percentage of senquence length)")
+    args = parser.parse_args()
+    points_linking(args.fn_in, args.fn_out, args.nbpx, args.thres, args.minlen)
b
diff -r 000000000000 -r 04e692ee53a8 points_association_nn.xml
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/points_association_nn.xml Thu Jul 22 22:29:47 2021 +0000
[
@@ -0,0 +1,42 @@
+<tool id="ip_points_association_nn" name="Association of points" version="0.0.1" profile="20.05"> 
+    <description>in consecutive frames (slices) using the nearest neighbor algorithm</description>
+    <requirements>
+        <requirement type="package" version="1.20.2">numpy</requirement>
+        <requirement type="package" version="3.0.7">openpyxl</requirement>
+        <requirement type="package" version="1.2.4">pandas</requirement>
+        <requirement type="package" version="0.18.1">scikit-image</requirement>
+    </requirements>
+    <command>
+    <![CDATA[
+    python '$__tool_directory__/points_association_nn.py'
+         '$fn_in'
+         ./output.xlsx
+         '$nbpx'
+         '$thres'
+         '$minlen'
+    ]]>
+    </command>
+    <inputs>
+        <param name="fn_in" type="data" format="tabular" label="Name of input file (tsv tabular)" />
+        <param name="nbpx" type="integer" value="6" label="Neighborhood size in pixel" />
+        <param name="thres" type="float" value="25" label="Percentage (%) of the global maximal intensity for thresholding some event" />
+        <param name="minlen" type="float" value="50" label="Minimum length of tracks (% of sequence length)" />
+    </inputs>
+    <outputs>
+        <data format="xlsx" name="fn_out" from_work_dir="output.xlsx" />
+    </outputs>
+    <tests>
+        <test>
+            <param name="fn_in" value="spots_detected.tsv"/>
+            <param name="nbpx" value="6"/>
+            <param name="thres" value="25"/>
+            <param name="minlen" value="50"/>
+            <output name="fn_out" value="spots_linked.xlsx" ftype="xlsx" compare="sim_size"/>
+        </test>
+    </tests>
+    <help>
+    **What it does**
+
+    This tool associates points in consecutive frames (slices) using the nearest neighbor algorithm.
+    </help>
+</tool>
b
diff -r 000000000000 -r 04e692ee53a8 test-data/spots_detected.tsv
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test-data/spots_detected.tsv Thu Jul 22 22:29:47 2021 +0000
b
b'@@ -0,0 +1,462 @@\n+FRAME\tPOS_X\tPOS_Y\tINTENSITY\n+1\t126\t202\t55939.36\n+1\t218\t227\t41590.82\n+1\t132\t201\t41849.38\n+1\t120\t199\t45491.74\n+1\t113\t177\t27103.64\n+1\t95\t135\t33206.37\n+1\t130\t209\t36872.91\n+1\t128\t170\t30994.56\n+1\t315\t240\t38767.19\n+1\t123\t195\t32988.47\n+1\t117\t181\t40031.76\n+1\t118\t184\t36812.77\n+1\t127\t181\t29651.66\n+1\t134\t192\t28531.76\n+1\t137\t189\t35093.95\n+1\t195\t223\t30206.57\n+1\t78\t87\t30367.18\n+1\t146\t195\t26779.82\n+1\t175\t225\t28515.74\n+1\t213\t227\t30812.06\n+1\t114\t158\t31229.97\n+1\t171\t223\t26982.37\n+1\t87\t117\t27143.89\n+1\t129\t194\t29475.93\n+1\t115\t192\t28510.64\n+1\t248\t230\t22615.54\n+1\t154\t208\t26394.30\n+1\t109\t153\t26268.78\n+1\t328\t234\t26387.65\n+1\t138\t179\t20215.40\n+1\t159\t212\t24400.22\n+1\t335\t233\t21698.80\n+1\t387\t198\t24933.29\n+1\t121\t154\t18710.45\n+1\t308\t239\t22328.27\n+1\t258\t230\t18680.93\n+1\t340\t229\t15937.72\n+1\t78\t78\t18151.49\n+1\t81\t80\t24704.83\n+1\t117\t161\t24293.65\n+1\t120\t164\t24242.78\n+1\t160\t215\t23759.44\n+1\t86\t141\t10949.28\n+1\t222\t230\t22277.61\n+1\t370\t215\t18379.39\n+1\t109\t199\t18850.01\n+1\t83\t109\t15871.12\n+1\t117\t155\t15703.14\n+1\t100\t182\t17281.91\n+1\t168\t227\t17431.67\n+1\t392\t197\t15897.67\n+1\t184\t227\t11346.34\n+1\t294\t237\t10236.84\n+1\t377\t210\t15642.67\n+1\t152\t226\t9851.10\n+1\t93\t145\t12584.04\n+1\t147\t212\t14735.83\n+1\t80\t101\t11021.47\n+1\t394\t193\t14063.18\n+1\t105\t139\t12039.57\n+1\t301\t237\t11466.69\n+1\t273\t233\t8421.28\n+1\t111\t170\t14406.46\n+1\t237\t228\t9466.36\n+1\t106\t206\t14522.55\n+1\t83\t75\t11115.23\n+1\t290\t238\t12636.07\n+1\t205\t229\t12808.78\n+1\t381\t207\t11531.86\n+1\t92\t196\t9024.83\n+1\t185\t222\t11196.17\n+1\t180\t221\t8339.93\n+1\t146\t185\t7207.70\n+1\t142\t209\t11730.52\n+1\t88\t101\t10415.08\n+1\t235\t231\t11264.22\n+1\t85\t103\t5911.48\n+1\t104\t183\t8101.94\n+1\t85\t148\t7290.63\n+1\t145\t189\t9776.95\n+1\t361\t219\t7009.70\n+1\t282\t236\t8853.80\n+1\t121\t169\t7733.35\n+1\t107\t180\t8135.17\n+1\t266\t235\t7470.55\n+1\t79\t96\t6920.43\n+1\t152\t219\t5623.45\n+1\t149\t220\t5889.31\n+2\t126\t202\t59098.11\n+2\t126\t205\t53383.42\n+2\t218\t227\t46745.07\n+2\t120\t199\t43055.88\n+2\t116\t180\t45577.81\n+2\t127\t181\t42874.19\n+2\t130\t209\t41387.22\n+2\t123\t195\t33569.00\n+2\t113\t177\t31339.11\n+2\t128\t170\t35552.32\n+2\t135\t191\t30546.65\n+2\t95\t136\t34169.11\n+2\t315\t238\t31430.32\n+2\t314\t240\t30015.35\n+2\t116\t201\t26928.89\n+2\t196\t223\t29118.84\n+2\t175\t225\t29053.82\n+2\t213\t227\t34704.93\n+2\t115\t159\t29750.71\n+2\t146\t195\t27597.86\n+2\t328\t234\t28969.08\n+2\t113\t191\t25325.73\n+2\t79\t87\t27998.19\n+2\t340\t229\t19788.22\n+2\t154\t207\t25348.79\n+2\t81\t81\t25643.11\n+2\t172\t224\t25762.85\n+2\t87\t117\t24830.46\n+2\t169\t222\t19415.52\n+2\t325\t232\t23879.48\n+2\t248\t230\t23270.45\n+2\t110\t153\t24396.90\n+2\t387\t198\t23947.67\n+2\t87\t143\t16954.62\n+2\t118\t162\t26061.81\n+2\t78\t79\t23543.41\n+2\t258\t230\t16996.33\n+2\t121\t165\t22822.04\n+2\t387\t201\t25260.27\n+2\t335\t233\t19553.33\n+2\t223\t230\t22795.66\n+2\t159\t212\t20956.68\n+2\t370\t216\t19989.36\n+2\t320\t233\t13806.24\n+2\t308\t239\t17964.97\n+2\t105\t139\t18000.56\n+2\t109\t200\t17896.86\n+2\t83\t109\t16421.63\n+2\t105\t151\t17984.13\n+2\t137\t179\t12811.40\n+2\t110\t169\t14111.65\n+2\t228\t231\t18325.92\n+2\t392\t196\t14572.82\n+2\t153\t228\t14935.83\n+2\t122\t154\t11708.73\n+2\t83\t75\t13067.82\n+2\t153\t213\t16283.67\n+2\t295\t237\t9677.50\n+2\t377\t210\t13457.71\n+2\t273\t233\t9148.80\n+2\t205\t229\t13083.06\n+2\t381\t206\t10603.41\n+2\t80\t101\t9485.05\n+2\t152\t202\t15278.04\n+2\t185\t226\t11093.24\n+2\t161\t216\t15543.22\n+2\t146\t212\t14364.79\n+2\t235\t231\t12509.71\n+2\t290\t238\t13411.96\n+2\t93\t145\t9119.49\n+2\t142\t210\t13054.23\n+2\t239\t232\t13043.98\n+2\t92\t196\t10749.96\n+2\t185\t221\t9922.40\n+2\t299\t238\t13577.34\n+2\t278\t235\t11194.38\n+2\t100\t182\t11485.41\n+2\t145\t189\t11315.24\n+2\t237\t228\t11272.61\n+2\t107\t189\t8051.49\n+2\t87\t101\t8814.45\n+2\t146\t186\t7585.02\n+2\t106\t207\t8720.64\n+2\t109\t207\t7676.81\n+2\t402\t188\t7813.91\n+2\t88\t108\t7921.85\n+2\t360\t220\t7726.74\n+2\t86\t148\t7184.91\n+2\t106\t180\t8401.49\n+2\t95\t173\t4093.76\n+2\t266\t235\t5678.52\n+2\t122\t172\t5731.76\n+2\t127\t218\t4787.41\n+3\t126\t206\t56658.14\n+3\t126\t202\t54234.74\n+3\t218\t226\t35022.37\n+3\t119\t200\t40576.02\n+3\t133\t201\t44149.96\n+3\t115\t178\t43820.79\n+3\t124\t196\t41934.96\n+3\t117\t180\t43996.85\n+3\t130\t209\t42016.60\n+3\t136\t190\t39500.63\n+3\t316\t237\t31963.21\n+3\t97\t137\t36182.78\n+3\t127\t181\t31990.16\n+3\t119\t184\t34251.83\n+3\t114\t158\t29844.61\n+3\t146\t196\t37064.84\n+3\t128\t186\t34532.76\n+3\t176\t225\t30746.00\n+3\t196\t223\t29807.10\n+3\t213\t226\t25523.49\n+3\t128\t17'..b'34\t231\t13284.28\n+3\t236\t228\t8838.32\n+3\t106\t207\t10942.68\n+3\t381\t206\t8704.24\n+3\t238\t231\t13112.03\n+3\t109\t169\t9573.93\n+3\t88\t101\t9934.59\n+3\t104\t183\t7882.07\n+3\t85\t103\t8202.08\n+3\t361\t219\t7171.66\n+3\t278\t235\t10260.85\n+3\t268\t235\t8025.01\n+3\t146\t188\t9096.57\n+3\t283\t236\t9090.54\n+3\t399\t190\t6873.99\n+3\t145\t185\t6134.20\n+3\t105\t179\t6141.86\n+3\t97\t176\t6082.70\n+3\t110\t207\t4505.63\n+3\t127\t219\t5028.42\n+3\t94\t171\t6146.80\n+4\t126\t202\t55939.36\n+4\t218\t227\t41590.82\n+4\t132\t201\t41849.38\n+4\t120\t199\t45491.74\n+4\t113\t177\t27103.64\n+4\t95\t135\t33206.37\n+4\t130\t209\t36872.91\n+4\t128\t170\t30994.56\n+4\t315\t240\t38767.19\n+4\t123\t195\t32988.47\n+4\t117\t181\t40031.76\n+4\t118\t184\t36812.77\n+4\t127\t181\t29651.66\n+4\t134\t192\t28531.76\n+4\t137\t189\t35093.95\n+4\t195\t223\t30206.57\n+4\t78\t87\t30367.18\n+4\t146\t195\t26779.82\n+4\t175\t225\t28515.74\n+4\t213\t227\t30812.06\n+4\t114\t158\t31229.97\n+4\t171\t223\t26982.37\n+4\t87\t117\t27143.89\n+4\t129\t194\t29475.93\n+4\t115\t192\t28510.64\n+4\t248\t230\t22615.54\n+4\t154\t208\t26394.30\n+4\t109\t153\t26268.78\n+4\t328\t234\t26387.65\n+4\t138\t179\t20215.40\n+4\t159\t212\t24400.22\n+4\t335\t233\t21698.80\n+4\t387\t198\t24933.29\n+4\t121\t154\t18710.45\n+4\t308\t239\t22328.27\n+4\t258\t230\t18680.93\n+4\t340\t229\t15937.72\n+4\t78\t78\t18151.49\n+4\t81\t80\t24704.83\n+4\t117\t161\t24293.65\n+4\t120\t164\t24242.78\n+4\t160\t215\t23759.44\n+4\t86\t141\t10949.28\n+4\t222\t230\t22277.61\n+4\t370\t215\t18379.39\n+4\t109\t199\t18850.01\n+4\t83\t109\t15871.12\n+4\t117\t155\t15703.14\n+4\t100\t182\t17281.91\n+4\t168\t227\t17431.67\n+4\t392\t197\t15897.67\n+4\t184\t227\t11346.34\n+4\t294\t237\t10236.84\n+4\t377\t210\t15642.67\n+4\t152\t226\t9851.10\n+4\t93\t145\t12584.04\n+4\t147\t212\t14735.83\n+4\t80\t101\t11021.47\n+4\t394\t193\t14063.18\n+4\t105\t139\t12039.57\n+4\t301\t237\t11466.69\n+4\t273\t233\t8421.28\n+4\t111\t170\t14406.46\n+4\t237\t228\t9466.36\n+4\t106\t206\t14522.55\n+4\t83\t75\t11115.23\n+4\t290\t238\t12636.07\n+4\t205\t229\t12808.78\n+4\t381\t207\t11531.86\n+4\t92\t196\t9024.83\n+4\t185\t222\t11196.17\n+4\t180\t221\t8339.93\n+4\t146\t185\t7207.70\n+4\t142\t209\t11730.52\n+4\t88\t101\t10415.08\n+4\t235\t231\t11264.22\n+4\t85\t103\t5911.48\n+4\t104\t183\t8101.94\n+4\t85\t148\t7290.63\n+4\t145\t189\t9776.95\n+4\t361\t219\t7009.70\n+4\t282\t236\t8853.80\n+4\t121\t169\t7733.35\n+4\t107\t180\t8135.17\n+4\t266\t235\t7470.55\n+4\t79\t96\t6920.43\n+4\t152\t219\t5623.45\n+4\t149\t220\t5889.31\n+5\t126\t206\t56658.14\n+5\t126\t202\t54234.74\n+5\t218\t226\t35022.37\n+5\t119\t200\t40576.02\n+5\t133\t201\t44149.96\n+5\t115\t178\t43820.79\n+5\t124\t196\t41934.96\n+5\t117\t180\t43996.85\n+5\t130\t209\t42016.60\n+5\t136\t190\t39500.63\n+5\t316\t237\t31963.21\n+5\t97\t137\t36182.78\n+5\t127\t181\t31990.16\n+5\t119\t184\t34251.83\n+5\t114\t158\t29844.61\n+5\t146\t196\t37064.84\n+5\t128\t186\t34532.76\n+5\t176\t225\t30746.00\n+5\t196\t223\t29807.10\n+5\t213\t226\t25523.49\n+5\t128\t171\t28664.10\n+5\t152\t204\t24687.37\n+5\t329\t234\t28277.06\n+5\t78\t87\t26725.30\n+5\t81\t87\t30473.18\n+5\t172\t225\t26643.89\n+5\t120\t163\t26474.56\n+5\t387\t197\t21111.91\n+5\t86\t117\t20367.28\n+5\t335\t233\t20895.73\n+5\t324\t232\t23086.43\n+5\t339\t229\t15275.48\n+5\t129\t194\t24033.17\n+5\t123\t185\t24073.76\n+5\t77\t79\t19152.47\n+5\t169\t222\t17195.61\n+5\t132\t175\t25123.86\n+5\t81\t81\t24895.65\n+5\t109\t153\t21172.27\n+5\t117\t161\t24299.24\n+5\t307\t240\t19257.85\n+5\t370\t215\t18554.16\n+5\t222\t230\t22017.20\n+5\t104\t139\t20194.29\n+5\t248\t230\t23396.01\n+5\t257\t230\t17273.50\n+5\t116\t154\t15659.69\n+5\t114\t190\t21449.57\n+5\t158\t211\t18454.95\n+5\t83\t109\t17776.43\n+5\t84\t75\t15659.83\n+5\t158\t215\t19800.41\n+5\t109\t199\t15805.24\n+5\t101\t140\t20715.26\n+5\t143\t210\t20769.18\n+5\t88\t144\t20132.54\n+5\t93\t145\t12670.17\n+5\t228\t231\t19053.86\n+5\t393\t195\t13463.33\n+5\t80\t101\t13305.58\n+5\t105\t151\t15913.95\n+5\t137\t179\t15719.37\n+5\t167\t226\t17465.42\n+5\t293\t238\t15459.81\n+5\t377\t209\t10830.81\n+5\t185\t222\t13711.26\n+5\t108\t189\t16955.73\n+5\t185\t226\t12648.79\n+5\t153\t227\t14010.45\n+5\t101\t186\t14180.61\n+5\t298\t238\t16257.78\n+5\t121\t189\t14906.88\n+5\t151\t210\t12848.29\n+5\t274\t233\t11026.17\n+5\t93\t197\t14149.59\n+5\t234\t231\t13284.28\n+5\t236\t228\t8838.32\n+5\t106\t207\t10942.68\n+5\t381\t206\t8704.24\n+5\t238\t231\t13112.03\n+5\t109\t169\t9573.93\n+5\t88\t101\t9934.59\n+5\t104\t183\t7882.07\n+5\t85\t103\t8202.08\n+5\t361\t219\t7171.66\n+5\t278\t235\t10260.85\n+5\t268\t235\t8025.01\n+5\t146\t188\t9096.57\n+5\t283\t236\t9090.54\n+5\t399\t190\t6873.99\n+5\t145\t185\t6134.20\n+5\t105\t179\t6141.86\n+5\t97\t176\t6082.70\n+5\t110\t207\t4505.63\n+5\t127\t219\t5028.42\n+5\t94\t171\t6146.80\n'
b
diff -r 000000000000 -r 04e692ee53a8 test-data/spots_linked.xlsx
b
Binary file test-data/spots_linked.xlsx has changed