Conversion of streamlines from trk to tck

Not as far as I know. But interestingly, I do know that Marc Côté is working on .tck support for nibabel - there’s an example there for conversion from .trk to .tck, so it looks like it should work.

Not yet. We have an open issue for it, but it seems that none of us has found time to implement it.

You can also do this by wrapping the nipype interfaces. Take a look at nipype.interfaces.mrtrix.MRTrix2TrackVis. I did this a few years ago now, so there may be a better way now :-). (sorry, the indenting after the if statements was lost)

 #! /usr/bin/env python
 # Jan 2014
 # Christopher J. Steele
 # simple script to convert mrtrix .tck to trackvis .trk
 # using nipype interface
import sys
if len(sys.argv) < 3:
    print "Loads NiPy module to transform file"
    print "Usage:",sys.argv[0], "<base_image> <input_file>"
    print """base_image - file with resolution of template
input_file - MRTrix track file (.tck)"""
    print "Output: TrackVis track vile (.trk)"
    sys.exit(0)

import nipype.interfaces.mrtrix as mrt
mr=mrt.MRTrix2TrackVis()
mr.inputs.image_file=sys.argv[1]
mr.inputs.in_file=sys.argv[2]
mr.inputs.out_filename=sys.argv[2].split('.')[-2] + '.trk'
mr.run()
mr.inputs.print_traits()
1 Like

Have you figured out a way to do this in reverse-- trk to tck, or is this a one way street?

A, sorry about that. I read it the wrong way the first time.
As @jdtournier pointed out above, this is an open issue that looks mostly solved but not yet integrated into nibabel. I think that the example that was referred to is here: https://gist.github.com/MarcCote/ea6842cc4c3950f7596fc3c8a0be0154

Dear all,

Thank you for this instructive post, which is helpful as I preprocess DSI data with DSI-studio, and am going to build the tract using MRtrix3. However, I came across the issue using trk2tck.py. The error message is shown in the following,

hsiang-yuanlin% python ~/Documents/PyScripts/trk2tck.py /Volumes/HY_SSD/DSI/ASD_FU_0502/ASD_5220-1/2.trk.gz

Traceback (most recent call last):
File “/Users/hsiang-yuanlin/Documents/PyScripts/trk2tck.py”, line 31, in
main()
File “/Users/hsiang-yuanlin/Documents/PyScripts/trk2tck.py”, line 28, in main
nib.streamlines.save(trk.tractogram, output_filename)
File “/usr/local/lib/python2.7/site-packages/nibabel/streamlines/init.py”, line 118, in save
raise ValueError(msg)
ValueError: Unknown tractogram file format: ‘/Volumes/HY_SSD/DSI/ASD_FU_0502/ASD_5220-1/2.tr.tck’

Does someone know how to deal with this issue?

Thank you very much!!!

Hsiang-Yuan

Looks like it is not available in the current pip version of nibabel (2.1.0). It was merged into the github repo last month, so you can likely clone and use from there if you need it immediately.

–> https://github.com/nipy/nibabel/tree/master/nibabel/streamlines

Hello all,

Just for the record on the Internet, I also have another (round about) approach which likely will be obsolete pretty soon but just in case some people might find this as an easier alternative.

The steps:

  1. In a bash shell:
    camino_to_trackvis -i streamlines.trk -o streamlines.Bfloat (Camino to and fro trackvis utility)
    vtkstreamlines < streamlines.Bfloat > streamlines.vtk (Camino command)

  2. In Matlab:
    SL = get_streamlines('streamlines.Bfloat', [1, 1, 1]);
    WriteStreamlinesForMrtrix3(SL, 'streamlines');

  3. In a bash shell:
    tckconvert streamlines-[].txt streamlines.tck

The two Matlab functions (get_streamlines and WriteStreamlinesForMrtrix3) are pasted below. I could not attach them. Sorry the get_streamlines.m is not as elegant. I had written it a while back.

Thanks.
Nagesh

====================
WriteStreamlinesForMrtrix3.m

