Repository: Project-MONAI/MONAI

Issue:
ITKReader does not read NRRD05 images with 'measurement frame' field
**Describe the bug**
ITKReader throws an error when reading NRRD05 images with the "measurement frame" field. Here's part of the traceback:
```
~/workspace/envs/sono-env/lib/python3.6/site-packages/monai/data/image_reader.py in _get_meta_dict(self, img)
    220             if key.startswith("ITK_original_"):
    221                 continue
--> 222             meta_dict[key] = img_meta_dict[key]
    223         meta_dict["origin"] = np.asarray(img.GetOrigin())
    224         meta_dict["spacing"] = np.asarray(img.GetSpacing())

~/workspace/envs/sono-env/lib/python3.6/site-packages/itk/ITKCommonBasePython.py in __getitem__(self, key)
    887         import itk
    888         obj = self.Get(key)
--> 889         return itk.down_cast(obj).GetMetaDataObjectValue()
    890     def __len__(self):
    891         return self.GetKeys().size()

~/workspace/envs/sono-env/lib/python3.6/site-packages/itkExtras.py in down_cast(obj)
   1198         raise RuntimeError(
   1199             "Can't downcast to a specialization of %s" %
-> 1200             className)
   1201     else:
   1202         return t.cast(obj)

RuntimeError: Can't downcast to a specialization of MetaDataObject
```
**To Reproduce**
Run
```
import numpy as np
import SimpleITK as sitk
from monai.data import ITKReader
from monai.transforms import LoadImage

fn = "tmp.seg.nrrd"
data = np.zeros((3,3,3))
im = sitk.GetImageFromArray(data)
im.SetMetaData("NRRD_measurement frame", "(1,0,0) (0,1,0) (0,0,1)")
sitk.WriteImage(im, fn)

LoadImage(reader=ITKReader())(fn)
```
Commenting out the `SetMetaData` line runs fine.

**Expected behavior**
ITKReader reads NRRD005 files.

**Environment**
```
================================
Printing MONAI config...
================================
MONAI version: 0.4.0
Numpy version: 1.17.4
Pytorch version: 1.6.0.dev20200528+cu101
MONAI flags: HAS_EXT = False, USE_COMPILED = False
MONAI rev id: 0563a4467fa602feca92d91c7f47261868d171a1

Optional dependencies:
Pytorch Ignite version: 0.4.2
Nibabel version: 3.2.1
scikit-image version: 0.17.2
Pillow version: 6.2.1
Tensorboard version: 2.4.0
gdown version: 3.12.2
TorchVision version: 0.7.0.dev20200528+cu101
ITK version: 5.1.2
tqdm version: 4.54.1
lmdb version: NOT INSTALLED or UNKNOWN VERSION.
psutil version: NOT INSTALLED or UNKNOWN VERSION.
```

**Additional context**
Is ignoring the `measurement frame` field like this here an acceptable fix? https://github.com/adamaji/MONAI/commit/13cbe07e5af795f67560189b36a1486d4ce69f0e If so, I can issue the PR.



Discussion:
Hi @thewtex ,

Could you please help take a look at this issue and give a solution?

Thanks in advance.

Hi @thewtex @aylward ,

Could you please help check this issue and provide some solution or guide?

Thanks in advance.

@Nic-Ma looking into the issue, I cannot reproduce it on MONAI `master`,

```
In [1]: import numpy as np
   ...: import SimpleITK as sitk
   ...: from monai.data import ITKReader
   ...: from monai.transforms import LoadImage
---------------------------------------------------------------------------
ImportError                               Traceback (most recent call last)
<ipython-input-1-7f860b729d04> in <module>
      1 import numpy as np
      2 import SimpleITK as sitk
----> 3 from monai.data import ITKReader
      4 from monai.transforms import LoadImage

ImportError: cannot import name 'ITKReader' from 'monai.data' (/home/matt/src/MONAI/monai/data/__init__.py)
```

any pointers to how this has been refactored?
Can you import any other MONAI modules?
Thanks.

I can:

```
import monai
import monai.data
```

but

