fabio 0.1.1__zip
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- Library/Python/2.7/site-packages/fabio/GEimage.py +334 -0
- Library/Python/2.7/site-packages/fabio/GEimage.pyc +0 -0
- Library/Python/2.7/site-packages/fabio/GEimage_old.py +501 -0
- Library/Python/2.7/site-packages/fabio/GEimage_old.pyc +0 -0
- Library/Python/2.7/site-packages/fabio/HiPiCimage.py +106 -0
- Library/Python/2.7/site-packages/fabio/HiPiCimage.pyc +0 -0
- Library/Python/2.7/site-packages/fabio/OXDimage.py +442 -0
- Library/Python/2.7/site-packages/fabio/OXDimage.pyc +0 -0
- Library/Python/2.7/site-packages/fabio/TiffIO.py +1197 -0
- Library/Python/2.7/site-packages/fabio/TiffIO.pyc +0 -0
- Library/Python/2.7/site-packages/fabio/__init__.py +10 -0
- Library/Python/2.7/site-packages/fabio/__init__.pyc +0 -0
- Library/Python/2.7/site-packages/fabio/adscimage.py +137 -0
- Library/Python/2.7/site-packages/fabio/adscimage.pyc +0 -0
- Library/Python/2.7/site-packages/fabio/binaryimage.py +87 -0
- Library/Python/2.7/site-packages/fabio/binaryimage.pyc +0 -0
- Library/Python/2.7/site-packages/fabio/bruker100image.py +96 -0
- Library/Python/2.7/site-packages/fabio/bruker100image.pyc +0 -0
- Library/Python/2.7/site-packages/fabio/brukerimage.py +195 -0
- Library/Python/2.7/site-packages/fabio/brukerimage.pyc +0 -0
- Library/Python/2.7/site-packages/fabio/byte_offset.so +0 -0
- Library/Python/2.7/site-packages/fabio/cbfimage.py +758 -0
- Library/Python/2.7/site-packages/fabio/cbfimage.pyc +0 -0
- Library/Python/2.7/site-packages/fabio/cf_io.so +0 -0
- Library/Python/2.7/site-packages/fabio/compression.py +388 -0
- Library/Python/2.7/site-packages/fabio/compression.pyc +0 -0
- Library/Python/2.7/site-packages/fabio/converters.py +54 -0
- Library/Python/2.7/site-packages/fabio/converters.pyc +0 -0
- Library/Python/2.7/site-packages/fabio/datIO.py +60 -0
- Library/Python/2.7/site-packages/fabio/datIO.pyc +0 -0
- Library/Python/2.7/site-packages/fabio/dm3image.py +219 -0
- Library/Python/2.7/site-packages/fabio/dm3image.pyc +0 -0
- Library/Python/2.7/site-packages/fabio/edfimage.py +924 -0
- Library/Python/2.7/site-packages/fabio/edfimage.pyc +0 -0
- Library/Python/2.7/site-packages/fabio/fabioimage.py +556 -0
- Library/Python/2.7/site-packages/fabio/fabioimage.pyc +0 -0
- Library/Python/2.7/site-packages/fabio/fabioutils.py +491 -0
- Library/Python/2.7/site-packages/fabio/fabioutils.pyc +0 -0
- Library/Python/2.7/site-packages/fabio/file_series.py +313 -0
- Library/Python/2.7/site-packages/fabio/file_series.pyc +0 -0
- Library/Python/2.7/site-packages/fabio/fit2dmaskimage.py +94 -0
- Library/Python/2.7/site-packages/fabio/fit2dmaskimage.pyc +0 -0
- Library/Python/2.7/site-packages/fabio/fit2dspreadsheetimage.py +85 -0
- Library/Python/2.7/site-packages/fabio/fit2dspreadsheetimage.pyc +0 -0
- Library/Python/2.7/site-packages/fabio/kcdimage.py +131 -0
- Library/Python/2.7/site-packages/fabio/kcdimage.pyc +0 -0
- Library/Python/2.7/site-packages/fabio/mar345_IO.so +0 -0
- Library/Python/2.7/site-packages/fabio/mar345image.py +320 -0
- Library/Python/2.7/site-packages/fabio/mar345image.pyc +0 -0
- Library/Python/2.7/site-packages/fabio/marccdimage.py +309 -0
- Library/Python/2.7/site-packages/fabio/marccdimage.pyc +0 -0
- Library/Python/2.7/site-packages/fabio/openimage.py +162 -0
- Library/Python/2.7/site-packages/fabio/openimage.pyc +0 -0
- Library/Python/2.7/site-packages/fabio/pilatusimage.py +81 -0
- Library/Python/2.7/site-packages/fabio/pilatusimage.pyc +0 -0
- Library/Python/2.7/site-packages/fabio/pnmimage.py +165 -0
- Library/Python/2.7/site-packages/fabio/pnmimage.pyc +0 -0
- Library/Python/2.7/site-packages/fabio/readbytestream.py +86 -0
- Library/Python/2.7/site-packages/fabio/readbytestream.pyc +0 -0
- Library/Python/2.7/site-packages/fabio/tifimage.py +283 -0
- Library/Python/2.7/site-packages/fabio/tifimage.pyc +0 -0
- Library/Python/2.7/site-packages/fabio/xsdimage.py +134 -0
- Library/Python/2.7/site-packages/fabio/xsdimage.pyc +0 -0
- Library/Python/2.7/site-packages/fabio-0.1.1-py2.7.egg-info/PKG-INFO +11 -0
- Library/Python/2.7/site-packages/fabio-0.1.1-py2.7.egg-info/SOURCES.txt +114 -0
- Library/Python/2.7/site-packages/fabio-0.1.1-py2.7.egg-info/dependency_links.txt +1 -0
- Library/Python/2.7/site-packages/fabio-0.1.1-py2.7.egg-info/top_level.txt +4 -0
|
@@ -0,0 +1,1197 @@
|
|
|
1
|
+
#/*##########################################################################
|
|
2
|
+
# Copyright (C) 2012 European Synchrotron Radiation Facility
|
|
3
|
+
#
|
|
4
|
+
# This file is part of the PyMca X-ray Fluorescence Toolkit developed at
|
|
5
|
+
# the ESRF by the Software group.
|
|
6
|
+
#
|
|
7
|
+
# This file is free software; you can redistribute it and/or modify it
|
|
8
|
+
# under the terms of the GNU Lesser General Public License as published by the Free
|
|
9
|
+
# Software Foundation; either version 2 of the License, or (at your option)
|
|
10
|
+
# any later version.
|
|
11
|
+
#
|
|
12
|
+
# PyMca is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
13
|
+
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
14
|
+
# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
|
|
15
|
+
# details.
|
|
16
|
+
#
|
|
17
|
+
#############################################################################*/
|
|
18
|
+
__author__ = "V.A. Sole - ESRF Data Analysis"
|
|
19
|
+
__revision__ = 1501
|
|
20
|
+
|
|
21
|
+
import sys
|
|
22
|
+
import os
|
|
23
|
+
import struct
|
|
24
|
+
import numpy
|
|
25
|
+
|
|
26
|
+
DEBUG = 0
|
|
27
|
+
ALLOW_MULTIPLE_STRIPS = False
|
|
28
|
+
|
|
29
|
+
TAG_ID = { 256:"NumberOfColumns", # S or L ImageWidth
|
|
30
|
+
257:"NumberOfRows", # S or L ImageHeight
|
|
31
|
+
258:"BitsPerSample", # S Number of bits per component
|
|
32
|
+
259:"Compression", # SHORT (1 - NoCompression, ...
|
|
33
|
+
262:"PhotometricInterpretation", # SHORT (0 - WhiteIsZero, 1 -BlackIsZero, 2 - RGB, 3 - Palette color
|
|
34
|
+
270:"ImageDescription", # ASCII
|
|
35
|
+
273:"StripOffsets", # S or L, for each strip, the byte offset of the strip
|
|
36
|
+
278:"RowsPerStrip", # S or L, number of rows in each back may be not for the last
|
|
37
|
+
279:"StripByteCounts", # S or L, The number of bytes in the strip AFTER any compression
|
|
38
|
+
305:"Software", # ASCII
|
|
39
|
+
306:"Date", # ASCII
|
|
40
|
+
320:"Colormap", # Colormap of Palette-color Images
|
|
41
|
+
339:"SampleFormat", # SHORT Interpretation of data in each pixel
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
#TILES ARE TO BE SUPPORTED TOO ...
|
|
45
|
+
TAG_NUMBER_OF_COLUMNS = 256
|
|
46
|
+
TAG_NUMBER_OF_ROWS = 257
|
|
47
|
+
TAG_BITS_PER_SAMPLE = 258
|
|
48
|
+
TAG_PHOTOMETRIC_INTERPRETATION = 262
|
|
49
|
+
TAG_COMPRESSION = 259
|
|
50
|
+
TAG_IMAGE_DESCRIPTION = 270
|
|
51
|
+
TAG_STRIP_OFFSETS = 273
|
|
52
|
+
TAG_ROWS_PER_STRIP = 278
|
|
53
|
+
TAG_STRIP_BYTE_COUNTS = 279
|
|
54
|
+
TAG_SOFTWARE = 305
|
|
55
|
+
TAG_DATE = 306
|
|
56
|
+
TAG_COLORMAP = 320
|
|
57
|
+
TAG_SAMPLE_FORMAT = 339
|
|
58
|
+
|
|
59
|
+
FIELD_TYPE = {1:('BYTE', "B"),
|
|
60
|
+
2:('ASCII', "s"), #string ending with binary zero
|
|
61
|
+
3:('SHORT', "H"),
|
|
62
|
+
4:('LONG', "I"),
|
|
63
|
+
5:('RATIONAL', "II"),
|
|
64
|
+
6:('SBYTE', "b"),
|
|
65
|
+
7:('UNDEFINED', "B"),
|
|
66
|
+
8:('SSHORT', "h"),
|
|
67
|
+
9:('SLONG', "i"),
|
|
68
|
+
10:('SRATIONAL', "ii"),
|
|
69
|
+
11:('FLOAT', "f"),
|
|
70
|
+
12:('DOUBLE', "d")}
|
|
71
|
+
|
|
72
|
+
FIELD_TYPE_OUT = { 'B': 1,
|
|
73
|
+
's': 2,
|
|
74
|
+
'H': 3,
|
|
75
|
+
'I': 4,
|
|
76
|
+
'II': 5,
|
|
77
|
+
'b': 6,
|
|
78
|
+
'h': 8,
|
|
79
|
+
'i': 9,
|
|
80
|
+
'ii': 10,
|
|
81
|
+
'f': 11,
|
|
82
|
+
'd': 12}
|
|
83
|
+
|
|
84
|
+
#sample formats (http://www.awaresystems.be/imaging/tiff/tiffflags/sampleformat.html)
|
|
85
|
+
SAMPLE_FORMAT_UINT = 1
|
|
86
|
+
SAMPLE_FORMAT_INT = 2
|
|
87
|
+
SAMPLE_FORMAT_FLOAT = 3 #floating point
|
|
88
|
+
SAMPLE_FORMAT_VOID = 4 #undefined data, usually assumed UINT
|
|
89
|
+
SAMPLE_FORMAT_COMPLEXINT = 5
|
|
90
|
+
SAMPLE_FORMAT_COMPLEXIEEEFP = 6
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
class TiffIO(object):
|
|
95
|
+
def __init__(self, filename, mode=None, cache_length=20, mono_output=False):
|
|
96
|
+
if mode is None:
|
|
97
|
+
mode = 'rb'
|
|
98
|
+
if 'b' not in mode:
|
|
99
|
+
mode = mode + 'b'
|
|
100
|
+
if 'a' in mode.lower():
|
|
101
|
+
raise IOError("Mode %s makes no sense on TIFF files. Consider 'rb+'" % mode)
|
|
102
|
+
if ('w' in mode):
|
|
103
|
+
if '+' not in mode:
|
|
104
|
+
mode += '+'
|
|
105
|
+
#if isinstance(filename, file): #does not work in python 3
|
|
106
|
+
if hasattr(filename, "seek"):
|
|
107
|
+
fd = filename
|
|
108
|
+
self._access = None
|
|
109
|
+
else:
|
|
110
|
+
#the b is needed for windows and python 3
|
|
111
|
+
fd = open(filename, mode)
|
|
112
|
+
self._access = mode
|
|
113
|
+
|
|
114
|
+
self._initInternalVariables(fd)
|
|
115
|
+
self._maxImageCacheLength = cache_length
|
|
116
|
+
self._forceMonoOutput = mono_output
|
|
117
|
+
|
|
118
|
+
def _initInternalVariables(self, fd=None):
|
|
119
|
+
if fd is None:
|
|
120
|
+
fd = self.fd
|
|
121
|
+
else:
|
|
122
|
+
self.fd = fd
|
|
123
|
+
# read the order
|
|
124
|
+
fd.seek(0)
|
|
125
|
+
order = fd.read(2).decode()
|
|
126
|
+
if len(order):
|
|
127
|
+
if order == "II":
|
|
128
|
+
#intel, little endian
|
|
129
|
+
fileOrder = "little"
|
|
130
|
+
self._structChar = '<'
|
|
131
|
+
elif order == "MM":
|
|
132
|
+
#motorola, high endian
|
|
133
|
+
fileOrder = "big"
|
|
134
|
+
self._structChar = '>'
|
|
135
|
+
else:
|
|
136
|
+
raise IOError("File is not a Mar CCD file, nor a TIFF file")
|
|
137
|
+
a = fd.read(2)
|
|
138
|
+
fortyTwo = struct.unpack(self._structChar + "H", a)[0]
|
|
139
|
+
if fortyTwo != 42:
|
|
140
|
+
raise IOError("Invalid TIFF version %d" % fortyTwo)
|
|
141
|
+
else:
|
|
142
|
+
if DEBUG:
|
|
143
|
+
print("VALID TIFF VERSION")
|
|
144
|
+
if sys.byteorder != fileOrder:
|
|
145
|
+
swap = True
|
|
146
|
+
else:
|
|
147
|
+
swap = False
|
|
148
|
+
else:
|
|
149
|
+
if sys.byteorder == "little":
|
|
150
|
+
self._structChar = '<'
|
|
151
|
+
else:
|
|
152
|
+
self._structChar = '>'
|
|
153
|
+
swap = False
|
|
154
|
+
self._swap = swap
|
|
155
|
+
self._IFD = []
|
|
156
|
+
self._imageDataCacheIndex = []
|
|
157
|
+
self._imageDataCache = []
|
|
158
|
+
self._imageInfoCacheIndex = []
|
|
159
|
+
self._imageInfoCache = []
|
|
160
|
+
self.getImageFileDirectories(fd)
|
|
161
|
+
|
|
162
|
+
def __makeSureFileIsOpen(self):
|
|
163
|
+
if not self.fd.closed:
|
|
164
|
+
return
|
|
165
|
+
if DEBUG:
|
|
166
|
+
print("Reopening closed file")
|
|
167
|
+
fileName = self.fd.name
|
|
168
|
+
if self._access is None:
|
|
169
|
+
#we do not own the file
|
|
170
|
+
#open in read mode
|
|
171
|
+
newFile = open(fileName, 'rb')
|
|
172
|
+
else:
|
|
173
|
+
newFile = open(fileName, self._access)
|
|
174
|
+
self.fd = newFile
|
|
175
|
+
|
|
176
|
+
def __makeSureFileIsClosed(self):
|
|
177
|
+
if self._access is None:
|
|
178
|
+
#we do not own the file
|
|
179
|
+
if DEBUG:
|
|
180
|
+
print("Not closing not owned file")
|
|
181
|
+
return
|
|
182
|
+
|
|
183
|
+
if not self.fd.closed:
|
|
184
|
+
self.fd.close()
|
|
185
|
+
|
|
186
|
+
def getNumberOfImages(self):
|
|
187
|
+
#update for the case someone has done anything?
|
|
188
|
+
self._updateIFD()
|
|
189
|
+
return len(self._IFD)
|
|
190
|
+
|
|
191
|
+
def _updateIFD(self):
|
|
192
|
+
self.__makeSureFileIsOpen()
|
|
193
|
+
self.getImageFileDirectories()
|
|
194
|
+
self.__makeSureFileIsClosed()
|
|
195
|
+
|
|
196
|
+
def getImageFileDirectories(self, fd=None):
|
|
197
|
+
if fd is None:
|
|
198
|
+
fd = self.fd
|
|
199
|
+
else:
|
|
200
|
+
self.fd = fd
|
|
201
|
+
st = self._structChar
|
|
202
|
+
fd.seek(4)
|
|
203
|
+
self._IFD = []
|
|
204
|
+
nImages = 0
|
|
205
|
+
fmt = st + 'I'
|
|
206
|
+
inStr = fd.read(struct.calcsize(fmt))
|
|
207
|
+
if not len(inStr):
|
|
208
|
+
offsetToIFD = 0
|
|
209
|
+
else:
|
|
210
|
+
offsetToIFD = struct.unpack(fmt, inStr)[0]
|
|
211
|
+
if DEBUG:
|
|
212
|
+
print("Offset to first IFD = %d" % offsetToIFD)
|
|
213
|
+
while offsetToIFD != 0:
|
|
214
|
+
self._IFD.append(offsetToIFD)
|
|
215
|
+
nImages += 1
|
|
216
|
+
fd.seek(offsetToIFD)
|
|
217
|
+
fmt = st + 'H'
|
|
218
|
+
numberOfDirectoryEntries = struct.unpack(fmt, fd.read(struct.calcsize(fmt)))[0]
|
|
219
|
+
if DEBUG:
|
|
220
|
+
print("Number of directory entries = %d" % numberOfDirectoryEntries)
|
|
221
|
+
|
|
222
|
+
fmt = st + 'I'
|
|
223
|
+
fd.seek(offsetToIFD + 2 + 12 * numberOfDirectoryEntries)
|
|
224
|
+
offsetToIFD = struct.unpack(fmt, fd.read(struct.calcsize(fmt)))[0]
|
|
225
|
+
if DEBUG:
|
|
226
|
+
print("Next Offset to IFD = %d" % offsetToIFD)
|
|
227
|
+
#offsetToIFD = 0
|
|
228
|
+
if DEBUG:
|
|
229
|
+
print("Number of images found = %d" % nImages)
|
|
230
|
+
return nImages
|
|
231
|
+
|
|
232
|
+
def _parseImageFileDirectory(self, nImage):
|
|
233
|
+
offsetToIFD = self._IFD[nImage]
|
|
234
|
+
st = self._structChar
|
|
235
|
+
fd = self.fd
|
|
236
|
+
fd.seek(offsetToIFD)
|
|
237
|
+
fmt = st + 'H'
|
|
238
|
+
numberOfDirectoryEntries = struct.unpack(fmt, fd.read(struct.calcsize(fmt)))[0]
|
|
239
|
+
if DEBUG:
|
|
240
|
+
print("Number of directory entries = %d" % numberOfDirectoryEntries)
|
|
241
|
+
|
|
242
|
+
fmt = st + 'HHI4s'
|
|
243
|
+
tagIDList = []
|
|
244
|
+
fieldTypeList = []
|
|
245
|
+
nValuesList = []
|
|
246
|
+
valueOffsetList = []
|
|
247
|
+
for i in range(numberOfDirectoryEntries):
|
|
248
|
+
tagID, fieldType, nValues, valueOffset = struct.unpack(fmt, fd.read(12))
|
|
249
|
+
tagIDList.append(tagID)
|
|
250
|
+
fieldTypeList.append(fieldType)
|
|
251
|
+
nValuesList.append(nValues)
|
|
252
|
+
if nValues == 1:
|
|
253
|
+
ftype, vfmt = FIELD_TYPE[fieldType]
|
|
254
|
+
if ftype not in ['ASCII', 'RATIONAL', 'SRATIONAL']:
|
|
255
|
+
vfmt = st + vfmt
|
|
256
|
+
actualValue = struct.unpack(vfmt, valueOffset[0: struct.calcsize(vfmt)])[0]
|
|
257
|
+
valueOffsetList.append(actualValue)
|
|
258
|
+
else:
|
|
259
|
+
valueOffsetList.append(valueOffset)
|
|
260
|
+
elif (nValues < 5) and (fieldType == 2):
|
|
261
|
+
ftype, vfmt = FIELD_TYPE[fieldType]
|
|
262
|
+
vfmt = st + "%d%s" % (nValues, vfmt)
|
|
263
|
+
actualValue = struct.unpack(vfmt, valueOffset[0: struct.calcsize(vfmt)])[0]
|
|
264
|
+
valueOffsetList.append(actualValue)
|
|
265
|
+
else:
|
|
266
|
+
valueOffsetList.append(valueOffset)
|
|
267
|
+
if DEBUG:
|
|
268
|
+
if tagID in TAG_ID:
|
|
269
|
+
print("tagID = %s" % TAG_ID[tagID])
|
|
270
|
+
else:
|
|
271
|
+
print("tagID = %d" % tagID)
|
|
272
|
+
print("fieldType = %s" % FIELD_TYPE[fieldType][0])
|
|
273
|
+
print("nValues = %d" % nValues)
|
|
274
|
+
#if nValues == 1:
|
|
275
|
+
# print("valueOffset = %s" % valueOffset)
|
|
276
|
+
return tagIDList, fieldTypeList, nValuesList, valueOffsetList
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
def _readIFDEntry(self, tag, tagIDList, fieldTypeList, nValuesList, valueOffsetList):
|
|
281
|
+
fd = self.fd
|
|
282
|
+
st = self._structChar
|
|
283
|
+
idx = tagIDList.index(tag)
|
|
284
|
+
nValues = nValuesList[idx]
|
|
285
|
+
output = []
|
|
286
|
+
ftype, vfmt = FIELD_TYPE[fieldTypeList[idx]]
|
|
287
|
+
vfmt = st + "%d%s" % (nValues, vfmt)
|
|
288
|
+
requestedBytes = struct.calcsize(vfmt)
|
|
289
|
+
if nValues == 1:
|
|
290
|
+
output.append(valueOffsetList[idx])
|
|
291
|
+
elif requestedBytes < 5:
|
|
292
|
+
output.append(valueOffsetList[idx])
|
|
293
|
+
else:
|
|
294
|
+
offset = fd.seek(struct.unpack(st + "I", valueOffsetList[idx])[0])
|
|
295
|
+
output = struct.unpack(vfmt, fd.read(requestedBytes))
|
|
296
|
+
return output
|
|
297
|
+
|
|
298
|
+
def getData(self, nImage, **kw):
|
|
299
|
+
if nImage >= len(self._IFD):
|
|
300
|
+
#update prior to raise an index error error
|
|
301
|
+
self._updateIFD()
|
|
302
|
+
return self._readImage(nImage, **kw)
|
|
303
|
+
|
|
304
|
+
def getImage(self, nImage):
|
|
305
|
+
return self.getData(nImage)
|
|
306
|
+
|
|
307
|
+
def getInfo(self, nImage, **kw):
|
|
308
|
+
if nImage >= len(self._IFD):
|
|
309
|
+
#update prior to raise an index error error
|
|
310
|
+
self._updateIFD()
|
|
311
|
+
current = self._IFD[nImage]
|
|
312
|
+
return self._readInfo(nImage)
|
|
313
|
+
|
|
314
|
+
def _readInfo(self, nImage, close=True):
|
|
315
|
+
if nImage in self._imageInfoCacheIndex:
|
|
316
|
+
if DEBUG:
|
|
317
|
+
print("Reading info from cache")
|
|
318
|
+
return self._imageInfoCache[self._imageInfoCacheIndex.index(nImage)]
|
|
319
|
+
|
|
320
|
+
#read the header
|
|
321
|
+
self.__makeSureFileIsOpen()
|
|
322
|
+
tagIDList, fieldTypeList, nValuesList, valueOffsetList = self._parseImageFileDirectory(nImage)
|
|
323
|
+
|
|
324
|
+
#rows and columns
|
|
325
|
+
nColumns = valueOffsetList[tagIDList.index(TAG_NUMBER_OF_COLUMNS)]
|
|
326
|
+
nRows = valueOffsetList[tagIDList.index(TAG_NUMBER_OF_ROWS)]
|
|
327
|
+
|
|
328
|
+
#bits per sample
|
|
329
|
+
idx = tagIDList.index(TAG_BITS_PER_SAMPLE)
|
|
330
|
+
nBits = valueOffsetList[idx]
|
|
331
|
+
if nValuesList[idx] != 1:
|
|
332
|
+
#this happens with RGB and friends, nBits is not a single value
|
|
333
|
+
nBits = self._readIFDEntry(TAG_BITS_PER_SAMPLE,
|
|
334
|
+
tagIDList, fieldTypeList, nValuesList, valueOffsetList)
|
|
335
|
+
|
|
336
|
+
|
|
337
|
+
if TAG_COLORMAP in tagIDList:
|
|
338
|
+
idx = tagIDList.index(TAG_COLORMAP)
|
|
339
|
+
tmpColormap = self._readIFDEntry(TAG_COLORMAP,
|
|
340
|
+
tagIDList, fieldTypeList, nValuesList, valueOffsetList)
|
|
341
|
+
if max(tmpColormap) > 255:
|
|
342
|
+
tmpColormap = numpy.array(tmpColormap, dtype=numpy.uint16)
|
|
343
|
+
tmpColormap = (tmpColormap / 256.).astype(numpy.uint8)
|
|
344
|
+
else:
|
|
345
|
+
tmpColormap = numpy.array(tmpColormap, dtype=numpy.uint8)
|
|
346
|
+
tmpColormap.shape = 3, -1
|
|
347
|
+
colormap = numpy.zeros((tmpColormap.shape[-1], 3), tmpColormap.dtype)
|
|
348
|
+
colormap[:, :] = tmpColormap.T
|
|
349
|
+
tmpColormap = None
|
|
350
|
+
else:
|
|
351
|
+
colormap = None
|
|
352
|
+
|
|
353
|
+
#sample format
|
|
354
|
+
if TAG_SAMPLE_FORMAT in tagIDList:
|
|
355
|
+
sampleFormat = valueOffsetList[tagIDList.index(TAG_SAMPLE_FORMAT)]
|
|
356
|
+
else:
|
|
357
|
+
#set to unknown
|
|
358
|
+
sampleFormat = SAMPLE_FORMAT_VOID
|
|
359
|
+
|
|
360
|
+
# compression
|
|
361
|
+
compression = False
|
|
362
|
+
compression_type = 1
|
|
363
|
+
if TAG_COMPRESSION in tagIDList:
|
|
364
|
+
compression_type = valueOffsetList[tagIDList.index(TAG_COMPRESSION)]
|
|
365
|
+
if compression_type == 1:
|
|
366
|
+
compression = False
|
|
367
|
+
else:
|
|
368
|
+
compression = True
|
|
369
|
+
|
|
370
|
+
#photometric interpretation
|
|
371
|
+
interpretation = 1
|
|
372
|
+
if TAG_PHOTOMETRIC_INTERPRETATION in tagIDList:
|
|
373
|
+
interpretation = valueOffsetList[tagIDList.index(TAG_PHOTOMETRIC_INTERPRETATION)]
|
|
374
|
+
else:
|
|
375
|
+
print("WARNING: Non standard TIFF. Photometric interpretation TAG missing")
|
|
376
|
+
helpString = ""
|
|
377
|
+
if sys.version > '2.6':
|
|
378
|
+
helpString = eval('b""')
|
|
379
|
+
|
|
380
|
+
if TAG_IMAGE_DESCRIPTION in tagIDList:
|
|
381
|
+
imageDescription = self._readIFDEntry(TAG_IMAGE_DESCRIPTION,
|
|
382
|
+
tagIDList, fieldTypeList, nValuesList, valueOffsetList)
|
|
383
|
+
if type(imageDescription) in [type([1]), type((1,))]:
|
|
384
|
+
imageDescription = helpString.join(imageDescription)
|
|
385
|
+
else:
|
|
386
|
+
imageDescription = "%d/%d" % (nImage + 1, len(self._IFD))
|
|
387
|
+
|
|
388
|
+
if sys.version < '3.0':
|
|
389
|
+
defaultSoftware = "Unknown Software"
|
|
390
|
+
else:
|
|
391
|
+
defaultSoftware = bytes("Unknown Software",
|
|
392
|
+
encoding='utf-8')
|
|
393
|
+
if TAG_SOFTWARE in tagIDList:
|
|
394
|
+
software = self._readIFDEntry(TAG_SOFTWARE,
|
|
395
|
+
tagIDList, fieldTypeList, nValuesList, valueOffsetList)
|
|
396
|
+
if type(software) in [type([1]), type((1,))]:
|
|
397
|
+
software = helpString.join(software)
|
|
398
|
+
else:
|
|
399
|
+
software = defaultSoftware
|
|
400
|
+
|
|
401
|
+
if software == defaultSoftware:
|
|
402
|
+
try:
|
|
403
|
+
if sys.version < '3.0':
|
|
404
|
+
if imageDescription.upper().startswith("IMAGEJ"):
|
|
405
|
+
software = imageDescription.split("=")[0]
|
|
406
|
+
else:
|
|
407
|
+
tmpString = imageDescription.decode()
|
|
408
|
+
if tmpString.upper().startswith("IMAGEJ"):
|
|
409
|
+
software = bytes(tmpString.split("=")[0],
|
|
410
|
+
encoding='utf-8')
|
|
411
|
+
except:
|
|
412
|
+
pass
|
|
413
|
+
|
|
414
|
+
if TAG_DATE in tagIDList:
|
|
415
|
+
date = self._readIFDEntry(TAG_DATE,
|
|
416
|
+
tagIDList, fieldTypeList, nValuesList, valueOffsetList)
|
|
417
|
+
if type(date) in [type([1]), type((1,))]:
|
|
418
|
+
date = helpString.join(date)
|
|
419
|
+
else:
|
|
420
|
+
date = "Unknown Date"
|
|
421
|
+
|
|
422
|
+
stripOffsets = self._readIFDEntry(TAG_STRIP_OFFSETS,
|
|
423
|
+
tagIDList, fieldTypeList, nValuesList, valueOffsetList)
|
|
424
|
+
if TAG_ROWS_PER_STRIP in tagIDList:
|
|
425
|
+
rowsPerStrip = self._readIFDEntry(TAG_ROWS_PER_STRIP,
|
|
426
|
+
tagIDList, fieldTypeList, nValuesList, valueOffsetList)[0]
|
|
427
|
+
else:
|
|
428
|
+
rowsPerStrip = nRows
|
|
429
|
+
print("WARNING: Non standard TIFF. Rows per strip TAG missing")
|
|
430
|
+
|
|
431
|
+
if TAG_STRIP_BYTE_COUNTS in tagIDList:
|
|
432
|
+
stripByteCounts = self._readIFDEntry(TAG_STRIP_BYTE_COUNTS,
|
|
433
|
+
tagIDList, fieldTypeList, nValuesList, valueOffsetList)
|
|
434
|
+
else:
|
|
435
|
+
print("WARNING: Non standard TIFF. Strip byte counts TAG missing")
|
|
436
|
+
if hasattr(nBits, 'index'):
|
|
437
|
+
expectedSum = 0
|
|
438
|
+
for n in nBits:
|
|
439
|
+
expectedSum += int(nRows * nColumns * n / 8)
|
|
440
|
+
else:
|
|
441
|
+
expectedSum = int(nRows * nColumns * nBits / 8)
|
|
442
|
+
stripByteCounts = [expectedSum]
|
|
443
|
+
|
|
444
|
+
if close:
|
|
445
|
+
self.__makeSureFileIsClosed()
|
|
446
|
+
|
|
447
|
+
if self._forceMonoOutput and (interpretation > 1):
|
|
448
|
+
#color image but asked monochrome output
|
|
449
|
+
nBits = 32
|
|
450
|
+
colormap = None
|
|
451
|
+
sampleFormat = SAMPLE_FORMAT_FLOAT
|
|
452
|
+
interpretation = 1
|
|
453
|
+
#we cannot rely on any cache in this case
|
|
454
|
+
useInfoCache = False
|
|
455
|
+
if DEBUG:
|
|
456
|
+
print("FORCED MONO")
|
|
457
|
+
else:
|
|
458
|
+
useInfoCache = True
|
|
459
|
+
|
|
460
|
+
info = {}
|
|
461
|
+
info["nRows"] = nRows
|
|
462
|
+
info["nColumns"] = nColumns
|
|
463
|
+
info["nBits"] = nBits
|
|
464
|
+
info["compression"] = compression
|
|
465
|
+
info["compression_type"] = compression_type
|
|
466
|
+
info["imageDescription"] = imageDescription
|
|
467
|
+
info["stripOffsets"] = stripOffsets #This contains the file offsets to the data positions
|
|
468
|
+
info["rowsPerStrip"] = rowsPerStrip
|
|
469
|
+
info["stripByteCounts"] = stripByteCounts #bytes in strip since I do not support compression
|
|
470
|
+
info["software"] = software
|
|
471
|
+
info["date"] = date
|
|
472
|
+
info["colormap"] = colormap
|
|
473
|
+
info["sampleFormat"] = sampleFormat
|
|
474
|
+
info["photometricInterpretation"] = interpretation
|
|
475
|
+
infoDict = {}
|
|
476
|
+
if sys.version < '3.0':
|
|
477
|
+
testString = 'PyMca'
|
|
478
|
+
else:
|
|
479
|
+
testString = eval('b"PyMca"')
|
|
480
|
+
if software.startswith(testString):
|
|
481
|
+
#str to make sure python 2.x sees it as string and not unicode
|
|
482
|
+
if sys.version < '3.0':
|
|
483
|
+
descriptionString = imageDescription
|
|
484
|
+
else:
|
|
485
|
+
descriptionString = str(imageDescription.decode())
|
|
486
|
+
#interpret the image description in terms of supplied
|
|
487
|
+
#information at writing time
|
|
488
|
+
items = descriptionString.split('=')
|
|
489
|
+
for i in range(int(len(items) / 2)):
|
|
490
|
+
key = "%s" % items[i * 2]
|
|
491
|
+
#get rid of the \n at the end of the value
|
|
492
|
+
value = "%s" % items[i * 2 + 1][:-1]
|
|
493
|
+
infoDict[key] = value
|
|
494
|
+
info['info'] = infoDict
|
|
495
|
+
|
|
496
|
+
if (self._maxImageCacheLength > 0) and useInfoCache:
|
|
497
|
+
self._imageInfoCacheIndex.insert(0, nImage)
|
|
498
|
+
self._imageInfoCache.insert(0, info)
|
|
499
|
+
if len(self._imageInfoCacheIndex) > self._maxImageCacheLength:
|
|
500
|
+
self._imageInfoCacheIndex = self._imageInfoCacheIndex[:self._maxImageCacheLength]
|
|
501
|
+
self._imageInfoCache = self._imageInfoCache[:self._maxImageCacheLength]
|
|
502
|
+
return info
|
|
503
|
+
|
|
504
|
+
def _readImage(self, nImage, **kw):
|
|
505
|
+
if DEBUG:
|
|
506
|
+
print("Reading image %d" % nImage)
|
|
507
|
+
if 'close' in kw:
|
|
508
|
+
close = kw['close']
|
|
509
|
+
else:
|
|
510
|
+
close = True
|
|
511
|
+
rowMin = kw.get('rowMin', None)
|
|
512
|
+
rowMax = kw.get('rowMax', None)
|
|
513
|
+
if nImage in self._imageDataCacheIndex:
|
|
514
|
+
if DEBUG:
|
|
515
|
+
print("Reading image data from cache")
|
|
516
|
+
return self._imageDataCache[self._imageDataCacheIndex.index(nImage)]
|
|
517
|
+
|
|
518
|
+
self.__makeSureFileIsOpen()
|
|
519
|
+
if self._forceMonoOutput:
|
|
520
|
+
oldMono = True
|
|
521
|
+
else:
|
|
522
|
+
oldMono = False
|
|
523
|
+
try:
|
|
524
|
+
self._forceMonoOutput = False
|
|
525
|
+
info = self._readInfo(nImage, close=False)
|
|
526
|
+
self._forceMonoOutput = oldMono
|
|
527
|
+
except:
|
|
528
|
+
self._forceMonoOutput = oldMono
|
|
529
|
+
raise
|
|
530
|
+
compression = info['compression']
|
|
531
|
+
compression_type = info['compression_type']
|
|
532
|
+
if compression:
|
|
533
|
+
if compression_type != 32773:
|
|
534
|
+
raise IOError("Compressed TIFF images not supported except packbits")
|
|
535
|
+
else:
|
|
536
|
+
#PackBits compression
|
|
537
|
+
if DEBUG:
|
|
538
|
+
print("Using PackBits compression")
|
|
539
|
+
|
|
540
|
+
interpretation = info["photometricInterpretation"]
|
|
541
|
+
if interpretation == 2:
|
|
542
|
+
#RGB
|
|
543
|
+
pass
|
|
544
|
+
#raise IOError("RGB Image. Only grayscale images supported")
|
|
545
|
+
elif interpretation == 3:
|
|
546
|
+
#Palette Color Image
|
|
547
|
+
pass
|
|
548
|
+
#raise IOError("Palette-color Image. Only grayscale images supported")
|
|
549
|
+
elif interpretation > 2:
|
|
550
|
+
#Palette Color Image
|
|
551
|
+
raise IOError("Only grayscale images supported")
|
|
552
|
+
|
|
553
|
+
nRows = info["nRows"]
|
|
554
|
+
nColumns = info["nColumns"]
|
|
555
|
+
nBits = info["nBits"]
|
|
556
|
+
colormap = info["colormap"]
|
|
557
|
+
sampleFormat = info["sampleFormat"]
|
|
558
|
+
|
|
559
|
+
if rowMin is None:
|
|
560
|
+
rowMin = 0
|
|
561
|
+
|
|
562
|
+
if rowMax is None:
|
|
563
|
+
rowMax = nRows - 1
|
|
564
|
+
|
|
565
|
+
if rowMin < 0:
|
|
566
|
+
rowMin = nRows - rowMin
|
|
567
|
+
|
|
568
|
+
if rowMax < 0:
|
|
569
|
+
rowMax = nRows - rowMax
|
|
570
|
+
|
|
571
|
+
if rowMax < rowMin:
|
|
572
|
+
txt = "Max Row smaller than Min Row. Reverse selection not supported"
|
|
573
|
+
raise NotImplemented(txt)
|
|
574
|
+
|
|
575
|
+
if rowMin >= nRows:
|
|
576
|
+
raise IndexError("Image only has %d rows" % nRows)
|
|
577
|
+
|
|
578
|
+
if rowMax >= nRows:
|
|
579
|
+
raise IndexError("Image only has %d rows" % nRows)
|
|
580
|
+
|
|
581
|
+
if sampleFormat == SAMPLE_FORMAT_FLOAT:
|
|
582
|
+
if nBits == 32:
|
|
583
|
+
dtype = numpy.float32
|
|
584
|
+
elif nBits == 64:
|
|
585
|
+
dtype = numpy.float64
|
|
586
|
+
else:
|
|
587
|
+
raise ValueError("Unsupported number of bits for a float: %d" % nBits)
|
|
588
|
+
elif sampleFormat in [SAMPLE_FORMAT_UINT, SAMPLE_FORMAT_VOID]:
|
|
589
|
+
if nBits in [8, (8, 8, 8), [8, 8, 8]]:
|
|
590
|
+
dtype = numpy.uint8
|
|
591
|
+
elif nBits in [16, (16, 16, 16), [16, 16, 16]]:
|
|
592
|
+
dtype = numpy.uint16
|
|
593
|
+
elif nBits in [32, (32, 32, 32), [32, 32, 32]]:
|
|
594
|
+
dtype = numpy.uint32
|
|
595
|
+
elif nBits in [64, (64, 64, 64), [64, 64, 64]]:
|
|
596
|
+
dtype = numpy.uint64
|
|
597
|
+
else:
|
|
598
|
+
raise ValueError("Unsupported number of bits for unsigned int: %s" % (nBits,))
|
|
599
|
+
elif sampleFormat == SAMPLE_FORMAT_INT:
|
|
600
|
+
if nBits in [8, (8, 8, 8), [8, 8, 8]]:
|
|
601
|
+
dtype = numpy.int8
|
|
602
|
+
elif nBits in [16, (16, 16, 16), [16, 16, 16]]:
|
|
603
|
+
dtype = numpy.int16
|
|
604
|
+
elif nBits in [32, (32, 32, 32), [32, 32, 32]]:
|
|
605
|
+
dtype = numpy.int32
|
|
606
|
+
elif nBits in [64, (64, 64, 64), [64, 64, 64]]:
|
|
607
|
+
dtype = numpy.int64
|
|
608
|
+
else:
|
|
609
|
+
raise ValueError("Unsupported number of bits for signed int: %s" % (nBits,))
|
|
610
|
+
else:
|
|
611
|
+
raise ValueError("Unsupported combination. Bits = %s Format = %d" % (nBits, sampleFormat))
|
|
612
|
+
if hasattr(nBits, 'index'):
|
|
613
|
+
image = numpy.zeros((nRows, nColumns, len(nBits)), dtype=dtype)
|
|
614
|
+
elif colormap is not None:
|
|
615
|
+
#should I use colormap dtype?
|
|
616
|
+
image = numpy.zeros((nRows, nColumns, 3), dtype=dtype)
|
|
617
|
+
else:
|
|
618
|
+
image = numpy.zeros((nRows, nColumns), dtype=dtype)
|
|
619
|
+
|
|
620
|
+
fd = self.fd
|
|
621
|
+
st = self._structChar
|
|
622
|
+
stripOffsets = info["stripOffsets"] #This contains the file offsets to the data positions
|
|
623
|
+
rowsPerStrip = info["rowsPerStrip"]
|
|
624
|
+
stripByteCounts = info["stripByteCounts"] #bytes in strip since I do not support compression
|
|
625
|
+
|
|
626
|
+
rowStart = 0
|
|
627
|
+
if len(stripOffsets) == 1:
|
|
628
|
+
bytesPerRow = int(stripByteCounts[0] / rowsPerStrip)
|
|
629
|
+
fd.seek(stripOffsets[0] + rowMin * bytesPerRow)
|
|
630
|
+
nBytes = (rowMax - rowMin + 1) * bytesPerRow
|
|
631
|
+
if self._swap:
|
|
632
|
+
readout = numpy.fromstring(fd.read(nBytes), dtype).byteswap()
|
|
633
|
+
else:
|
|
634
|
+
readout = numpy.fromstring(fd.read(nBytes), dtype)
|
|
635
|
+
if hasattr(nBits, 'index'):
|
|
636
|
+
readout.shape = -1, nColumns, len(nBits)
|
|
637
|
+
elif info['colormap'] is not None:
|
|
638
|
+
readout = colormap[readout]
|
|
639
|
+
else:
|
|
640
|
+
readout.shape = -1, nColumns
|
|
641
|
+
image[rowMin:rowMax + 1, :] = readout
|
|
642
|
+
else:
|
|
643
|
+
for i in range(len(stripOffsets)):
|
|
644
|
+
#the amount of rows
|
|
645
|
+
nRowsToRead = rowsPerStrip
|
|
646
|
+
rowEnd = int(min(rowStart + nRowsToRead, nRows))
|
|
647
|
+
if rowEnd < rowMin:
|
|
648
|
+
rowStart += nRowsToRead
|
|
649
|
+
continue
|
|
650
|
+
if (rowStart > rowMax):
|
|
651
|
+
break
|
|
652
|
+
#we are in position
|
|
653
|
+
fd.seek(stripOffsets[i])
|
|
654
|
+
#the amount of bytes to read
|
|
655
|
+
nBytes = stripByteCounts[i]
|
|
656
|
+
if compression_type == 32773:
|
|
657
|
+
try:
|
|
658
|
+
bufferBytes = bytes()
|
|
659
|
+
except:
|
|
660
|
+
#python 2.5 ...
|
|
661
|
+
bufferBytes = ""
|
|
662
|
+
#packBits
|
|
663
|
+
readBytes = 0
|
|
664
|
+
#intermediate buffer
|
|
665
|
+
tmpBuffer = fd.read(nBytes)
|
|
666
|
+
while readBytes < nBytes:
|
|
667
|
+
n = struct.unpack('b', tmpBuffer[readBytes:(readBytes + 1)])[0]
|
|
668
|
+
readBytes += 1
|
|
669
|
+
if n >= 0:
|
|
670
|
+
#should I prevent reading more than the
|
|
671
|
+
#length of the chain? Let's python raise
|
|
672
|
+
#the exception...
|
|
673
|
+
bufferBytes += tmpBuffer[readBytes:\
|
|
674
|
+
readBytes + (n + 1)]
|
|
675
|
+
readBytes += (n + 1)
|
|
676
|
+
elif n > -128:
|
|
677
|
+
bufferBytes += (-n + 1) * tmpBuffer[readBytes:(readBytes + 1)]
|
|
678
|
+
readBytes += 1
|
|
679
|
+
else:
|
|
680
|
+
#if read -128 ignore the byte
|
|
681
|
+
continue
|
|
682
|
+
if self._swap:
|
|
683
|
+
readout = numpy.fromstring(bufferBytes, dtype).byteswap()
|
|
684
|
+
else:
|
|
685
|
+
readout = numpy.fromstring(bufferBytes, dtype)
|
|
686
|
+
if hasattr(nBits, 'index'):
|
|
687
|
+
readout.shape = -1, nColumns, len(nBits)
|
|
688
|
+
elif info['colormap'] is not None:
|
|
689
|
+
readout = colormap[readout]
|
|
690
|
+
readout.shape = -1, nColumns, 3
|
|
691
|
+
else:
|
|
692
|
+
readout.shape = -1, nColumns
|
|
693
|
+
image[rowStart:rowEnd, :] = readout
|
|
694
|
+
else:
|
|
695
|
+
if 1:
|
|
696
|
+
#use numpy
|
|
697
|
+
if self._swap:
|
|
698
|
+
readout = numpy.fromstring(fd.read(nBytes), dtype).byteswap()
|
|
699
|
+
else:
|
|
700
|
+
readout = numpy.fromstring(fd.read(nBytes), dtype)
|
|
701
|
+
if hasattr(nBits, 'index'):
|
|
702
|
+
readout.shape = -1, nColumns, len(nBits)
|
|
703
|
+
elif colormap is not None:
|
|
704
|
+
readout = colormap[readout]
|
|
705
|
+
readout.shape = -1, nColumns, 3
|
|
706
|
+
else:
|
|
707
|
+
readout.shape = -1, nColumns
|
|
708
|
+
image[rowStart:rowEnd, :] = readout
|
|
709
|
+
else:
|
|
710
|
+
#using struct
|
|
711
|
+
readout = numpy.array(struct.unpack(st + "%df" % int(nBytes / 4), fd.read(nBytes)),
|
|
712
|
+
dtype=dtype)
|
|
713
|
+
if hasattr(nBits, 'index'):
|
|
714
|
+
readout.shape = -1, nColumns, len(nBits)
|
|
715
|
+
elif colormap is not None:
|
|
716
|
+
readout = colormap[readout]
|
|
717
|
+
readout.shape = -1, nColumns, 3
|
|
718
|
+
else:
|
|
719
|
+
readout.shape = -1, nColumns
|
|
720
|
+
image[rowStart:rowEnd, :] = readout
|
|
721
|
+
rowStart += nRowsToRead
|
|
722
|
+
if close:
|
|
723
|
+
self.__makeSureFileIsClosed()
|
|
724
|
+
|
|
725
|
+
if len(image.shape) == 3:
|
|
726
|
+
#color image
|
|
727
|
+
if self._forceMonoOutput:
|
|
728
|
+
#color image, convert to monochrome
|
|
729
|
+
image = (image[:, :, 0] * 0.114 + \
|
|
730
|
+
image[:, :, 1] * 0.587 + \
|
|
731
|
+
image[:, :, 2] * 0.299).astype(numpy.float32)
|
|
732
|
+
|
|
733
|
+
if (rowMin == 0) and (rowMax == (nRows - 1)):
|
|
734
|
+
self._imageDataCacheIndex.insert(0, nImage)
|
|
735
|
+
self._imageDataCache.insert(0, image)
|
|
736
|
+
if len(self._imageDataCacheIndex) > self._maxImageCacheLength:
|
|
737
|
+
self._imageDataCacheIndex = self._imageDataCacheIndex[:self._maxImageCacheLength]
|
|
738
|
+
self._imageDataCache = self._imageDataCache[:self._maxImageCacheLength]
|
|
739
|
+
|
|
740
|
+
return image
|
|
741
|
+
|
|
742
|
+
def writeImage(self, image0, info=None, software=None, date=None):
|
|
743
|
+
if software is None:
|
|
744
|
+
software = 'PyMca.TiffIO'
|
|
745
|
+
#if date is None:
|
|
746
|
+
# date = time.ctime()
|
|
747
|
+
|
|
748
|
+
self.__makeSureFileIsOpen()
|
|
749
|
+
fd = self.fd
|
|
750
|
+
#prior to do anything, perform some tests
|
|
751
|
+
if not len(image0.shape):
|
|
752
|
+
raise ValueError("Empty image")
|
|
753
|
+
if len(image0.shape) == 1:
|
|
754
|
+
#get a different view
|
|
755
|
+
image = image0[:]
|
|
756
|
+
image.shape = 1, -1
|
|
757
|
+
else:
|
|
758
|
+
image = image0
|
|
759
|
+
|
|
760
|
+
if image.dtype == numpy.float64:
|
|
761
|
+
image = image.astype(numpy.float32)
|
|
762
|
+
fd.seek(0)
|
|
763
|
+
mode = fd.mode
|
|
764
|
+
name = fd.name
|
|
765
|
+
if 'w' in mode:
|
|
766
|
+
#we have to overwrite the file
|
|
767
|
+
self.__makeSureFileIsClosed()
|
|
768
|
+
fd = None
|
|
769
|
+
if os.path.exists(name):
|
|
770
|
+
os.remove(name)
|
|
771
|
+
fd = open(name, mode='wb+')
|
|
772
|
+
self._initEmptyFile(fd)
|
|
773
|
+
self.fd = fd
|
|
774
|
+
|
|
775
|
+
#read the file size
|
|
776
|
+
self.__makeSureFileIsOpen()
|
|
777
|
+
fd = self.fd
|
|
778
|
+
fd.seek(0, os.SEEK_END)
|
|
779
|
+
endOfFile = fd.tell()
|
|
780
|
+
if fd.tell() == 0:
|
|
781
|
+
self._initEmptyFile(fd)
|
|
782
|
+
fd.seek(0, os.SEEK_END)
|
|
783
|
+
endOfFile = fd.tell()
|
|
784
|
+
|
|
785
|
+
#init internal variables
|
|
786
|
+
self._initInternalVariables(fd)
|
|
787
|
+
st = self._structChar
|
|
788
|
+
|
|
789
|
+
#get the image file directories
|
|
790
|
+
nImages = self.getImageFileDirectories()
|
|
791
|
+
if DEBUG:
|
|
792
|
+
print("File contains %d images" % nImages)
|
|
793
|
+
if nImages == 0:
|
|
794
|
+
fd.seek(4)
|
|
795
|
+
fmt = st + 'I'
|
|
796
|
+
fd.write(struct.pack(fmt, endOfFile))
|
|
797
|
+
else:
|
|
798
|
+
fd.seek(self._IFD[-1])
|
|
799
|
+
fmt = st + 'H'
|
|
800
|
+
numberOfDirectoryEntries = struct.unpack(fmt, fd.read(struct.calcsize(fmt)))[0]
|
|
801
|
+
fmt = st + 'I'
|
|
802
|
+
pos = self._IFD[-1] + 2 + 12 * numberOfDirectoryEntries
|
|
803
|
+
fd.seek(pos)
|
|
804
|
+
fmt = st + 'I'
|
|
805
|
+
fd.write(struct.pack(fmt, endOfFile))
|
|
806
|
+
fd.flush()
|
|
807
|
+
|
|
808
|
+
#and we can write at the end of the file, find out the file length
|
|
809
|
+
fd.seek(0, os.SEEK_END)
|
|
810
|
+
|
|
811
|
+
#get the description information from the input information
|
|
812
|
+
if info is None:
|
|
813
|
+
description = info
|
|
814
|
+
else:
|
|
815
|
+
description = "%s" % ""
|
|
816
|
+
for key in info.keys():
|
|
817
|
+
description += "%s=%s\n" % (key, info[key])
|
|
818
|
+
|
|
819
|
+
#get the image file directory
|
|
820
|
+
outputIFD = self._getOutputIFD(image, description=description,
|
|
821
|
+
software=software,
|
|
822
|
+
date=date)
|
|
823
|
+
|
|
824
|
+
#write the new IFD
|
|
825
|
+
fd.write(outputIFD)
|
|
826
|
+
|
|
827
|
+
#write the image
|
|
828
|
+
if self._swap:
|
|
829
|
+
fd.write(image.byteswap().tostring())
|
|
830
|
+
else:
|
|
831
|
+
fd.write(image.tostring())
|
|
832
|
+
|
|
833
|
+
fd.flush()
|
|
834
|
+
self.fd = fd
|
|
835
|
+
self.__makeSureFileIsClosed()
|
|
836
|
+
|
|
837
|
+
def _initEmptyFile(self, fd=None):
|
|
838
|
+
if fd is None:
|
|
839
|
+
fd = self.fd
|
|
840
|
+
if sys.byteorder == "little":
|
|
841
|
+
order = "II"
|
|
842
|
+
#intel, little endian
|
|
843
|
+
fileOrder = "little"
|
|
844
|
+
self._structChar = '<'
|
|
845
|
+
else:
|
|
846
|
+
order = "MM"
|
|
847
|
+
#motorola, high endian
|
|
848
|
+
fileOrder = "big"
|
|
849
|
+
self._structChar = '>'
|
|
850
|
+
st = self._structChar
|
|
851
|
+
if fileOrder == sys.byteorder:
|
|
852
|
+
self._swap = False
|
|
853
|
+
else:
|
|
854
|
+
self._swap = True
|
|
855
|
+
fd.seek(0)
|
|
856
|
+
if sys.version < '3.0':
|
|
857
|
+
fd.write(struct.pack(st + '2s', order))
|
|
858
|
+
fd.write(struct.pack(st + 'H', 42))
|
|
859
|
+
fd.write(struct.pack(st + 'I', 0))
|
|
860
|
+
else:
|
|
861
|
+
fd.write(struct.pack(st + '2s', bytes(order, 'utf-8')))
|
|
862
|
+
fd.write(struct.pack(st + 'H', 42))
|
|
863
|
+
fd.write(struct.pack(st + 'I', 0))
|
|
864
|
+
fd.flush()
|
|
865
|
+
|
|
866
|
+
def _getOutputIFD(self, image, description=None, software=None, date=None):
|
|
867
|
+
#the tags have to be in order
|
|
868
|
+
#the very minimum is
|
|
869
|
+
#256:"NumberOfColumns", # S or L ImageWidth
|
|
870
|
+
#257:"NumberOfRows", # S or L ImageHeight
|
|
871
|
+
#258:"BitsPerSample", # S Number of bits per component
|
|
872
|
+
#259:"Compression", # SHORT (1 - NoCompression, ...
|
|
873
|
+
#262:"PhotometricInterpretation", # SHORT (0 - WhiteIsZero, 1 -BlackIsZero, 2 - RGB, 3 - Palette color
|
|
874
|
+
#270:"ImageDescription", # ASCII
|
|
875
|
+
#273:"StripOffsets", # S or L, for each strip, the byte offset of the strip
|
|
876
|
+
#278:"RowsPerStrip", # S or L, number of rows in each back may be not for the last
|
|
877
|
+
#279:"StripByteCounts", # S or L, The number of bytes in the strip AFTER any compression
|
|
878
|
+
#305:"Software", # ASCII
|
|
879
|
+
#306:"Date", # ASCII
|
|
880
|
+
#339:"SampleFormat", # SHORT Interpretation of data in each pixel
|
|
881
|
+
|
|
882
|
+
nDirectoryEntries = 9
|
|
883
|
+
imageDescription = None
|
|
884
|
+
if description is not None:
|
|
885
|
+
descriptionLength = len(description)
|
|
886
|
+
while descriptionLength < 4:
|
|
887
|
+
description = description + " "
|
|
888
|
+
descriptionLength = len(description)
|
|
889
|
+
if sys.version >= '3.0':
|
|
890
|
+
description = bytes(description, 'utf-8')
|
|
891
|
+
elif type(description) != type(""):
|
|
892
|
+
try:
|
|
893
|
+
description = description.decode('utf-8')
|
|
894
|
+
except UnicodeDecodeError:
|
|
895
|
+
try:
|
|
896
|
+
description = description.decode('latin-1')
|
|
897
|
+
except UnicodeDecodeError:
|
|
898
|
+
description = "%s" % description
|
|
899
|
+
if sys.version > '2.6':
|
|
900
|
+
description = description.encode('utf-8', errors="ignore")
|
|
901
|
+
description = "%s" % description
|
|
902
|
+
descriptionLength = len(description)
|
|
903
|
+
imageDescription = struct.pack("%ds" % descriptionLength, description)
|
|
904
|
+
nDirectoryEntries += 1
|
|
905
|
+
|
|
906
|
+
#software
|
|
907
|
+
if software is not None:
|
|
908
|
+
softwareLength = len(software)
|
|
909
|
+
while softwareLength < 4:
|
|
910
|
+
software = software + " "
|
|
911
|
+
softwareLength = len(software)
|
|
912
|
+
if sys.version >= '3.0':
|
|
913
|
+
software = bytes(software, 'utf-8')
|
|
914
|
+
softwarePackedString = struct.pack("%ds" % softwareLength, software)
|
|
915
|
+
nDirectoryEntries += 1
|
|
916
|
+
else:
|
|
917
|
+
softwareLength = 0
|
|
918
|
+
|
|
919
|
+
if date is not None:
|
|
920
|
+
dateLength = len(date)
|
|
921
|
+
if sys.version >= '3.0':
|
|
922
|
+
date = bytes(date, 'utf-8')
|
|
923
|
+
datePackedString = struct.pack("%ds" % dateLength, date)
|
|
924
|
+
dateLength = len(datePackedString)
|
|
925
|
+
nDirectoryEntries += 1
|
|
926
|
+
else:
|
|
927
|
+
dateLength = 0
|
|
928
|
+
|
|
929
|
+
nRows, nColumns = image.shape
|
|
930
|
+
dtype = image.dtype
|
|
931
|
+
bitsPerSample = int(dtype.str[-1]) * 8
|
|
932
|
+
|
|
933
|
+
#only uncompressed data
|
|
934
|
+
compression = 1
|
|
935
|
+
|
|
936
|
+
#interpretation, black is zero
|
|
937
|
+
interpretation = 1
|
|
938
|
+
|
|
939
|
+
#image description
|
|
940
|
+
if imageDescription is not None:
|
|
941
|
+
descriptionLength = len(imageDescription)
|
|
942
|
+
else:
|
|
943
|
+
descriptionLength = 0
|
|
944
|
+
|
|
945
|
+
#strip offsets
|
|
946
|
+
#we are putting them after the directory and the directory is
|
|
947
|
+
#at the end of the file
|
|
948
|
+
self.fd.seek(0, os.SEEK_END)
|
|
949
|
+
endOfFile = self.fd.tell()
|
|
950
|
+
if endOfFile == 0:
|
|
951
|
+
#empty file
|
|
952
|
+
endOfFile = 8
|
|
953
|
+
|
|
954
|
+
#rows per strip
|
|
955
|
+
if ALLOW_MULTIPLE_STRIPS:
|
|
956
|
+
#try to segment the image in several pieces
|
|
957
|
+
if not (nRows % 4):
|
|
958
|
+
rowsPerStrip = int(nRows / 4)
|
|
959
|
+
elif not (nRows % 10):
|
|
960
|
+
rowsPerStrip = int(nRows / 10)
|
|
961
|
+
elif not (nRows % 8):
|
|
962
|
+
rowsPerStrip = int(nRows / 8)
|
|
963
|
+
elif not (nRows % 4):
|
|
964
|
+
rowsPerStrip = int(nRows / 4)
|
|
965
|
+
elif not (nRows % 2):
|
|
966
|
+
rowsPerStrip = int(nRows / 2)
|
|
967
|
+
else:
|
|
968
|
+
rowsPerStrip = nRows
|
|
969
|
+
else:
|
|
970
|
+
rowsPerStrip = nRows
|
|
971
|
+
|
|
972
|
+
#stripByteCounts
|
|
973
|
+
stripByteCounts = int(nColumns * rowsPerStrip * bitsPerSample / 8)
|
|
974
|
+
|
|
975
|
+
if descriptionLength > 4:
|
|
976
|
+
stripOffsets0 = endOfFile + dateLength + descriptionLength + \
|
|
977
|
+
2 + 12 * nDirectoryEntries + 4
|
|
978
|
+
else:
|
|
979
|
+
stripOffsets0 = endOfFile + dateLength + \
|
|
980
|
+
2 + 12 * nDirectoryEntries + 4
|
|
981
|
+
|
|
982
|
+
if softwareLength > 4:
|
|
983
|
+
stripOffsets0 += softwareLength
|
|
984
|
+
|
|
985
|
+
stripOffsets = [stripOffsets0]
|
|
986
|
+
stripOffsetsLength = 0
|
|
987
|
+
stripOffsetsString = None
|
|
988
|
+
|
|
989
|
+
st = self._structChar
|
|
990
|
+
|
|
991
|
+
if rowsPerStrip != nRows:
|
|
992
|
+
nStripOffsets = int(nRows / rowsPerStrip)
|
|
993
|
+
fmt = st + 'I'
|
|
994
|
+
stripOffsetsLength = struct.calcsize(fmt) * nStripOffsets
|
|
995
|
+
stripOffsets0 += stripOffsetsLength
|
|
996
|
+
#the length for the stripByteCounts will be the same
|
|
997
|
+
stripOffsets0 += stripOffsetsLength
|
|
998
|
+
stripOffsets = []
|
|
999
|
+
for i in range(nStripOffsets):
|
|
1000
|
+
value = stripOffsets0 + i * stripByteCounts
|
|
1001
|
+
stripOffsets.append(value)
|
|
1002
|
+
if i == 0:
|
|
1003
|
+
stripOffsetsString = struct.pack(fmt, value)
|
|
1004
|
+
stripByteCountsString = struct.pack(fmt, stripByteCounts)
|
|
1005
|
+
else:
|
|
1006
|
+
stripOffsetsString += struct.pack(fmt, value)
|
|
1007
|
+
stripByteCountsString += struct.pack(fmt, stripByteCounts)
|
|
1008
|
+
|
|
1009
|
+
if DEBUG:
|
|
1010
|
+
print("IMAGE WILL START AT %d" % stripOffsets[0])
|
|
1011
|
+
|
|
1012
|
+
#sample format
|
|
1013
|
+
if dtype in [numpy.float32, numpy.float64] or\
|
|
1014
|
+
dtype.str[-2] == 'f':
|
|
1015
|
+
sampleFormat = SAMPLE_FORMAT_FLOAT
|
|
1016
|
+
elif dtype in [numpy.uint8, numpy.uint16, numpy.uint32, numpy.uint64]:
|
|
1017
|
+
sampleFormat = SAMPLE_FORMAT_UINT
|
|
1018
|
+
elif dtype in [numpy.int8, numpy.int16, numpy.int32, numpy.int64]:
|
|
1019
|
+
sampleFormat = SAMPLE_FORMAT_INT
|
|
1020
|
+
else:
|
|
1021
|
+
raise ValueError("Unsupported data type %s" % dtype)
|
|
1022
|
+
|
|
1023
|
+
info = {}
|
|
1024
|
+
info["nColumns"] = nColumns
|
|
1025
|
+
info["nRows"] = nRows
|
|
1026
|
+
info["nBits"] = bitsPerSample
|
|
1027
|
+
info["compression"] = compression
|
|
1028
|
+
info["photometricInterpretation"] = interpretation
|
|
1029
|
+
info["stripOffsets"] = stripOffsets
|
|
1030
|
+
info["rowsPerStrip"] = rowsPerStrip
|
|
1031
|
+
info["stripByteCounts"] = stripByteCounts
|
|
1032
|
+
info["date"] = date
|
|
1033
|
+
info["sampleFormat"] = sampleFormat
|
|
1034
|
+
|
|
1035
|
+
outputIFD = ""
|
|
1036
|
+
if sys.version > '2.6':
|
|
1037
|
+
outputIFD = eval('b""')
|
|
1038
|
+
|
|
1039
|
+
fmt = st + "H"
|
|
1040
|
+
outputIFD += struct.pack(fmt, nDirectoryEntries)
|
|
1041
|
+
|
|
1042
|
+
fmt = st + "HHII"
|
|
1043
|
+
outputIFD += struct.pack(fmt, TAG_NUMBER_OF_COLUMNS,
|
|
1044
|
+
FIELD_TYPE_OUT['I'],
|
|
1045
|
+
1,
|
|
1046
|
+
info["nColumns"])
|
|
1047
|
+
outputIFD += struct.pack(fmt, TAG_NUMBER_OF_ROWS,
|
|
1048
|
+
FIELD_TYPE_OUT['I'],
|
|
1049
|
+
1,
|
|
1050
|
+
info["nRows"])
|
|
1051
|
+
|
|
1052
|
+
fmt = st + 'HHIHH'
|
|
1053
|
+
outputIFD += struct.pack(fmt, TAG_BITS_PER_SAMPLE,
|
|
1054
|
+
FIELD_TYPE_OUT['H'],
|
|
1055
|
+
1,
|
|
1056
|
+
info["nBits"], 0)
|
|
1057
|
+
fmt = st + 'HHIHH'
|
|
1058
|
+
outputIFD += struct.pack(fmt, TAG_COMPRESSION,
|
|
1059
|
+
FIELD_TYPE_OUT['H'],
|
|
1060
|
+
1,
|
|
1061
|
+
info["compression"], 0)
|
|
1062
|
+
fmt = st + 'HHIHH'
|
|
1063
|
+
outputIFD += struct.pack(fmt, TAG_PHOTOMETRIC_INTERPRETATION,
|
|
1064
|
+
FIELD_TYPE_OUT['H'],
|
|
1065
|
+
1,
|
|
1066
|
+
info["photometricInterpretation"], 0)
|
|
1067
|
+
|
|
1068
|
+
if imageDescription is not None:
|
|
1069
|
+
descriptionLength = len(imageDescription)
|
|
1070
|
+
if descriptionLength > 4:
|
|
1071
|
+
fmt = st + 'HHII'
|
|
1072
|
+
outputIFD += struct.pack(fmt, TAG_IMAGE_DESCRIPTION,
|
|
1073
|
+
FIELD_TYPE_OUT['s'],
|
|
1074
|
+
descriptionLength,
|
|
1075
|
+
info["stripOffsets"][0] - \
|
|
1076
|
+
2 * stripOffsetsLength - \
|
|
1077
|
+
descriptionLength)
|
|
1078
|
+
else:
|
|
1079
|
+
#it has to have length 4
|
|
1080
|
+
fmt = st + 'HHI%ds' % descriptionLength
|
|
1081
|
+
outputIFD += struct.pack(fmt, TAG_IMAGE_DESCRIPTION,
|
|
1082
|
+
FIELD_TYPE_OUT['s'],
|
|
1083
|
+
descriptionLength,
|
|
1084
|
+
description)
|
|
1085
|
+
|
|
1086
|
+
if len(stripOffsets) == 1:
|
|
1087
|
+
fmt = st + 'HHII'
|
|
1088
|
+
outputIFD += struct.pack(fmt, TAG_STRIP_OFFSETS,
|
|
1089
|
+
FIELD_TYPE_OUT['I'],
|
|
1090
|
+
1,
|
|
1091
|
+
info["stripOffsets"][0])
|
|
1092
|
+
else:
|
|
1093
|
+
fmt = st + 'HHII'
|
|
1094
|
+
outputIFD += struct.pack(fmt, TAG_STRIP_OFFSETS,
|
|
1095
|
+
FIELD_TYPE_OUT['I'],
|
|
1096
|
+
len(stripOffsets),
|
|
1097
|
+
info["stripOffsets"][0] - 2 * stripOffsetsLength)
|
|
1098
|
+
|
|
1099
|
+
fmt = st + 'HHII'
|
|
1100
|
+
outputIFD += struct.pack(fmt, TAG_ROWS_PER_STRIP,
|
|
1101
|
+
FIELD_TYPE_OUT['I'],
|
|
1102
|
+
1,
|
|
1103
|
+
info["rowsPerStrip"])
|
|
1104
|
+
|
|
1105
|
+
if len(stripOffsets) == 1:
|
|
1106
|
+
fmt = st + 'HHII'
|
|
1107
|
+
outputIFD += struct.pack(fmt, TAG_STRIP_BYTE_COUNTS,
|
|
1108
|
+
FIELD_TYPE_OUT['I'],
|
|
1109
|
+
1,
|
|
1110
|
+
info["stripByteCounts"])
|
|
1111
|
+
else:
|
|
1112
|
+
fmt = st + 'HHII'
|
|
1113
|
+
outputIFD += struct.pack(fmt, TAG_STRIP_BYTE_COUNTS,
|
|
1114
|
+
FIELD_TYPE_OUT['I'],
|
|
1115
|
+
len(stripOffsets),
|
|
1116
|
+
info["stripOffsets"][0] - stripOffsetsLength)
|
|
1117
|
+
|
|
1118
|
+
if software is not None:
|
|
1119
|
+
if softwareLength > 4:
|
|
1120
|
+
fmt = st + 'HHII'
|
|
1121
|
+
outputIFD += struct.pack(fmt, TAG_SOFTWARE,
|
|
1122
|
+
FIELD_TYPE_OUT['s'],
|
|
1123
|
+
softwareLength,
|
|
1124
|
+
info["stripOffsets"][0] - \
|
|
1125
|
+
2 * stripOffsetsLength - \
|
|
1126
|
+
descriptionLength - softwareLength - dateLength)
|
|
1127
|
+
else:
|
|
1128
|
+
#it has to have length 4
|
|
1129
|
+
fmt = st + 'HHI%ds' % softwareLength
|
|
1130
|
+
outputIFD += struct.pack(fmt, TAG_SOFTWARE,
|
|
1131
|
+
FIELD_TYPE_OUT['s'],
|
|
1132
|
+
softwareLength,
|
|
1133
|
+
softwarePackedString)
|
|
1134
|
+
|
|
1135
|
+
if date is not None:
|
|
1136
|
+
fmt = st + 'HHII'
|
|
1137
|
+
outputIFD += struct.pack(fmt, TAG_DATE,
|
|
1138
|
+
FIELD_TYPE_OUT['s'],
|
|
1139
|
+
dateLength,
|
|
1140
|
+
info["stripOffsets"][0] - \
|
|
1141
|
+
2 * stripOffsetsLength - \
|
|
1142
|
+
descriptionLength - dateLength)
|
|
1143
|
+
|
|
1144
|
+
fmt = st + 'HHIHH'
|
|
1145
|
+
outputIFD += struct.pack(fmt, TAG_SAMPLE_FORMAT,
|
|
1146
|
+
FIELD_TYPE_OUT['H'],
|
|
1147
|
+
1,
|
|
1148
|
+
info["sampleFormat"], 0)
|
|
1149
|
+
fmt = st + 'I'
|
|
1150
|
+
outputIFD += struct.pack(fmt, 0)
|
|
1151
|
+
|
|
1152
|
+
if softwareLength > 4:
|
|
1153
|
+
outputIFD += softwarePackedString
|
|
1154
|
+
|
|
1155
|
+
if date is not None:
|
|
1156
|
+
outputIFD += datePackedString
|
|
1157
|
+
|
|
1158
|
+
if imageDescription is not None:
|
|
1159
|
+
if descriptionLength > 4:
|
|
1160
|
+
outputIFD += imageDescription
|
|
1161
|
+
|
|
1162
|
+
if stripOffsetsString is not None:
|
|
1163
|
+
outputIFD += stripOffsetsString
|
|
1164
|
+
outputIFD += stripByteCountsString
|
|
1165
|
+
|
|
1166
|
+
return outputIFD
|
|
1167
|
+
|
|
1168
|
+
|
|
1169
|
+
if __name__ == "__main__":
|
|
1170
|
+
filename = sys.argv[1]
|
|
1171
|
+
dtype = numpy.uint16
|
|
1172
|
+
if not os.path.exists(filename):
|
|
1173
|
+
print("Testing file creation")
|
|
1174
|
+
tif = TiffIO(filename, mode='wb+')
|
|
1175
|
+
data = numpy.arange(10000).astype(dtype)
|
|
1176
|
+
data.shape = 100, 100
|
|
1177
|
+
tif.writeImage(data, info={'Title':'1st'})
|
|
1178
|
+
tif = None
|
|
1179
|
+
if os.path.exists(filename):
|
|
1180
|
+
print("Testing image appending")
|
|
1181
|
+
tif = TiffIO(filename, mode='rb+')
|
|
1182
|
+
tif.writeImage((data * 2).astype(dtype), info={'Title':'2nd'})
|
|
1183
|
+
tif = None
|
|
1184
|
+
tif = TiffIO(filename)
|
|
1185
|
+
print("Number of images = %d" % tif.getNumberOfImages())
|
|
1186
|
+
for i in range(tif.getNumberOfImages()):
|
|
1187
|
+
info = tif.getInfo(i)
|
|
1188
|
+
for key in info:
|
|
1189
|
+
if key not in ["colormap"]:
|
|
1190
|
+
print("%s = %s" % (key, info[key]))
|
|
1191
|
+
elif info['colormap'] is not None:
|
|
1192
|
+
print("RED %s = %s" % (key, info[key][0:10, 0]))
|
|
1193
|
+
print("GREEN %s = %s" % (key, info[key][0:10, 1]))
|
|
1194
|
+
print("BLUE %s = %s" % (key, info[key][0:10, 2]))
|
|
1195
|
+
data = tif.getImage(i)[0, 0:10]
|
|
1196
|
+
print("data [0, 0:10] = ", data)
|
|
1197
|
+
|