def populate_new_fields(apps, schema_editor):
     Series = apps.get_model("django_dicom", "Series")
     for series in Series.objects.all():
         header = DicomHeader(str(series.image_set.first().dcm))
         series.pulse_sequence_name = header.get((0x0019, 0x109C))
         series.sequence_name = header.get((0x0018, 0x0024))
         series.save()
Esempio n. 2
0
    def get_or_create_from_dcm(self,
                               path: Path,
                               autoremove: bool = True) -> Tuple:
        """
        Gets or creates an :class:`~django_dicom.models.image.Image` instance
        based on the contents of the provided *.dcm* path.

        Parameters
        ----------
        path : :class:`pathlib.Path`
            Local *.dcm* file path
        autoremove : bool, optional
            Whether to remove the local copy of the *.dcm* file under
            MEDIA_ROOT if creation fails, by default True

        Returns
        -------
        Tuple[Image, bool]
            image, created
        """

        header = DicomHeader(path)
        uid = header.get("SOPInstanceUID")
        try:
            existing = self.get(uid=uid)
        except ObjectDoesNotExist:
            new_instance = self.create_from_dcm(path, autoremove=autoremove)
            return new_instance, True
        else:
            return existing, False
    def scan_subject_to_session(apps, schema_editor):
        Subject = get_subject_model()
        Scan = apps.get_model("django_mri", "Scan")
        Session = apps.get_model("django_mri", "Session")

        for scan in Scan.objects.all():
            subject = Subject.objects.filter(
                id_number=scan.dicom.patient.uid
            ).first()
            first_image = scan.dicom.image_set.first().dcm.path
            header = DicomHeader(first_image)
            date = header.get("StudyDate") or header.get("SeriesDate")
            try:
                time = header.get("StudyTime")
            except ValueError:
                time = datetime.time()
            else:
                if not time:
                    time = datetime.time()
            session_time = datetime.datetime.combine(date, time)
            if not subject:
                session = Session.objects.create(time=session_time)
                scan.session_id = session.id
            else:
                session = subject.mri_session_set.filter(
                    time=session_time
                ).first()
                if not session:
                    session = Session.objects.create(
                        subject_id=subject.id, time=session_time
                    )
                else:
                    existing_scan_number = Scan.objects.filter(
                        session_id=session.id, number=scan.number
                    )
                    if existing_scan_number:
                        session = Session.objects.create(
                            subject_id=subject.id, time=session_time
                        )
                scan.session_id = session.id
            scan.save()
Esempio n. 4
0
class Image:
    """
    This class represents a single DICOM image (i.e. `.dcm` file) and provides
    unified access to it's header information and data.

    """
    def __init__(self, raw):
        """
        The Image class should be initialized with either a string or a
        :class:`~pathlib.Path` instance representing the path of a .dcm file.
        Another option is to initialize it with a :class:`~pydicom.FileDataset`
        instance, however, in that case make sure that the `stop_before_pixels`
        parameter is set to False, otherwise reading pydicom's `pixel_array`
        will fail.

        Parameters
        ----------
        raw : str, pathlib.Path, or pydicom.FileDataset
            A single DICOM image.
        parser : type, optional
            An object with a public `parse()` method that may be used to parse
            data elements, by default Parser.
        """

        self.raw = read_file(raw, read_data=True)
        self.header = Header(self.raw)
        self.warnings = []
        self._data = self.read_raw_data()

    def read_raw_data(self) -> np.ndarray:
        """
        Reads the pixel array data as returned by pydicom.

        Returns
        -------
        np.ndarray
            Pixel array data
        """

        try:
            return self.raw.pixel_array
        except (AttributeError, ValueError) as exception:
            warning = messages.DATA_READ_FAILURE.format(exception=exception)
            warnings.warn(warning)
            if warning not in self.warnings:
                self.warnings.append(warning)

    def fix_data(self) -> np.ndarray:
        """
        Applies any required transformation to the data.

        Returns
        -------
        np.ndarray
            Pixel array data
        """

        if self.is_mosaic:
            mosaic = Mosaic(self._data, self.header)
            return mosaic.fold()
        return self._data

    def get_default_relative_path(self) -> Path:
        patient_uid = self.header.get("PatientID")
        series_uid = self.header.get("SeriesInstanceUID")
        name = str(self.header.get("InstanceNumber", 0)) + ".dcm"
        return Path(patient_uid, series_uid, name)

    @property
    def is_mosaic(self) -> bool:
        """
        Checks whether a 3D volume is encoded as a 2D Mosaic.
        For more information, see the
        :class:`~dicom_parser.utils.siemens.mosaic.Mosaic`
        class.

        Returns
        -------
        bool
            Whether the image is a mosaic encoded volume
        """

        return "MOSAIC" in self.header.get("ImageType")

    @property
    def is_fmri(self) -> bool:
        """
        Returns True for fMRI images according to their header information.

        Returns
        -------
        bool
            Whether this image represents fMRI data.
        """

        return self.header.detected_sequence == "fMRI"

    @property
    def data(self) -> np.ndarray:
        """
        Returns the pixel data array after having applied any required
        transformations.

        Returns
        -------
        np.ndarray
            Pixel data array.
        """

        return self.fix_data()

    @property
    def default_relative_path(self) -> Path:
        return self.get_default_relative_path()
