def send(self): self.progressCallback("Starting send to %s using self.protocol" % self.destinationUrl.toString()) if self.protocol == "DICOMweb": # DICOMweb try: import dicomweb_client except ModuleNotFoundError: logging.info( "Installing dicomweb-client for sending DICOM using DICOMweb protocol" ) pip_install('dicomweb-client') for file in self.files: if not self.progressCallback( "Sending %s to %s using %s" % (file, self.destinationUrl.toString(), self.protocol)): raise UserWarning( "Sending was cancelled, upload is incomplete.") from dicomweb_client.api import DICOMwebClient client = DICOMwebClient(url=self.destinationUrl.toString(), chunk_size=500000) import pydicom dataset = pydicom.dcmread(file) client.store_instances(datasets=[dataset]) else: # DIMSE (traditional DICOM networking) for file in self.files: self.start(file) if not self.progressCallback("Sent %s to %s:%s" % (file, self.destinationUrl.host(), self.destinationUrl.port())): raise UserWarning( "Sending was cancelled, upload is incomplete.")
def test_headers(httpserver): name = 'my-token' value = 'topsecret' headers = {name: value} client = DICOMwebClient(httpserver.url, headers=headers) client.store_instances([]) request = httpserver.requests[0] assert request.headers[name] == value
def _store_instances(args): '''Loads Instances from files on disk and stores them.''' client = DICOMwebClient(args.url, username=args.username, password=args.password, ca_bundle=args.ca_bundle, cert=args.cert) datasets = list() for f in args.files: ds = pydicom.dcmread(f) datasets.append(ds) client.store_instances(datasets)
def _PubsubCallback(self, message): # type: pubsub_v1.Message -> None """Processes a Pubsub message. This function will retrieve the instance (specified in Pubsub message) from the Cloud Healthcare API. Then it will invoke CMLE to get the prediction results. Finally (and optionally), it will store the inference results back to the Cloud Healthcare API as a DICOM Structured Report. The instance URL to the Structured Report containing the prediction is then published to a pub/sub. Args: message: Incoming pubsub message. """ image_instance_path = message.data.decode() _logger.debug('Received instance in pubsub feed: %s', image_instance_path) try: parsed_message = pubsub_format.ParseMessage( message, dicom_path.Type.INSTANCE) except exception.CustomExceptionError: _logger.info('Invalid input path: %s', image_instance_path) message.ack() return input_path = parsed_message.input_path authed_session = session_utils.create_session_from_gcp_credentials() dicomweb_url = posixpath.join(_HEALTHCARE_API_URL_PREFIX, input_path.dicomweb_path_str) input_client = DICOMwebClient(dicomweb_url, authed_session) if not self._IsMammoInstance(input_client, input_path): _logger.info( 'Instance is not of type MG modality, ignoring message: %s', image_instance_path) message.ack() return _logger.info('Processing instance: %s', image_instance_path) # Retrieve instance from DICOM API in JPEG format. image_jpeg_bytes = input_client.retrieve_instance_rendered( input_path.study_uid, input_path.series_uid, input_path.instance_uid, media_types=('image/jpeg', )) # Retrieve study level information study_json = input_client.search_for_studies( fields=['all'], search_filters={'StudyInstanceUID': input_path.study_uid})[0] # Get the predicted score and class from the inference model in Cloud ML or # AutoML. try: predicted_class, predicted_score = self._predictor.Predict( image_jpeg_bytes) except PermissionDenied as e: _logger.error('Permission error running prediction service: %s', e) message.nack() return except InvalidArgument as e: _logger.error( 'Invalid arguments when running prediction service: %s', e) message.nack() return # Print the prediction. text = 'Base path: %s\nPredicted class: %s\nPredicted score: %s' % ( image_instance_path, predicted_class, predicted_score) _logger.info(text) # If user requested destination DICOM store for inference, create a DICOM # structured report that stores the prediction. if self._dicom_store_path: # Create DICOMwebClient with output url. output_dicom_web_url = posixpath.join( _HEALTHCARE_API_URL_PREFIX, self._dicom_store_path.dicomweb_path_str) output_client = DICOMwebClient(output_dicom_web_url, authed_session) # Generate series uid and instance uid for the structured report. sr_instance_uid = pydicom.uid.generate_uid(prefix=None) sr_series_uid = pydicom.uid.generate_uid(prefix=None) sr_dataset = _BuildSR(study_json, text, sr_series_uid, sr_instance_uid) try: output_client.store_instances([sr_dataset]) except RuntimeError as e: _logger.error('Error storing DICOM in API: %s', e) message.nack() return # If user requested that new structured reports be published to a channel, # publish the instance path of the Structured Report structured_report_path = dicom_path.FromPath( self._dicom_store_path, study_uid=input_path.study_uid, series_uid=sr_series_uid, instance_uid=sr_instance_uid) self._PublishInferenceResultsReady(str(structured_report_path)) _logger.info('Published structured report with path: %s', structured_report_path) # Ack the message (successful or invalid message). message.ack() self._success_count += 1
def send(self): self.progressCallback("Starting send to %s using self.protocol" % self.destinationUrl.toString()) if self.protocol == "DICOMweb": # DICOMweb # Ensure that correct version of dicomweb-client Python package is installed needRestart = False needInstall = False minimumDicomwebClientVersion = "0.51" try: import dicomweb_client from packaging import version if version.parse(dicomweb_client.__version__) < version.parse( minimumDicomwebClientVersion): if not slicer.util.confirmOkCancelDisplay( f"DICOMweb sending requires installation of dicomweb-client (version {minimumDicomwebClientVersion} or later).\nClick OK to upgrade dicomweb-client and restart the application." ): self.showBrowserOnEnter = False return needRestart = True needInstall = True except ModuleNotFoundError: needInstall = True if needInstall: # pythonweb-client 0.50 was broken (https://github.com/MGHComputationalPathology/dicomweb-client/issues/41) progressDialog = slicer.util.createProgressDialog( labelText= 'Upgrading dicomweb-client. This may take a minute...', maximum=0) slicer.app.processEvents() slicer.util.pip_install( f'dicomweb-client>={minimumDicomwebClientVersion}') import dicomweb_client progressDialog.close() if needRestart: slicer.util.restart() # Establish connection import dicomweb_client.log dicomweb_client.log.configure_logging(2) from dicomweb_client.api import DICOMwebClient effectiveServerUrl = self.destinationUrl.toString() session = None headers = {} # Setting up of the DICOMweb client from various server parameters can be done # in plugins in the future, but for now just hardcode special initialization # steps for a few server types. if "kheops" in effectiveServerUrl: # Kheops DICOMweb API endpoint from browser view URL url = qt.QUrl(effectiveServerUrl) if url.path().startswith('/view/'): # This is a Kheops viewer URL. # Retrieve the token from the viewer URL and use the Kheops API URL to connect to the server. token = url.path().replace('/view/', '') effectiveServerUrl = "https://demo.kheops.online/api" from requests.auth import HTTPBasicAuth from dicomweb_client.session_utils import create_session_from_auth auth = HTTPBasicAuth('token', token) session = create_session_from_auth(auth) client = DICOMwebClient(url=effectiveServerUrl, session=session, headers=headers) for file in self.files: if not self.progressCallback( f"Sending {file} to {self.destinationUrl.toString()} using {self.protocol}" ): raise UserWarning( "Sending was cancelled, upload is incomplete.") import pydicom dataset = pydicom.dcmread(file) client.store_instances(datasets=[dataset]) else: # DIMSE (traditional DICOM networking) for file in self.files: self.start(file) if not self.progressCallback( f"Sent {file} to {self.destinationUrl.host()}:{self.destinationUrl.port()}" ): raise UserWarning( "Sending was cancelled, upload is incomplete.")