Package fabio :: Module edfimage
[hide private]
[frames] | no frames]

Source Code for Module fabio.edfimage

  1  #!/usr/bin/env python 
  2  # -*- coding: utf8 -*- 
  3  """ 
  4   
  5  License: GPLv2+ 
  6   
  7  Authors: 
  8  ........ 
  9  * Henning O. Sorensen & Erik Knudsen: 
 10    Center for Fundamental Research: Metal Structures in Four Dimensions; 
 11    Risoe National Laboratory; 
 12    Frederiksborgvej 399; 
 13    DK-4000 Roskilde; 
 14    email:erik.knudsen@risoe.dk 
 15  * Jon Wright & Jérôme Kieffer: 
 16    European Synchrotron Radiation Facility; 
 17    Grenoble (France) 
 18   
 19   
 20  """ 
 21  from __future__ import with_statement 
 22  import os, logging, types 
 23  logger = logging.getLogger("edfimage") 
 24  import numpy 
 25  from fabioimage import fabioimage 
 26  from fabioutils import isAscii, toAscii, nice_int 
 27  from compression import decBzip2, decGzip, decZlib 
 28   
 29   
 30  BLOCKSIZE = 512 
 31  DATA_TYPES = {  "SignedByte"    :  numpy.int8, 
 32                  "Signed8"       :  numpy.int8, 
 33                  "UnsignedByte"  :  numpy.uint8, 
 34                  "Unsigned8"     :  numpy.uint8, 
 35                  "SignedShort"   :  numpy.int16, 
 36                  "Signed16"      :  numpy.int16, 
 37                  "UnsignedShort" :  numpy.uint16, 
 38                  "Unsigned16"    :  numpy.uint16, 
 39                  "UnsignedShortInteger" : numpy.uint16, 
 40                  "SignedInteger" :  numpy.int32, 
 41                  "Signed32"      :  numpy.int32, 
 42                  "UnsignedInteger":  numpy.uint32, 
 43                  "Unsigned32"    :  numpy.uint32, 
 44                  "SignedLong"    :  numpy.int32, 
 45                  "UnsignedLong"  :  numpy.uint32, 
 46                  "Signed64"      :  numpy.int64, 
 47                  "Unsigned64"    :  numpy.uint64, 
 48                  "FloatValue"    :  numpy.float32, 
 49                  "FLOATVALUE"    :  numpy.float32, 
 50                  "FLOAT"         :  numpy.float32, # fit2d 
 51                  "Float"         :  numpy.float32, # fit2d 
 52                  "FloatIEEE32"   :  numpy.float32, 
 53                  "Float32"       :  numpy.float32, 
 54                  "Double"        :  numpy.float64, 
 55                  "DoubleValue"   :  numpy.float64, 
 56                  "FloatIEEE64"   :  numpy.float64, 
 57                  "DoubleIEEE64"  :  numpy.float64} 
 58  try: 
 59      DATA_TYPES["FloatIEEE128" ] =  numpy.float128 
 60      DATA_TYPES["DoubleIEEE128" ] =  numpy.float128 
 61      DATA_TYPES["QuadrupleValue" ] =  numpy.float128 
 62   
 63  except AttributeError: 
 64      # not in your numpy 
 65      pass 
 66   
 67  NUMPY_EDF_DTYPE = {"int8"       :"SignedByte", 
 68                     "int16"      :"SignedShort", 
 69                     "int32"      :"SignedInteger", 
 70                     "int64"      :"Signed64", 
 71                     "uint8"      :"UnsignedByte", 
 72                     "uint16"     :"UnsignedShort", 
 73                     "uint32"     :"UnsignedInteger", 
 74                     "uint64"     :"Unsigned64", 
 75                     "float32"    :"FloatValue", 
 76                     "float64"    :"DoubleValue", 
 77                     "float128"   :"QuadrupleValue", 
 78               } 
 79   
 80  MINIMUM_KEYS = ['HEADERID', 
 81                  'IMAGE', 
 82                  'BYTEORDER', 
 83                  'DATATYPE', 
 84                  'DIM_1', 
 85                  'DIM_2', 
 86                  'SIZE'] # Size is thought to be essential for writing at least 
 87   
 88  DEFAULT_VALUES = { 
 89                    # I do not define default values as they will be calculated at write time 
 90                    # JK20110415 
 91                    } 