Esempio n. 5
0
    def import_path(
        self,
        path: Path,
        progressbar: bool = True,
        report: bool = True,
        persistent: bool = True,
        force_patient_uid: bool = False,
    ) -> QuerySet:
        """
        Iterates the given directory tree and imports any *.dcm* files found
        within it.

        Parameters
        ----------
        path : :class:`pathlib.Path`
            Base path for recursive *.dcm* import
        progressbar : bool, optional
            Whether to display a progressbar or not, by default True
        report : bool, optional
            Whether to print out a summary report when finished or not, by
            default True
        persistent : bool, optional
            Whether to continue and raise a warning or to raise an exception
            when failing to read a DICOM file's header
        force_patient_uid : bool, optional
            If patient UID for an existing image doesn't match the DB value,
            change the DB value to match that of the imported image

        Returns
        -------
        :class:`~django.db.models.query.QuerySet`
            The created :class:`~django_dicom.models.image.Image` instances
        """

        # Create an iterator
        iterator = Path(path).rglob("*.dcm")
        if progressbar:
            # Create a progressbar wrapped iterator using tqdm
            iterator = create_progressbar(iterator, unit="image")

        if report:
            counter = {"created": 0, "existing": 0}

        # Keep a list of all the created images' primary keys
        created_ids = []

        # Keep a list of patient UID mismatches to log
        patient_uid_mismatch = []

        for dcm_path in iterator:

            # Atomic image import
            # For more information see:
            # https://docs.djangoproject.com/en/3.0/topics/db/transactions/#controlling-transactions-explicitly
            with transaction.atomic():
                try:
                    image, created = self.get_or_create_from_dcm(
                        dcm_path, autoremove=True)
                except InvalidDicomError as e:
                    if persistent:
                        IMPORT_LOGGER.warning(e)
                        continue
                    else:
                        raise

            if report:
                counter_key = "created" if created else "existing"
                counter[counter_key] += 1

            if created:
                created_ids.append(image.id)
            else:
                if image.patient.uid not in patient_uid_mismatch:
                    # Validate patient UID for existing images
                    header = DicomHeader(dcm_path)
                    patient_uid = header.get("PatientID")
                    if patient_uid != image.patient.uid:
                        # Log patient UID mismatch
                        image_uid = header.get("SOPInstanceUID")
                        message = PATIENT_UID_MISMATCH.format(
                            image_uid=image_uid,
                            db_value=image.patient.uid,
                            patient_uid=patient_uid,
                        )
                        IMPORT_LOGGER.warning(message)
                        patient_uid_mismatch.append(image.patient.uid)
        if report:
            self.report_import_path_results(path, counter)

        return self.filter(id__in=created_ids)
Esempio n. 6
0
 def test_get_with_a_missing_csa_header_returns_none(self):
     header = Header(TEST_GE_LOCALIZER_PATH)
     result = header.get("CSASeriesHeaderInfo")
     self.assertIsNone(result)
Esempio n. 7
0
 def test_get_with_a_csa_header_returns_dict(self):
     header = Header(TEST_IMAGE_PATH)
     result = header.get("CSASeriesHeaderInfo")
     self.assertIsInstance(result, dict)