Ejemplo n.º 1
0
def _retrieve_series_metadata(args):
    '''Retrieves metadata for all Instances of a given Series and either
    writes it to standard output or to files on disk.
    '''
    client = DICOMwebClient(args.url,
                            username=args.username,
                            password=args.password,
                            ca_bundle=args.ca_bundle,
                            cert=args.cert)
    metadata = client.retrieve_series_metadata(args.study_instance_uid,
                                               args.series_instance_uid)
    if args.save:
        for md in metadata:
            tag = client.lookup_tag('SOPInstanceUID')
            sop_instance_uid = md[tag]['Value'][0]
            _save_metadata(md, args.output_dir, sop_instance_uid,
                           args.prettify, args.dicomize)
    else:
        _print_metadata(metadata, args.prettify, args.dicomize)
Ejemplo n.º 2
0
def importFromDICOMWeb(dicomWebEndpoint,
                       studyInstanceUID,
                       seriesInstanceUID=None,
                       accessToken=None):
    """
    Downloads and imports DICOM series from a DICOMweb instance.
    Progress is displayed and if errors occur then they are displayed in a popup window in the end.
    If all the instances in a series are already imported then the series will not be retrieved and imported again.

    :param dicomWebEndpoint: Endpoint URL for retrieving the study/series from DICOMweb
    :param studyInstanceUID: UID for the study to be downloaded
    :param seriesInstanceUID: UID for the series to be downloaded. If not specified, all series will be downloaded from the study
    :param accessToken: Optional access token for the query
    :return: List of imported study UIDs

    Example: calling from PythonSlicer console

    .. code-block:: python

      from DICOMLib import DICOMUtils
      loadedUIDs = DICOMUtils.importFromDICOMWeb(dicomWebEndpoint="https://yourdicomweburl/dicomWebEndpoint",
                                               studyInstanceUID="2.16.840.1.113669.632.20.1211.10000509338")
                                               accessToken="YOUR_ACCESS_TOKEN")

    """

    from dicomweb_client.api import DICOMwebClient

    seriesImported = []
    errors = []
    clientLogger = logging.getLogger('dicomweb_client')
    originalClientLogLevel = clientLogger.level

    progressDialog = slicer.util.createProgressDialog(
        parent=slicer.util.mainWindow(), value=0, maximum=100)
    try:
        progressDialog.labelText = f'Retrieving series list...'
        slicer.app.processEvents()

        if accessToken is None:
            client = DICOMwebClient(url=dicomWebEndpoint)
        else:
            client = DICOMwebClient(
                url=dicomWebEndpoint,
                headers={"Authorization": f"Bearer {accessToken}"},
            )

        seriesList = client.search_for_series(
            study_instance_uid=studyInstanceUID)
        seriesInstanceUIDs = []
        if seriesInstanceUID is not None:
            seriesInstanceUIDs = [seriesInstanceUID]
        else:
            for series in seriesList:
                currentSeriesInstanceUID = series['0020000E']['Value'][0]
                seriesInstanceUIDs.append(currentSeriesInstanceUID)

        # Turn off detailed logging, because it would slow down the file transfer
        clientLogger.setLevel(logging.WARNING)

        fileNumber = 0
        cancelled = False
        for seriesIndex, currentSeriesInstanceUID in enumerate(
                seriesInstanceUIDs):
            progressDialog.labelText = f'Retrieving series {seriesIndex+1} of {len(seriesInstanceUIDs)}...'
            slicer.app.processEvents()

            try:
                seriesInfo = client.retrieve_series_metadata(
                    study_instance_uid=studyInstanceUID,
                    series_instance_uid=currentSeriesInstanceUID)
                numberOfInstances = len(seriesInfo)

                # Skip retrieve and import of this series if it is already imported
                alreadyImportedInstances = slicer.dicomDatabase.instancesForSeries(
                    currentSeriesInstanceUID)
                seriesAlreadyImported = True
                for serieInfo in seriesInfo:
                    sopInstanceUID = serieInfo['00080018']['Value'][0]
                    if sopInstanceUID not in alreadyImportedInstances:
                        seriesAlreadyImported = False
                        break
                if seriesAlreadyImported:
                    seriesImported.append(currentSeriesInstanceUID)
                    continue

                instances = client.iter_series(
                    study_instance_uid=studyInstanceUID,
                    series_instance_uid=currentSeriesInstanceUID)

                slicer.app.processEvents()
                cancelled = progressDialog.wasCanceled
                if cancelled:
                    break

                outputDirectoryBase = slicer.dicomDatabase.databaseDirectory + "/DICOMweb"
                if not os.access(outputDirectoryBase, os.F_OK):
                    os.makedirs(outputDirectoryBase)
                outputDirectoryBase += "/" + qt.QDateTime.currentDateTime(
                ).toString("yyyyMMdd-hhmmss")
                outputDirectory = qt.QTemporaryDir(
                    outputDirectoryBase)  # Add unique substring to directory
                outputDirectory.setAutoRemove(False)
                outputDirectoryPath = outputDirectory.path()

                for instanceIndex, instance in enumerate(instances):
                    progressDialog.setValue(
                        int(100 * instanceIndex / numberOfInstances))
                    slicer.app.processEvents()
                    cancelled = progressDialog.wasCanceled
                    if cancelled:
                        break
                    filename = outputDirectoryPath + "/" + str(
                        fileNumber) + ".dcm"
                    instance.save_as(filename)
                    fileNumber += 1

                if cancelled:
                    # cancel was requested in instance retrieve loop,
                    # stop the entire import process
                    break

                importDicom(outputDirectoryPath)
                seriesImported.append(currentSeriesInstanceUID)

            except Exception as e:
                import traceback
                errors.append(
                    f"Error importing series {currentSeriesInstanceUID}: {str(e)} ({traceback.format_exc()})"
                )

    except Exception as e:
        import traceback
        errors.append(f"{str(e)} ({traceback.format_exc()})")

    finally:
        progressDialog.close()
        clientLogger.setLevel(originalClientLogLevel)

    if errors:
        slicer.util.errorDisplay(
            f"Errors occurred during DICOMweb import of {len(errors)} series.",
            detailedText="\n\n".join(errors))
    elif cancelled and (len(seriesImported) < len(seriesInstanceUIDs)):
        slicer.util.infoDisplay(
            f"DICOMweb import has been interrupted after completing {len(seriesImported)} out of {len(seriesInstanceUIDs)} series."
        )

    return seriesImported