def getDicomFileName(cfg, scanNum, fileNum): """ This function takes in different variables (which are both specific to the specific scan and the general setup for the entire experiment) to produce the full filename for the dicom file of interest. Used externally. """ if scanNum < 0: raise ValidationError( "ScanNumber not supplied or invalid {}".format(scanNum)) # the naming pattern is provided in the toml file if cfg.dicomNamePattern is None: raise InvocationError("Missing config settings dicomNamePattern") if '{TR' in cfg.dicomNamePattern: fileName = cfg.dicomNamePattern.format(SCAN=scanNum, TR=fileNum) else: scanNumStr = str(scanNum).zfill(2) fileNumStr = str(fileNum).zfill(3) fileName = cfg.dicomNamePattern.format(scanNumStr, fileNumStr) fullFileName = os.path.join(cfg.dicomDir, fileName) return fullFileName
def encodeMessageData(message, data, compress): """ b64 encode binary data in preparation for sending. Updates the message header as needed Args: message (dict): message header data (bytes): binary data compress (bool): whether to compress binary data Returns: Modified message dict with appropriate fields filled in """ message['hash'] = hashlib.md5(data).hexdigest() dataSize = len(data) if compress or dataSize > (20 * 2**20): message['compressed'] = True data = zlib.compress(data) message['data'] = b64encode(data).decode('utf-8') message['dataSize'] = dataSize # if 'compressed' in message: # print('Compression ratio: {:.2f}'.format(len(message['data'])/dataSize)) if len(message['data']) > 100 * 1024 * 1024: message['data'] = None raise ValidationError( 'encodeMessageData: encoded file exceeds max size of 100MB') return message
def _checkAllowedFileTypes(self, filename: str) -> bool: """ Class-private function for checking if a file is allowed.""" if self.allowedFileTypes is None or len(self.allowedFileTypes) == 0: raise ValidationError( 'DataInterface: no allowed file types are set') if filename is None or filename == '': return True if self.allowedFileTypes[0] == '*': return True if filename[-1] == '*': # wildcards will be filtered later return True fileExtension = Path(filename).suffix if fileExtension not in self.allowedFileTypes: raise ValidationError( f"File type {fileExtension} not in list of allowed file types {self.allowedFileTypes}. " "Specify allowed filetypes with FileServer -f parameter.") return True
def _checkAllowedDirs(self, dir: str) -> bool: if self.allowedDirs is None or len(self.allowedDirs) == 0: raise ValidationError( 'DataInterface: no allowed directories are set') if dir is None: return True if self.allowedDirs[0] == '*': return True dirMatch = False for allowedDir in self.allowedDirs: if dir.startswith(allowedDir): dirMatch = True break if dirMatch is False: raise ValidationError( f'Path {dir} not within list of allowed directories {self.allowedDirs}. ' 'Make sure you specified a full (absolute) path. ' 'Specify allowed directories with FileServer -d parameter.') return True
def getDicomFileName(cfg, scanNum, fileNum): if scanNum < 0: raise ValidationError( "ScanNumber not supplied of invalid {}".format(scanNum)) scanNumStr = str(scanNum).zfill(2) fileNumStr = str(fileNum).zfill(3) if cfg.dicomNamePattern is None: raise InvocationError("Missing config settings dicomNamePattern") fileName = cfg.dicomNamePattern.format(scanNumStr, fileNumStr) fullFileName = os.path.join(cfg.dicomDir, fileName) return fullFileName
def encodeMessageData(message, data, compress): message['hash'] = hashlib.md5(data).hexdigest() dataSize = len(data) if compress or dataSize > (20*2**20): message['compressed'] = True data = zlib.compress(data) message['data'] = b64encode(data).decode('utf-8') message['dataSize'] = dataSize # if 'compressed' in message: # print('Compression ratio: {:.2f}'.format(len(message['data'])/dataSize)) if len(message['data']) > 100*1024*1024: message['data'] = None raise ValidationError('encodeMessageData: encoded file exceeds max size of 100MB') return message
def __init__(self, archivePath, **entities): """ Args: archivePath: Absolute path of the BIDS archive. entities: BIDS entities (subject, session, task, run, suffix, datatype) that define the particular subject/run of the data to stream """ self.bidsArchive = BidsArchive(archivePath) # TODO - when we have BidsRun # self.bidsRun = self.bidsArchive.getBidsRun(**entities) images = self.bidsArchive.getImages(**entities) if len(images) == 0: raise ValidationError('No matching images found') if len(images) > 1: raise ValidationError('More than one match, please give more specific subject/session/task/run') self.bidsImage = images[0] self.niftiImage = self.bidsImage.get_image() self.filename = self.niftiImage.get_filename() self.imgVolumes = nib.four_to_three(self.niftiImage) self.metadata = self.bidsArchive.getSidecarMetadata(self.filename, includeEntities=True) self.metadata.pop('extension') self.numVolumes = len(self.imgVolumes) self.nextVol = 0
def _filterFileList(self, fileList: List[str]) -> List[str]: """Class-private funtion to filter a list of files to include only allowed ones. Args: fileList - list of files to filter Returns: filtered fileList - containing only the allowed files """ if self.allowedFileTypes is None or len(self.allowedFileTypes) == 0: raise ValidationError( 'DataInterface: no allowed file types are set') if self.allowedFileTypes[0] == '*': return fileList filteredList = [] for filename in fileList: if os.path.isdir(filename): continue fileExtension = Path(filename).suffix if fileExtension in self.allowedFileTypes: filteredList.append(filename) return filteredList
def getImageData(self, streamId: int, imageIndex: int = None, timeout: int = 5) -> pydicom.dataset.FileDataset: """ Get data from a stream initialized with initScannerStream or initOpenNeuroStream Args: streamId: Id of a previously opened stream. imageIndex: Which image from the stream to retrieve. If left blank it will retrieve the next image in the stream (next after either the last request or starting from 0 if no previous requests) Returns: The bytes array representing the image data returns pydicom.dataset.FileDataset """ if self.currentStreamId == 0 or self.currentStreamId != streamId or self.streamInfo.streamId != streamId: raise ValidationError( f"StreamID mismatch {self.currentStreamId} : {streamId}") if imageIndex is None: imageIndex = self.streamInfo.imgIndex filename = self.streamInfo.filePattern.format(TR=imageIndex) retries = 0 while retries < 5: retries += 1 try: data = self.watchFile(filename, timeout) dicomImg = readDicomFromBuffer(data) # Convert pixel data to a numpy.ndarray internally. # Note: the conversion cause error in pickle encoding # dicomImg.convert_pixel_data() self.streamInfo.imgIndex = imageIndex + 1 return dicomImg except TimeoutError as err: logging.warning( f"Timeout waiting for {filename}. Retry in 100 ms") time.sleep(0.1) except Exception as err: logging.error( f"getImageData Error, filename {filename} err: {err}") return None return None