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

Source Code for Module fabio.mar345image

  1  #!/usr/bin/env python 
  2  #coding: utf8  
  3  from __future__ import with_statement 
  4  __doc__ = """ 
  5   
  6  Authors: 
  7  ........ 
  8  * Henning O. Sorensen & Erik Knudsen: 
  9    Center for Fundamental Research: Metal Structures in Four Dimensions; 
 10    Risoe National Laboratory; 
 11    Frederiksborgvej 399; 
 12    DK-4000 Roskilde; 
 13    email:erik.knudsen@risoe.dk 
 14  * Jon Wright, Jérôme Kieffer & Gaël Goret: 
 15    European Synchrotron Radiation Facility; 
 16    Grenoble (France) 
 17   
 18            
 19  """ 
 20   
 21  from fabioimage import fabioimage 
 22  import numpy, struct, time, sys 
 23  import logging 
 24  logger = logging.getLogger("mar345image") 
 25  from compression import compPCK, decPCK 
26 27 -class mar345image(fabioimage):
28 _need_a_real_file = True
29 - def __init__(self, *args, **kwargs):
30 fabioimage.__init__(self, *args, **kwargs) 31 self.numhigh = None 32 self.numpixels = None
33
34 - def read(self, fname, frame=None):
35 """ Read a mar345 image""" 36 self.filename = fname 37 f = self._open(self.filename, "rb") 38 self._readheader(f) 39 if 'compressed' in self.header['Format']: 40 try: 41 self.data = decPCK(f, self.dim1, self.dim2, self.numhigh) 42 except Exception, error: 43 logger.error('%s. importing the mar345_io backend: generate an empty 1x1 picture' % error) 44 f.close() 45 self.dim1 = 1 46 self.dim2 = 1 47 self.bytecode = numpy.int # 48 self.data = numpy.resize(numpy.array([0], numpy.int), [1, 1]) 49 return self 50 51 else: 52 logger.error("cannot handle these formats yet " + \ 53 "due to lack of documentation") 54 return None 55 self.bytecode = numpy.uint 56 f.close() 57 return self
58
59 - def _readheader(self, infile=None):
60 """ Read a mar345 image header """ 61 # clip was not used anywhere - commented out 62 # clip = '\x00' 63 #using a couple of local variables inside this function 64 f = infile 65 h = {} 66 67 #header is 4096 bytes long 68 l = f.read(64) 69 #the contents of the mar345 header is taken to be as 70 # described in 71 # http://www.mar-usa.com/support/downloads/mar345_formats.pdf 72 #the first 64 bytes are 4-byte integers (but in the CBFlib 73 # example image it seems to 128 bytes?) 74 #first 4-byte integer is a marker to check endianness 75 if struct.unpack("<i", l[0:4])[0] == 1234: 76 fs = '<i' 77 else: 78 fs = '>i' 79 80 #image dimensions 81 self.dim1 = self.dim2 = int(struct.unpack(fs, l[4:8])[0]) 82 #number of high intensity pixels 83 self.numhigh = struct.unpack(fs, l[2 * 4 : (2 + 1) * 4])[0] 84 h['NumHigh'] = self.numhigh 85 #Image format 86 i = struct.unpack(fs, l[3 * 4 : (3 + 1) * 4])[0] 87 if i == 1: 88 h['Format'] = 'compressed' 89 elif i == 2: 90 h['Format'] = 'spiral' 91 else: 92 h['Format'] = 'compressed' 93 logger.warning("image format could not be detetermined" + \ 94 "- assuming compressed mar345") 95 #collection mode 96 h['Mode'] = {0:'Dose', 1: 'Time'}[struct.unpack(fs, l[4 * 4:(4 + 1) * 4])[0]] 97 #total number of pixels 98 self.numpixels = struct.unpack(fs, l[5 * 4:(5 + 1) * 4])[0] 99 h['NumPixels'] = str(self.numpixels) 100 #pixel dimensions (length,height) in mm 101 h['PixelLength'] = struct.unpack(fs, l[6 * 4:(6 + 1) * 4])[0] / 1000.0 102 h['PixelHeight'] = struct.unpack(fs, l[7 * 4:(7 + 1) * 4])[0] / 1000.0 103 #x-ray wavelength in AA 104 h['Wavelength'] = struct.unpack(fs, l[8 * 4:(8 + 1) * 4])[0] / 1000000.0 105 #used distance 106 h['Distance'] = struct.unpack(fs, l[9 * 4:(9 + 1) * 4])[0] / 1000.0 107 #starting and ending phi 108 h['StartPhi'] = struct.unpack(fs, l[10 * 4:11 * 4])[0] / 1000.0 109 h['EndPhi'] = struct.unpack(fs, l[11 * 4:12 * 4])[0] / 1000.0 110 #starting and ending omega 111 h['StartOmega'] = struct.unpack(fs, l[12 * 4:13 * 4])[0] / 1000.0 112 h['EndOmega'] = struct.unpack(fs, l[13 * 4:14 * 4])[0] / 1000.0 113 #Chi and Twotheta angles 114 h['Chi'] = struct.unpack(fs, l[14 * 4:15 * 4])[0] / 1000.0 115 h['TwoTheta'] = struct.unpack(fs, l[15 * 4:16 * 4])[0] / 1000.0 116 117 #the rest of the header is ascii 118 # TODO: validate these values against the binaries already read 119 l = f.read(128) 120 if not 'mar research' in l: 121 logger.warning("the string \"mar research\" should be in " + \ 122 "bytes 65-76 of the header but was not") 123 start = 128 124 else: 125 start = l.index('mar research') 126 f.seek(64 + start) 127 l = f.read(4096 - start - 64).strip() 128 for m in l.splitlines(): 129 if m == 'END OF HEADER': 130 break 131 n = m.split(' ', 1) 132 if n[0] == '': 133 continue 134 if n[0] in ('PROGRAM', 'DATE', 'SCANNER', 'HIGH', 'MULTIPLIER', 135 'GAIN', 'WAVELENGTH', 'DISTANCE', 'RESOLUTION', 136 'CHI', 'TWOTHETA', 'MODE', 'TIME', 'GENERATOR', 137 'MONOCHROMATOR', 'REMARK'): 138 logger.debug("reading: %s %s", n[0], n[1]) 139 h[n[0]] = n[1].strip() 140 continue 141 if n[0] in ('FORMAT'): 142 (h['DIM'], h['FORMAT_TYPE'], h['NO_PIXELS']) = n[1].split() 143 continue 144 if n[0] in ('PIXEL', 'OFFSET', 'PHI', 'OMEGA', 'COUNTS', 145 'CENTER', 'INTENSITY', 'HISTOGRAM', 'COLLIMATOR'): 146 n = m.split() 147 h.update([(n[0] + '_' + n[j], n[j + 1]) for j in range(1, len(n), 2)]) 148 continue 149 self.header = h 150 return h
151
152 - def write(self, fname):
153 """Try to write mar345 file. This is still in beta version. 154 It uses CCP4 (LGPL) PCK1 algo from JPA""" 155 headers = self._writeheader() 156 hotpixels = self._high_intensity_pixel_records() 157 compressed_stream = compPCK(self.data) 158 try: 159 outfile = self._open(fname, mode="wb") 160 outfile.write(headers) 161 outfile.write(hotpixels) 162 outfile.write(compressed_stream) 163 outfile.close() 164 except Exception, error: 165 logger.error("Error in writing file %s: %s" % (fname, error))
166
167 - def _writeheader(self, linesep="\n", size=4096):#the standard padding does not inclued
168 """ 169 @param linesep: end of line separator 170 @return string/bytes containing the mar345 header 171 """ 172 try: 173 version = sys.modules["fabio"].version 174 except (KeyError, AttributeError): 175 version = "0.1.1" 176 lnsep = len(linesep) 177 178 self.header["HIGH"] = str(self.nb_overflow_pixels()) 179 binheader = numpy.zeros(16, "int32") 180 binheader[:4] = numpy.array([1234, self.dim1, int(self.header["HIGH"]), 1]) 181 binheader[4] = (self.header.get("MODE", "TIME") == "TIME") 182 binheader[5] = self.dim1 * self.dim2 183 binheader[6] = int(self.header.get("PIXEL_LENGTH", 1)) 184 binheader[7] = int(self.header.get("PIXEL_HEIGHT", 1)) 185 binheader[8] = int(float(self.header.get("WAVELENGTH", 1)) * 1e6) 186 binheader[9] = int(float(self.header.get("DISTANCE", 1)) * 1e3) 187 binheader[10] = int(float(self.header.get("PHI_START", 1)) * 1e3) 188 binheader[11] = int(float(self.header.get("PHI_END", 1)) * 1e3) 189 binheader[12] = int(float(self.header.get("OMEGA_START", 1)) * 1e3) 190 binheader[13] = int(float(self.header.get("OMEGA_END", 1)) * 1e3) 191 binheader[14] = int(float(self.header.get("CHI", 1)) * 1e3) 192 binheader[15] = int(float(self.header.get("TWOTHETA", 1)) * 1e3) 193 lstout = [binheader.tostring() + 'mar research'.ljust(64 - lnsep)] 194 lstout.append("PROGRAM".ljust(15) + (str(self.header.get("PROGRAM", "FabIO Version %s" % (version))).ljust(49 - lnsep))) 195 lstout.append("DATE".ljust(15) + (str(self.header.get("DATE", time.ctime()))).ljust(49 - lnsep)) 196 key = "SCANNER" 197 if key in self.header: 198 lstout.append(key.ljust(15) + str(self.header[key]).ljust(49 - lnsep)) 199 key = "FORMAT_TYPE" 200 if key in self.header: 201 lstout.append("FORMAT".ljust(15) + ("%s %s %s" % (self.dim1, self.header[key], self.dim1 * self.dim2)).ljust(49 - lnsep)) 202 key = "HIGH" 203 if key in self.header: 204 lstout.append(key.ljust(15) + str(self.header[key]).ljust(49 - lnsep)) 205 key1 = "PIXEL_LENGTH" 206 key2 = "PIXEL_HEIGHT" 207 if (key1 in self.header) and (key2 in self.header): 208 lstout.append("PIXEL".ljust(15) + ("LENGTH %s HEIGHT %s" % (self.header[key1], self.header[key2])).ljust(49 - lnsep)) 209 key1 = "OFFSET_ROFF" 210 key2 = "OFFSET_TOFF" 211 if key1 in self.header and key2 in self.header: 212 lstout.append("OFFSET".ljust(15) + ("ROFF %s TOFF %s" % (self.header[key1], self.header[key2])).ljust(49 - lnsep)) 213 key = "MULTIPLIER" 214 if key in self.header: 215 lstout.append(key.ljust(15) + str(self.header[key]).ljust(49 - lnsep)) 216 key = "GAIN" 217 if key in self.header: 218 lstout.append(key.ljust(15) + str(self.header[key]).ljust(49 - lnsep)) 219 key = "WAVELENGTH" 220 if key in self.header: 221 lstout.append(key.ljust(15) + str(self.header[key]).ljust(49 - lnsep)) 222 key = "DISTANCE" 223 if key in self.header: 224 lstout.append(key.ljust(15) + str(self.header[key]).ljust(49 - lnsep)) 225 key = "RESOLUTION" 226 if key in self.header: 227 lstout.append(key.ljust(15) + str(self.header[key]).ljust(49 - lnsep)) 228 key1 = "PHI_START" 229 key2 = "PHI_END" 230 key3 = "PHI_OSC" 231 if (key1 in self.header) and (key2 in self.header) and (key3 in self.header): 232 lstout.append("PHI".ljust(15) + ("START %s END %s OSC %s" % (self.header[key1], self.header[key2], self.header[key3])).ljust(49 - lnsep)) 233 key1 = "OMEGA_START" 234 key2 = "OMEGA_END" 235 key3 = "OMEGA_OSC" 236 if (key1 in self.header) and (key2 in self.header) and (key3 in self.header): 237 lstout.append("OMEGA".ljust(15) + ("START %s END %s OSC %s" % (self.header[key1], self.header[key2], self.header[key3])).ljust(49 - lnsep)) 238 key = "CHI" 239 if key in self.header: 240 lstout.append(key.ljust(15) + str(self.header[key]).ljust(49 - lnsep)) 241 key = "TWOTHETA" 242 if key in self.header: 243 lstout.append(key.ljust(15) + str(self.header[key]).ljust(49 - lnsep)) 244 key1 = "CENTER_X" 245 key2 = "CENTER_Y" 246 if (key1 in self.header) and (key2 in self.header): 247 lstout.append("CENTER".ljust(15) + ("X %s Y %s" % (self.header[key1], self.header[key2])).ljust(49 - lnsep)) 248 key = "MODE" 249 if key in self.header: 250 lstout.append(key.ljust(15) + str(self.header[key]).ljust(49 - lnsep)) 251 key = "TIME" 252 if key in self.header: 253 lstout.append(key.ljust(15) + str(self.header[key]).ljust(49 - lnsep)) 254 key1 = "COUNTS_START" 255 key2 = "COUNTS_END" 256 key3 = "COUNTS_NMEAS" 257 if key1 in self.header and key2 in self.header and key3 in self.header: 258 lstout.append("COUNTS".ljust(15) + ("START %s END %s NMEAS %s" % (self.header[key1], self.header[key2], self.header[key3])).ljust(49 - lnsep)) 259 key1 = "COUNTS_MIN" 260 key2 = "COUNTS_MAX" 261 if key1 in self.header and key2 in self.header: 262 lstout.append("COUNTS".ljust(15) + ("MIN %s MAX %s" % (self.header[key1], self.header[key2])).ljust(49 - lnsep)) 263 key1 = "COUNTS_AVE" 264 key2 = "COUNTS_SIG" 265 if key1 in self.header and key2 in self.header: 266 lstout.append("COUNTS".ljust(15) + ("AVE %s SIG %s" % (self.header[key1], self.header[key2])).ljust(49 - lnsep)) 267 key1 = "INTENSITY_MIN" 268 key2 = "INTENSITY_MAX" 269 key3 = "INTENSITY_AVE" 270 key4 = "INTENSITY_SIG" 271 if key1 in self.header and key2 in self.header and key3 in self.header and key4 in self.header: 272 lstout.append("INTENSITY".ljust(15) + ("MIN %s MAX %s AVE %s SIG %s" % (self.header[key1], self.header[key2], self.header[key3], self.header[key4])).ljust(49 - lnsep)) 273 key1 = "HISTOGRAM_START" 274 key2 = "HISTOGRAM_END" 275 key3 = "HISTOGRAM_MAX" 276 if key1 in self.header and key2 in self.header and key3 in self.header: 277 lstout.append("HISTOGRAM".ljust(15) + ("START %s END %s MAX %s" % (self.header[key1], self.header[key2], self.header[key3])).ljust(49 - lnsep)) 278 key = "GENERATOR" 279 if key in self.header: 280 lstout.append(key.ljust(15) + str(self.header[key]).ljust(49 - lnsep)) 281 key = "MONOCHROMATOR" 282 if key in self.header: 283 lstout.append(key.ljust(15) + str(self.header[key]).ljust(49 - lnsep)) 284 key1 = "COLLIMATOR_WIDTH" 285 key2 = "COLLIMATOR_HEIGHT" 286 if key1 in self.header and key2 in self.header: 287 lstout.append("COLLIMATOR".ljust(15) + ("WIDTH %s HEIGHT %s" % (self.header[key1], self.header[key2])).ljust(49 - lnsep)) 288 key = "REMARK" 289 if key in self.header: 290 lstout.append(key.ljust(15) + str(self.header[key]).ljust(49 - lnsep)) 291 else: 292 lstout.append(key.ljust(64 - lnsep)) 293 key = "END OF HEADER" 294 lstout.append(key) 295 return linesep.join(lstout).ljust(size)
296 297
298 - def _high_intensity_pixel_records(self):
299 flt_data = self.data.flatten() 300 pix_location = numpy.where(flt_data > 65535)[0] 301 nb_pix = pix_location.size 302 if nb_pix % 8 == 0: 303 tmp = numpy.zeros((nb_pix, 2), dtype="int32") 304 else: 305 tmp = numpy.zeros(((nb_pix // 8 + 1) * 8, 2), dtype="int32") 306 tmp[:nb_pix, 0] = pix_location + 1 307 tmp[:nb_pix, 1] = flt_data[pix_location] 308 return tmp.tostring()
309
310 - def nb_overflow_pixels(self):
311 return (self.data > 65535).sum()
312 313 @staticmethod
314 - def checkData(data=None):
315 if data is None: 316 return None 317 else: 318 # enforce square image 319 shape = data.shape 320 assert len(shape) == 2, "image has 2 dimensions" 321 mshape = max(shape) 322 z = numpy.zeros((mshape, mshape), dtype=int) 323 z[:shape[0], :shape[1]] = data 324 return z
325