Module flowpy.flow_io
21 | 25 | Expand source code 26 |
27 |import numpy as np
28 | import png
29 | import struct
30 |
31 | from pathlib import Path
32 | from warnings import warn
33 |
34 |
35 | def flow_write(output_file, flow, format=None):
36 | """
37 | Writes optical flow to file.
38 |
39 | Parameters
40 | ----------
41 | output_file: {str, pathlib.Path, file}
42 | Path of the file to write or file object.
43 | flow: numpy.ndarray
44 | 3D flow in the HWF (Height, Width, Flow) layout.
45 | flow[..., 0] should be the x-displacement
46 | flow[..., 1] should be the y-displacement
47 | format: str, optional
48 | Specify in what format the flow is written, accepted formats: "png" or "flo"
49 | If None, it is guessed on the file extension
50 |
51 | See Also
52 | --------
53 | flow_read
54 |
55 | """
56 |
57 | supported_formats = ("png", "flo")
58 |
59 | output_format = guess_extension(output_file, override=format)
60 |
61 | with FileManager(output_file, "wb") as f:
62 | if output_format == "png":
63 | flow_write_png(f, flow)
64 | else:
65 | flow_write_flo(f, flow)
66 |
67 |
68 | def flow_read(input_file, format=None):
69 | """
70 | Reads optical flow from file
71 |
72 | Parameters
73 | ----------
74 | output_file: {str, pathlib.Path, file}
75 | Path of the file to read or file object.
76 | format: str, optional
77 | Specify in what format the flow is raed, accepted formats: "png" or "flo"
78 | If None, it is guess on the file extension
79 |
80 | Returns
81 | -------
82 | flow: numpy.ndarray
83 | 3D flow in the HWF (Height, Width, Flow) layout.
84 | flow[..., 0] is the x-displacement
85 | flow[..., 1] is the y-displacement
86 |
87 | Notes
88 | -----
89 |
90 | The flo format is dedicated to optical flow and was first used in Middlebury optical flow database.
91 | The original defition can be found here: http://vision.middlebury.edu/flow/code/flow-code/flowIO.cpp
92 |
93 | The png format uses 16-bit RGB png to store optical flows.
94 | It was developped along with the KITTI Vision Benchmark Suite.
95 | More information can be found here: http://www.cvlibs.net/datasets/kitti/eval_scene_flow.php?benchmark=flow
96 |
97 | The both handle flow with invalid ``invalid'' values, to deal with occlusion for example.
98 | We convert such invalid values to NaN.
99 |
100 | See Also
101 | --------
102 | flow_write
103 |
104 | """
105 |
106 | input_format = guess_extension(input_file, override=format)
107 |
108 | with FileManager(input_file, "rb") as f:
109 | if input_format == "png":
110 | output = flow_read_png(f)
111 | else:
112 | output = flow_read_flo(f)
113 |
114 | return output
115 |
116 |
117 | def flow_read_flo(f):
118 | if (f.read(4) != b'PIEH'):
119 | warn("{} does not have a .flo file signature".format(f.name))
120 |
121 | width, height = struct.unpack("II", f.read(8))
122 | result = np.fromfile(f, dtype="float32").reshape((height, width, 2))
123 |
124 | # Set invalid flows to NaN
125 | mask_u = np.greater(np.abs(result[..., 0]), 1e9, where=(~np.isnan(result[..., 0])))
126 | mask_v = np.greater(np.abs(result[..., 1]), 1e9, where=(~np.isnan(result[..., 1])))
127 |
128 | result[mask_u | mask_v] = np.NaN
129 |
130 | return result
131 |
132 |
133 | def flow_write_flo(f, flow):
134 | SENTINEL = 1666666800.0 # Only here to look like Middlebury original files
135 | height, width, _ = flow.shape
136 |
137 | image = flow.copy()
138 | image[np.isnan(image)] = SENTINEL
139 |
140 | f.write(b'PIEH')
141 | f.write(struct.pack("II", width, height))
142 | image.astype(np.float32).tofile(f)
143 |
144 |
145 | def flow_read_png(f):
146 | width, height, stream, *_ = png.Reader(f).read()
147 |
148 | file_content = np.concatenate(list(stream)).reshape((height, width, 3))
149 | flow, valid = file_content[..., 0:2], file_content[..., 2]
150 |
151 | flow = (flow.astype(np.float) - 2 ** 15) / 64.
152 |
153 | flow[~valid.astype(np.bool)] = np.NaN
154 |
155 | return flow
156 |
157 |
158 | def flow_write_png(f, flow):
159 | SENTINEL = 0. # Only here to look like original KITTI files
160 | height, width, _ = flow.shape
161 | flow_copy = flow.copy()
162 |
163 | valid = ~(np.isnan(flow[..., 0]) | np.isnan(flow[..., 1]))
164 | flow_copy[~valid] = SENTINEL
165 |
166 | flow_copy = (flow_copy * 64. + 2 ** 15).astype(np.uint16)
167 | image = np.dstack((flow_copy, valid))
168 |
169 | writer = png.Writer(width, height, bitdepth=16, greyscale=False)
170 | writer.write(f, image.reshape((height, 3 * width)))
171 |
172 |
173 | class FileManager:
174 | def __init__(self, abstract_file, mode):
175 | self.abstract_file = abstract_file
176 | self.opened_file = None
177 | self.mode = mode
178 |
179 | def __enter__(self):
180 | if isinstance(self.abstract_file, str):
181 | self.opened_file = open(self.abstract_file, self.mode)
182 | elif isinstance(self.abstract_file, Path):
183 | self.opened_file = self.abstract_file.open(self.mode)
184 | else:
185 | return self.abstract_file
186 |
187 | return self.opened_file
188 |
189 | def __exit__(self, exc_type, exc_value, traceback):
190 | if self.opened_file is not None:
191 | self.opened_file.close()
192 |
193 |
194 | def guess_extension(abstract_file, override=None):
195 | if override is not None:
196 | return override
197 |
198 | if isinstance(abstract_file, str):
199 | return Path(abstract_file).suffix[1:]
200 | elif isinstance(abstract_file, Path):
201 | return abstract_file.suffix[1:]
202 |
203 | return Path(abstract_file.name).suffix[1:]
204 | Functions
212 |-
213 |
214 | def flow_read(input_file, format=None) 215 |
216 | -
217 | 243 |
Reads optical flow from file
218 |Parameters
219 |-
220 |
output_file:{str, pathlib.Path, file}
221 | - Path of the file to read or file object. 222 |
format:str, optional
223 | - Specify in what format the flow is raed, accepted formats: "png" or "flo" 224 | If None, it is guess on the file extension 225 |
Returns
227 |-
228 |
flow:numpy.ndarray
229 | - 3D flow in the HWF (Height, Width, Flow) layout. 230 | flow[…, 0] is the x-displacement 231 | flow[…, 1] is the y-displacement 232 |
Notes
234 |The flo format is dedicated to optical flow and was first used in Middlebury optical flow database. 235 | The original defition can be found here: http://vision.middlebury.edu/flow/code/flow-code/flowIO.cpp
236 |The png format uses 16-bit RGB png to store optical flows. 237 | It was developped along with the KITTI Vision Benchmark Suite. 238 | More information can be found here: http://www.cvlibs.net/datasets/kitti/eval_scene_flow.php?benchmark=flow
239 |The both handle flow with invalid ``invalid'' values, to deal with occlusion for example. 240 | We convert such invalid values to NaN.
241 |See Also
242 |244 |295 |245 | Expand source code 246 |
247 |
294 |def flow_read(input_file, format=None): 248 | """ 249 | Reads optical flow from file 250 | 251 | Parameters 252 | ---------- 253 | output_file: {str, pathlib.Path, file} 254 | Path of the file to read or file object. 255 | format: str, optional 256 | Specify in what format the flow is raed, accepted formats: "png" or "flo" 257 | If None, it is guess on the file extension 258 | 259 | Returns 260 | ------- 261 | flow: numpy.ndarray 262 | 3D flow in the HWF (Height, Width, Flow) layout. 263 | flow[..., 0] is the x-displacement 264 | flow[..., 1] is the y-displacement 265 | 266 | Notes 267 | ----- 268 | 269 | The flo format is dedicated to optical flow and was first used in Middlebury optical flow database. 270 | The original defition can be found here: http://vision.middlebury.edu/flow/code/flow-code/flowIO.cpp 271 | 272 | The png format uses 16-bit RGB png to store optical flows. 273 | It was developped along with the KITTI Vision Benchmark Suite. 274 | More information can be found here: http://www.cvlibs.net/datasets/kitti/eval_scene_flow.php?benchmark=flow 275 | 276 | The both handle flow with invalid ``invalid'' values, to deal with occlusion for example. 277 | We convert such invalid values to NaN. 278 | 279 | See Also 280 | -------- 281 | flow_write 282 | 283 | """ 284 | 285 | input_format = guess_extension(input_file, override=format) 286 | 287 | with FileManager(input_file, "rb") as f: 288 | if input_format == "png": 289 | output = flow_read_png(f) 290 | else: 291 | output = flow_read_flo(f) 292 | 293 | return output
296 | 297 | def flow_read_flo(f) 298 |
299 | -
300 |
301 | 302 |320 |
303 | Expand source code 304 |
305 |
319 |def flow_read_flo(f): 306 | if (f.read(4) != b'PIEH'): 307 | warn("{} does not have a .flo file signature".format(f.name)) 308 | 309 | width, height = struct.unpack("II", f.read(8)) 310 | result = np.fromfile(f, dtype="float32").reshape((height, width, 2)) 311 | 312 | # Set invalid flows to NaN 313 | mask_u = np.greater(np.abs(result[..., 0]), 1e9, where=(~np.isnan(result[..., 0]))) 314 | mask_v = np.greater(np.abs(result[..., 1]), 1e9, where=(~np.isnan(result[..., 1]))) 315 | 316 | result[mask_u | mask_v] = np.NaN 317 | 318 | return result
321 | 322 | def flow_read_png(f) 323 |
324 | -
325 |
326 | 327 |342 |
328 | Expand source code 329 |
330 |
341 |def flow_read_png(f): 331 | width, height, stream, *_ = png.Reader(f).read() 332 | 333 | file_content = np.concatenate(list(stream)).reshape((height, width, 3)) 334 | flow, valid = file_content[..., 0:2], file_content[..., 2] 335 | 336 | flow = (flow.astype(np.float) - 2 ** 15) / 64. 337 | 338 | flow[~valid.astype(np.bool)] = np.NaN 339 | 340 | return flow
343 | 344 | def flow_write(output_file, flow, format=None) 345 |
346 | -
347 | 362 |
Writes optical flow to file.
348 |Parameters
349 |-
350 |
output_file:{str, pathlib.Path, file}
351 | - Path of the file to write or file object. 352 |
flow:numpy.ndarray
353 | - 3D flow in the HWF (Height, Width, Flow) layout. 354 | flow[…, 0] should be the x-displacement 355 | flow[…, 1] should be the y-displacement 356 |
format:str, optional
357 | - Specify in what format the flow is written, accepted formats: "png" or "flo" 358 | If None, it is guessed on the file extension 359 |
See Also
361 |363 |398 |364 | Expand source code 365 |
366 |
397 |def flow_write(output_file, flow, format=None): 367 | """ 368 | Writes optical flow to file. 369 | 370 | Parameters 371 | ---------- 372 | output_file: {str, pathlib.Path, file} 373 | Path of the file to write or file object. 374 | flow: numpy.ndarray 375 | 3D flow in the HWF (Height, Width, Flow) layout. 376 | flow[..., 0] should be the x-displacement 377 | flow[..., 1] should be the y-displacement 378 | format: str, optional 379 | Specify in what format the flow is written, accepted formats: "png" or "flo" 380 | If None, it is guessed on the file extension 381 | 382 | See Also 383 | -------- 384 | flow_read 385 | 386 | """ 387 | 388 | supported_formats = ("png", "flo") 389 | 390 | output_format = guess_extension(output_file, override=format) 391 | 392 | with FileManager(output_file, "wb") as f: 393 | if output_format == "png": 394 | flow_write_png(f, flow) 395 | else: 396 | flow_write_flo(f, flow)
399 | 400 | def flow_write_flo(f, flow) 401 |
402 | -
403 |
404 | 405 |419 |
406 | Expand source code 407 |
408 |
418 |def flow_write_flo(f, flow): 409 | SENTINEL = 1666666800.0 # Only here to look like Middlebury original files 410 | height, width, _ = flow.shape 411 | 412 | image = flow.copy() 413 | image[np.isnan(image)] = SENTINEL 414 | 415 | f.write(b'PIEH') 416 | f.write(struct.pack("II", width, height)) 417 | image.astype(np.float32).tofile(f)
420 | 421 | def flow_write_png(f, flow) 422 |
423 | -
424 |
425 | 426 |443 |
427 | Expand source code 428 |
429 |
442 |def flow_write_png(f, flow): 430 | SENTINEL = 0. # Only here to look like original KITTI files 431 | height, width, _ = flow.shape 432 | flow_copy = flow.copy() 433 | 434 | valid = ~(np.isnan(flow[..., 0]) | np.isnan(flow[..., 1])) 435 | flow_copy[~valid] = SENTINEL 436 | 437 | flow_copy = (flow_copy * 64. + 2 ** 15).astype(np.uint16) 438 | image = np.dstack((flow_copy, valid)) 439 | 440 | writer = png.Writer(width, height, bitdepth=16, greyscale=False) 441 | writer.write(f, image.reshape((height, 3 * width)))
444 | 445 | def guess_extension(abstract_file, override=None) 446 |
447 | -
448 |
449 | 450 |464 |
451 | Expand source code 452 |
453 |
463 |def guess_extension(abstract_file, override=None): 454 | if override is not None: 455 | return override 456 | 457 | if isinstance(abstract_file, str): 458 | return Path(abstract_file).suffix[1:] 459 | elif isinstance(abstract_file, Path): 460 | return abstract_file.suffix[1:] 461 | 462 | return Path(abstract_file.name).suffix[1:]
465 |
Classes
469 |-
470 |
471 | class FileManager 472 | (abstract_file, mode) 473 |
474 | -
475 |
476 | 477 |500 |
478 | Expand source code 479 |
480 |
499 |class FileManager: 481 | def __init__(self, abstract_file, mode): 482 | self.abstract_file = abstract_file 483 | self.opened_file = None 484 | self.mode = mode 485 | 486 | def __enter__(self): 487 | if isinstance(self.abstract_file, str): 488 | self.opened_file = open(self.abstract_file, self.mode) 489 | elif isinstance(self.abstract_file, Path): 490 | self.opened_file = self.abstract_file.open(self.mode) 491 | else: 492 | return self.abstract_file 493 | 494 | return self.opened_file 495 | 496 | def __exit__(self, exc_type, exc_value, traceback): 497 | if self.opened_file is not None: 498 | self.opened_file.close()
501 |