def check_send_dicom(self): ts = [ ExplicitVRLittleEndian, ImplicitVRLittleEndian, ExplicitVRBigEndian ] # create application entity with Find and Move SOP classes as SCU and # Storage SOP class as SCP MyAE = AE("DCMTK", 9999, [ PatientRootFindSOPClass, PatientRootMoveSOPClass, VerificationSOPClass ], [StorageSOPClass], ts) MyAE.OnAssociateResponse = OnAssociateResponse MyAE.OnAssociateRequest = OnAssociateRequest MyAE.OnReceiveStore = OnReceiveStore MyAE.start() # remote application entity PrismaAE = dict(Address="134.157.205.1", Port=104, AET="AN_MRC35181") # create association with remote AE self.log.info("Request association on PRISMA") assoc = MyAE.RequestAssociation(PrismaAE) # perform a DICOM ECHO st = assoc.VerificationSOPClass.SCU(1) self.log.info('DICOM Echo done with status "%s"', st) try: self.check_send_dicom_from_remoteAE(assoc) except Exception as e: self.log.warning('CODE ERROR because of %s', e) assoc.Release(0) # AGAIN with VERIO VerioAE = dict(Address="134.157.205.51", Port=104, AET="MRC40527") # create association with remote AE self.log.info("Request association on VERIO") assoc = MyAE.RequestAssociation(VerioAE) # perform a DICOM ECHO st = assoc.VerificationSOPClass.SCU(1) self.log.info('DICOM Echo done with status "%s"', st) #try : self.check_send_dicom_from_remoteAE(assoc) #except Exception as e: # self.log.warning('CODE ERROR because of %s',e) assoc.Release(0) MyAE.Quit()
def dcm_ser_level_find(aet, node, port, laet, patid, studyuid): ''' Use pynetdicom to perform a series level query. The result is a list of SeriesLevelFields records. ''' global msg_id msg_id += 1 # Create application entity and association with remote AE (port number of 0 to stop it putting up a listen) ae = AE(laet, port=0, SOPSCU=[PatientRootFindSOPClass], SOPSCP=[]) ae.start() assoc = ae.RequestAssociation({'Address': node, 'Port': port, 'AET': aet}) # Query object d = Dataset() d.PatientID = patid d.StudyInstanceUID = studyuid d.SeriesInstanceUID = '' d.Modality = '' d.SeriesDate = '' d.SeriesNumber = '' d.SeriesDescription = '' d.BodyPartExamined = '' d.NumberOfSeriesRelatedInstances = '' d.QueryRetrieveLevel = 'SERIES' # Request returns a generator responses = [] matches = assoc.PatientRootFindSOPClass.SCU(d, msg_id) for state, ds in matches: if state == 'Pending': patid = ds.PatientID if 'PatientID' in ds else '' studyuid = ds.StudyInstanceUID if 'StudyInstanceUID' in ds else '' seriesuid = ds.SeriesInstanceUID if 'SeriesInstanceUID' in ds else '' seriesdate = ds.SeriesDate if 'SeriesDate' in ds else '' seriesdescr = ds.SeriesDescription if 'SeriesDescription' in ds else '' seriesnumber = ds.SeriesNumber if 'SeriesNumber' in ds else '' modality = ds.Modality if 'Modality' in ds else '' bodypart = ds.BodyPartExamined if 'BodyPartExamined' in ds else '' nimages = ds.NumberOfSeriesRelatedInstances if 'NumberOfSeriesRelatedInstances' in ds else 0 responses.append( SeriesLevelFields(modality, seriesnumber, seriesuid, seriesdescr, bodypart, nimages)) assoc.Release(0) ae.Quit() return responses
def dcm_stu_level_find(aet, node, port, laet, patid): ''' Use pynetdicom to perform a study level query. The result is a list of StudyLevelFields records. ''' global msg_id msg_id += 1 # Create application entity and association with remote AE (port number of 0 to stop it putting up a listen) ae = AE(laet, port=0, SOPSCU=[PatientRootFindSOPClass], SOPSCP=[]) ae.start() assoc = ae.RequestAssociation({'Address': node, 'Port': port, 'AET': aet}) # Query object d = Dataset() d.PatientID = patid d.StudyInstanceUID = '' d.StudyID = '' d.StudyDate = '' d.StudyDescription = '' d.NumberOfStudyRelatedSeries = '' d.QueryRetrieveLevel = 'STUDY' # Request returns a generator responses = [] matches = assoc.PatientRootFindSOPClass.SCU(d, msg_id) for state, ds in matches: if state == 'Pending': patid = ds.PatientID if 'PatientID' in ds else '' studyid = ds.StudyID if 'StudyID' in ds else '' studyuid = ds.StudyInstanceUID if 'StudyInstanceUID' in ds else '' studydate = ds.StudyDate if 'StudyDate' in ds else '' studydescr = ds.StudyDescription if 'StudyDescription' in ds else '' nseries = ds.NumberOfStudyRelatedSeries if 'NumberOfStudyRelatedSeries' in ds else 0 responses.append( StudyLevelFields(studyid, studyuid, studydate, studydescr, nseries)) assoc.Release(0) ae.Quit() return responses
def dcm_pat_level_find(aet, node, port, laet, query_map): ''' Use pynetdicom to perform a patient level query. The result is a list of PatientLevelFields records. ''' global msg_id msg_id += 1 # Create application entity and association with remote AE (port number of 0 to stop it putting up a listen) ae = AE(laet, port=0, SOPSCU=[PatientRootFindSOPClass], SOPSCP=[]) ae.start() assoc = ae.RequestAssociation({'Address': node, 'Port': port, 'AET': aet}) # Query object d = Dataset() d.PatientID = patid d.PatientName = patname d.PatientBirthDate = birthdate d.PatientSex = sex d.NumberOfPatientRelatedStudies = '' d.QueryRetrieveLevel = 'PATIENT' # Request returns a generator responses = [] matches = assoc.PatientRootFindSOPClass.SCU(d, msg_id) for state, ds in matches: if state == 'Pending': patname = ds.PatientName if 'PatientName' in ds else 'Unknown' patid = ds.PatientID if 'PatientID' in ds else '' patdob = ds.PatientBirthDate if 'PatientBirthDate' in ds else '' patsex = ds.PatientSex if 'PatientSex' in ds else '' nstudies = ds.NumberOfPatientRelatedStudies if 'NumberOfPatientRelatedStudies' in ds else 0 responses.append( PatientLevelFields(patname, patid, patdob, patsex, nstudies)) assoc.Release(0) ae.Quit() return responses
def dcm_img_level_find(aet, node, port, laet, patid, studyuid, seriesuid): ''' Use pynetdicom to perform a image level query. The result is a list of ImageLevelFields records. ''' global msg_id msg_id += 1 # Create application entity and association with remote AE (port number of 0 to stop it putting up a listen) ae = AE(laet, port=0, SOPSCU=[PatientRootFindSOPClass], SOPSCP=[]) ae.start() assoc = ae.RequestAssociation({'Address': node, 'Port': port, 'AET': aet}) # Query object d = Dataset() d.PatientID = patid d.StudyInstanceUID = studyuid d.SeriesInstanceUID = seriesuid d.SOPInstanceUID = '' d.InstanceNumber = '' d.QueryRetrieveLevel = 'IMAGE' # Request returns a generator responses = [] matches = assoc.PatientRootFindSOPClass.SCU(d, msg_id) for state, ds in matches: if state == 'Pending': patid = ds.PatientID if 'PatientID' in ds else '' studyuid = ds.StudyInstanceUID if 'StudyInstanceUID' in ds else '' seriesuid = ds.SeriesInstanceUID if 'SeriesInstanceUID' in ds else '' imageuid = ds.SOPInstanceUID if 'SOPInstanceUID' in ds else '' imagenumber = ds.InstanceNumber if 'InstanceNumber' in ds else '' responses.append(ImageLevelFields(imageuid, imagenumber)) assoc.Release(0) ae.Quit() return responses
# create application entity MyAE = AE( args.aet, args.p, [PatientRootFindSOPClass, PatientRootMoveSOPClass, VerificationSOPClass], [StorageSOPClass], ts) MyAE.OnAssociateResponse = OnAssociateResponse MyAE.OnAssociateRequest = OnAssociateRequest MyAE.OnReceiveStore = OnReceiveStore MyAE.start() # remote application entity RemoteAE = dict(Address=args.remotehost, Port=args.remoteport, AET=args.aec) # create association with remote AE print "Request association" assoc = MyAE.RequestAssociation(RemoteAE) # perform a DICOM ECHO print "DICOM Echo ... ", st = assoc.VerificationSOPClass.SCU(1) print 'done with status "%s"' % st print "DICOM FindSCU ... ", d = Dataset() d.PatientsName = args.searchstring d.QueryRetrieveLevel = "PATIENT" d.PatientID = "*" st = assoc.PatientRootFindSOPClass.SCU(d, 1) print 'done with status "%s"' % st for ss in st:
def _pynetdicom_img_get_worker(aet, node, port, laet, patid, studyuid, seriesuid, imageuid, savedir, queue, returns): ''' Use pynetdicom to perform a series level c-get fetch. This is to be run in a separate worker thread. The c-store callbacks save the images and push a CStoreResponse object onto a queue for the main thread. The function returns normally, which will lead to the threading terminating. This is detected by the parent thread. ''' global msg_id msg_id += 1 # a slight kludge as python 2.7 doesn't have 'nonlocal' qualifier image_counter = [0] # The callback function for C-STORE sub-operations def on_receive_store(cget_obj, ds): try: # print 'C-STORE callback(cget=%s,instno=%d)' % (cget_obj, ds.InstanceNumber) # This is a bit of a hack but we need to add some file-meta file_meta = Dataset() file_meta.add_new((2, 0x01), 'OB', b'\0\1') # FileMetaInformationVersion file_meta.add_new((2, 0x02), 'UI', ds.SOPClassUID) # MediaStorageSOPClassUID file_meta.add_new((2, 0x03), 'UI', ds.SOPInstanceUID) # MediaStorageSOPInstanceUID file_meta.add_new((2, 0x10), 'UI', ExplicitVRLittleEndian) # TransferSyntaxUID file_meta.add_new((2, 0x12), 'UI', '1.2.40.0.13.1.1') # ImplementationClassUID file_meta.add_new((2, 0x13), 'SH', 'pynetdicom') # ImplementationVersionName ds.file_meta = file_meta # and specify the encoding ds.is_implicit_VR = False ds.is_little_endian = True save_path = os.path.join(savedir, '%05d.dcm' % (image_counter[0] + 1)) # the WriteLikeOriginal=False flag is need to get preamble etc right ds.save_as(filename=save_path, WriteLikeOriginal=False) image_counter[0] += 1 pcid = image_counter[0] # for the sake of argument # actually what we want here is the message object so we can extract the no of operations, status etc status = 0 queue.put(CStoreResponse(pcid, status)) except Exception as e: # print 'Error saving: C-STORE callback(cget=%s, instno=%d), saving to %s [%s]' % (cget_obj, ds.InstanceNumber, save_path, e) print('Error Saving: %s' % e) return 1 # ?? RHD # zero corresponds to DICOM Success return 0 # Create application entity and association with remote AE (port number of 0 to stop it putting up a listen) ae = AE(laet, port=0, SOPSCU=[PatientRootGetSOPClass], SOPSCP=[ RTPlanStorageSOPClass, CTImageStorageSOPClass, MRImageStorageSOPClass, RTImageStorageSOPClass ]) ae.OnReceiveStore = on_receive_store ae.start() assoc = ae.RequestAssociation({'Address': node, 'Port': port, 'AET': aet}) # Query object d = Dataset() d.PatientID = str(patid) d.StudyInstanceUID = str(studyuid) d.SeriesInstanceUID = str(seriesuid) d.SOPInstanceUID = str(imageuid) d.QueryRetrieveLevel = 'IMAGE' st_ds = assoc.PatientRootGetSOPClass.SCU(d, msg_id) if st_ds is not None: for status, ds in st_ds: pass # print 'status = %s, dataset = %s' % (status, ds) # print 'Done' returns.append(status) return
class ApplicationEntityService(object): """DICOM application entity service """ __metaclass__ = SingletonType def __init__(self): """Default constructor """ # Setup logger - use config file self._logger = logging.getLogger(__name__) logging.config.fileConfig('logging.ini', disable_existing_loggers=False) self._ae = None self._downloadDir = "" self._guiThread = None self._fileCounter = 0 def __del__(self): """Default destructor """ if self.isReady: self.quit() ######## ######## ####### ######## ######## ######## ######## #### ######## ###### ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ######## ######## ## ## ######## ###### ######## ## ## ###### ###### ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ####### ## ######## ## ## ## #### ######## ###### @property def ae(self): """AE Getter """ return self._ae @property def isReady(self): """Is ready Getter """ return self._ae is not None @property def downloadDir(self): """Download directory Getter """ return self._downloadDir @downloadDir.setter def downloadDir(self, value): """Download directory Setter """ self._downloadDir = value ## ## ######## ######## ## ## ####### ######## ###### ### ### ## ## ## ## ## ## ## ## ## ## #### #### ## ## ## ## ## ## ## ## ## ## ### ## ###### ## ######### ## ## ## ## ###### ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ######## ## ## ## ####### ######## ###### def init(self, name, port): """Initialize and start AE """ # setup AE self._ae = AE(name, port, [ PatientRootFindSOPClass, StudyRootFindSOPClass, PatientRootMoveSOPClass, StudyRootMoveSOPClass, VerificationSOPClass ], [StorageSOPClass, VerificationSOPClass]) self._ae.OnAssociateRequest = self.onAssociateRequest self._ae.OnAssociateResponse = self.onAssociateResponse self._ae.OnReceiveStore = self.onReceiveStore self._ae.OnReceiveEcho = self.onReceiveEcho self._ae.start() self._logger.info("AE created: " + name + ":" + str(port)) def quit(self): """Quite AE """ self._ae.Quit() def requestAssociation(self, remoteAe): """Create association with remote AE """ self._logger.debug("Request association") return self._ae.RequestAssociation(remoteAe) def echo(self, association): """Perform a DICOM echo """ status = association.VerificationSOPClass.SCU(1) self._logger.debug("Echo done with status: " + str(status)) return status def find(self, data, thread=None): """Perform DICOM find """ association = data[0] queryLvl = data[1] arguments = len(data) patientNameFilter = "" patientIdFilter = "*" studyUidFilter = "*" seriesUidFilter = "*" d = Dataset() if (arguments >= 3): patientNameFilter = data[2] if (arguments >= 4): patientIdFilter = data[3] d.ModalitiesInStudy = "" if (arguments >= 5): studyUidFilter = data[4] if (arguments >= 6): seriesUidFilter = data[5] d.QueryRetrieveLevel = queryLvl d.PatientsName = patientNameFilter d.PatientID = patientIdFilter d.PatientSex = "" d.PatientBirthDate = "" d.StudyInstanceUID = studyUidFilter d.AccessionNumber = "" d.StudyDescription = "" d.StudyDate = "" d.SeriesInstanceUID = seriesUidFilter d.SeriesDate = "" d.SeriesTime = "" d.SeriesDescription = "" d.Modality = "" status = association.PatientRootFindSOPClass.SCU(d, 1) self._logger.debug("Find done with status: " + str(status)) if thread: thread.emit(QtCore.SIGNAL("finished(QVariant)"), status) return None else: return status def findPatient(self, data, thread=None): """Perform DICOM patient find """ association = data[0] queryLvl = data[1] d = Dataset() d.QueryRetrieveLevel = queryLvl status = association.PatientRootFindSOPClass.SCU(d, 1) self._logger.debug("Find DICOM patinet done with status: " + str(status)) if thread: thread.emit(QtCore.SIGNAL("finished(QVariant)"), status) return None else: return status def findStudy(self, data, thread=None): """Perform DICOM study find """ association = data[0] queryLvl = data[1] d = Dataset() d.QueryRetrieveLevel = queryLvl status = association.StudyRootFindSOPClass.SCU(d, 1) self._logger.debug("Find DICOM study done with status: " + str(status)) if thread: thread.emit(QtCore.SIGNAL("finished(QVariant)"), status) return None else: return status def queryRetrieveTree(self, data, thread=None): """ """ rootNode = data[0] # rootNode remoteAe = data[1] # remoteAe self._guiThread = thread self._fileCounter = 0 count = len(rootNode.children) downloaded = 0 thread.emit(QtCore.SIGNAL("log(QString)"), "Retrieving DICOM data can take some time please wait...") for patient in rootNode.children: if patient.isChecked: thread.emit( QtCore.SIGNAL("log(QString)"), "Processing DICOM patient: " + patient.name + " (" + patient.id + ")") self.queryRetrievePatient(patient.id, patient.name, remoteAe, thread) else: for study in patient.children: if study.isChecked: thread.emit(QtCore.SIGNAL("log(QString)"), "Processing DICOM study: " + study.suid) self.queryRetrieveStudy() else: for series in study.children: if series.isChecked: thread.emit( QtCore.SIGNAL("log(QString)"), "Processing DICOM study: " + series.suid) self.queryRetrieveSeries() downloaded = downloaded + 1 thread.emit(QtCore.SIGNAL("taskUpdated"), [downloaded, count]) thread.emit(QtCore.SIGNAL('log(QString)'), 'Finished!') thread.emit(QtCore.SIGNAL('message(QString)'), "Retrieve job was successful.") def queryRetrievePatient(self, patientId, patientName, remoteAe, thread=None): """ """ queryLvl = "PATIENT" association = self._ae.RequestAssociation(remoteAe) result = self.find([association, queryLvl, patientName, patientId]) for entity in result: if not entity[1]: continue d = Dataset() try: d.PatientID = entity[1].PatientID except Exception, err: self._logger.error(str(err)) continue subAssociation = self._ae.RequestAssociation(remoteAe) generator = subAssociation.PatientRootMoveSOPClass.SCU( d, self._ae.name, 1) for subentity in generator: self._logger.info(subentity) subAssociation.Release(0) association.Release(0)
class Pacs: def __init__(self, port=1234, aet='ACME1', output='out', implicit=None, explicit=None): self.remoteAE = None self.onDicomSaved = None self.import_folder = None self.logger = logging.getLogger(__name__) if implicit: ts = [ImplicitVRLittleEndian] elif explicit: ts = [ExplicitVRLittleEndian] else: ts = [ ExplicitVRLittleEndian, ImplicitVRLittleEndian, ExplicitVRBigEndian ] self.port = port self.aet = aet self.output = output self.logger.info("Creating local AE='%s' port=%d" % (aet, port)) self.MyAE = AE(aet, port, [ StudyRootFindSOPClass, StudyRootMoveSOPClass, PatientRootFindSOPClass, PatientRootMoveSOPClass, VerificationSOPClass ], [StorageSOPClass], ts) self.MyAE.OnAssociateResponse = self.OnAssociateResponse self.MyAE.OnAssociateRequest = self.OnAssociateRequest self.MyAE.OnReceiveStore = self.OnReceiveStore def connect(self, remotehost, remoteport, aec): self.remoteAE = dict(Address=remotehost, Port=remoteport, AET=aec) self.logger.debug("starting local application entity") self.MyAE.start() # create association with remote AE try: self.logger.info("Requesting association to %s" % self.remoteAE) assoc = self.MyAE.RequestAssociation(self.remoteAE) # perform a DICOM ECHO st = assoc.VerificationSOPClass.SCU(1) except: self.logger.critical( "Unable to get association with %s, is the server running?" % self.remoteAE) raise self.logger.info('association done with status "%s"' % st) now = datetime.now() self.import_folder = os.path.join(self.output, now.strftime('%Y.%m.%d:%H:%M:%S')) assoc.Release(0) def OnAssociateResponse(self, association): self.logger.debug("Association response received") def OnAssociateRequest(self, association): self.logger.debug("Association resquested") return True def OnReceiveStore(self, SOPClass, ds): # do something with dataset. For instance, store it. self.logger.debug( "Received C-STORE SeriesInstanceUID:'%s', SOPInstanceUID:'%s''" % (ds.SeriesInstanceUID, ds.SOPInstanceUID)) file_meta = Dataset() file_meta.MediaStorageSOPClassUID = '1.2.840.10008.5.1.4.1.1.2' # !! Need valid UID herecopy_dicom file_meta.MediaStorageSOPInstanceUID = "1.2.3" # !!! Need valid UIDs here file_meta.ImplementationClassUID = "1.2.3.4" folder = os.path.join(self.import_folder, ds.StudyID) if not os.path.isdir(folder): os.makedirs(folder) filename = '%s/%s.dcm' % (folder, ds.SOPInstanceUID) fileds = FileDataset(filename, {}, file_meta=file_meta, preamble="\0" * 128) fileds.update(ds) fileds.save_as(filename) self.logger.info("file %s written" % filename) if self.onDicomSaved: self.logger.info("calling callback") self.onDicomSaved(filename) # must return appropriate status return SOPClass.Success def query(self, dataset): assoc = self.MyAE.RequestAssociation(self.remoteAE) if dataset.QueryRetrieveLevel == "STUDY": st = assoc.StudyRootFindSOPClass.SCU(dataset, 1) elif dataset.QueryRetrieveLevel == "PATIENT": st = assoc.PatientRootFindSOPClass.SCU(dataset, 1) else: raise AttributeError("Unsupported PatientRootFindSOPClass " + dataset.QueryRetrieveLevel) items = [ss[1] for ss in st if ss[1]] assoc.Release(0) return items def list_studies(self): d = Dataset() d.QueryRetrieveLevel = "STUDY" d.SeriesInstanceUID = '*' return self.query(d) def copy_dicom(self, dataset): assoc = self.MyAE.RequestAssociation(self.remoteAE) gen = assoc.StudyRootMoveSOPClass.SCU(dataset, self.aet, 1) for gg in gen: # we have to access the item to copy it (it will be done asynchronously) self.logger.debug("copying %s" % gg) x = gg assoc.Release(0) def quit(self): self.MyAE.Quit()