"""
IO for multitiff files (:mod:`fluiddyn.io.multitiff`)
=====================================================
.. autofunction:: reorganize_single_frame_3Dscannedpiv_data
.. autofunction:: reorganize_piv3dscanning_doubleframe
.. autofunction:: reorganize_piv2d_singleframe
.. autofunction:: reorganize_piv2d_doubleframe
"""
import glob
import io
import os
import sys
import time
from math import ceil, log10
from packaging import version
from .image import _image_from_array
from .query import query_yes_no
try:
import PIL
from PIL import Image
except ImportError:
pass
def glob_sorted(s):
l = glob.glob(s)
l.sort()
return l
def _should_we_stop():
if query_yes_no("Some folders already exist\n Do you want to continue?"):
return False
return True
def _save_new_file(im, base_path, outputext, erase=False):
"""Convert and save file if destination path does not exist.
FIXME: The `im.convert` function calls result in ResourceWarning
in Python 3.x. Almost certainly a Pillow bug. Can be suppressed by
adding the following lines.
>>> import warnings
>>> warnings.simplefilter('ignore', ResourceWarning)
"""
path_save = base_path + "." + outputext
if not erase and os.path.exists(path_save):
return False
if outputext == "jp2":
with im.convert(mode="L") as new_im:
new_im.save(path_save, quality_mode="rate", quality_layers=[8])
else:
with im.convert(mode="I") as new_im:
new_im.save(path_save)
return True
def imsave(path, arrays, as_int=False):
"""Save a multi-frame image sequence.
Parameters
----------
path : str
Output file name or full path.
arrays : array-like
A iterable containing multiple 2D numpy arrays, representing frames.
as_int : bool
Convert to integer or not.
"""
pil_version = version.parse(PIL.__version__)
if pil_version < version.parse("3.4.0"):
raise ImportError("imsave for multiframe TIFF not supported.")
im_list = [_image_from_array(a, as_int) for a in arrays]
if pil_version < version.parse("4.2.0"):
from warnings import warn
warn(
"imsave for multiframe TIFF uses an intermediate GIF workaround.",
DeprecationWarning,
)
gif = im_list.pop(0)
with io.BytesIO() as output:
gif.save(output, format="GIF", save_all=True, append_images=im_list)
gif.close()
with Image.open(output) as im:
im.save(path, format="TIFF", save_all=True)
else:
im_list[0].save(
path,
compression="tiff_deflate",
save_all=True,
append_images=im_list[1:],
)
[docs]
def reorganize_single_frame_3Dscannedpiv_data(
files, nb_levels, outputdir=".", outputext="tif", erase=False
):
"""
Reorganize data from multi tiff into a folders (one for each level).
Parameters
----------
files : str
Root of the names of files: example files = 'van_karman_flow*'
nb_levels : int
Number of levels in the scanned PIV
outputdir : '.'
Output folder.
outputext : str
The output files extension
erase : {False, bool}
If erase, the existing files are replaced.
Notes
-----
Data is organize as:
- outputdir/level1/im0.tif, outputdir/level1/im1.tif ...
- outputdir/level2/im0.tif, outputdir/level2/im1.tif ...
"""
path_files = glob_sorted(files)
if len(path_files) == 0:
return
nb_images = get_approximate_number_images(path_files)
if nb_images == 0:
return
format_index = ":0{}d".format(int(ceil(log10(nb_images))))
format_level = ":0{}d".format(int(ceil(log10(nb_levels))))
index_im = 0
for ind in range(nb_levels):
dir_lev = (outputdir + "/level{" + format_level + "}").format(ind)
if not os.path.exists(dir_lev):
os.makedirs(dir_lev)
for path_tiff in path_files:
t_start = time.time()
print(
"Convert and save file\n" + path_tiff + "\nin directory\n" + outputdir
)
with Image.open(path_tiff) as im:
index_im_in_tiff = 0
while True:
try:
im.seek(index_im_in_tiff)
except EOFError:
break
else:
base_path = (
outputdir
+ "/level{"
+ format_level
+ "}/im{"
+ format_index
+ "}"
).format(index_im % nb_levels, index_im // nb_levels)
if _save_new_file(im, base_path, outputext, erase):
print(
"\r file {}; in {:.2f} s".format(
index_im, time.time() - t_start
),
end="",
)
sys.stdout.flush()
index_im += 1
index_im_in_tiff += 1
print("")
[docs]
def reorganize_piv3dscanning_doubleframe(
files, nb_levels, outputdir=".", outputext="tif", erase=False
):
"""
Reorganize data from multi tiff into a folders (one for each level).
Parameters
----------
files : str
Root of the names of files: example files = 'van_karman_flow*'
nb_levels : int
Number of levels in the scanned PIV
outputdir : '.'
Output folder.
outputext : str
The output files extension
erase : {False, bool}
If erase, the existing files are replaced.
Notes
-----
Data is organized as:
- outputdir/level1/im0.tif, outputdir/level1/im1.tif ...
- outputdir/level2/im0.tif, outputdir/level2/im1.tif ...
"""
path_files = glob_sorted(files)
if len(path_files) == 0:
return
nb_images = get_approximate_number_images(path_files)
if nb_images == 0:
return
format_index = ":0{}d".format(int(ceil(log10(nb_images))))
format_level = ":0{}d".format(int(ceil(log10(nb_levels))))
index_im = 0
for ind in range(nb_levels):
dir_lev = (outputdir + "/level{" + format_level + "}").format(ind)
if not os.path.exists(dir_lev):
os.makedirs(dir_lev)
for path_tiff in path_files:
t_start = time.time()
print(
"Convert and save file\n" + path_tiff + "\nin directory\n" + outputdir
)
with Image.open(path_tiff) as im:
index_im_in_tiff = 0
while True:
try:
im.seek(index_im_in_tiff)
except EOFError:
break
except SyntaxError:
print(
"SyntaxError with file",
path_tiff,
"\nStop the conversion for this file.",
)
return
else:
index_time = index_im // nb_levels
if index_time % 2 == 0:
letter = "a"
else:
letter = "b"
base_path = (
outputdir
+ "/level{"
+ format_level
+ "}/im{"
+ format_index
+ "}"
+ letter
).format(index_im % nb_levels, index_time // 2)
if _save_new_file(im, base_path, outputext, erase):
print(
"\r file {}; in {:.2f} s".format(
index_im, time.time() - t_start
),
end="",
)
sys.stdout.flush()
index_im += 1
index_im_in_tiff += 1
print("")
def count_number_images(path_files):
# warning: can be very slow
nb_images = 0
for path_tiff in path_files:
with Image.open(path_tiff) as image:
nb_images += image.n_frames
print(
"taking in account file {}, nb_images = {}".format(
path_tiff, nb_images
)
)
return nb_images
def get_approximate_number_images(path_files):
# can be much faster than count_number_images
path = path_files[0]
with Image.open(path) as image:
nb_images = image.n_frames
return len(path_files) * nb_images
[docs]
def reorganize_piv2d_singleframe(
files, outputdir=".", outputext="tif", erase=False
):
"""
Reorganize data from multi tiff (single frame 2D).
Parameters
----------
files : str
Root of the names of files: example files = 'van_karman_flow*'
outputdir : '.'
Output folder
outputext : str
The output files extension
erase : {False, bool}
If erase, the existing files are replaced.
Notes
-----
Data is organize as:
outputdir/im0.tif, outputdir/im1.tif ...
"""
path_files = glob_sorted(files)
if len(path_files) == 0:
return
nb_images = get_approximate_number_images(path_files)
if nb_images == 0:
return
format_index = ":0{}d".format(int(ceil(log10(nb_images))))
if not os.path.exists(outputdir):
os.makedirs(outputdir)
index_im = 0
for path_tiff in path_files:
t_start = time.time()
print(
"Convert and save file\n" + path_tiff + "\nin directory\n" + outputdir
)
with Image.open(path_tiff) as im:
index_im_in_tiff = 0
while True:
try:
im.seek(index_im_in_tiff)
except EOFError:
break
else:
base_path = outputdir + ("/im{" + format_index + "}").format(
index_im
)
if _save_new_file(im, base_path, outputext, erase):
print(
"\r file {}; in {:.2f} s".format(
index_im, time.time() - t_start
),
end="",
)
sys.stdout.flush()
index_im += 1
index_im_in_tiff += 1
print("")
print(
"End convert file {} in {} s".format(
os.path.split(path_tiff)[0], time.time() - t_start
)
)
sys.stdout.flush()
[docs]
def reorganize_piv2d_doubleframe(
files, outputdir=".", outputext="tif", erase=False
):
"""
Reorganize data from multi tiff (double frame 2D).
Parameters
----------
files : str
Root of the names of files: example files = 'van_karman_flow*'
outputdir : '.'
Output folder
outputext : str
The output files extension
erase : {False, bool}
If erase, the existing files are replaced.
Notes
-----
Data is organize as:
- outputdir/im0a.tif, outputdir/im0b.tif, outputdir/im1a.tif ...
"""
path_files = glob_sorted(files)
if len(path_files) == 0:
return
nb_images = get_approximate_number_images(path_files)
if nb_images == 0:
return
format_index = ":0{}d".format(int(ceil(log10(nb_images))))
index_im = 0
if not os.path.exists(outputdir):
os.makedirs(outputdir)
for path_tiff in path_files:
t_start = time.time()
print(
"Convert and save file\n" + path_tiff + "\nin directory\n" + outputdir
)
with Image.open(path_tiff) as im:
index_im_in_tiff = 0
while True:
try:
im.seek(index_im_in_tiff)
except EOFError:
break
else:
if index_im % 2 == 0:
letter = "a"
else:
letter = "b"
base_path = (
outputdir
+ ("/im{" + format_index + "}").format(index_im // 2)
+ letter
)
if _save_new_file(im, base_path, outputext, erase):
print(
"\r file {}; in {:.2f} s".format(
index_im, time.time() - t_start
),
end="",
)
sys.stdout.flush()
index_im += 1
index_im_in_tiff += 1
print("")
print(
"End convert file {} in {} s".format(
os.path.split(path_tiff)[0], time.time() - t_start
)
)
sys.stdout.flush()