Repository: mne-tools/mne-bids

Issue:
Validator warning for write_anat with no acquisition
I get the following warning from the validator during write_anat without passing an acquisition parameter:

File Path: NIfTI file's header field for unit information for x, y, z, and t dimensions empty or too short

	Type:		Warning
	File:		sub-sample_ses-01_T1w.nii.gz
	Location:		data_BIDS/sub-sample/ses-01/anat/sub-sample_ses-01_T1w.nii.gz
	Reason:		NIfTI file's header field for unit information for x, y, z, and t dimensions empty or too short

======================================================

It's not clear if the acquisition parameter would fix this, maybe someone can suggest how to get these parameters from an MGH file. 


Discussion:
Also, when I use mri_convert to change the MGH file to NIfTI, the dataset is successfully validated. This makes sense to me and per @jasmainak this would suggest this is an MNE-BIDS issue.
can you provide some data to replicate the pb?

>

Sure:

from mne_bids import write_anat
from mne import read_trans

subject = 'sample'
session = '01'
t1f = 'MNE-sample-data/subjects/sample/mri/T1.mgz'
raw = 'MNE-sample-data/MEG/sample/sample_audvis-raw.fif'
trans = 'MNE-sample-data/MEG/sample/sample_audvis_raw-trans.fif'
write_anat('data_BIDS', subject, t1f, session=session,
                    raw=raw, trans=read_trans(transf), overwrite=True)

That worked but raised the validator error for me.

Sorry 

```py
from mne_bids import write_anat

from mne.datasets import sample
from mne import read_trans
from mne.io import read_raw_fif

subject = 'sample'
session = '01'

data_path = sample.data_path()
t1f = data_path + '/subjects/sample/mri/T1.mgz'
raw = read_raw_fif(data_path + '/MEG/sample/sample_audvis_raw.fif')
transf = data_path + '/MEG/sample/sample_audvis_raw-trans.fif'
write_anat('data_BIDS', subject, t1f, session=session,
                  raw=raw, trans=read_trans(transf), overwrite=True)
```
I can confirm the problem. I edited your comment @alexrockhill 

Here is the validator output

```sh
(mne) mainak@mainak-ThinkPad-W540 ~/Desktop $ bids-validator data_BIDS/
	1: [ERR] The compulsory file /dataset_description.json is missing. See Section 03 (Modality agnostic files) of the BIDS specification. (code: 57 - DATASET_DESCRIPTION_JSON_MISSING)

	1: [WARN] Nifti file's header field for unit information for x, y, z, and t dimensions empty or too short (code: 41 - NIFTI_UNIT)
		./sub-sample/ses-01/anat/sub-sample_ses-01_T1w.nii.gz

	2: [WARN] The recommended file /README is missing. See Section 03 (Modality agnostic files) of the BIDS specification. (code: 101 - README_FILE_MISSING)


        Summary:               Available Tasks:        Available Modalities: 
        2 Files, 4.77MB                                T1w                   
        1 - Subject                                                          
        1 - Session                                                          


If you have any questions please post on https://neurostars.org/tags/bids
```
can you have a look @jasmainak ?

>


Resolving patch:
diff --git a/examples/convert_mri_and_trans.py b/examples/convert_mri_and_trans.py
--- a/examples/convert_mri_and_trans.py
+++ b/examples/convert_mri_and_trans.py
@@ -39,7 +39,9 @@
 
 import numpy as np
 import matplotlib.pyplot as plt
+
 from nilearn.plotting import plot_anat
+
 import mne
 from mne.datasets import sample
 from mne.source_space import head_to_mri
@@ -148,6 +150,7 @@
     plot_anat(t1_mgh_fname, axes=axs[point_idx],
               cut_coords=mri_pos[point_idx, :],
               title=label)
+plt.show()
 
 
 ###############################################################################
