petal-qc 0.0.0__py3-none-any.whl
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.
Potentially problematic release.
This version of petal-qc might be problematic. Click here for more details.
- petal_qc/BTreport/CheckBTtests.py +321 -0
- petal_qc/BTreport/__init__.py +0 -0
- petal_qc/BTreport/bustapeReport.py +144 -0
- petal_qc/__init__.py +14 -0
- petal_qc/metrology/Cluster.py +90 -0
- petal_qc/metrology/DataFile.py +47 -0
- petal_qc/metrology/PetalMetrology.py +327 -0
- petal_qc/metrology/__init__.py +0 -0
- petal_qc/metrology/all2csv.py +57 -0
- petal_qc/metrology/analyze_locking_points.py +597 -0
- petal_qc/metrology/cold_noise.py +106 -0
- petal_qc/metrology/comparisonTable.py +59 -0
- petal_qc/metrology/convert_mitutoyo.py +175 -0
- petal_qc/metrology/convert_smartscope.py +145 -0
- petal_qc/metrology/coreMetrology.py +402 -0
- petal_qc/metrology/data2csv.py +63 -0
- petal_qc/metrology/do_Metrology.py +117 -0
- petal_qc/metrology/flatness4nigel.py +157 -0
- petal_qc/metrology/gtkutils.py +120 -0
- petal_qc/metrology/petal_flatness.py +353 -0
- petal_qc/metrology/show_data_file.py +118 -0
- petal_qc/metrology/testSummary.py +37 -0
- petal_qc/metrology/test_paralelism.py +71 -0
- petal_qc/thermal/CSVImage.py +69 -0
- petal_qc/thermal/DebugPlot.py +76 -0
- petal_qc/thermal/IRBFile.py +768 -0
- petal_qc/thermal/IRCore.py +110 -0
- petal_qc/thermal/IRDataGetter.py +359 -0
- petal_qc/thermal/IRPetal.py +1338 -0
- petal_qc/thermal/IRPetalParam.py +71 -0
- petal_qc/thermal/PetalColorMaps.py +62 -0
- petal_qc/thermal/Petal_IR_Analysis.py +142 -0
- petal_qc/thermal/PipeFit.py +598 -0
- petal_qc/thermal/__init__.py +0 -0
- petal_qc/thermal/analyze_IRCore.py +417 -0
- petal_qc/thermal/contours.py +378 -0
- petal_qc/thermal/create_IRCore.py +185 -0
- petal_qc/thermal/pipe_read.py +182 -0
- petal_qc/thermal/show_IR_petal.py +420 -0
- petal_qc/utils/Geometry.py +756 -0
- petal_qc/utils/Progress.py +182 -0
- petal_qc/utils/__init__.py +0 -0
- petal_qc/utils/all_files.py +35 -0
- petal_qc/utils/docx_utils.py +186 -0
- petal_qc/utils/fit_utils.py +188 -0
- petal_qc/utils/utils.py +180 -0
- petal_qc-0.0.0.dist-info/METADATA +29 -0
- petal_qc-0.0.0.dist-info/RECORD +51 -0
- petal_qc-0.0.0.dist-info/WHEEL +5 -0
- petal_qc-0.0.0.dist-info/entry_points.txt +3 -0
- petal_qc-0.0.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,768 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""A python port of https://github.com/jonathanschilling/irb.
|
|
3
|
+
|
|
4
|
+
This is in turn inspired by https://github.com/tomsoftware/Irbis-File-Format.
|
|
5
|
+
|
|
6
|
+
Do not expect 'optimized' python code. The java one was not neither...
|
|
7
|
+
"""
|
|
8
|
+
import bz2
|
|
9
|
+
import copy
|
|
10
|
+
import datetime
|
|
11
|
+
import os
|
|
12
|
+
import pickle
|
|
13
|
+
import struct
|
|
14
|
+
import sys
|
|
15
|
+
from collections.abc import Iterable
|
|
16
|
+
from pathlib import Path
|
|
17
|
+
|
|
18
|
+
import matplotlib.pyplot as plt
|
|
19
|
+
import numpy as np
|
|
20
|
+
|
|
21
|
+
OLE_TIME_ZERO = datetime.datetime(1899, 12, 30, 0, 0, 0)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def ole2datetime(oledt):
|
|
25
|
+
"""Convert OLE_TIME to python datetime."""
|
|
26
|
+
return OLE_TIME_ZERO + datetime.timedelta(days=float(oledt))
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def is_iterable(obj):
|
|
30
|
+
"""Tell if an object is iterable. Strings are not considered iterables."""
|
|
31
|
+
if isinstance(obj, Iterable):
|
|
32
|
+
if isinstance(obj, str) or isinstance(obj, bytes):
|
|
33
|
+
return False
|
|
34
|
+
else:
|
|
35
|
+
return True
|
|
36
|
+
else:
|
|
37
|
+
return False
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class BinaryFile(object):
|
|
41
|
+
"""Encapsulates a binary file."""
|
|
42
|
+
|
|
43
|
+
def __init__(self, ifile):
|
|
44
|
+
"""Initialize from a file-like object."""
|
|
45
|
+
self.ifile = ifile
|
|
46
|
+
|
|
47
|
+
# Figure out the file size.
|
|
48
|
+
old_pos = self.ifile.tell()
|
|
49
|
+
self.ifile.seek(0, os.SEEK_END)
|
|
50
|
+
self.end = self.ifile.tell()
|
|
51
|
+
self.ifile.seek(old_pos)
|
|
52
|
+
|
|
53
|
+
def get_from_file(self, fmt, offset=-1):
|
|
54
|
+
"""Get data from a binary file.
|
|
55
|
+
|
|
56
|
+
Args:
|
|
57
|
+
----
|
|
58
|
+
fmt: The format (see struct)
|
|
59
|
+
offset (int, optional): THe offset in the file.
|
|
60
|
+
Defaults to -1, which ignores the offset.
|
|
61
|
+
|
|
62
|
+
Return:
|
|
63
|
+
------
|
|
64
|
+
array: The output data
|
|
65
|
+
|
|
66
|
+
"""
|
|
67
|
+
if offset > 0:
|
|
68
|
+
self.ifile.seek(offset)
|
|
69
|
+
|
|
70
|
+
sz = struct.calcsize(fmt)
|
|
71
|
+
dt = self.ifile.read(sz)
|
|
72
|
+
return struct.unpack(fmt, dt)
|
|
73
|
+
|
|
74
|
+
def read(self, n):
|
|
75
|
+
"""Read bytes from file."""
|
|
76
|
+
return self.ifile.read(n)
|
|
77
|
+
|
|
78
|
+
def seek(self, pos=0, whence=os.SEEK_SET):
|
|
79
|
+
"""Delegate teh seek functionality."""
|
|
80
|
+
return self.ifile.seek(pos, whence)
|
|
81
|
+
|
|
82
|
+
def tell(self):
|
|
83
|
+
"""Delegate the tell funcionality."""
|
|
84
|
+
return self.ifile.tell()
|
|
85
|
+
|
|
86
|
+
def __getValue(self, n, offset, fmt1, fmt2):
|
|
87
|
+
"""Get values from file."""
|
|
88
|
+
if n == 1:
|
|
89
|
+
return self.get_from_file(fmt1, offset)[0]
|
|
90
|
+
else:
|
|
91
|
+
return self.get_from_file(fmt2.format(n), offset)
|
|
92
|
+
|
|
93
|
+
def getInt(self, n=1, offset=-1):
|
|
94
|
+
"""Get an int."""
|
|
95
|
+
return self.__getValue(n, offset, "<i", "<{}i")
|
|
96
|
+
|
|
97
|
+
def getShort(self, n=1, offset=-1):
|
|
98
|
+
"""Get a short."""
|
|
99
|
+
return self.__getValue(n, offset, "<h", "<{}h")
|
|
100
|
+
|
|
101
|
+
def getFloat(self, n=1, offset=-1):
|
|
102
|
+
"""Get a float."""
|
|
103
|
+
return self.__getValue(n, offset, "<f", "<{}f")
|
|
104
|
+
|
|
105
|
+
def getDouble(self, n=1, offset=-1):
|
|
106
|
+
"""Get a float."""
|
|
107
|
+
return self.__getValue(n, offset, "<d", "<{}d")
|
|
108
|
+
|
|
109
|
+
def getUChar(self, n=1, offset=-1):
|
|
110
|
+
"""Get an unsigned char."""
|
|
111
|
+
return self.__getValue(n, offset, "<B", "<{}B")
|
|
112
|
+
|
|
113
|
+
def getString(self, n, offset=-1):
|
|
114
|
+
"""Get a string array."""
|
|
115
|
+
out = self.get_from_file("<{}c".format(n), offset)
|
|
116
|
+
ifirst = out.index(b'\x00')
|
|
117
|
+
return b"".join(out[:ifirst])
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
class HeaderBlock(object):
|
|
121
|
+
"""A header block in the IRB file."""
|
|
122
|
+
|
|
123
|
+
UNKNOWN, EMPTY, IMAGE, PREVIEW, TEXT_INFO, HEADER, AUDIO = (
|
|
124
|
+
-1, 0, 1, 2, 3, 4, 7)
|
|
125
|
+
|
|
126
|
+
header_name = {
|
|
127
|
+
UNKNOWN: "Unknown",
|
|
128
|
+
EMPTY: "Empty",
|
|
129
|
+
IMAGE: "Image",
|
|
130
|
+
PREVIEW: "Preview",
|
|
131
|
+
TEXT_INFO: "Text Info",
|
|
132
|
+
HEADER: "Header",
|
|
133
|
+
AUDIO: "Audio"
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
def __init__(self, ifile):
|
|
137
|
+
"""Initialize the object.
|
|
138
|
+
|
|
139
|
+
Args:
|
|
140
|
+
----
|
|
141
|
+
ifile: The input file
|
|
142
|
+
|
|
143
|
+
"""
|
|
144
|
+
self.ifile = ifile
|
|
145
|
+
self.where = ifile.tell()
|
|
146
|
+
self.type = ifile.getInt()
|
|
147
|
+
self.word2 = ifile.getInt()
|
|
148
|
+
self.frameIndex = ifile.getInt()
|
|
149
|
+
self.offset = ifile.getInt()
|
|
150
|
+
self.size = ifile.getInt()
|
|
151
|
+
|
|
152
|
+
# head has fixed size of 0x6C0
|
|
153
|
+
# but check against headerSize...
|
|
154
|
+
headerSize = 0x6C0
|
|
155
|
+
if (headerSize > self.size):
|
|
156
|
+
headerSize = self.size
|
|
157
|
+
|
|
158
|
+
# headerOffset = 0
|
|
159
|
+
|
|
160
|
+
self.imageOffset = headerSize
|
|
161
|
+
self.imageSize = self.size - self.imageOffset
|
|
162
|
+
|
|
163
|
+
self.word6 = ifile.getInt()
|
|
164
|
+
self.word7 = ifile.getInt()
|
|
165
|
+
self.word8 = ifile.getInt()
|
|
166
|
+
self.end = ifile.tell()
|
|
167
|
+
|
|
168
|
+
def get_name(self):
|
|
169
|
+
"""Return the header type as string."""
|
|
170
|
+
return HeaderBlock.header_name[self.type]
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
class IRBImage(object):
|
|
174
|
+
"""An image in the IRBis file."""
|
|
175
|
+
|
|
176
|
+
FLAGS_OFFSET = 1084
|
|
177
|
+
CELSIUS_OFFSET = 273.15
|
|
178
|
+
|
|
179
|
+
def __init__(self, block):
|
|
180
|
+
"""Initialize.
|
|
181
|
+
|
|
182
|
+
Args:
|
|
183
|
+
----
|
|
184
|
+
ifile: The input file
|
|
185
|
+
block: The block with the information.
|
|
186
|
+
|
|
187
|
+
"""
|
|
188
|
+
self.block = block
|
|
189
|
+
ifile = block.ifile
|
|
190
|
+
old_position = ifile.tell()
|
|
191
|
+
|
|
192
|
+
# GEt to the correct position in buffer
|
|
193
|
+
ifile.seek(block.offset)
|
|
194
|
+
self.bytesPerPixel = ifile.getShort()
|
|
195
|
+
self.compressed = ifile.getShort()
|
|
196
|
+
self.width = ifile.getShort()
|
|
197
|
+
self.height = ifile.getShort()
|
|
198
|
+
|
|
199
|
+
zero = ifile.getInt()
|
|
200
|
+
zero = ifile.getShort()
|
|
201
|
+
|
|
202
|
+
widthM1 = ifile.getShort()
|
|
203
|
+
if self.width-1 != widthM1:
|
|
204
|
+
print("width-1 check failed")
|
|
205
|
+
|
|
206
|
+
zero = ifile.getShort()
|
|
207
|
+
heightM1 = ifile.getShort()
|
|
208
|
+
if self.height - 1 != heightM1:
|
|
209
|
+
print("height-1 check failed")
|
|
210
|
+
|
|
211
|
+
zero = ifile.getShort() # -32768 VarioCAM
|
|
212
|
+
zero = ifile.getShort()
|
|
213
|
+
|
|
214
|
+
# Get other parameters
|
|
215
|
+
self.emissivity = ifile.getFloat()
|
|
216
|
+
self.distance = ifile.getFloat()
|
|
217
|
+
self.envTemp = ifile.getFloat()
|
|
218
|
+
|
|
219
|
+
zero = ifile.getShort()
|
|
220
|
+
zero = ifile.getShort() # -32768 VarioCAM
|
|
221
|
+
|
|
222
|
+
self.pathTemp = ifile.getFloat()
|
|
223
|
+
|
|
224
|
+
zero = ifile.getShort() # 0x65 for variocam
|
|
225
|
+
zero = ifile.getShort() # 16256 for VarioCAM
|
|
226
|
+
|
|
227
|
+
self.centerWavelength = ifile.getFloat()
|
|
228
|
+
|
|
229
|
+
zero = ifile.getShort()
|
|
230
|
+
zero = ifile.getShort()
|
|
231
|
+
zero = ifile.getShort()
|
|
232
|
+
zero = ifile.getShort()
|
|
233
|
+
|
|
234
|
+
if self.width > 10000 or self.height > 10000:
|
|
235
|
+
print("Image size out of range ({}x{})".format(
|
|
236
|
+
self.width, self.height))
|
|
237
|
+
return
|
|
238
|
+
|
|
239
|
+
flagsPosition = block.offset + IRBImage.FLAGS_OFFSET
|
|
240
|
+
self.readImageFlags(flagsPosition)
|
|
241
|
+
|
|
242
|
+
# TODO: is this HeaderBlock.headerSize ?
|
|
243
|
+
self.data_offset = 0x6C0
|
|
244
|
+
self.palette_offset = 60
|
|
245
|
+
self.offset = block.offset
|
|
246
|
+
self._image = None
|
|
247
|
+
# self.readImageData(ifile)
|
|
248
|
+
|
|
249
|
+
ifile.seek(old_position)
|
|
250
|
+
|
|
251
|
+
def __getstate__(self):
|
|
252
|
+
"""Select memeber that will not be pickled."""
|
|
253
|
+
state = self.__dict__.copy()
|
|
254
|
+
del state['block']
|
|
255
|
+
return state
|
|
256
|
+
|
|
257
|
+
def __setstate__(self, state):
|
|
258
|
+
"""Restore block."""
|
|
259
|
+
self.__dict__.update(state)
|
|
260
|
+
self.block = None
|
|
261
|
+
|
|
262
|
+
def readImageFlags(self, position):
|
|
263
|
+
"""Read Image Flags."""
|
|
264
|
+
ifile = self.block.ifile
|
|
265
|
+
old_position = ifile.tell()
|
|
266
|
+
|
|
267
|
+
self.calibRangeMin = ifile.getFloat(offset=position+92)
|
|
268
|
+
self.calibRangeMax = ifile.getFloat(offset=position+96)
|
|
269
|
+
self.device = ifile.getString(12, position+142)
|
|
270
|
+
self.deviceSN = ifile.getString(16, position+450)
|
|
271
|
+
self.optics = ifile.getString(32, position+202)
|
|
272
|
+
self.opticsResolution = ifile.getString(32, position+234)
|
|
273
|
+
self.opticsText = ifile.getString(48, position+554)
|
|
274
|
+
|
|
275
|
+
self.shortRangeStartErr = ifile.getFloat(offset=position+532)
|
|
276
|
+
self.shotRangeSize = ifile.getFloat(offset=position+536)
|
|
277
|
+
|
|
278
|
+
self.timeStampRaw = ifile.getDouble(offset=position+540)
|
|
279
|
+
self.timestampMillisecond = ifile.getInt(offset=position+548)
|
|
280
|
+
self.timestamp = ole2datetime(self.timeStampRaw)
|
|
281
|
+
|
|
282
|
+
ifile.seek(old_position)
|
|
283
|
+
|
|
284
|
+
def readImageData(self):
|
|
285
|
+
"""Get Image data."""
|
|
286
|
+
ifile = self.block.ifile
|
|
287
|
+
offset = self.offset
|
|
288
|
+
data_offset = self.data_offset
|
|
289
|
+
palette_offset = self.palette_offset
|
|
290
|
+
dataSize = self.width * self.height
|
|
291
|
+
self._image = np.zeros(dataSize)
|
|
292
|
+
matrixDataPos = 0
|
|
293
|
+
v1_pos = data_offset
|
|
294
|
+
v2_pos = v1_pos + dataSize
|
|
295
|
+
v1 = 0
|
|
296
|
+
v2 = 0
|
|
297
|
+
v2_count = 0
|
|
298
|
+
|
|
299
|
+
# Read Palette
|
|
300
|
+
self.readPalette(ifile, offset + palette_offset)
|
|
301
|
+
|
|
302
|
+
# Read the data
|
|
303
|
+
v1_data = ifile.getUChar(n=dataSize, offset=offset+data_offset)
|
|
304
|
+
|
|
305
|
+
if self.compressed:
|
|
306
|
+
# Compression
|
|
307
|
+
# Read Data
|
|
308
|
+
n2 = self.block.size-data_offset-dataSize
|
|
309
|
+
v2_data = ifile.getUChar(n=n2)
|
|
310
|
+
v2_pos = 0
|
|
311
|
+
|
|
312
|
+
for i in range(dataSize):
|
|
313
|
+
if v2_count < 1:
|
|
314
|
+
v2_count = v2_data[v2_pos]
|
|
315
|
+
v2 = v2_data[v2_pos+1]
|
|
316
|
+
P1 = self.palette[v2+1]
|
|
317
|
+
P2 = self.palette[v2]
|
|
318
|
+
v2_pos += 2
|
|
319
|
+
|
|
320
|
+
v2_count -= 1
|
|
321
|
+
|
|
322
|
+
v1 = v1_data[i]
|
|
323
|
+
f = v1/256.0
|
|
324
|
+
|
|
325
|
+
# linear interpolation betweeen eighboring palette entries
|
|
326
|
+
v = P1*f + P2*(1.0-f)
|
|
327
|
+
if v < 0.0:
|
|
328
|
+
v = 0.0
|
|
329
|
+
|
|
330
|
+
self._image[matrixDataPos] = v
|
|
331
|
+
matrixDataPos += 1
|
|
332
|
+
|
|
333
|
+
else:
|
|
334
|
+
# No compression
|
|
335
|
+
for i in range(dataSize):
|
|
336
|
+
v1 = v2_data[v1_pos]
|
|
337
|
+
v2 = v2_data[v1_pos+1]
|
|
338
|
+
P1 = self.palette[v2+1]
|
|
339
|
+
P2 = self.palette[v2]
|
|
340
|
+
v1_pos += 2
|
|
341
|
+
|
|
342
|
+
f = v1/256.0
|
|
343
|
+
|
|
344
|
+
# linear interpolation betweeen eighboring palette entries
|
|
345
|
+
v = P1*f + P2*(1.0-f)
|
|
346
|
+
if v < 0.0:
|
|
347
|
+
v = 0.0
|
|
348
|
+
|
|
349
|
+
self._image[matrixDataPos] = v
|
|
350
|
+
matrixDataPos += 1
|
|
351
|
+
|
|
352
|
+
self._image -= IRBImage.CELSIUS_OFFSET
|
|
353
|
+
self._image = self.image.reshape(self.height, self.width)
|
|
354
|
+
self._image = np.flipud(self._image)
|
|
355
|
+
|
|
356
|
+
def readPalette(self, ifile, offset):
|
|
357
|
+
"""Read palette from input buffer."""
|
|
358
|
+
old_position = ifile.tell()
|
|
359
|
+
ifile.seek(offset)
|
|
360
|
+
self.palette = np.array(ifile.getFloat(n=256))
|
|
361
|
+
ifile.seek(old_position)
|
|
362
|
+
|
|
363
|
+
def __getImage(self):
|
|
364
|
+
"""Get image."""
|
|
365
|
+
if self._image is None:
|
|
366
|
+
self.readImageData()
|
|
367
|
+
|
|
368
|
+
return self._image
|
|
369
|
+
|
|
370
|
+
def __setImage(self, val):
|
|
371
|
+
"""Set the image."""
|
|
372
|
+
self._image = val
|
|
373
|
+
|
|
374
|
+
def __delImage(self):
|
|
375
|
+
"""Delete the imafe"""
|
|
376
|
+
del self._image
|
|
377
|
+
|
|
378
|
+
# Get image
|
|
379
|
+
image = property(lambda self: self.__getImage(),
|
|
380
|
+
lambda self, val: self.__setImage(val),
|
|
381
|
+
lambda self: self.__delImage())
|
|
382
|
+
|
|
383
|
+
|
|
384
|
+
class IRBFile(object):
|
|
385
|
+
"""An IRB file.
|
|
386
|
+
|
|
387
|
+
Read a file or list of files and store the different blocks and immages.
|
|
388
|
+
|
|
389
|
+
Examples
|
|
390
|
+
--------
|
|
391
|
+
An IRB file is opened like this:
|
|
392
|
+
|
|
393
|
+
irbf = IRBFile.open_file( file )
|
|
394
|
+
|
|
395
|
+
This wull get just the header information, but not yet the full heavy
|
|
396
|
+
load of values per pixel. These will only be loaded when access is
|
|
397
|
+
requested.
|
|
398
|
+
|
|
399
|
+
One can iterate over the images or frames using
|
|
400
|
+
irbf.images()
|
|
401
|
+
|
|
402
|
+
That will return an iterator. An example of use:
|
|
403
|
+
|
|
404
|
+
for i, img in enumerate(irbf.images()):
|
|
405
|
+
values_i = img.image ...
|
|
406
|
+
|
|
407
|
+
One can access a given image by its index:
|
|
408
|
+
img = irbf.getImage(i)
|
|
409
|
+
|
|
410
|
+
The total nubmer of images of frams available is given by
|
|
411
|
+
irbf.n_images()
|
|
412
|
+
|
|
413
|
+
"""
|
|
414
|
+
|
|
415
|
+
def __init__(self, fname, concatenate=False):
|
|
416
|
+
"""Initialize.
|
|
417
|
+
|
|
418
|
+
Args:
|
|
419
|
+
----
|
|
420
|
+
fname: Path of input file or list of files.
|
|
421
|
+
|
|
422
|
+
"""
|
|
423
|
+
self.concatenate = concatenate
|
|
424
|
+
self.imgList = []
|
|
425
|
+
self.blocks = []
|
|
426
|
+
self.ifiles = []
|
|
427
|
+
self.file_images = []
|
|
428
|
+
self.current_file = 0
|
|
429
|
+
|
|
430
|
+
if not is_iterable(fname):
|
|
431
|
+
fname = [fname]
|
|
432
|
+
|
|
433
|
+
for fnam in fname:
|
|
434
|
+
fb = open(fnam, 'rb')
|
|
435
|
+
ifile = BinaryFile(fb)
|
|
436
|
+
if not concatenate:
|
|
437
|
+
self.imgList = []
|
|
438
|
+
|
|
439
|
+
self.parseHeader(ifile)
|
|
440
|
+
self.ifiles.append(ifile)
|
|
441
|
+
self.file_images.append(self.imgList)
|
|
442
|
+
|
|
443
|
+
self.set_file(self.current_file)
|
|
444
|
+
|
|
445
|
+
def __getstate__(self):
|
|
446
|
+
"""Select memeber that will not be pickled."""
|
|
447
|
+
state = self.__dict__.copy()
|
|
448
|
+
del state['blocks']
|
|
449
|
+
del state['ifiles']
|
|
450
|
+
return state
|
|
451
|
+
|
|
452
|
+
def __setstate__(self, state):
|
|
453
|
+
"""Restore block."""
|
|
454
|
+
self.__dict__.update(state)
|
|
455
|
+
self.blocks = []
|
|
456
|
+
self.ifiles = []
|
|
457
|
+
|
|
458
|
+
# number of files
|
|
459
|
+
nfiles = property(lambda self: len(self.ifiles))
|
|
460
|
+
|
|
461
|
+
# number of images
|
|
462
|
+
nimages = property(lambda self: len(self.imgList))
|
|
463
|
+
|
|
464
|
+
def parseHeader(self, ifile):
|
|
465
|
+
"""Read the header."""
|
|
466
|
+
magic = ifile.read(5)
|
|
467
|
+
# if magic[1:-1] != "IRB":
|
|
468
|
+
# print("Not an IRB file")
|
|
469
|
+
|
|
470
|
+
# GEt the file type
|
|
471
|
+
self.fileType = ifile.read(8)
|
|
472
|
+
if b"IRBIS" in self.fileType:
|
|
473
|
+
self.is_sequence = True
|
|
474
|
+
else:
|
|
475
|
+
self.is_sequence = False
|
|
476
|
+
|
|
477
|
+
# GEt Second filetype (ignored)
|
|
478
|
+
tmp = ifile.read(8)
|
|
479
|
+
|
|
480
|
+
flag1 = ifile.getInt()
|
|
481
|
+
blockOffset = ifile.getInt()
|
|
482
|
+
blockCount = ifile.getInt()
|
|
483
|
+
|
|
484
|
+
# Get the blocks
|
|
485
|
+
ifile.seek(blockOffset)
|
|
486
|
+
for iblk in range(blockCount):
|
|
487
|
+
block = HeaderBlock(ifile)
|
|
488
|
+
if block.type != HeaderBlock.EMPTY:
|
|
489
|
+
self.blocks.append(block)
|
|
490
|
+
|
|
491
|
+
for block in self.blocks:
|
|
492
|
+
if block.type == HeaderBlock.IMAGE:
|
|
493
|
+
image = IRBImage(block)
|
|
494
|
+
self.imgList.append(image)
|
|
495
|
+
|
|
496
|
+
if self.is_sequence:
|
|
497
|
+
offset = -1
|
|
498
|
+
for block in self.blocks:
|
|
499
|
+
if block.type == HeaderBlock.HEADER:
|
|
500
|
+
offset = block.offset
|
|
501
|
+
|
|
502
|
+
ifile.seek(offset)
|
|
503
|
+
while True:
|
|
504
|
+
space_left = ifile.end - ifile.tell()
|
|
505
|
+
if space_left < 32:
|
|
506
|
+
break
|
|
507
|
+
|
|
508
|
+
block = HeaderBlock(ifile)
|
|
509
|
+
if block.type == HeaderBlock.IMAGE:
|
|
510
|
+
if block.offset + block.imageSize > ifile.end:
|
|
511
|
+
break
|
|
512
|
+
|
|
513
|
+
image = IRBImage(block)
|
|
514
|
+
self.imgList.append(image)
|
|
515
|
+
|
|
516
|
+
elif block.type == HeaderBlock.HEADER:
|
|
517
|
+
offset = block.offset
|
|
518
|
+
ifile.seek(offset)
|
|
519
|
+
|
|
520
|
+
elif block.type == HeaderBlock.TEXT_INFO:
|
|
521
|
+
pass
|
|
522
|
+
|
|
523
|
+
def set_concatenate(self, val):
|
|
524
|
+
"""Set the 'concatenate' mode.
|
|
525
|
+
|
|
526
|
+
If trye Files are read one after the other, if False, fileas are read in parallel.
|
|
527
|
+
"""
|
|
528
|
+
if self.concatenate == val:
|
|
529
|
+
return
|
|
530
|
+
|
|
531
|
+
self.concatenate = val
|
|
532
|
+
self.imgList = []
|
|
533
|
+
for i in range(self.nfiles):
|
|
534
|
+
for im in self.file_images[i]:
|
|
535
|
+
self.imgList.append(im)
|
|
536
|
+
|
|
537
|
+
def set_file(self, i):
|
|
538
|
+
"""Selects i-th file.
|
|
539
|
+
|
|
540
|
+
This will select the images from that file.
|
|
541
|
+
|
|
542
|
+
Args:
|
|
543
|
+
i: the index of the file.
|
|
544
|
+
"""
|
|
545
|
+
if i < 0 or i > self.nfiles:
|
|
546
|
+
raise IndexError
|
|
547
|
+
|
|
548
|
+
self.current_file = i
|
|
549
|
+
self.imgList = self.file_images[i]
|
|
550
|
+
|
|
551
|
+
def getImage(self, i):
|
|
552
|
+
"""Get i-th image in current file.
|
|
553
|
+
|
|
554
|
+
Use set_file to change to another IRBfile.
|
|
555
|
+
"""
|
|
556
|
+
if i > self.nimages:
|
|
557
|
+
return None
|
|
558
|
+
|
|
559
|
+
if self.imgList[i].image is None:
|
|
560
|
+
self.imgList[i].readImageData()
|
|
561
|
+
|
|
562
|
+
return self.imgList[i]
|
|
563
|
+
|
|
564
|
+
def images(self, first=0, nframes=-1):
|
|
565
|
+
"""Generator to loop on images in current file."""
|
|
566
|
+
nyield = 0
|
|
567
|
+
if self.concatenate:
|
|
568
|
+
for i, im in enumerate(self.imgList):
|
|
569
|
+
if i < first:
|
|
570
|
+
continue
|
|
571
|
+
|
|
572
|
+
if nframes > 0 and nyield >= nframes:
|
|
573
|
+
break
|
|
574
|
+
|
|
575
|
+
nyield += 1
|
|
576
|
+
yield [im]
|
|
577
|
+
else:
|
|
578
|
+
for i, im in enumerate(zip(*self.file_images)):
|
|
579
|
+
if i < first:
|
|
580
|
+
continue
|
|
581
|
+
|
|
582
|
+
if nframes > 0 and nyield >= nframes:
|
|
583
|
+
break
|
|
584
|
+
|
|
585
|
+
nyield += 1
|
|
586
|
+
yield im
|
|
587
|
+
|
|
588
|
+
def append_average(self, nframes=-1, debug=False):
|
|
589
|
+
"""Append the average of all images to the list of images.
|
|
590
|
+
|
|
591
|
+
Args:
|
|
592
|
+
----
|
|
593
|
+
debug (bool, optional): True for debugging. Defaults to False.
|
|
594
|
+
|
|
595
|
+
Raises
|
|
596
|
+
------
|
|
597
|
+
RuntimeError: Will raise a RunTimeError if IRBfile is empty.
|
|
598
|
+
|
|
599
|
+
"""
|
|
600
|
+
# Get all images
|
|
601
|
+
out = []
|
|
602
|
+
for ifile in range(self.nfiles):
|
|
603
|
+
self.set_file(ifile)
|
|
604
|
+
list_values = []
|
|
605
|
+
if nframes < 0:
|
|
606
|
+
nframes = self.nimages
|
|
607
|
+
|
|
608
|
+
img = None
|
|
609
|
+
for i in range(self.nimages):
|
|
610
|
+
img = self.getImage(i)
|
|
611
|
+
if debug:
|
|
612
|
+
print('Image: ', i, ) # ' - Size: ',img.height,' x ', img.width)
|
|
613
|
+
|
|
614
|
+
values_i = img.image
|
|
615
|
+
list_values.append(values_i)
|
|
616
|
+
if i >= nframes:
|
|
617
|
+
break
|
|
618
|
+
|
|
619
|
+
if len(list_values) == 0:
|
|
620
|
+
continue
|
|
621
|
+
|
|
622
|
+
# and compute the average
|
|
623
|
+
values_average = np.mean(list_values, axis=0)
|
|
624
|
+
|
|
625
|
+
# Append the average to the list
|
|
626
|
+
# Copy data from last image for average_image, some info will be replaced
|
|
627
|
+
avg_img = copy.copy(img)
|
|
628
|
+
self.imgList.append(avg_img)
|
|
629
|
+
|
|
630
|
+
# Replace values of last img with values for average_image
|
|
631
|
+
# avg_img.block.end = 0 # Unsure what this is...
|
|
632
|
+
# avg_img.block.frameIndex += 1 # Correct
|
|
633
|
+
# avg_img.block.ifile.end = 0 # ifile is a bit weird to understand, but the only obviious difference is end
|
|
634
|
+
# avg_img.block.imageSize = 0 # Unsure what this is...
|
|
635
|
+
# avg_img.block.offset = 0 # Unsure what this is...
|
|
636
|
+
# avg_img.block.size = 0 # Unsure what this is...
|
|
637
|
+
# avg_img.block.where = 0 # Unsure what this is...
|
|
638
|
+
# avg_img.offset = 0 # Unsure what this is... 98: 8769050, 99: 88362978
|
|
639
|
+
# avg_img.palette = np.zeros(256,) # Unsure what this is...
|
|
640
|
+
# avg_img.timeStampRaw = img.timeStampRaw # currently the timestamp of the last image/frame
|
|
641
|
+
# avg_img.timestamp = img.timestamp # currently the timestamp of the last image/frame
|
|
642
|
+
# avg_img.timestampMillisecond = img.timestampMillisecond # currently the timestamp of the last image/frame
|
|
643
|
+
avg_img.image = values_average # Here go the new values
|
|
644
|
+
self.has_average = True
|
|
645
|
+
|
|
646
|
+
# Hope I found them all!
|
|
647
|
+
print('Average calculated!')
|
|
648
|
+
|
|
649
|
+
if debug:
|
|
650
|
+
x = 0
|
|
651
|
+
y = 0
|
|
652
|
+
xy_average = np.mean([frame[x, y]for frame in list_values])
|
|
653
|
+
|
|
654
|
+
print('Average on pixel ', x, ' , ', y, ' is ', xy_average)
|
|
655
|
+
print('The pixel ', x, ' , ', y, ' is in values_average is ', values_average[x, y])
|
|
656
|
+
|
|
657
|
+
out.append(avg_img)
|
|
658
|
+
|
|
659
|
+
if len(out) != self.nfiles:
|
|
660
|
+
raise RuntimeError("No images found to compute the average")
|
|
661
|
+
|
|
662
|
+
return out
|
|
663
|
+
|
|
664
|
+
def save(self, fname):
|
|
665
|
+
"""Save teh list of images in pickle format."""
|
|
666
|
+
# Be sure that all images are loaded.
|
|
667
|
+
for i in range(self.nimages):
|
|
668
|
+
if self.imgList[i].image is None:
|
|
669
|
+
self.imgList[i].readImageData()
|
|
670
|
+
|
|
671
|
+
with bz2.BZ2File(fname, 'w') as ofile:
|
|
672
|
+
pickle.dump(self, ofile)
|
|
673
|
+
|
|
674
|
+
@staticmethod
|
|
675
|
+
def load(fname):
|
|
676
|
+
"""Load an IRBFile objecr."""
|
|
677
|
+
out = None
|
|
678
|
+
with bz2.BZ2File(fname, 'rb') as ifile:
|
|
679
|
+
out = pickle.load(ifile)
|
|
680
|
+
|
|
681
|
+
return out
|
|
682
|
+
|
|
683
|
+
|
|
684
|
+
def open_file(fname):
|
|
685
|
+
"""Opens a file an returns a IRBFile instance.
|
|
686
|
+
|
|
687
|
+
Args:
|
|
688
|
+
----
|
|
689
|
+
fname: Path of input file or list of files.
|
|
690
|
+
|
|
691
|
+
"""
|
|
692
|
+
irb = None
|
|
693
|
+
if is_iterable(fname):
|
|
694
|
+
# we assume it is a list of IRB files...
|
|
695
|
+
try:
|
|
696
|
+
irbf = IRBFile(fname)
|
|
697
|
+
|
|
698
|
+
except FileNotFoundError as eee:
|
|
699
|
+
print(eee)
|
|
700
|
+
|
|
701
|
+
else:
|
|
702
|
+
# it is a single file path. Check that it exists.
|
|
703
|
+
ifile = Path(fname).expanduser().resolve()
|
|
704
|
+
if not ifile.exists():
|
|
705
|
+
print("Input file does not exist.")
|
|
706
|
+
return None
|
|
707
|
+
|
|
708
|
+
suffix = ifile.suffix.lower()
|
|
709
|
+
if suffix == ".irb":
|
|
710
|
+
irbf = IRBFile(fname)
|
|
711
|
+
|
|
712
|
+
else:
|
|
713
|
+
try:
|
|
714
|
+
irbf = IRBFile.load(fname)
|
|
715
|
+
except Exception as eee:
|
|
716
|
+
print(eee)
|
|
717
|
+
irbf = None
|
|
718
|
+
|
|
719
|
+
return irbf
|
|
720
|
+
|
|
721
|
+
|
|
722
|
+
if __name__ == "__main__":
|
|
723
|
+
"""Example of use of IRBFile.
|
|
724
|
+
|
|
725
|
+
Shows all the images in a file.
|
|
726
|
+
|
|
727
|
+
"""
|
|
728
|
+
from argparse import ArgumentParser
|
|
729
|
+
parser = ArgumentParser()
|
|
730
|
+
parser.add_argument('files', nargs='*', help="Input files")
|
|
731
|
+
parser.add_argument("--save", action="store_true",
|
|
732
|
+
default=False,
|
|
733
|
+
help="save all figures")
|
|
734
|
+
options = parser.parse_args()
|
|
735
|
+
if len(options.files) == 0:
|
|
736
|
+
print("I need an input file")
|
|
737
|
+
sys.exit()
|
|
738
|
+
|
|
739
|
+
IRfile = IRBFile(options.files)
|
|
740
|
+
nimgs = IRfile.nimages
|
|
741
|
+
print("Number of images: {}".format(nimgs))
|
|
742
|
+
|
|
743
|
+
fig = None
|
|
744
|
+
nimg = 0
|
|
745
|
+
ratio = -1
|
|
746
|
+
for img in IRfile.images():
|
|
747
|
+
tmin = np.min(img.image)
|
|
748
|
+
print("Tmin {:1f} - {}x{}".format(tmin, img.width, img.height))
|
|
749
|
+
if ratio < 0:
|
|
750
|
+
ratio = float(img.width)/float(img.height)
|
|
751
|
+
|
|
752
|
+
plt.clf()
|
|
753
|
+
plt.gca().clear()
|
|
754
|
+
pcm = plt.imshow(img.image, origin='lower', cmap="jet")
|
|
755
|
+
plt.gca().set_title("Min T. {:.1f} - {}".format(tmin, img.timestamp))
|
|
756
|
+
plt.colorbar(pcm, ax=plt.gca())
|
|
757
|
+
plt.draw()
|
|
758
|
+
if options.save:
|
|
759
|
+
if fig is None:
|
|
760
|
+
fig = plt.gcf()
|
|
761
|
+
fig.set_size_inches(6*ratio, 6)
|
|
762
|
+
|
|
763
|
+
fig.savefig('fig_{}.png'.format(nimg), dpi=300)
|
|
764
|
+
nimg += 1
|
|
765
|
+
|
|
766
|
+
plt.pause(0.00001)
|
|
767
|
+
|
|
768
|
+
plt.show()
|