import os
import vtk
import numpy as np
from PIL import Image
from vtk.util import numpy_support
from fury.utils import set_input
from urllib.request import urlretrieve
[docs]def load_image(filename, as_vtktype=False, use_pillow=True):
"""Load an image.
Parameters
----------
filename: str
should be png, bmp, jpeg or jpg files
as_vtktype: bool, optional
if True, return vtk output otherwise an ndarray. Default False.
use_pillow: bool, optional
Use pillow python library to load the files. Default True
Returns
-------
image: ndarray or vtk output
desired image array
"""
is_url = filename.lower().startswith('http://') \
or filename.lower().startswith('https://')
if is_url:
image_name = os.path.basename(filename)
if len(image_name.split('.')) < 2:
raise IOError(f'{filename} is not a valid image URL')
urlretrieve(filename, image_name)
filename = image_name
if use_pillow:
with Image.open(filename) as pil_image:
if pil_image.mode in ['RGBA', 'RGB', 'L']:
image = np.asarray(pil_image)
elif pil_image.mode.startswith('I;16'):
raw = pil_image.tobytes('raw', pil_image.mode)
dtype = '>u2' if pil_image.mode.endswith('B') else '<u2'
image = np.frombuffer(raw, dtype=dtype)
image.reshape(pil_image.size[::-1]).astype('=u2')
else:
try:
image = pil_image.convert('RGBA')
except ValueError:
raise RuntimeError('Unknown image mode {}'
.format(pil_image.mode))
image = np.asarray(pil_image)
if as_vtktype:
if image.ndim not in [2, 3]:
raise IOError("only 2D (L, RGB, RGBA) or 3D image available")
vtk_image = vtk.vtkImageData()
depth = 1 if image.ndim == 2 else image.shape[2]
# width, height
vtk_image.SetDimensions(image.shape[1], image.shape[0], depth)
vtk_image.SetExtent(0, image.shape[1] - 1,
0, image.shape[0] - 1,
0, 0)
vtk_image.SetSpacing(1.0, 1.0, 1.0)
vtk_image.SetOrigin(0.0, 0.0, 0.0)
arr_tmp = np.flipud(image)
arr_tmp = arr_tmp.reshape(image.shape[1] * image.shape[0], depth)
arr_tmp = np.ascontiguousarray(arr_tmp, dtype=image.dtype)
vtk_array_type = numpy_support.get_vtk_array_type(image.dtype)
uchar_array = numpy_support.numpy_to_vtk(arr_tmp, deep=True,
array_type=vtk_array_type)
vtk_image.GetPointData().SetScalars(uchar_array)
image = vtk_image
if is_url:
os.remove(filename)
return image
d_reader = {".png": vtk.vtkPNGReader,
".bmp": vtk.vtkBMPReader,
".jpeg": vtk.vtkJPEGReader,
".jpg": vtk.vtkJPEGReader,
".tiff": vtk.vtkTIFFReader,
".tif": vtk.vtkTIFFReader}
extension = os.path.splitext(os.path.basename(filename).lower())[1]
if extension.lower() not in d_reader.keys():
raise IOError("Impossible to read the file {0}: Unknown extension {1}".
format(filename, extension))
reader = d_reader.get(extension)()
reader.SetFileName(filename)
reader.Update()
reader.GetOutput().GetPointData().GetArray(0).SetName("original")
if not as_vtktype:
w, h, _ = reader.GetOutput().GetDimensions()
vtk_array = reader.GetOutput().GetPointData().GetScalars()
components = vtk_array.GetNumberOfComponents()
image = numpy_support.vtk_to_numpy(vtk_array).reshape(h, w, components)
image = np.flipud(image)
if is_url:
os.remove(filename)
return reader.GetOutput() if as_vtktype else image
[docs]def save_image(arr, filename, compression_quality=75,
compression_type='deflation', use_pillow=True):
"""Save a 2d or 3d image.
Expect an image with the following shape: (H, W) or (H, W, 1) or
(H, W, 3) or (H, W, 4).
Parameters
----------
arr : ndarray
array to save
filename : string
should be png, bmp, jpeg or jpg files
compression_quality : int, optional
compression_quality for jpeg data.
0 = Low quality, 100 = High quality
compression_type : str, optional
compression type for tiff file
select between: None, lzw, deflation (default)
use_pillow : bool, optional
Use imageio python library to save the files.
"""
if arr.ndim > 3:
raise IOError("Image Dimensions should be <=3")
d_writer = {".png": vtk.vtkPNGWriter,
".bmp": vtk.vtkBMPWriter,
".jpeg": vtk.vtkJPEGWriter,
".jpg": vtk.vtkJPEGWriter,
".tiff": vtk.vtkTIFFWriter,
".tif": vtk.vtkTIFFWriter,
}
extension = os.path.splitext(os.path.basename(filename).lower())[1]
if extension.lower() not in d_writer.keys():
raise IOError("Impossible to save the file {0}: Unknown extension {1}".
format(filename, extension))
if use_pillow:
im = Image.fromarray(arr)
im.save(filename, quality=compression_quality)
return
if arr.ndim == 2:
arr = arr[..., None]
shape = arr.shape
arr = np.flipud(arr)
if extension.lower() in ['.png', ]:
arr = arr.astype(np.uint8)
arr = arr.reshape((shape[1] * shape[0], shape[2]))
arr = np.ascontiguousarray(arr, dtype=arr.dtype)
vtk_array_type = numpy_support.get_vtk_array_type(arr.dtype)
vtk_array = numpy_support.numpy_to_vtk(num_array=arr,
deep=True,
array_type=vtk_array_type)
# Todo, look the following link for managing png 16bit
# https://stackoverflow.com/questions/15667947/vtkpngwriter-printing-out-black-images
vtk_data = vtk.vtkImageData()
vtk_data.SetDimensions(shape[1], shape[0], shape[2])
vtk_data.SetExtent(0, shape[1] - 1,
0, shape[0] - 1,
0, 0)
vtk_data.SetSpacing(1.0, 1.0, 1.0)
vtk_data.SetOrigin(0.0, 0.0, 0.0)
vtk_data.GetPointData().SetScalars(vtk_array)
writer = d_writer.get(extension)()
writer.SetFileName(filename)
writer.SetInputData(vtk_data)
if extension.lower() in [".jpg", ".jpeg"]:
writer.ProgressiveOn()
writer.SetQuality(compression_quality)
if extension.lower() in [".tif", ".tiff"]:
if not compression_type:
writer.SetCompressionToNoCompression()
elif compression_type.lower() == 'lzw':
writer.SetCompressionToLZW()
elif compression_type.lower() == 'deflation':
writer.SetCompressionToDeflate()
writer.Write()
[docs]def load_polydata(file_name):
"""Load a vtk polydata to a supported format file.
Supported file formats are VTK, VTP, FIB, PLY, STL XML and OBJ
Parameters
----------
file_name : string
Returns
-------
output : vtkPolyData
"""
file_extension = file_name.split(".")[-1].lower()
poly_reader = {"vtk": vtk.vtkPolyDataReader,
"vtp": vtk.vtkXMLPolyDataReader,
"fib": vtk.vtkPolyDataReader,
"ply": vtk.vtkPLYReader,
"stl": vtk.vtkSTLReader,
"xml": vtk.vtkXMLPolyDataReader}
if file_extension in poly_reader.keys():
reader = poly_reader.get(file_extension)()
elif file_extension == "obj":
# Special case, since there is two obj format
reader = vtk.vtkOBJReader()
reader.SetFileName(file_name)
reader.Update()
if reader.GetOutput().GetNumberOfCells() == 0:
reader = vtk.vtkMNIObjectReader()
else:
raise IOError("." + file_extension + " is not supported by FURY")
reader.SetFileName(file_name)
reader.Update()
return reader.GetOutput()
[docs]def save_polydata(polydata, file_name, binary=False, color_array_name=None):
"""Save a vtk polydata to a supported format file.
Save formats can be VTK, FIB, PLY, STL and XML.
Parameters
----------
polydata : vtkPolyData
file_name : string
binary : bool
color_array_name: ndarray
"""
# get file extension (type)
file_extension = file_name.split(".")[-1].lower()
poly_writer = {"vtk": vtk.vtkPolyDataWriter,
"vtp": vtk.vtkXMLPolyDataWriter,
"fib": vtk.vtkPolyDataWriter,
"ply": vtk.vtkPLYWriter,
"stl": vtk.vtkSTLWriter,
"xml": vtk.vtkXMLPolyDataWriter}
if file_extension in poly_writer.keys():
writer = poly_writer.get(file_extension)()
elif file_extension == "obj":
# Special case, since there is two obj format
find_keyword = file_name.lower().split(".")
if "mni" in find_keyword or "mnc" in find_keyword:
writer = vtk.vtkMNIObjectWriter()
else:
raise IOError("Wavefront obj requires a scene \n"
" for MNI obj, use '.mni.obj' extension")
else:
raise IOError("." + file_extension + " is not supported by FURY")
writer.SetFileName(file_name)
writer = set_input(writer, polydata)
if color_array_name is not None and file_extension == "ply":
writer.SetArrayName(color_array_name)
if binary:
writer.SetFileTypeToBinary()
writer.Update()
writer.Write()