diff --git a/mne_bids/write.py b/mne_bids/write.py
--- a/mne_bids/write.py
+++ b/mne_bids/write.py
@@ -1015,17 +1015,22 @@ def write_anat(bids_root, subject, t1w, session=None, acquisition=None,
         raise ValueError('`t1w` must be a path to a T1 weighted MRI data file '
                          ', or a nibabel image object, but it is of type '
                          '"{}"'.format(type(t1w)))
-    t1_mgh = nib.MGHImage(t1w.dataobj, t1w.affine)
 
+    t1w = nib.Nifti1Image(t1w.dataobj, t1w.affine)
+    # XYZT_UNITS = NIFT_UNITS_MM (10 in binary or 2 in decimal)
+    # seems to be the default for Nifti files
+    # https://nifti.nimh.nih.gov/nifti-1/documentation/nifti1fields/nifti1fields_pages/xyzt_units.html
+    if t1w.header['xyzt_units'] == 0:
+        t1w.header['xyzt_units'] = np.array(10, dtype='uint8')
     # Now give the NIfTI file a BIDS name and write it to the BIDS location
     t1w_basename = make_bids_basename(subject=subject, session=session,
                                       acquisition=acquisition, prefix=anat_dir,
                                       suffix='T1w.nii.gz')
     if not op.exists(t1w_basename):
-        nib.save(t1_mgh, t1w_basename)
+        nib.save(t1w, t1w_basename)
     elif overwrite:
         os.remove(t1w_basename)
-        nib.save(t1_mgh, t1w_basename)
+        nib.save(t1w, t1w_basename)
     else:
         raise IOError('Wanted to write a file but it already exists and '
                       '`overwrite` is set to False. File: "{}"'
@@ -1051,6 +1056,7 @@ def write_anat(bids_root, subject, t1w, session=None, acquisition=None,
         mri_landmarks = apply_trans(trans, meg_landmarks, move=True) * 1e3
 
         # Get landmarks in voxel space, using the mgh version of our T1 data
+        t1_mgh = nib.MGHImage(t1w.dataobj, t1w.affine)
         vox2ras_tkr = t1_mgh.header.get_vox2ras_tkr()
         ras2vox_tkr = np.linalg.inv(vox2ras_tkr)
         mri_landmarks = apply_trans(ras2vox_tkr, mri_landmarks)  # in vox


Test patch:
diff --git a/mne_bids/tests/test_write.py b/mne_bids/tests/test_write.py
--- a/mne_bids/tests/test_write.py
+++ b/mne_bids/tests/test_write.py
@@ -48,6 +48,7 @@
     task=task)
 bids_basename_minimal = make_bids_basename(subject=subject_id, task=task)
 
+
 # WINDOWS issues:
 # the bids-validator development version does not work properly on Windows as
 # of 2019-06-25 --> https://github.com/bids-standard/bids-validator/issues/790
@@ -56,16 +57,26 @@
 # using the stable bids-validator and make a direct call of bids-validator
 # also: for windows, shell = True is needed to call npm, bids-validator etc.
 # see: https://stackoverflow.com/q/28891053/5201771
-shell = False
-bids_validator_exe = ['bids-validator']
-if platform.system() == 'Windows':
-    shell = True
-    exe = os.getenv('VALIDATOR_EXECUTABLE', 'n/a')
-    if 'VALIDATOR_EXECUTABLE' != 'n/a':
-        bids_validator_exe = ['node', exe]
-
-
-def test_fif():
+@pytest.fixture(scope="session")
+def _bids_validate():
+    """Fixture to run BIDS validator."""
+    shell = False
+    bids_validator_exe = ['bids-validator', '--config.error=41',
+                          '--config.error=41']
+    if platform.system() == 'Windows':
+        shell = True
+        exe = os.getenv('VALIDATOR_EXECUTABLE', 'n/a')
+        if 'VALIDATOR_EXECUTABLE' != 'n/a':
+            bids_validator_exe = ['node', exe]
+
+    def _validate(output_path):
+        cmd = bids_validator_exe + [output_path]
+        run_subprocess(cmd, shell=shell)
+
+    return _validate
+
+
+def test_fif(_bids_validate):
     """Test functionality of the write_raw_bids conversion for fif."""
     output_path = _TempDir()
     data_path = testing.data_path()
@@ -98,8 +109,7 @@ def test_fif():
     with pytest.warns(UserWarning, match='No events found or provided.'):
         write_raw_bids(raw, bids_basename, output_path, overwrite=False)
 
-    cmd = bids_validator_exe + [output_path]
-    run_subprocess(cmd, shell=shell)
+    _bids_validate(output_path)
 
     # write the same data but pretend it is empty room data:
     raw = mne.io.read_raw_fif(raw_fname)
@@ -174,8 +184,7 @@ def test_fif():
     with pytest.raises(ValueError, match='raw.filenames is missing'):
         write_raw_bids(raw, bids_basename2, output_path)
 
-    cmd = bids_validator_exe + [output_path]
-    run_subprocess(cmd, shell=shell)
+    _bids_validate(output_path)
 
     assert op.exists(op.join(output_path, 'participants.tsv'))
 
@@ -203,7 +212,7 @@ def test_fif():
         assert 'part' in FILE
 
 
-def test_kit():
+def test_kit(_bids_validate):
     """Test functionality of the write_raw_bids conversion for KIT data."""
     output_path = _TempDir()
     data_path = op.join(base_path, 'kit', 'tests', 'data')
@@ -224,8 +233,8 @@ def test_kit():
     write_raw_bids(raw, kit_bids_basename, output_path,
                    events_data=events_fname,
                    event_id=event_id, overwrite=False)
-    cmd = bids_validator_exe + [output_path]
-    run_subprocess(cmd, shell=shell)
+
+    _bids_validate(output_path)
     assert op.exists(op.join(output_path, 'participants.tsv'))
 
     read_raw_bids(kit_bids_basename + '_meg.sqd', output_path)
@@ -268,8 +277,8 @@ def test_kit():
                    kit_bids_basename.replace('sub-01', 'sub-%s' % subject_id2),
                    output_path, events_data=events_fname, event_id=event_id,
                    overwrite=False)
-    cmd = bids_validator_exe + [output_path]
-    run_subprocess(cmd, shell=shell)
+
+    _bids_validate(output_path)
     # ensure the marker files are renamed correctly
     marker_fname = make_bids_basename(
         subject=subject_id2, session=session_id, task=task, run=run,
@@ -295,7 +304,7 @@ def test_kit():
             overwrite=True)
 
 
-def test_ctf():
+def test_ctf(_bids_validate):
     """Test functionality of the write_raw_bids conversion for CTF data."""
     output_path = _TempDir()
     data_path = op.join(testing.data_path(download=False), 'CTF')
@@ -305,8 +314,7 @@ def test_ctf():
     with pytest.warns(UserWarning, match='No line frequency'):
         write_raw_bids(raw, bids_basename, output_path=output_path)
 
-    cmd = bids_validator_exe + [output_path]
-    run_subprocess(cmd, shell=shell)
+    _bids_validate(output_path)
     with pytest.warns(UserWarning, match='Did not find any events'):
         raw = read_raw_bids(bids_basename + '_meg.ds', output_path)
 
@@ -317,7 +325,7 @@ def test_ctf():
     assert op.exists(op.join(output_path, 'participants.tsv'))
 
 
-def test_bti():
+def test_bti(_bids_validate):
     """Test functionality of the write_raw_bids conversion for BTi data."""
     output_path = _TempDir()
     data_path = op.join(base_path, 'bti', 'tests', 'data')
@@ -331,9 +339,7 @@ def test_bti():
     write_raw_bids(raw, bids_basename, output_path, verbose=True)
 
     assert op.exists(op.join(output_path, 'participants.tsv'))
-
-    cmd = bids_validator_exe + [output_path]
-    run_subprocess(cmd, shell=shell)
+    _bids_validate(output_path)
 
     raw = read_raw_bids(bids_basename + '_meg', output_path)
 
@@ -342,7 +348,7 @@ def test_bti():
 # see: https://github.com/mne-tools/mne-python/pull/6558
 @pytest.mark.skipif(LooseVersion(mne.__version__) < LooseVersion('0.19'),
                     reason="requires mne 0.19.dev0 or higher")
-def test_vhdr():
+def test_vhdr(_bids_validate):
     """Test write_raw_bids conversion for BrainVision data."""
     output_path = _TempDir()
     data_path = op.join(base_path, 'brainvision', 'tests', 'data')
@@ -357,9 +363,7 @@ def test_vhdr():
 
     # write with injected bad channels
     write_raw_bids(raw, bids_basename_minimal, output_path, overwrite=False)
-
-    cmd = bids_validator_exe + [output_path]
-    run_subprocess(cmd, shell=shell)
+    _bids_validate(output_path)
 
     # read and also get the bad channels
     raw = read_raw_bids(bids_basename_minimal + '_eeg.vhdr', output_path)
@@ -394,12 +398,10 @@ def test_vhdr():
                            for i in mne.pick_types(raw.info, eeg=True)})
     output_path = _TempDir()
     write_raw_bids(raw, bids_basename, output_path, overwrite=False)
