oncoray / mirp

Medical Image Radiomics Processor
https://oncoray.github.io/mirp/
European Union Public License 1.2
51 stars 14 forks source link

Release version 2.3.0 #98

Closed alexzwanenburg closed 2 weeks ago

alexzwanenburg commented 2 weeks ago
alexzwanenburg commented 2 weeks ago

Current errors (with pydicom version 3.0.0):

FAILED        [ 16%]
test\dicom_mr_adc_test.py:9 (test_basic_adc_mr_feature_extraction)
def test_basic_adc_mr_feature_extraction():
        # Multi-frame ADC image
>       data = extract_features_and_images(
            write_features=False,
            export_features=True,
            write_images=False,
            export_images=True,
            image_export_format="native",
            image=os.path.join(CURRENT_DIR, "data", "adc_images_pm_dicom4qi", "data_1", "image.dcm"),
            base_feature_families="statistics"
        )

dicom_mr_adc_test.py:12: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
..\mirp\extract_features_and_images.py:373: in extract_features_and_images
    results = [workflow.standard_extraction(image_export_format=image_export_format) for workflow in workflows]
..\mirp\extract_features_and_images.py:373: in <listcomp>
    results = [workflow.standard_extraction(image_export_format=image_export_format) for workflow in workflows]
..\mirp\_workflows\standardWorkflow.py:330: in standard_extraction
    for image, masks in self.standard_image_processing():