function WriteStreamlinesForMrtrix3(SL, filePrefix)
idxCells = arrayfun(@(x){x-1}, 1:length(SL));
cellfun(@(s, idx) save(sprintf('%s-%.6d.txt', filePrefix, idx), 's', '-ascii'), SL, idxCells', 'UniformOutput', false);
return

====================

====================
get_streamlines.m

function SL = get_streamlines(filename,pixsize)
f = fopen(filename,'r', 'b');
fend = 0;
i = 1; %index
maxP = 0;
tic;
while (fend == 0)
    %read streamline from input file
    a = fread(f,1,'float');
    if (isempty(a))
        break;
    end
    fread(f,1,'float');
    Npoints = floor(a);
    if (Npoints>maxP)
        maxP = Npoints;
    end
    fread(f,[3 Npoints], 'float');
    i = i+1;
end
fclose(f);
%disp (['Total number of streamlines: ' num2str(i-1)]);
%disp (['Longest streamline: ' num2str(maxP) ' points']);

%Now actual reading.
f = fopen(filename,'r', 'b');
fend = 0;
SL=cell(i-1,1);
i=1;
while (fend == 0)
    %read streamline from input file
    a = fread(f,1,'float');
    if (isempty(a))
        break;
    end
    fread(f,1,'float');
    Npoints = floor(a);
    xyz = fread(f,[3 Npoints], 'float');
    xyz(1, :) = xyz(1,:)/pixsize(1);
    xyz(2, :) = xyz(2,:)/pixsize(2);
    xyz(3, :) = xyz(3,:)/pixsize(3);
    xyz = xyz';
    SL{i} = xyz;
    i = i+1;
end
fclose(f);
%fprintf('Read the file in %f sec.',toc);
return

====================

Hi Chris,

Thanks a lot, it works now!

For any others wondering the, nibabel conversion doesn’t work. It’s broke you can’t use any mrtrix functions with the converted tratograms. For instance, if you run tckmap to try and generate a TDI image all you get is black image of all 0’s.

nadluru do you know if your procedure works with tckmap and other mrtrix functions?

Revisiting this because i’ve recently had an issue with data conversion.

I’ve been running the same tck -> trk conversion for years, which uses nipype’s mrtrix interface ( essentially ) :

import nipype.interfaces.mrtrix as mrt
import shutil

tck2trk = mrt.MRTrix2TrackVis()
tck2trk.inputs.image_file = b0file
tck2trk.inputs.in_file = os.path.join(os.path.dirname(infile), '%s.tck' % tckname)
tck2trk.run()
shutil.move('converted.trk',os.path.join(os.path.dirname(infile), '%s.trk' % tckname))

Currently, the output data no longer lines up in trackvis ( L = trackvis, R = mrview )

I believe this started when i upgraded mrtrix3 from RC1 -> RC2. I’ve also tried it on RC3 with the same results. Any ideas whats going on? Has mrtrix3 changed the way it stores things in headers of the data that could attribute to this? Its nice to be able to use trackvis for easy filtering by lengths, ROIs, etc

That’s odd. Nothing has changed in the tck format for a decade or so… And since vertices are stored relative to scanner coordinates, there’s very little that can go wrong. I don’t see how there could be an issue on that front.

However, I think TRK format is relative to the image grid…? In which case it’s presumably dependent on using the same image when converting the tracks as when displaying them – but I’ve no experience with this, so there’s a good chance I might be plain wrong…

i’m using the same nii.gz image to convert and display.

I was thinking maybe something in the headers of the nii.gz could’ve changed? The thing that struck me as a bit odd was the transform vs q-form/s-form and the RAS matrix from the trk file. Wasn’t sure if these should all be the same.

tesla:av cmp12$ mrinfo trace.nii.gz 
************************************************
Image:               "trace.nii.gz"
************************************************
  Dimensions:        384 x 384 x 48
  Voxel size:        0.599 x 0.599 x 0.6
  Data strides:      [ -1 -2 3 ]
  Format:            NIfTI-1.1 (GZip compressed)
  Data type:         32 bit float (little endian)
  Intensity scaling: offset = 0, multiplier = 1
  Transform:                    1          -0           0      -114.7
                               -0           1           0      -74.21
                               -0          -0           1       2.889
  comments:          untitled
  mrtrix_version:    3.0_RC2-82-gbb77205e

The q-form / s-form from the trace.nii.gz data as produced from dwiextract via fslhd

qto_xyz:1      -0.599000  0.000000  0.000000  114.697006
qto_xyz:2      0.000000  -0.599000  0.000000  155.209015
qto_xyz:3      0.000000  0.000000  0.600000  2.889300
qto_xyz:4      0.000000  0.000000  0.000000  1.000000

The metadata from the converted trk file.

tesla:av cmp12$ /Applications/Diffusion\ Toolkit.app/Contents/MacOS/track_info converted.trk 

ID string:           	TRACK
Version:             	2
Dimension:           	384 384 48
Voxel size:          	0.599 0.599 0.6
Voxel order:         	LPS
Voxel order original:	LPS
Voxel to RAS matrix:	
	   -0.5990     0.0000     0.0000   114.6970 
	    0.0000    -0.5990     0.0000   155.2090 
	    0.0000     0.0000     0.6000     2.8893 
	    0.0000     0.0000     0.0000     1.0000 

Image Orientation:	1.0000/0.0000/0.0000/0.0000/1.0000/0.0000
Orientation patches: 	none
Number of scalars:	0
Number of properties:	0
Number of tracks:	1000000

The output of mrinfo doesn’t report the information as stored in the NIfTI directly, but rather as MRtrix3 interprets it. In this instance, the fact that fslhd and the TRK metadata match is all we need to know.

Incidentally, the only time the mrinfo output would be expected to match the sform as stored in the NIfTI header is in the special case of close-to-axial orientations (i.e. strides: 1 2 3) with unit voxel sizes. This is because the transform reported by mrinfo is the pure rotation applied to the image axes, having modified them to be close to axial (see the documentation for details). In contrast, the sform includes the voxel scaling, and does not try to account for the orientation of the axes in any way.

So the issue is elsewhere. I have no idea what could be causing this – as I mentioned earlier, there’s been no changes to the format or conventions assumed for the streamlines data. There have been a few changes to the NIfTI handling though, which may account for the discrepancy – but these were included into 3.0_RC3, not RC2. We also had quite a bit of fun investigating fundamental instabilities in the qform if you’re really interested – but these changes are on master only, not even 3.0_RC3. I can’t find any changes in the NIfTI handling between RC1 & RC2 – here’s the full diff for the relevant files:

$ git log -p 3.0_RC1..3.0_RC2 -- core/file/nifti* core/formats/nifti*

which basically gives copyright notice changes and not a lot else:

commit 3db1bb2b07b2cbeed35596a63c67bdd310a0492d
Author: Robert Smith <robert.smith@florey.edu.au>
Date:   Sat May 20 01:08:36 2017 +1000

    Remove #include <vector> usages
    
    This header should no longer be included directly, since class MR::vector (defined in core/types.h) should always be used.

diff --git a/core/file/nifti_utils.h b/core/file/nifti_utils.h
index ba232f38f..b5b2d9398 100644
--- a/core/file/nifti_utils.h
+++ b/core/file/nifti_utils.h
@@ -15,8 +15,6 @@
 #ifndef __file_nifti_utils_h__
 #define __file_nifti_utils_h__
 
-#include <vector>
-
 #include "types.h"
 
 namespace MR

commit 729dd6cfc1a773f0f16592b8533c6e9d89d03ac3
Author: Thijs Dhollander <thijs.dhollander@gmail.com>
Date:   Mon May 15 10:33:05 2017 +1000

    copyright update and cleanup

diff --git a/core/file/nifti1_utils.cpp b/core/file/nifti1_utils.cpp
index ded0e761e..9b53dd81e 100644
--- a/core/file/nifti1_utils.cpp
+++ b/core/file/nifti1_utils.cpp
@@ -1,4 +1,4 @@
-/* Copyright (c) 2008-2017 the MRtrix3 contributors
+/* Copyright (c) 2008-2017 the MRtrix3 contributors.
  *
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
diff --git a/core/file/nifti1_utils.h b/core/file/nifti1_utils.h
index ecd412789..16b117eaa 100644
--- a/core/file/nifti1_utils.h
+++ b/core/file/nifti1_utils.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2008-2017 the MRtrix3 contributors
+/* Copyright (c) 2008-2017 the MRtrix3 contributors.
  *
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
diff --git a/core/file/nifti2.h b/core/file/nifti2.h
index d9e4de475..5643fa909 100644
--- a/core/file/nifti2.h
+++ b/core/file/nifti2.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2008-2017 the MRtrix3 contributors
+/* Copyright (c) 2008-2017 the MRtrix3 contributors.
  *
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
diff --git a/core/file/nifti2_utils.cpp b/core/file/nifti2_utils.cpp
index 1d76659c1..d0fafa7ee 100644
--- a/core/file/nifti2_utils.cpp
+++ b/core/file/nifti2_utils.cpp
@@ -1,4 +1,4 @@
-/* Copyright (c) 2008-2017 the MRtrix3 contributors
+/* Copyright (c) 2008-2017 the MRtrix3 contributors.
  *
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
diff --git a/core/file/nifti2_utils.h b/core/file/nifti2_utils.h
index dcd629e9c..39f272b8f 100644
--- a/core/file/nifti2_utils.h
+++ b/core/file/nifti2_utils.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2008-2017 the MRtrix3 contributors
+/* Copyright (c) 2008-2017 the MRtrix3 contributors.
  *
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
diff --git a/core/file/nifti_utils.cpp b/core/file/nifti_utils.cpp
index 1d0d6e19c..2e909f671 100644
--- a/core/file/nifti_utils.cpp
+++ b/core/file/nifti_utils.cpp
@@ -1,4 +1,4 @@
-/* Copyright (c) 2008-2017 the MRtrix3 contributors
+/* Copyright (c) 2008-2017 the MRtrix3 contributors.
  *
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
diff --git a/core/file/nifti_utils.h b/core/file/nifti_utils.h
index 18e9d0bf0..ba232f38f 100644
--- a/core/file/nifti_utils.h
+++ b/core/file/nifti_utils.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2008-2017 the MRtrix3 contributors
+/* Copyright (c) 2008-2017 the MRtrix3 contributors.
  *
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
diff --git a/core/formats/nifti1.cpp b/core/formats/nifti1.cpp
index f188bb60c..06d4977df 100644
--- a/core/formats/nifti1.cpp
+++ b/core/formats/nifti1.cpp
@@ -1,4 +1,4 @@
-/* Copyright (c) 2008-2017 the MRtrix3 contributors
+/* Copyright (c) 2008-2017 the MRtrix3 contributors.
  *
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
diff --git a/core/formats/nifti1_gz.cpp b/core/formats/nifti1_gz.cpp
index 41938ff75..588f1a322 100644
--- a/core/formats/nifti1_gz.cpp
+++ b/core/formats/nifti1_gz.cpp
@@ -1,4 +1,4 @@
-/* Copyright (c) 2008-2017 the MRtrix3 contributors
+/* Copyright (c) 2008-2017 the MRtrix3 contributors.
  *
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
diff --git a/core/formats/nifti2.cpp b/core/formats/nifti2.cpp
index 0ccd4cde6..06c76faa1 100644
--- a/core/formats/nifti2.cpp
+++ b/core/formats/nifti2.cpp
@@ -1,4 +1,4 @@
-/* Copyright (c) 2008-2017 the MRtrix3 contributors
+/* Copyright (c) 2008-2017 the MRtrix3 contributors.
  *
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
diff --git a/core/formats/nifti2_gz.cpp b/core/formats/nifti2_gz.cpp
index c63e8e22b..b79a28145 100644
--- a/core/formats/nifti2_gz.cpp
+++ b/core/formats/nifti2_gz.cpp
@@ -1,4 +1,4 @@
-/* Copyright (c) 2008-2017 the MRtrix3 contributors
+/* Copyright (c) 2008-2017 the MRtrix3 contributors.
  *
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this

In short, I can’t think of anything that would affect this… The only other option that might play a role here is if there’s a discrepancy between the sform and the qform in your reference image. We did introduce checks on that front in 3.0_RC3 (this commit), along with a config file option to set which of the two to use when they don’t match (this commit). I’m not sure what might have happened in the past, although I have a feeling it would have silently defaulted to the sform. If that doesn’t match what nipype is defaulting to in case of mismatch, that could introduce the kinds of errors you’re seeing… You should be able to check this with a quick look through fslhd – and a 3.0_RC3 version of mrinfo should also warn you if there is a mismatch…

Great. Thanks for the confirmation and looking into this. I reprocessed with RC3 and had the same result.

I then processed a scan off of our other scanner with the exact same procedures and everything lined up perfectly as before. Looks like i’ll be digging in deeper to the data coming off the new scanner.

Hey @Chris_Steele @jdtournier,

I have been trying to find the correct implementation of trk2tck for a month now, and have only been able to find that they have implemented it using nibabel.streamlines.load and nibabel.streamlines.save into .tck but when you use normal mrtrix functions into the same they do not work, and raise an error “invalid mrtrix tracks”.
Be it the streamlines_tck or another implementation on brainslife.io, app-trk2tck, they have also used the nibabel save and load methods which do not convert .trk(s) to .tck(s).
Please let me know if there are any solution available now to convert between these file formats.

Regards,
Anoushkrit Goel

Hi @anoushkrit,

Not sure I can help a great deal here, but my first recommendation is to make sure you’re using the most up to date version of nibabel available – it might have been an issue with an earlier version. If that doesn’t solve the problem, can you send me a (small) example output from the conversion (just the tck file should be enough) so I can look into what it is about it that is invalid. It might be a simple fix, and if so we might be able to feed that back to the nibabel team and get it fixed upstream.

Hi @jdtournier
Thank you!! for such a prompt reply, currently I am using nibabel==3.2.2 currently. I also tried cloning the latest nibabel version from git, but somehow that didn’t work and conflicted with other packages in the virtual env. However, I am attaching the converted .tck file for your reference. This I created using nibabel.streamlines.load and the save method to .tck which has been used in the above mentioned implementations, where tckinfo doesn’t work. Please find attached the converted .tck for your reference.

Regards,
Anoushkrit Goel

OK, the file you sent me has a .tck extension, but clearly isn’t in our format. It starts with the characters TRACK, which according to the TrackVis documentation, is what you’d expect for their trk format. It looks like no conversion was performed at all…

Hi everyone,

just for information, I managed to convert between .tck and .trk using DIPY. It has a StatefulTractogram class that can be used to conveniently convert between various track formats. It was really easy even for me, who is not a Python programmer.

tutorial on the topic DIPY : Docs 1.5.0 - Read/Write streamline files

Best regards

Samuel

1 Like