1
2
3 """
4 FabIO class for dealing with TIFF images.
5 In facts wraps TiffIO from V. Armando Solé (available in PyMca) or falls back to PIL
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 * Jérôme Kieffer:
16 European Synchrotron Radiation Facility;
17 Grenoble (France)
18
19 License: GPLv3+
20 """
21
22 __authors__ = ["Jérôme Kieffer", "Henning O. Sorensen", "Erik Knudsen"]
23 __date__ = "11/07/2011"
24 __license__ = "GPLv3+"
25 __copyright__ = "ESRF, Grenoble & Risoe National Laboratory"
26 __status__ = "stable"
27 import time, logging, struct
28 logger = logging.getLogger("tifimage")
29 try:
30 import Image
31 except ImportError:
32 logger.warning("PIL is not installed ... trying to do without")
33 Image = None
34 import numpy
35 from fabioimage import fabioimage
36 from TiffIO import TiffIO
37
38 PIL_TO_NUMPY = { "I;16": numpy.uint16,
39 "F": numpy.float32,
40 "1": numpy.bool,
41 "I": numpy.int32,
42 "L": numpy.uint8,
43 }
44
45 LITTLE_ENDIAN = 1234
46 BIG_ENDIAN = 3412
47
48 TYPES = {0:'invalid', 1:'byte', 2:'ascii', 3:'short', 4:'long', 5:'rational', 6:'sbyte', 7:'undefined', 8:'sshort', 9:'slong', 10:'srational', 11:'float', 12:'double'}
49
50 TYPESIZES = {0:0, 1:1, 2:1, 3:2, 4:4, 5:8, 6:1, 7:1, 8:2, 9:4, 10:8, 11:4, 12:8}
51
52 baseline_tiff_tags = {
53 256:'ImageWidth',
54 257:'ImageLength',
55 306:'DateTime',
56 315:'Artist',
57 258:'BitsPerSample',
58 265:'CellLength',
59 264:'CellWidth',
60 259:'Compression',
61
62 262:'PhotometricInterpretation',
63 296:'ResolutionUnit',
64 282:'XResolution',
65 283:'YResolution',
66
67 278:'RowsPerStrip',
68 273:'StripOffset',
69 279:'StripByteCounts',
70
71 270:'ImageDescription',
72 271:'Make',
73 272:'Model',
74 320:'ColorMap',
75 305:'Software',
76 339:'SampleFormat',
77 33432:'Copyright'
78 }
79
81 """
82 Images in TIF format
83 Wraps TiffIO
84 """
85 _need_a_seek_to_read = True
86
88 """ Tifimage constructor adds an nbits member attribute """
89 self.nbits = None
90 fabioimage.__init__(self, *args, **kwds)
91 self.lib = None
92
94 """
95 Try to read Tiff images header...
96 """
97
98
99
100
101
102
103
104
105
106
107
108
109
110 header = numpy.fromstring(infile.read(64), numpy.uint16)
111 self.dim1 = int(header[9])
112 self.dim2 = int(header[15])
113
114 self.nbits = int(header[21])
115
116 - def read(self, fname, frame=None):
117 """
118 Wrapper for TiffIO.
119 """
120 infile = self._open(fname, "rb")
121 self._readheader(infile)
122 infile.seek(0)
123 self.lib = None
124 try:
125 tiffIO = TiffIO(infile)
126 if tiffIO.getNumberOfImages() > 0:
127
128 self.data = tiffIO.getImage(0)
129 self.header = tiffIO.getInfo(0)
130 except Exception, error:
131 logger.warning("Unable to read %s with TiffIO due to %s, trying PIL" % (fname, error))
132 else:
133 if self.data.ndim == 2:
134 self.dim2, self.dim1 = self.data.shape
135 elif self.data.ndim == 3:
136 self.dim2, self.dim1, ncol = self.data.shape
137 logger.warning("Third dimension is the color")
138 else:
139 logger.warning("dataset has %s dimensions (%s), check for errors !!!!", self.data.ndim, self.data.shape)
140 self.lib = "TiffIO"
141
142 if (self.lib is None):
143 if Image:
144 try:
145 infile.seek(0)
146 self.pilimage = Image.open(infile)
147 except Exception:
148 logger.error("Error in opening %s with PIL" % fname)
149 self.lib = None
150 infile.seek(0)
151 else:
152 self.lib = "PIL"
153 self.dim1, self.dim2 = self.pilimage.size
154 if self.pilimage.mode in PIL_TO_NUMPY:
155 self.data = numpy.fromstring(self.pilimage.tostring(), PIL_TO_NUMPY[self.pilimage.mode])
156 else:
157 self.data = numpy.fromstring(self.pilimage.convert("F").tostring(), numpy.float32)
158 self.data.shape = (self.dim2, self.dim1)
159 else:
160 logger.error("Error in opening %s: no tiff reader managed to read the file.", fname)
161 self.lib = None
162 infile.seek(0)
163
164 self.bpp = len(numpy.zeros(1, dtype=self.data.dtype).tostring())
165 self.bytecode = self.data.dtype.name
166 self.resetvals()
167 return self
168
170 """
171 Overrides the fabioimage.write method and provides a simple TIFF image writer.
172 @param fname: name of the file to save the image to
173 @tag_type fname: string or unicode (file?)...
174 """
175 tiffIO = TiffIO(fname, mode="w")
176 tiffIO.writeImage(self.data, info=self.header, software="fabio.tifimage", date=time.ctime())
177
178
179
180
183 if string[:4] == "II\x2a\x00":
184 self.byteorder = LITTLE_ENDIAN
185 elif string[:4] == 'MM\x00\x2a':
186 self.byteorder = BIG_ENDIAN
187 else:
188 logger.warning("Warning: This does not appear to be a tiff file")
189
190 offset_first_ifd = struct.unpack_from("h", string[4:])[0]
191 self.ifd = [Image_File_Directory()]
192 offset_next = self.ifd[0].unpack(string, offset_first_ifd)
193 while (offset_next != 0):
194 self.ifd.append(Image_File_Directory())
195 offset_next = self.ifd[-1].unpack(string, offset_next)
196
197 self.header = {}
198
199 for entry in self.ifd[0].entries:
200 if entry.tag in baseline_tiff_tags.keys():
201 self.header[baseline_tiff_tags[entry.tag]] = entry.val
202 else:
203 self.header[entry.tag] = entry.val
204
206 - def __init__(self, instring=None, offset= -1):
207 self.entries = []
208 self.offset = offset
209 self.count = None
210
211 - def unpack(self, instring, offset= -1):
212 if (offset == -1): offset = self.offset
213
214 strInput = instring[offset:]
215 self.count = struct.unpack_from("H", strInput[:2])[0]
216
217 for i in range(self.count - 1):
218 e = Image_File_Directory_entry().unpack(strInput[2 + 12 * (i + 1):])
219 if (e != None):
220 self.entries.append(e)
221
222 for e in self.entries:
223 if (e.val == None):
224 e.extract_data(instring)
225
226 offset_next = struct.unpack_from("L", instring[offset + 2 + self.count * 12:])[0]
227 return offset_next
228
230 - def __init__(self, tag=0, tag_type=0, count=0, offset=0):
231 self.tag = tag
232 self.tag_type = tag_type
233 self.count = count
234 self.val_offset = offset
235 self.val = None
236
237 - def unpack(self, strInput):
238 idfentry = strInput[:12]
239
240
241
242 (tag, tag_type, count) = struct.unpack_from("HHL", idfentry)
243 self.tag = tag
244 self.count = count
245 self.tag_type = tag_type
246 self.val = None
247 if (count <= 0):
248 logger.warning("Tag # %s has an invalid count: %s. Tag is ignored" % (tag, count))
249 return
250 if(count * TYPESIZES[tag_type] <= 4):
251 self.val_offset = 8
252 self.extract_data(idfentry)
253 self.val_offset = None
254 else:
255 self.val_offset = struct.unpack_from("L", idfentry[8:])[0]
256 return self
257
259 tag_type = self.tag_type
260 if (TYPES[tag_type] == 'byte'):
261 self.val = struct.unpack_from("B", full_string[self.val_offset:])[0]
262 elif (TYPES[tag_type] == 'short'):
263 self.val = struct.unpack_from("H", full_string[self.val_offset:])[0]
264 elif (TYPES[tag_type] == 'long'):
265 self.val = struct.unpack_from("L", full_string[self.val_offset:])[0]
266 elif (TYPES[tag_type] == 'sbyte'):
267 self.val = struct.unpack_from("b", full_string[self.val_offset:])[0]
268 elif (TYPES[tag_type] == 'sshort'):
269 self.val = struct.unpack_from("h", full_string[self.val_offset:])[0]
270 elif (TYPES[tag_type] == 'slong'):
271 self.val = struct.unpack_from("l", full_string[self.val_offset:])[0]
272 elif (TYPES[tag_type] == 'ascii'):
273 self.val = full_string[self.val_offset:self.val_offset + max(self.count, 4)]
274 elif (TYPES[tag_type] == 'rational'):
275 if self.val_offset != None:
276 (num, den) = struct.unpack_from("LL", full_string[self.val_offset:])
277 print self.val_offset
278 self.val = float(num) / den
279 elif (TYPES[tag_type] == 'srational'):
280 if self.val_offset != None:
281 (num, den) = struct.unpack_from("ll", full_string[self.val_offset:])
282 self.val = float(num) / den,
283 elif (TYPES[tag_type] == 'float'):
284 self.val = struct.unpack_from("f", full_string[self.val_offset:])[0]
285 elif (TYPES[tag_type] == 'double'):
286 if self.val_offset != None:
287 self.val = struct.unpack_from("d", full_string[self.val_offset:])[0]
288 else:
289 logger.warning("unrecognized type of strInputentry self: %s tag: %s type: %s TYPE: %s" % (self, baseline_tiff_tags[self.tag], self.tag_type, TYPES[tag_type]))
290