+    _bids_validate(output_path)
 
-    cmd = bids_validator_exe + [output_path]
-    run_subprocess(cmd, shell=shell)
 
-
-def test_edf():
+def test_edf(_bids_validate):
     """Test write_raw_bids conversion for European Data Format data."""
     output_path = _TempDir()
     data_path = op.join(testing.data_path(), 'EDF')
@@ -426,9 +428,7 @@ def test_edf():
 
     bids_fname = bids_basename.replace('run-01', 'run-%s' % run2)
     write_raw_bids(raw, bids_fname, output_path, overwrite=True)
-
-    cmd = bids_validator_exe + [output_path]
-    run_subprocess(cmd, shell=shell)
+    _bids_validate(output_path)
 
     # ensure there is an EMG channel in the channels.tsv:
     channels_tsv = make_bids_basename(
@@ -451,12 +451,10 @@ def test_edf():
                            for i in mne.pick_types(raw.info, eeg=True)})
     output_path = _TempDir()
     write_raw_bids(raw, bids_basename, output_path)
+    _bids_validate(output_path)
 
-    cmd = bids_validator_exe + [output_path]
-    run_subprocess(cmd, shell=shell)
 
-
-def test_bdf():
+def test_bdf(_bids_validate):
     """Test write_raw_bids conversion for Biosemi data."""
     output_path = _TempDir()
     data_path = op.join(base_path, 'edf', 'tests', 'data')