```
from monai.transforms import LoadImage`
```

fails similarly.

I installed with:

```
pip install -e .
```

Is this not expected to work? Is there a better way? Thanks, @Nic-Ma  
@thewtex Not sure where your error is coming from, but we might end up going down a rabbit hole trying to fix it.

From the [installation instructions](https://docs.monai.io/en/latest/installation.html#installation-guide), maybe you could simply try one of the other methods? Github options 1 or 2 might be good, depending on whether you plan on modifying the source code or not.
yes perhaps run `pip uninstall monai` and/or `python -m pip uninstall monai` before installation, and double check `print(monai.__file__)`
@rijobro @wyli thanks for the pointers. After starting from a clean conda environment, I ran into the same issue with `BUILD_MONAI=1 python setup.py develop`. But, I did a `git pull`, and I think I must have landed on an unlucky commit.  It is working now :unlock: :four_leaf_clover:. Thanks again!
This is addressed in https://github.com/InsightSoftwareConsortium/ITK/pull/2270, and will be available in a few weeks with the release of itk 5.2rc2.
Thanks for looking into this @thewtex !
@adamaji The `itk-5.2rc2` packages can be installed with: `pip install --upgrade --pre itk`. Has this been addressed?
Yes, that build addresses my issue -- thanks! Should a requirements.txt be updated here to close this issue now, or should this be closed once that change is in a stable release?

Edit: For anyone running the "reproduce" section from the original post, it will return an image with `'NRRD_measurement frame': ((666666.0, ...), ...`, which should be fine as I believe it's just the default value when the dims of the measurement frame and image are different https://github.com/InsightSoftwareConsortium/ITK/blob/c2b399eef1248465a848a26231de66a8c9715132/Modules/IO/NRRD/src/itkNrrdImageIO.cxx#L1057 Loading a real image seems to work fine for me.
@adamaji great, thanks for testing!

There will be another ITK release candidate, with the final release cut over the next month.

@wyli @Nic-Ma is it ok to add a dependency in `monai` on the release candidates?
@rijobro 
@thewtex just checking I haven't misunderstood, do you mean setting the itk entry in requirements-dev.txt to use the release candidate?
@rijobro yes, *requirements-dev.txt* and *setup.cfg*.
My gut feeling is not to have pre-releases in our `requirements.txt` but I don't mind if others disagree.

My suggestion would be to display a warning until the new ITK comes out, something along the lines of:

```python
if filename[-5:].lower() == ".nrrd" and itk.__version__ < something (5.2?):
    warnings.warn("Correct reading of NRRD files requires ITK >= 5.2: `pip install --upgrade --pre itk`")
```
>My gut feeling is not to have pre-releases in our requirements.txt

I think I agree with this, I can handle making the suggested changes sometime today or tomorrow if folks agree here
Is there a way to create a branch that uses the release candidate, so it is
easy for people to test?    It would be nice to be able to point people to
the branch when they are having I/O issues.

s

On Tue, Feb 16, 2021 at 10:36 AM adam aji <notifications@github.com> wrote:

> My gut feeling is not to have pre-releases in our requirements.txt
>
> I think I agree with this, I can handle making the suggested changes
> sometime today or tomorrow if folks agree here
>
> —
> You are receiving this because you were mentioned.
> Reply to this email directly, view it on GitHub
> <https://github.com/Project-MONAI/MONAI/issues/1451#issuecomment-779916948>,
> or unsubscribe
> <https://github.com/notifications/unsubscribe-auth/AACEJLY4JUI5VXEDELC5AXTS7KGIZANCNFSM4WC3GZ5A>
> .
>


-- 
Stephen R. Aylward, Ph.D.
Senior Director of Strategic Initiatives
---
Kitware: *Delivering innovative, open source, scientific software.*

@aylward I'm not sure of a clean way to go about that -- what's the difference between pointing somebody to run `pip install --upgrade --pre itk` versus checking out a branch of Monai which uses release candidate libraries? Some questions I would have: is the branch ITK specific? Would we then want branches for the other libraries? Should we point people to using the release candidates of certain IO libraries if we're not sure that the release candidate fixes the issue? 
A branch would just be a temporary solution until the official ITK release
- I agree that it is far from ideal, but I'm searching for a compromise.

The motivation is that MONAI is getting a large number of questions
regarding I/O, and in particular, this version of ITK (RC2 and beyond) is
intended to address those DICOM, NIFTI, and image coordinate ordering
issues specifically for MONAI IO.    We should perhaps try to involve as
many people as possible in the nightly testing of ITK+MONAI, and that is
arguably best accomplished by lowering the barriers to its use.

Perhaps telling folks to pip install is the better solution, but if they
then suggest a bug/update, it can perhaps become problematic
regarding where to commit a fix to MONAI that will allow the community to
keep moving forward with evaluating RC2 (e.g., if that fix isn't compatible
with the prior version of ITK that MONAI uses).

In general, I fully agree that using an RC from a 3rd party app is bad
practice, but since this ITK RC is so focused on fixing issues with MONAI,
perhaps an exception / compromise of some sort needs to be made so that
progress can be made in both toolkits simultaneously.

Again - just my opinion.   We could also simply hold off on testing
MONAI+ITK, but then bugs found in that release of ITK IO would not be fixed
until its next release in a few months, and so MONAI would continue to have
IO issues...definitely not a rapid development cycle...

Thanks,
Stephen

On Wed, Feb 17, 2021 at 10:09 AM adam aji <notifications@github.com> wrote:

> @aylward <https://github.com/aylward> I'm not sure of a clean way to go
> about that -- what's the difference between pointing somebody to run pip
> install --upgrade --pre itk versus checking out a branch of Monai which
> uses release candidate libraries? Some questions I would have: is the
> branch ITK specific? Would we then want branches for the other libraries?
> Should we point people to using the release candidates of certain IO
> libraries if we're not sure that the release candidate fixes the issue?
>
> —
> You are receiving this because you were mentioned.
> Reply to this email directly, view it on GitHub
> <https://github.com/Project-MONAI/MONAI/issues/1451#issuecomment-780620796>,
> or unsubscribe
> <https://github.com/notifications/unsubscribe-auth/AACEJLYWZN5JUD34IBWQSK3S7PL2FANCNFSM4WC3GZ5A>
> .
>


-- 
Stephen R. Aylward, Ph.D.
Senior Director of Strategic Initiatives
---
Kitware: *Delivering innovative, open source, scientific software.*

Hmm I see, that makes sense, I wasn't aware of the situation. In that case I think I can agree that a separate temporary branch to facilitate joint ITK and Monai development would be helpful, but I'll defer to main contributors here (@rijobro or @wyli ?) Maybe this should be pulled off into a separate issue?

For this issue specifically, I can incorporate the warning as suggested by https://github.com/Project-MONAI/MONAI/issues/1451#issuecomment-779746129 since this is a known issue and is compatible with prior versions of ITK?
As I said earlier, I don't have particularly strong feelings either way. I suppose if we were to put in the release candidate into `requirements.txt` etc., this would only be on the master branch, and would never be in a release tag. Assuming, of course, that the ITK release happens before MONAI's next release -- about a month away. 

Any other opinions? (Might be slightly quiet this week because of Chinese new year.) @ericspod @wyli @Nic-Ma 
I think the solution in https://github.com/Project-MONAI/MONAI/issues/1451#issuecomment-779746129 looks nice for now. if we frequently see this type of collaboration in the future we should come up with a systematic approach...

Resolving patch:
diff --git a/monai/data/image_reader.py b/monai/data/image_reader.py
--- a/monai/data/image_reader.py
+++ b/monai/data/image_reader.py
@@ -10,6 +10,7 @@
 # limitations under the License.
 
 import os
+import warnings
 from abc import ABC, abstractmethod
 from typing import TYPE_CHECKING, Any, Callable, Dict, List, Optional, Sequence, Tuple, Union
 
@@ -219,6 +220,16 @@ def _get_meta_dict(self, img) -> Dict:
             # ignore deprecated, legacy members that cause issues
             if key.startswith("ITK_original_"):
                 continue
+            if (
+                key == "NRRD_measurement frame"
+                and int(itk.Version.GetITKMajorVersion()) == 5
+                and int(itk.Version.GetITKMinorVersion()) < 2
+            ):
+                warnings.warn(
+                    "Ignoring 'measurement frame' field. "
+                    "Correct reading of NRRD05 files requires ITK >= 5.2: `pip install --upgrade --pre itk`"
+                )
+                continue
             meta_dict[key] = img_meta_dict[key]
         meta_dict["origin"] = np.asarray(img.GetOrigin())
         meta_dict["spacing"] = np.asarray(img.GetSpacing())


Test patch: