Skip to content

JSON: import simple array as a single header entry#1771

Closed
jdtournier wants to merge 2 commits intomasterfrom
json_import_simple_array_as_one_entry
Closed

JSON: import simple array as a single header entry#1771
jdtournier wants to merge 2 commits intomasterfrom
json_import_simple_array_as_one_entry

Conversation

@jdtournier
Copy link
Member

Apparently converting data via dcm2niix and converting to mif with a JSON import results in the slice timings being represented as multiple separate header entries, rather than the single entry with a comma-separated list that a direct mrconvert call produces. This means a call to dwipreproc fails when used with slice to vol, with error message:

dwipreproc: No slice encoding direction information present; assuming third axis corresponds to slices
Traceback (most recent call last):
  File "/home/finn/Software/mrtrix3/bin/dwipreproc", line 233, in <module>
    app.error('Cannot use slice timing information in image header for slice-to-volume correction: Number of entries (' + len(slice_timing) + ') does not match number of slices (' + dwi_header.size()[2] + ')')
TypeError: cannot concatenate 'str' and 'int' objects

Note there's another fix here for that python error...

This pull request modifies the handling of the import of JSON arrays, so that:

  • if the array contains no sub-arrays, it'll concatenate into a single comma-separated list and insert the information as a single header entry;
  • if the array contains only sub-arrays, it'll concatenate each sub-array into a comma-separated list and insert the information as one header entry per sub-array
  • if it contains a mixture of simple elements and arrays, it'll throw an Exception

That last behaviour might need to be revised depending on whether there are any instances where we would reasonably be expected to handle a mix of elements and arrays. Alternatives are:

  • issue a warning and ignore
  • issue a warning and insert as one entry per element or sub-array.

But I'll admit I don't know enough about BIDS to make an informed decision here. I just expect that if we ever encounter a BIDS entry that consists of such a mix, we'll have trouble interpreting it correctly anyway...

@jdtournier jdtournier added the bug label Oct 17, 2019
@jdtournier jdtournier requested a review from Lestropie October 17, 2019 13:34
@jdtournier jdtournier self-assigned this Oct 17, 2019
@Lestropie
Copy link
Member

if the array contains no sub-arrays, it'll concatenate into a single comma-separated list and insert the information as a single header entry;
if the array contains only sub-arrays, it'll concatenate each sub-array into a comma-separated list

With #1735 (specifically here) and #1738 I found that in order to have the JSON export operate as intended for slice timing information extracted from DICOM, such array data needed to be saved as space-separated rather than comma-separated in the header. If it's comma-separated, the JSON .dump() function generates a list of lists with one entry (demonstration in #1738). What does the JSON data output by dcm2niix look like with respect to #1738?

@jdtournier
Copy link
Member Author

This is all based on on-going discussions with Finn Lennartsson - might be worth involving him in the discussion, but I don't think he has a GitHub account...

In any case, this is what dcm2niix produces for that entry:

"SliceTiming": [
0,
2.7625,
1.745,
0.7275,
3.49,
2.4725,
1.455,
0.4375,
3.2,
2.1825,
1.165,
0.145,
2.9075,
1.89,
0.8725,
3.635,
2.6175,
1.6,
0.5825,
3.345,
2.3275,
1.31,
0.2925,
3.055,
2.035,
1.0175,
0,
2.7625,
1.745,
0.7275,
3.49,
2.4725,
1.455,
0.4375,
3.2,
2.1825,
1.165,
0.145,
2.9075,
1.89,
0.8725,
3.635,
2.6175,
1.6,
0.5825,
3.345,
2.3275,
1.31,
0.2925,
3.055,
2.035,
1.0175 ],

And on the current master branch, this gets inserted as one value per SliceTiming entry using -json_import:

$ mrinfo -property SliceTiming dwi_raw.mif.gz
0
2.7625
1.745
0.7275
3.49
2.4725
1.455
...

Using the direct DICOM import all within MRtrix3, this ends up as a single comma-separated list of values in a single entry:

$ mrinfo -property SliceTiming dwi_raw.mif.gz
0,2.76250005,1.745,0.727500021,3.49000001,2.47250009,1.45500004,0.4375,3.20000005,2.18249989,1.16499996,0.144999996,2.90750003,1.88999999,0.872500002,3.63499999,2.61750007,1.60000002,0.582499981,3.34500003,2.3275001,1.30999994,0.292499989,3.05500007,2.03500009,1.01750004,0,2.76250005,1.745,0.727500021,3.49000001,2.47250009,1.45500004,0.4375,3.20000005,2.18249989,1.16499996,0.144999996,2.90750003,1.88999999,0.872500002,3.63499999,2.61750007,1.60000002,0.582499981,3.34500003,2.3275001,1.30999994,0.292499989,3.05500007,2.03500009,1.01750004

I guess #1735 and #1738 will definitely interact here, but I'm unsure about the details...

@Lestropie
Copy link
Member

I suspect there's a number of gremlins arising from the fact that I hurriedly got the JSON handling working well enough to be able to produce and process data that looked like BIDS, but wasn't cross-checked for compatibility with other packages.

#1735 is a fairly gross deviation from what the data should have been, for which I put a hack into dwipreproc to compensate.

#1738 better demonstrates the fundamental issue: JSON supports multiple data types and hierarchical data, whereas we internally store any non-compulsory header data as a std::map<std::string, std::string>. We therefore need to carefully consider how that conversion should take place in both directions. #1738 does a decent amount of gymnastics to make this work for export in what I think is a sensible way. This issue shows that the import stage needs more careful consideration as well more generally.

In terms of this specific case, one might ask: Given these data contain one entry per volume, and other header entries containing one entry per volume (e.g. dw_scheme) have one header entry (/ row) per volume, should the same be done for SliceTiming, even if it's only a single scalar value per volume? I tend to think that collapsing such data into a single row makes sense (in #1735 I put a bunch of logic gymnastics in place in dwipreproc just in case the data come in in unexpected formats), but that's nevertheless an explicit decision. But the reason I made that first comment is that if you make these changes in order for JSON import of SliceTiming to work as intended, but comma-delimit the string data in the header, then upon JSON export, SliceTiming will come out as a list of lists of floats rather than a list of floats.

Ideally we should have a unit test that imports a pre-prepared JSON file (with as many nasties as we wish to support) to an empty header, exports it, then raw diffs the JSON contents.

@Lestropie
Copy link
Member

Aborting; given magnitude of issues with JSON data handling, will not be resolving such on master, instead relying on imminent tag update that will include changes in #1843.

@Lestropie Lestropie closed this Jan 8, 2020
@jdtournier jdtournier deleted the json_import_simple_array_as_one_entry branch January 30, 2020 17:26
Lestropie added a commit that referenced this pull request Feb 5, 2020
Extracted changes from #1735 in order to apply to dev. In dwifslpreproc, perform a robust parsing of slice timing information from the image header.
During DICOM import, separate slice timings by space rather than comma; although the results of JSON export should be the same following #1771 / #1843, use of space delimiter for vector data is more consistent with other multi-dimensional data handling in image headers.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants