Repository: DIAGNijmegen/rse-panimg (language: python)

Issue:
Unknown colour space exception
We do not have any source images for this but on Grand Challenge we often see this error:

```
File "/opt/poetry/.venv/lib/python3.8/site-packages/panimg/panimg.py", line 40, in convert
_convert_directory(
File "/opt/poetry/.venv/lib/python3.8/site-packages/panimg/panimg.py", line 116, in _convert_directory
builder_result = _build_files(
File "/opt/poetry/.venv/lib/python3.8/site-packages/panimg/panimg.py", line 148, in _build_files
for result in builder(files=files):
File "/opt/poetry/.venv/lib/python3.8/site-packages/panimg/image_builders/metaio_mhd_mha.py", line 110, in image_builder_mhd
yield SimpleITKImage(
File "pydantic/main.py", line 341, in pydantic.main.BaseModel.__init__
raise validation_error
pydantic.error_wrappers.ValidationError: 1 validation error for SimpleITKImage
image
Unknown color space for MetaIO image: 112 (type=value_error)
```


Discussion / hints:
I have emailed the user who created this file to try and find out more information.
I suspect it is a red herring for a multi-component image that has more than 3 components for a voxel. This is generally the case if a format has coded TIME in the components instead of dimensions.
Maybe! The user has not got back to me so we're none the wiser.
Hi,
I met this problem when I save the output as a 4D mha (the shape is [D, W, H, C], C =45) file, where I want to save the segmentation of 45 organs of a patient into a 4D file but some organs have overlap. I can use SimleITK, ITK-SNAP, 3D Slicer to read or visualize the 4D mha file, but this package throughout an error about color space. I wonder if this package just supports C=1/3/4, according to this [line](https://github.com/DIAGNijmegen/rse-panimg/blob/2c83742722439f66569c3b4bd553fd2e06e4472d/panimg/models.py#L39?). I think the 4D datatype can deal with the overlap segmentation problem well, but I don't know how to deal with the color space error! Could you give us some advice?
Best,
Xiangde Luo.
@Luoxd1996 , reference a quick solution on [this gist](https://gist.github.com/chrisvanrun/fc83ae8b2c8c3415b917242c83ff795d).


The likely cause of seeing this error is because the naive way of creating 4D images would be something along the lines of:
```Python
    output_arr = np.ones((100, 200, 300, 45), dtype=np.uint8)
    image = SimpleITK.GetImageFromArray(output_arr)
    SimpleITK.WriteImage(image, "output.mha", True)
```

However, this will create a 3D vector image and not a 4D image. Subtle difference. Adding `isVector=False` as keyword arg to `GetImageFromArray()` does produce a 4D image but for some unclear reason, it reverses the `x, y, z, t` order. 

To clarify the difference between a 3D vector image and a 4D image, this is getting the first voxel value:
```Python
three_d_vector_image[0, 0, 0][0]
four_d_image[0, 0, 0, 0]
```
@chrisvanrun, thanks a lot! I got it!!!
Best.
@Luoxd1996 It is not acceptable to multi-channel requests, you already had a support thread going which I told you at 11am I was working on. Do not create extra work for my team like this, it slows everyone down.

Fix patch:
diff --git a/HISTORY.md b/HISTORY.md
index da49382..4d900b8 100644
--- a/HISTORY.md
+++ b/HISTORY.md
@@ -1,5 +1,11 @@
 # History
 
+## 0.13.0 (2023-07-17)
+
+* Increased maximum number of segments to 64
+* Required segmentations to be of type Int8 or UInt8
+* Reduced memory usage
+
 ## 0.12.0 (2023-06-02)
 
 * Add support for conversion of DICOM-WSI files to TIFF
diff --git a/panimg/image_builders/metaio_utils.py b/panimg/image_builders/metaio_utils.py
index 729c864..07aba09 100644
--- a/panimg/image_builders/metaio_utils.py
+++ b/panimg/image_builders/metaio_utils.py
@@ -278,6 +278,12 @@ def load_sitk_image(
 
     headers = validate_and_clean_additional_mh_headers(reader=reader)
 
+    if reader.GetNumberOfComponents() > 4:
+        raise ValidationError(
+            "Images with more than 4 channels not supported. "
+            "For 4D data please use the 4th dimension instead."
+        )
+
     # Header has been validated, read the pixel data
     if reader.GetDimension() in (2, 3, 4):
         sitk_image = reader.Execute()
diff --git a/panimg/models.py b/panimg/models.py
index bddc6dd..9b7fceb 100644
--- a/panimg/models.py
+++ b/panimg/models.py
@@ -203,10 +203,6 @@ def depth(self) -> Optional[int]:
 
         return depth or None
 
-    @property
-    def is_4d(self):
-        return len(self.image.GetSize()) == 4
-
     @staticmethod
     def _extract_first_float(value: str) -> float:
         if value.startswith("["):
@@ -273,7 +269,7 @@ def segments(self) -> Optional[FrozenSet[int]]:
 
         im_arr = GetArrayViewFromImage(self.image)
 
-        if self.is_4d:
+        if self.image.GetDimension() == 4:
             segments = set()
             n_volumes = self.image.GetSize()[3]
 
diff --git a/pyproject.toml b/pyproject.toml
index 09fee59..abf1d76 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
 [tool.poetry]
 name = "panimg"
-version = "0.12.0"
+version = "0.13.0"
 description = "Conversion of medical images to MHA and TIFF."
 license = "Apache-2.0"
 authors = ["James Meakin <panimg@jmsmkn.com>"]


Test patch:
diff --git a/tests/resources/channels/5_1_1_1_invalid.mha b/tests/resources/channels/5_1_1_1_invalid.mha
new file mode 100644
index 0000000..f67d54d
Binary files /dev/null and b/tests/resources/channels/5_1_1_1_invalid.mha differ
diff --git a/tests/test_models.py b/tests/test_models.py
index b376ecd..2b96bec 100644
--- a/tests/test_models.py
+++ b/tests/test_models.py
@@ -225,3 +225,23 @@ def test_segments(
 
     image = result.new_images.pop()
     assert image.segments == segments
+
+
+def test_invalid_4d(tmp_path_factory):
+    file = RESOURCE_PATH / "channels" / "5_1_1_1_invalid.mha"
+    result = _build_files(
+        builder=image_builders.image_builder_mhd,
+        files={file},
+        output_directory=tmp_path_factory.mktemp("output"),
+    )
+
+    assert result.consumed_files == set()
+    assert len(result.new_images) == 0
+    assert result.file_errors == {
+        file: [
+            (
+                "Mhd image builder: Images with more than 4 channels not supported. "
+                "For 4D data please use the 4th dimension instead."
+            )
+        ]
+    }