92 93 -class Frame(object):
94 """ 95 A class representing a single frame in an EDF file 96 """
97 - def __init__(self, data=None, header=None, header_keys=None, number=None):
98 if header is None: 99 self.header = {} 100 else: 101 self.header = dict(header) 102 103 if header_keys is None: 104 self.header_keys = self.header.keys() 105 else: 106 self.header_keys = header_keys[:] 107 for key in header_keys: 108 if key not in self.header: 109 logger.warning("Header key %s, in header_keys is not in header dictionary, poping !!!" % key) 110 self.header_keys.remove(key) 111 112 self.capsHeader = {} 113 for key in self.header_keys: 114 try: 115 self.capsHeader[key.upper()] = key 116 except AttributeError: 117 logger.warning("Header key %s is not a string" % key) 118 self._data = data 119 self.dims = [] 120 self.dim1 = 0 121 self.dim2 = 0 122 self.start = None # Position of start of raw data in file 123 self.size = None # size of raw data in file 124 self.file = None # opened file object with locking capabilities !!! 125 self.bpp = None 126 self._bytecode = None 127 if (number is not None) and isinstance(number, int): 128 self.iFrame = number 129 else: 130 self.iFrame = 0
131
132 - def parseheader(self, block):
133 """ 134 Parse the header in some EDF format from an already open file 135 136 @param block: string representing the header block 137 @type block: string, should be full ascii 138 @return: size of the binary blob 139 """ 140 #reset values ... 141 self.header = {} 142 self.capsHeader = {} 143 self.header_keys = [] 144 self.size = None 145 calcsize = 1 146 self.dims = [] 147 148 for line in block.split(';'): 149 if '=' in line: 150 key, val = line.split('=' , 1) 151 # Why would someone put null bytes in a header? 152 key = key.replace("\x00"," ").strip() 153 self.header[key] = val.replace("\x00"," ").strip() 154 self.capsHeader[key.upper()] = key 155 self.header_keys.append(key) 156 157 # Compute image size 158 if "SIZE" in self.capsHeader: 159 try: 160 self.size = nice_int(self.header[self.capsHeader["SIZE"]]) 161 except ValueError: 162 logger.warning("Unable to convert to integer : %s %s " % (self.capsHeader["SIZE"], self.header[self.capsHeader["SIZE"]])) 163 if "DIM_1" in self.capsHeader: 164 try: 165 dim1 = nice_int(self.header[self.capsHeader['DIM_1']]) 166 except ValueError: 167 logger.error("Unable to convert to integer Dim_1: %s %s" % (self.capsHeader["DIM_1"], self.header[self.capsHeader["DIM_1"]])) 168 else: 169 calcsize *= dim1 170 self.dims.append(dim1) 171 else: 172 logger.error("No Dim_1 in headers !!!") 173 if "DIM_2" in self.capsHeader: 174 try: 175 dim2 = nice_int(self.header[self.capsHeader['DIM_2']]) 176 except ValueError: 177 logger.error("Unable to convert to integer Dim_3: %s %s" % (self.capsHeader["DIM_2"], self.header[self.capsHeader["DIM_2"]])) 178 else: 179 calcsize *= dim2 180 self.dims.append(dim2) 181 else: 182 logger.error("No Dim_2 in headers !!!") 183 iDim = 3 184 # JON: this appears to be for nD images, but we don't treat those 185 while iDim is not None: 186 strDim = "DIM_%i" % iDim 187 if strDim in self.capsHeader: 188 try: 189 dim3 = nice_int(self.header[self.capsHeader[strDim]]) 190 except ValueError: 191 logger.error("Unable to convert to integer %s: %s %s" 192 % (strDim, self.capsHeader[strDim], self.header[self.capsHeader[strDim]])) 193 dim3 = None 194 iDim = None 195 else: 196 if dim3 > 1: 197 # Otherwise treat dim3==1 as a 2D image 198 calcsize *= dim3 199 self.dims.append(dim3) 200 iDim += 1 201 202 else: 203 logger.debug("No Dim_3 -> it is a 2D image") 204 iDim = None 205 if self._bytecode is None: 206 if "DATATYPE" in self.capsHeader: 207 self._bytecode = DATA_TYPES[self.header[self.capsHeader['DATATYPE']]] 208 else: 209 self._bytecode = numpy.uint16 210 logger.warning("Defaulting type to uint16") 211 self.bpp = len(numpy.array(0, self._bytecode).tostring()) 212 calcsize *= self.bpp 213 if (self.size is None): 214 self.size = calcsize 215 elif (self.size != calcsize): 216 if ("COMPRESSION" in self.capsHeader) and (self.header[self.capsHeader['COMPRESSION']].upper().startswith("NO")): 217 logger.info("Mismatch between the expected size %s and the calculated one %s" % (self.size, calcsize)) 218 self.size = calcsize 219 220 for i, n in enumerate(self.dims): 221 setattr(self, "dim%i" % (i + 1), n) 222 223 return self.size
224 225
226 - def swap_needed(self):
227 """ 228 Decide if we need to byteswap 229 """ 230 if ('Low' in self.header[self.capsHeader['BYTEORDER']] and numpy.little_endian) or \ 231 ('High' in self.header[self.capsHeader['BYTEORDER']] and not numpy.little_endian): 232 return False 233 if ('High' in self.header[self.capsHeader['BYTEORDER']] and numpy.little_endian) or \ 234 ('Low' in self.header[self.capsHeader['BYTEORDER']] and not numpy.little_endian): 235 if self.bpp in [2, 4, 8]: 236 return True 237 else: 238 return False
239 240
241 - def getData(self):
242 """ 243 Unpack a binary blob according to the specification given in the header 244 245 @return: dataset as numpy.ndarray 246 """ 247 data = None 248 if self._data is not None: 249 data = self._data 250 elif self.file is None: 251 data = self._data 252 else: 253 if self._bytecode is None: 254 if "DATATYPE" in self.capsHeader: 255 self._bytecode = DATA_TYPES[self.header[self.capsHeader["DATATYPE"]]] 256 else: 257 self._bytecode = numpy.uint16 258 dims = self.dims[:] 259 dims.reverse() 260 with self.file.lock: 261 if self.file.closed: 262 logger.error("file: %s from %s is closed. Cannot read data." % (self.file, self.file.filename)) 263 return 264 else: 265 self.file.seek(self.start) 266 fileData = self.file.read(self.size) 267 268 if ("COMPRESSION" in self.capsHeader): 269 compression = self.header[self.capsHeader["COMPRESSION"]].upper() 270 uncompressed_size = self.bpp 271 for i in dims: 272 uncompressed_size *= i 273 if "OFFSET" in compression : 274 try: 275 import byte_offset#IGNORE:F0401 276 except ImportError, error: 277 logger.error("Unimplemented compression scheme: %s (%s)" % (compression, error)) 278 else: 279 myData = byte_offset.analyseCython(fileData, size=uncompressed_size) 280 rawData = myData.astype(self._bytecode).tostring() 281 self.size = uncompressed_size 282 elif compression == "NONE": 283 rawData = fileData 284 elif "GZIP" in compression: 285 rawData = decGzip(fileData) 286 self.size = uncompressed_size 287 elif "BZ" in compression : 288 rawData = decBzip2(fileData) 289 self.size = uncompressed_size 290 elif "Z" in compression : 291 rawData = decZlib(fileData) 292 self.size = uncompressed_size 293 else: 294 logger.warning("Unknown compression scheme %s" % compression) 295 rawData = fileData 296 297 else: 298 rawData = fileData 299 300 expected = self.size 301 obtained = len(rawData) 302 if expected > obtained: 303 logger.error("Data stream is incomplete: %s < expected %s bytes" % (obtained, expected)) 304 rawData += "\x00" * (expected - obtained) 305 elif expected < len(rawData): 306 logger.info("Data stream contains trailing junk : %s > expected %s bytes" % (obtained, expected)) 307 rawData = rawData[:expected] 308 if self.swap_needed(): 309 data = numpy.fromstring(rawData, self._bytecode).byteswap().reshape(tuple(dims)) 310 else: 311 data = numpy.fromstring(rawData, self._bytecode).reshape(tuple(dims)) 312 self._data = data 313 self._bytecode = data.dtype.type 314 return data
315 - def setData(self, npa=None):
316 """Setter for data in edf frame""" 317 self._data = npa
318 data = property(getData, setData, "property: (edf)frame.data, uncompress the datablock when needed")
319 - def getByteCode(self):
320 if self._bytecode is None: 321 self._bytecode = self.data.dtype.type 322 323 return self._bytecode
324 - def setByteCode(self, _iVal):
325 self._bytecode = _iVal
326 bytecode = property(getByteCode, setByteCode) 327
328 - def getEdfBlock(self, force_type=None, fit2dMode=False):
329 """ 330 @param force_type: type of the dataset to be enforced like "float64" or "uint16" 331 @type force_type: string or numpy.dtype 332 @param fit2dMode: enforce compatibility with fit2d and starts countimg number of images at 1 333 @type fit2dMode: boolean 334 @return: ascii header block 335 @rtype: python string with the concatenation of the ascii header and the binary data block 336 """ 337 if force_type is not None: 338 data = self.data.astype(force_type) 339 else: 340 data = self.data 341 fit2dMode = bool(fit2dMode) 342 for key in self.header: 343 KEY = key.upper() 344 if KEY not in self.capsHeader: 345 self.capsHeader[KEY] = key 346 if key not in self.header_keys: 347 self.header_keys.append(key) 348 349 header = self.header.copy() 350 header_keys = self.header_keys[:] 351 capsHeader = self.capsHeader.copy() 352 353 listHeader = ["{\n"] 354 # First of all clean up the headers: 355 for i in capsHeader: 356 if "DIM_" in i: 357 header.pop(capsHeader[i]) 358 header_keys.remove(capsHeader[i]) 359 for KEY in ["SIZE", "EDF_BINARYSIZE", "EDF_HEADERSIZE", "BYTEORDER", "DATATYPE", "HEADERID", "IMAGE"]: 360 if KEY in capsHeader: 361 header.pop(capsHeader[KEY]) 362 header_keys.remove(capsHeader[KEY]) 363 if "EDF_DATABLOCKID" in capsHeader: 364 header_keys.remove(capsHeader["EDF_DATABLOCKID"]) 365 #but do not remove the value from dict, instead reset the key ... 366 if capsHeader["EDF_DATABLOCKID"] != "EDF_DataBlockID": 367 header["EDF_DataBlockID"] = header.pop(capsHeader["EDF_DATABLOCKID"]) 368 capsHeader["EDF_DATABLOCKID"] = "EDF_DataBlockID" 369 370 # Then update static headers freshly deleted 371 header_keys.insert(0, "Size") 372 header["Size"] = len(data.tostring()) 373 header_keys.insert(0, "HeaderID") 374 header["HeaderID"] = "EH:%06d:000000:000000" % (self.iFrame + fit2dMode) 375 header_keys.insert(0, "Image") 376 header["Image"] = str(self.iFrame + fit2dMode) 377 378 dims = list(data.shape) 379 nbdim = len(dims) 380 for i in dims: 381 key = "Dim_%i" % nbdim 382 header[key] = i 383 header_keys.insert(0, key) 384 nbdim -= 1 385 header_keys.insert(0, "DataType") 386 header["DataType"] = NUMPY_EDF_DTYPE[str(numpy.dtype(data.dtype))] 387 header_keys.insert(0, "ByteOrder") 388 if numpy.little_endian: 389 header["ByteOrder"] = "LowByteFirst" 390 else: 391 header["ByteOrder"] = "HighByteFirst" 392 approxHeaderSize = 100 393 for key in header: 394 approxHeaderSize += 7 + len(key) + len(str(header[key])) 395 approxHeaderSize = BLOCKSIZE * (approxHeaderSize // BLOCKSIZE + 1) 396 header_keys.insert(0, "EDF_HeaderSize") 397 header["EDF_HeaderSize"] = str(BLOCKSIZE * (approxHeaderSize // BLOCKSIZE + 1)) 398 header_keys.insert(0, "EDF_BinarySize") 399 header["EDF_BinarySize"] = len(data.tostring()) 400 header_keys.insert(0, "EDF_DataBlockID") 401 if not "EDF_DataBlockID" in header: 402 header["EDF_DataBlockID"] = "%i.Image.Psd" % (self.iFrame + fit2dMode) 403 preciseSize = 4 #2 before {\n 2 after }\n 404 for key in header_keys: 405 #Escape keys or values that are no ascii 406 strKey = str(key) 407 if not isAscii(strKey, listExcluded=["}", "{"]): 408 logger.warning("Non ascii key %s, skipping" % strKey) 409 continue 410 strValue = str(header[key]) 411 if not isAscii(strValue, listExcluded=["}", "{"]): 412 logger.warning("Non ascii value %s, skipping" % strValue) 413 continue 414 line = strKey + " = " + strValue + " ;\n" 415 preciseSize += len(line) 416 listHeader.append(line) 417 if preciseSize > approxHeaderSize: 418 logger.error("I expected the header block only at %s in fact it is %s" % (approxHeaderSize, preciseSize)) 419 for idx, line in enumerate(listHeader[:]): 420 if line.startswith("EDF_HeaderSize"): 421 headerSize = BLOCKSIZE * (preciseSize // BLOCKSIZE + 1) 422 newline = "EDF_HeaderSize = %s ;\n" % headerSize 423 delta = len(newline) - len(line) 424 if (preciseSize // BLOCKSIZE) != ((preciseSize + delta) // BLOCKSIZE): 425 headerSize = BLOCKSIZE * ((preciseSize + delta) // BLOCKSIZE + 1) 426 newline = "EDF_HeaderSize = %s ;\n" % headerSize 427 preciseSize = preciseSize + delta 428 listHeader[idx] = newline 429 break 430 else: 431 headerSize = approxHeaderSize 432 listHeader.append(" "*(headerSize - preciseSize) + "}\n") 433 return "".join(listHeader) + data.tostring()
434
435 436 437 -class edfimage(fabioimage):
438 """ Read and try to write the ESRF edf data format """ 439
440 - def __init__(self, data=None , header=None, header_keys=None, frames=None):
441 self.currentframe = 0 442 self.filesize = None 443 try: 444 dim = len(data.shape) 445 except Exception, error: #IGNORE:W0703 446 logger.debug("Data don't look like a numpy array (%s), resetting all!!" % error) 447 data = None 448 dim = 0 449 fabioimage.__init__(self, data, header) 450 if dim == 2: 451 fabioimage.__init__(self, data, header) 452 elif dim == 1 : 453 data.shape = (0, len(data)) 454 fabioimage.__init__(self, data, header) 455 elif dim == 3 : 456 fabioimage.__init__(self, data[0, :, :], header) 457 elif dim == 4 : 458 fabioimage.__init__(self, data[0, 0, :, :], header) 459 elif dim == 5 : 460 fabioimage.__init__(self, data[0, 0, 0, :, :], header) 461 462 if frames is None: 463 frame = Frame(data=self.data, header=self.header, 464 header_keys=header_keys , 465 number=self.currentframe) 466 self.__frames = [frame] 467 else: 468 self.__frames = frames
469 470 @staticmethod
471 - def checkHeader(header=None):
472 """ 473 Empty for fabioimage but may be populated by others classes 474 """ 475 if type(header) != types.DictionaryType: 476 return {} 477 new = {} 478 for key, value in header.items(): 479 new[toAscii(key, ";{}")] = toAscii(value, ";{}") 480 return new
481 482 @staticmethod
483 - def _readHeaderBlock(infile):
484 """ 485 Read in a header in some EDF format from an already open file 486 487 @param infile: file object open in read mode 488 @return: string (or None if no header was found. 489 """ 490 491 block = infile.read(BLOCKSIZE) 492 if len(block) < BLOCKSIZE: 493 logger.debug("Under-short header: only %i bytes in %s" % (len(block), infile.name)) 494 return 495 if (block.find("{") < 0) : 496 # This does not look like an edf file 497 logger.warning("no opening {. Corrupt header of EDF file %s" % infile.name) 498 return 499 while '}' not in block: 500 block = block + infile.read(BLOCKSIZE) 501 if len(block) > BLOCKSIZE * 20: 502 logger.warning("Runaway header in EDF file") 503 return 504 start = block.find("{") + 1 505 end = block.find("}") 506 507 # Now it is essential to go to the start of the binary part 508 if block[end: end + 3] == "}\r\n": 509 offset = end + 3 - len(block) 510 elif block[end: end + 2] == "}\n": 511 offset = end + 2 - len(block) 512 else: 513 logger.error("Unable to locate start of the binary section") 514 offset = None 515 if offset is not None: 516 infile.seek(offset, os.SEEK_CUR) 517 return block[start:end]
518 519
520 - def _readheader(self, infile):
521 """ 522 Read all headers in a file and populate self.header 523 data is not yet populated 524 @type infile: file object open in read mode 525 """ 526 self.__frames = [] 527 bContinue = True 528 while bContinue: 529 block = self._readHeaderBlock(infile) 530 if block is None: 531 bContinue = False 532 break 533 frame = Frame(number=self.nframes) 534 size = frame.parseheader(block) 535 frame.file = infile 536 frame.start = infile.tell() 537 frame.size = size 538 self.__frames += [frame] 539 try: 540 infile.seek(size, os.SEEK_CUR) 541 except Exception, error: 542 logger.warning("infile is %s" % infile) 543 logger.warning("Position is %s" % infile.tell()) 544 logger.warning("size is %s" % size) 545 logger.error("It seams this error occurs under windows when reading a (large-) file over network: %s ", error) 546 raise Exception(error) 547 548 if frame.start + size > infile.size: 549 logger.warning("Non complete datablock: got %s, expected %s" % (infile.size - frame.start, size)) 550 bContinue = False 551 break 552 553 for i, frame in enumerate(self.__frames): 554 missing = [] 555 for item in MINIMUM_KEYS: 556 if item not in frame.capsHeader: 557 missing.append(item) 558 if len(missing) > 0: 559 logger.info("EDF file %s frame %i misses mandatory keys: %s " % (self.filename, i, " ".join(missing))) 560 self.currentframe = 0
561 562
563 - def read(self, fname, frame=None):
564 """ 565 Read in header into self.header and 566 the data into self.data 567 """ 568 self.resetvals() 569 self.filename = fname 570 571 infile = self._open(fname, "rb") 572 self._readheader(infile) 573 if frame is None: 574 pass 575 elif frame < self.nframes: 576 self = self.getframe(frame) 577 else: 578 logger.error("Reading file %s You requested frame %s but only %s frames are available", fname, frame, self.nframes) 579 self.resetvals() 580 # ensure the PIL image is reset 581 self.pilimage = None 582 return self
583
584 - def swap_needed(self):
585 """ 586 Decide if we need to byteswap 587 """ 588 if ('Low' in self.header[self.capsHeader['BYTEORDER']] and numpy.little_endian) or \ 589 ('High' in self.header[self.capsHeader['BYTEORDER']] and not numpy.little_endian): 590 return False 591 if ('High' in self.header[self.capsHeader['BYTEORDER']] and numpy.little_endian) or \ 592 ('Low' in self.header[self.capsHeader['BYTEORDER']] and not numpy.little_endian): 593 if self.bpp in [2, 4, 8]: 594 return True 595 else: 596 return False
597
598 - def unpack(self):
599 """ 600 Unpack a binary blob according to the specification given in the header and return the dataset 601 602 @return: dataset as numpy.ndarray 603 """ 604 return self.__frames[self.currentframe].getData()
605 606
607 - def getframe(self, num):
608 """ returns the file numbered 'num' in the series as a fabioimage """ 609 newImage = None 610 if self.nframes == 1: 611 logger.debug("Single frame EDF; having fabioimage default behavour: %s" % num) 612 newImage = fabioimage.getframe(self, num) 613 elif num in xrange(self.nframes): 614 logger.debug("Multi frame EDF; having edfimage specific behavour: %s/%s" % (num, self.nframes)) 615 newImage = edfimage(frames=self.__frames) 616 newImage.currentframe = num 617 newImage.filename = self.filename 618 else: 619 txt = "Cannot access frame: %s/%s" % (num, self.nframes) 620 logger.error(txt) 621 raise ValueError("edfimage.getframe:" + txt) 622 return newImage
623 624
625 - def previous(self):
626 """ returns the previous file in the series as a fabioimage """ 627 newImage = None 628 if self.nframes == 1: 629 newImage = fabioimage.previous(self) 630 else: 631 newFrameId = self.currentframe - 1 632 newImage = self.getframe(newFrameId) 633 return newImage
634 635
636 - def next(self):
637 """ returns the next file in the series as a fabioimage """ 638 newImage = None 639 if self.nframes == 1: 640 newImage = fabioimage.next(self) 641 else: 642 newFrameId = self.currentframe + 1 643 newImage = self.getframe(newFrameId) 644 return newImage
645 646
647 - def write(self, fname, force_type=None, fit2dMode=False):
648 """ 649 Try to write a file 650 check we can write zipped also 651 mimics that fabian was writing uint16 (we sometimes want floats) 652 653 @param force_type: can be numpy.uint16 or simply "float" 654 @return: None 655 656 """ 657 658 outfile = self._open(fname, mode="wb") 659 for i, frame in enumerate(self.__frames): 660 frame.iFrame = i 661 outfile.write(frame.getEdfBlock(force_type=force_type, fit2dMode=fit2dMode)) 662 outfile.close()
663 664
665 - def appendFrame(self, frame=None, data=None, header=None):
666 """ 667 Method used add a frame to an EDF file 668 @param frame: frame to append to edf image 669 @type frame: instance of Frame 670 @return: None 671 """ 672 if isinstance(frame, Frame): 673 self.__frames.append(frame) 674 else: 675 self.__frames.append(Frame(data, header))
676 677
678 - def deleteFrame(self, frameNb=None):
679 """ 680 Method used to remove a frame from an EDF image. by default the last one is removed. 681 @param frameNb: frame number to remove, by default the last. 682 @type frameNb: integer 683 @return: None 684 """ 685 if frameNb is None: 686 self.__frames.pop() 687 else: 688 self.__frames.pop(frameNb)
689
690 - def fastReadData(self, filename=None):
691 """ 692 This is a special method that will read and return the data from another file ... 693 The aim is performances, ... but only supports uncompressed files. 694 695 @return: data from another file using positions from current edfimage 696 """ 697 if (filename is None) or not os.path.isfile(filename): 698 raise RuntimeError("edfimage.fastReadData is only valid with another file: %s does not exist" % (filename)) 699 data = None 700 frame = self.__frames[self.currentframe] 701 with open(filename, "rb")as f: 702 f.seek(frame.start) 703 raw = f.read(frame.size) 704 try: 705 data = numpy.fromstring(raw, dtype=self.bytecode) 706 data.shape = self.data.shape 707 except Exception, err : 708 logger.error("unable to convert file content to numpy array: %s", err) 709 return data
710
711 - def fastReadROI(self, filename, coords=None):
712 """ 713 Method reading Region of Interest of another file based on metadata available in current edfimage. 714 The aim is performances, ... but only supports uncompressed files. 715 716 @return: ROI-data from another file using positions from current edfimage 717 @rtype: numpy 2darray 718 """ 719 if (filename is None) or not os.path.isfile(filename): 720 raise RuntimeError("edfimage.fastReadData is only valid with another file: %s does not exist" % (filename)) 721 data = None 722 frame = self.__frames[self.currentframe] 723 724 if len(coords) == 4: 725 slice1 = self.make_slice(coords) 726 elif len(coords) == 2 and isinstance(coords[0], slice) and \ 727 isinstance(coords[1], slice): 728 slice1 = coords 729 else: 730 logger.warning('readROI: Unable to understand Region Of Interest: got %s', coords) 731 return 732 d1 = self.data.shape[-1] 733 start0 = slice1[0].start 734 start1 = slice1[1].start 735 slice2 = (slice(0, slice1[0].stop - start0, slice1[0].step), 736 slice(0, slice1[1].stop - start1, slice1[1].step)) 737 start = frame.start + self.bpp * (d1 * start0 + start1) 738 size = self.bpp * ((slice2[0].stop) * d1) 739 with open(filename, "rb")as f: 740 f.seek(start) 741 raw = f.read(size) 742 try: 743 data = numpy.fromstring(raw, dtype=self.bytecode) 744 data.shape = -1, d1 745 except Exception, err : 746 logger.error("unable to convert file content to numpy array: %s", err) 747 return data[slice2]
748 749 750 ################################################################################ 751 # Properties definition for header, data, header_keys and capsHeader 752 ################################################################################
753 - def getNbFrames(self):
754 """ 755 Getter for number of frames 756 """ 757 return len(self.__frames)
758 - def setNbFrames(self, val):
759 """ 760 Setter for number of frames ... should do nothing. Here just to avoid bugs 761 """ 762 if val != len(self.__frames): 763 logger.warning("trying to set the number of frames ")
764 nframes = property(getNbFrames, setNbFrames, "property: number of frames in EDF file") 765 766
767 - def getHeader(self):
768 """ 769 Getter for the headers. used by the property header, 770 """ 771 return self.__frames[self.currentframe].header
772 - def setHeader(self, _dictHeader):
773 """ 774 Enforces the propagation of the header to the list of frames 775 """ 776 try: 777 self.__frames[self.currentframe].header = _dictHeader 778 except AttributeError: 779 self.__frames = [Frame(header=_dictHeader)] 780 except IndexError: 781 if self.currentframe < len(self.__frames): 782 self.__frames.append(Frame(header=_dictHeader))
783 - def delHeader(self):
784 """ 785 Deleter for edf header 786 """ 787 self.__frames[self.currentframe].header = {}
788 header = property(getHeader, setHeader, delHeader, "property: header of EDF file") 789
790 - def getHeaderKeys(self):
791 """ 792 Getter for edf header_keys 793 """ 794 return self.__frames[self.currentframe].header_keys
795 - def setHeaderKeys(self, _listtHeader):
796 """ 797 Enforces the propagation of the header_keys to the list of frames 798 @param _listtHeader: list of the (ordered) keys in the header 799 @type _listtHeader: python list 800 """ 801 try: 802 self.__frames[self.currentframe].header_keys = _listtHeader 803 except AttributeError: 804 self.__frames = [Frame(header_keys=_listtHeader)] 805 except IndexError: 806 if self.currentframe < len(self.__frames): 807 self.__frames.append(Frame(header_keys=_listtHeader))
808 - def delHeaderKeys(self):
809 """ 810 Deleter for edf header_keys 811 """ 812 self.__frames[self.currentframe].header_keys = []
813 header_keys = property(getHeaderKeys, setHeaderKeys, delHeaderKeys, "property: header_keys of EDF file") 814
815 - def getData(self):
816 """ 817 getter for edf Data 818 @return: data for current frame 819 @rtype: numpy.ndarray 820 """ 821 npaData = None 822 try: 823 npaData = self.__frames[self.currentframe].data 824 except AttributeError: 825 self.__frames = [Frame()] 826 npaData = self.__frames[self.currentframe].data 827 except IndexError: 828 if self.currentframe < len(self.__frames): 829 self.__frames.append(Frame()) 830 npaData = self.__frames[self.currentframe].data 831 return npaData
832
833 - def setData(self, _data):
834 """ 835 Enforces the propagation of the data to the list of frames 836 @param _data: numpy array representing data 837 """ 838 try: 839 self.__frames[self.currentframe].data = _data 840 except AttributeError: 841 self.__frames = [Frame(data=_data)] 842 except IndexError: 843 if self.currentframe < len(self.__frames): 844 self.__frames.append(Frame(data=_data))
845 - def delData(self):
846 """ 847 deleter for edf Data 848 """ 849 self.__frames[self.currentframe].data = None
850 data = property(getData, setData, delData, "property: data of EDF file") 851
852 - def getCapsHeader(self):
853 """ 854 getter for edf headers keys in upper case 855 @return: data for current frame 856 @rtype: dict 857 """ 858 return self.__frames[self.currentframe].capsHeader
859 - def setCapsHeader(self, _data):
860 """ 861 Enforces the propagation of the header_keys to the list of frames 862 @param _data: numpy array representing data 863 """ 864 self.__frames[self.currentframe].capsHeader = _data
865 - def delCapsHeader(self):
866 """ 867 deleter for edf capsHeader 868 """ 869 self.__frames[self.currentframe].capsHeader = {}
870 capsHeader = property(getCapsHeader, setCapsHeader, delCapsHeader, "property: capsHeader of EDF file, i.e. the keys of the header in UPPER case.") 871
872 - def getDim1(self):
873 return self.__frames[self.currentframe].dim1
874 - def setDim1(self, _iVal):
875 try: 876 self.__frames[self.currentframe].dim1 = _iVal 877 except AttributeError: 878 self.__frames = [Frame()] 879 except IndexError: 880 if self.currentframe < len(self.__frames): 881 self.__frames.append(Frame()) 882 self.__frames[self.currentframe].dim1 = _iVal
883 dim1 = property(getDim1, setDim1)
884 - def getDim2(self):
885 return self.__frames[self.currentframe].dim2
886 - def setDim2(self, _iVal):
887 try: 888 self.__frames[self.currentframe].dim2 = _iVal 889 except AttributeError: 890 self.__frames = [Frame()] 891 except IndexError: 892 if self.currentframe < len(self.__frames): 893 self.__frames.append(Frame()) 894 self.__frames[self.currentframe].dim2 = _iVal
895 dim2 = property(getDim2, setDim2) 896
897 - def getDims(self):
898 return self.__frames[self.currentframe].dims
899 dims = property(getDims)
900 - def getByteCode(self):
901 return self.__frames[self.currentframe].bytecode
902 - def setByteCode(self, _iVal):
903 try: 904 self.__frames[self.currentframe].bytecode = _iVal 905 except AttributeError: 906 self.__frames = [Frame()] 907 except IndexError: 908 if self.currentframe < len(self.__frames): 909 self.__frames.append(Frame()) 910 self.__frames[self.currentframe].bytecode = _iVal
911 bytecode = property(getByteCode, setByteCode)
912 - def getBpp(self):
913 return self.__frames[self.currentframe].bpp
914 - def setBpp(self, _iVal):
915 try: 916 self.__frames[self.currentframe].bpp = _iVal 917 except AttributeError: 918 self.__frames = [Frame()] 919 except IndexError: 920 if self.currentframe < len(self.__frames): 921 self.__frames.append(Frame()) 922 self.__frames[self.currentframe].bpp = _iVal
923 bpp = property(getBpp, setBpp)
924