@@ -465,9 +463,7 @@ def test_bdf():
     raw = mne.io.read_raw_bdf(raw_fname)
     with pytest.warns(UserWarning, match='No line frequency found'):
         write_raw_bids(raw, bids_basename, output_path, overwrite=False)
-
-    cmd = bids_validator_exe + [output_path]
-    run_subprocess(cmd, shell=shell)
+    _bids_validate(output_path)
 
     # Test also the reading of channel types from channels.tsv
     # the first channel in the raw data is not MISC right now
@@ -495,7 +491,7 @@ def test_bdf():
         write_raw_bids(raw, bids_basename, output_path)
 
 
-def test_set():
+def test_set(_bids_validate):
     """Test write_raw_bids conversion for EEGLAB data."""
     # standalone .set file
     output_path = _TempDir()
@@ -522,9 +518,7 @@ def test_set():
     with pytest.raises(FileExistsError, match="already exists"):  # noqa: F821
         write_raw_bids(raw, bids_basename, output_path=output_path,
                        overwrite=False)
-
-    cmd = bids_validator_exe + [output_path]
-    run_subprocess(cmd, shell=shell)
+    _bids_validate(output_path)
 
     # check events.tsv is written
     # XXX: only from 0.18 onwards because events_from_annotations
@@ -541,13 +535,11 @@ def test_set():
                            for i in mne.pick_types(raw.info, eeg=True)})
     output_path = _TempDir()
     write_raw_bids(raw, bids_basename, output_path)
-
-    cmd = bids_validator_exe + [output_path]
-    run_subprocess(cmd, shell=shell)
+    _bids_validate(output_path)
 
 
 @requires_nibabel()
-def test_write_anat():
+def test_write_anat(_bids_validate):
     """Test writing anatomical data."""
     # Get the MNE testing sample data
     output_path = _TempDir()
@@ -574,10 +566,7 @@ def test_write_anat():
 
     anat_dir = write_anat(output_path, subject_id, t1w_mgh, session_id, acq,
                           raw=raw, trans=trans, verbose=True)
-
-    # Validate BIDS
-    cmd = bids_validator_exe + [output_path]
-    run_subprocess(cmd, shell=shell)
+    _bids_validate(output_path)
 
     # Validate that files are as expected
     t1w_json_path = op.join(anat_dir, 'sub-01_ses-01_acq-01_T1w.json')