| 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 |