..\mirp\_workflows\standardWorkflow.py:85: in standard_image_processing
    image, masks = read_image_and_masks(
..\mirp\_data_import\read_data.py:28: in read_image_and_masks
    image_out = image.to_object(**kwargs).promote()
..\mirp\_data_import\generic_file.py:780: in to_object
    self.load_data(**kwargs)
..\mirp\_data_import\dicom_multi_frame.py:157: in load_data
    image_data = self.image_metadata.pixel_array.astype(np.float32)
..\..\..\..\micromamba\envs\mirp\Lib\site-packages\pydicom\dataset.py:918: in __getattr__
    return object.__getattribute__(self, name)
..\..\..\..\micromamba\envs\mirp\Lib\site-packages\pydicom\dataset.py:2193: in pixel_array
    self.convert_pixel_data()
..\..\..\..\micromamba\envs\mirp\Lib\site-packages\pydicom\dataset.py:1726: in convert_pixel_data
    self._pixel_array = pixel_array(self, **opts)
..\..\..\..\micromamba\envs\mirp\Lib\site-packages\pydicom\pixels\utils.py:1430: in pixel_array
    return decoder.as_array(
..\..\..\..\micromamba\envs\mirp\Lib\site-packages\pydicom\pixels\decoders\base.py:989: in as_array
    runner.validate()
..\..\..\..\micromamba\envs\mirp\Lib\site-packages\pydicom\pixels\decoders\base.py:752: in validate
    self._validate_options()
..\..\..\..\micromamba\envs\mirp\Lib\site-packages\pydicom\pixels\decoders\base.py:821: in _validate_options
    super()._validate_options()
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <pydicom.pixels.decoders.base.DecodeRunner object at 0x0000024760D63210>

    def _validate_options(self) -> None:
        """Validate the supplied options to ensure they meet requirements."""
        prefix = "Missing required element: (0028"
        if self._opts.get("bits_allocated") is None:
            raise AttributeError(f"{prefix},0100) 'Bits Allocated'")

        if not 1 <= self.bits_allocated <= 64 or (
            self.bits_allocated != 1 and self.bits_allocated % 8
        ):
            raise ValueError(
                f"A (0028,0100) 'Bits Allocated' value of '{self.bits_allocated}' "
                "is invalid, it must be 1 or a multiple of 8 and in the range (1, 64)"
            )

        if self._opts.get("bits_stored") is None:
>           raise AttributeError(f"{prefix},0101) 'Bits Stored'")
E           AttributeError: Missing required element: (0028,0101) 'Bits Stored'

..\..\..\..\micromamba\envs\mirp\Lib\site-packages\pydicom\pixels\common.py:576: AttributeError
FAILED [ 94%]
test\read_image_and_mask_test.py:375 (test_read_dicom_image_and_mask_modality_specific)
@pytest.mark.ci
    def test_read_dicom_image_and_mask_modality_specific():
        # Read CT image.
        image_list = import_image_and_mask(
            image=os.path.join(CURRENT_DIR, "data", "sts_images", "STS_001", "CT", "dicom", "image"),
            mask=os.path.join(CURRENT_DIR, "data", "sts_images", "STS_001", "CT", "dicom", "mask", "RS.dcm")
        )

        image, roi_list = read_image_and_masks(image=image_list[0])
        assert isinstance(image, CTImage)
        assert len(roi_list) == 1
        assert all(isinstance(roi, BaseMask) for roi in roi_list)
        assert roi_list[0].roi_name == "GTV_Mass_CT"

        # Read PET image.
        image_list = import_image_and_mask(
            image=os.path.join(CURRENT_DIR, "data", "sts_images", "STS_001", "PET", "dicom", "image"),
            mask=os.path.join(CURRENT_DIR, "data", "sts_images", "STS_001", "PET", "dicom", "mask", "RS.dcm")
        )

        image, roi_list = read_image_and_masks(image=image_list[0])
        assert isinstance(image, PETImage)
        assert len(roi_list) == 1
        assert all(isinstance(roi, BaseMask) for roi in roi_list)
        assert roi_list[0].roi_name == "GTV_Mass_PET"

        # Read T1-weighted MR image.
        image_list = import_image_and_mask(
            image=os.path.join(CURRENT_DIR, "data", "sts_images", "STS_001", "MR_T1", "dicom", "image"),
            mask=os.path.join(CURRENT_DIR, "data", "sts_images", "STS_001", "MR_T1", "dicom", "mask", "RS.dcm")
        )

        image, roi_list = read_image_and_masks(image=image_list[0])
        assert isinstance(image, MRImage)
        assert len(roi_list) == 1
        assert all(isinstance(roi, BaseMask) for roi in roi_list)
        assert roi_list[0].roi_name == "GTV_Mass_MR_T1"

        # Read ADC multi-frame image -- note that we don't have a mask for this dataset.
        image_list = import_image_and_mask(
            image=os.path.join(CURRENT_DIR, "data", "adc_images_pm_dicom4qi", "data_1", "image.dcm")
        )

>       image, roi_list = read_image_and_masks(image_list[0])

read_image_and_mask_test.py:419: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
..\mirp\_data_import\read_data.py:28: in read_image_and_masks
    image_out = image.to_object(**kwargs).promote()
..\mirp\_data_import\generic_file.py:780: in to_object
    self.load_data(**kwargs)
..\mirp\_data_import\dicom_multi_frame.py:157: in load_data
    image_data = self.image_metadata.pixel_array.astype(np.float32)
..\..\..\..\micromamba\envs\mirp\Lib\site-packages\pydicom\dataset.py:918: in __getattr__
    return object.__getattribute__(self, name)
..\..\..\..\micromamba\envs\mirp\Lib\site-packages\pydicom\dataset.py:2193: in pixel_array
    self.convert_pixel_data()
..\..\..\..\micromamba\envs\mirp\Lib\site-packages\pydicom\dataset.py:1726: in convert_pixel_data
    self._pixel_array = pixel_array(self, **opts)
..\..\..\..\micromamba\envs\mirp\Lib\site-packages\pydicom\pixels\utils.py:1430: in pixel_array
    return decoder.as_array(
..\..\..\..\micromamba\envs\mirp\Lib\site-packages\pydicom\pixels\decoders\base.py:989: in as_array
    runner.validate()
..\..\..\..\micromamba\envs\mirp\Lib\site-packages\pydicom\pixels\decoders\base.py:752: in validate
    self._validate_options()
..\..\..\..\micromamba\envs\mirp\Lib\site-packages\pydicom\pixels\decoders\base.py:821: in _validate_options
    super()._validate_options()
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <pydicom.pixels.decoders.base.DecodeRunner object at 0x0000024761955BD0>

    def _validate_options(self) -> None:
        """Validate the supplied options to ensure they meet requirements."""
        prefix = "Missing required element: (0028"
        if self._opts.get("bits_allocated") is None:
            raise AttributeError(f"{prefix},0100) 'Bits Allocated'")

        if not 1 <= self.bits_allocated <= 64 or (
            self.bits_allocated != 1 and self.bits_allocated % 8
        ):
            raise ValueError(
                f"A (0028,0100) 'Bits Allocated' value of '{self.bits_allocated}' "
                "is invalid, it must be 1 or a multiple of 8 and in the range (1, 64)"
            )

        if self._opts.get("bits_stored") is None:
>           raise AttributeError(f"{prefix},0101) 'Bits Stored'")
E           AttributeError: Missing required element: (0028,0101) 'Bits Stored'

..\..\..\..\micromamba\envs\mirp\Lib\site-packages\pydicom\pixels\common.py:576: AttributeError

Issues seem to be caused by the same file -- possibly a malformed DICOM header?

alexzwanenburg commented 2 weeks ago

Added the missing DICOM tag to the file that caused the error. Failing tests now pass.

alexzwanenburg commented 2 weeks ago

Done 🎉