Module vedo.io
Submodule to read/write meshes and other objects in different formats, and other I/O functionalities.
Expand source code
import glob
import os
import time
import numpy as np
import vedo
import vedo.colors as colors
import vedo.utils as utils
import vtk
from vedo import settings
from vedo.assembly import Assembly
from vedo.mesh import Mesh
from vedo.picture import Picture
from vedo.pointcloud import Points
from vedo.volume import Volume
__doc__ = """
Submodule to read/write meshes and other objects in different formats,
and other I/O functionalities.
"""
__all__ = [
"load",
"download",
"gunzip",
"loadStructuredPoints",
"loadStructuredGrid",
"loadRectilinearGrid",
"loadUnStructuredGrid",
"loadTransform",
"writeTransform",
"write",
"exportWindow",
"importWindow",
"screenshot",
"ask",
"Video",
]
# example web page for X3D
_x3d_html = """
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title> vedo with x3d </title>
<!-- THESE ARE THE RELEVANT LINES: -->
<script src='https://www.x3dom.org/download/x3dom.js'> </script>
<link rel='stylesheet' type='text/css' href='https://www.x3dom.org/download/x3dom.css'/>
<style>
table, td, th { border: 1px solid black; background-color: powderblue;}
table {width: 70%; border-collapse: collapse;}
table th {width: 35%;}
</style>
</head>
<body style="font-family: Verdana">
<h1>Example html generated by vedo</h1>
This example loads a 3D scene from file ~fileoutput generated by
<a href="https://github.com/marcomusy/vedo">vedo</a>
(see <a href="https://github.com/marcomusy/vedo/tree/master/examples/other/export_x3d.py">export_x3d.py</a>).
<br><br>
<!-- THESE ARE THE RELEVANT LINES: -->
<x3d width='~widthpx' height='~heightpx'>
<scene>
<Inline url="~fileoutput"> </Inline>
</scene>
</x3d>
<h3>Nothing shows up above this line?</h3>
Enable your browser to load local files:
<br><b>Firefox</b>: type <code>about:config</code> in the URL bar and
change <code>privacy.file_unique_origin</code> from <code>True</code> to <code>False</code>
<br><b>Chrome</b>: from terminal type:
<code>google-chrome --enable-webgl --allow-file-access-from-files</code>
(see <a href="https://cmatskas.com/interacting-with-local-data-files-using-chrome/">here</a>)
<br>
<h3>Controls:</h3>
<h4><strong>Examine Mode (activate with key 'e'):</strong></h4>
<table>
<tbody>
<tr class="even description">
<th>Button</th>
<th>Function</th>
</tr>
<tr>
<td>Left Button / Left Button + Shift</td>
<td>Rotate</td>
</tr>
<tr>
<td>Mid Button / Left Button + Ctl</td>
<td>Pan</td>
</tr>
<tr>
<td>Right Button / Wheel / Left Button + Alt</td>
<td>Zoom</td>
</tr>
<tr>
<td>Left double click</td>
<td>Set center of rotation</td>
</tr>
</tbody>
</table>
<h4><strong>Walk Mode (activate with key 'w'):</strong></h4>
<table>
<tbody>
<tr class="even description">
<th>Button</th>
<th>Function</th>
</tr>
<tr>
<td>Left Button</td>
<td>Move forward</td>
</tr>
<tr>
<td>Right Button</td>
<td>Move backward</td>
</tr>
</tbody>
</table>
<h4><strong>Fly Mode (activate with key 'f'):</strong></h4>
<table>
<tbody>
<tr class="even description">
<th>Button</th>
<th>Function</th>
</tr>
<tr>
<td>Left Button</td>
<td>Move forward</td>
</tr>
<tr>
<td>Right Button</td>
<td>Move backward</td>
</tr>
</tbody>
</table>
<h3>Non-interactive camera movement</h3>
<table>
<tbody>
<tr class="even description">
<th>Key</th>
<th>Function</th>
</tr>
<tr>
<td>r</td>
<td>reset view</td>
</tr>
<tr>
<td>a</td>
<td>show all</td>
</tr>
<tr>
<td>u</td>
<td>upright</td>
</tr>
</tbody>
</table>
</body>
</html>
"""
def load(inputobj, unpack=True, force=False):
"""
Load ``Mesh``, ``Volume`` and ``Picture`` objects from file or from the web.
The output will depend on the file extension. See examples below.
Unzip is made on the fly, if file ends with `.gz`.
Can load an object directly from a URL address.
Parameters
----------
unpack : bool
unpack MultiBlockData into a flat list of objects.
force : bool
when downloading a file ignore any previous cached downloads and force a new one.
Example:
.. code-block:: python
from vedo import dataurl, load, show
# Return a Mesh object
g = load(dataurl+'250.vtk')
show(g)
# Return a list of 2 meshes
g = load([dataurl+'250.vtk', dataurl+'270.vtk'])
show(g)
# Return a list of meshes by reading all files in a directory
# (if directory contains DICOM files then a Volume is returned)
g = load('mydicomdir/')
show(g)
# Download a file from a URL address and unzip it on the fly
g = load('https://vedo.embl.es/examples/panther.stl.gz')
show(g)
"""
acts = []
if utils.isSequence(inputobj):
flist = inputobj
elif isinstance(inputobj, str) and inputobj.startswith('https://'):
flist = [inputobj]
else:
flist = sorted(glob.glob(inputobj))
for fod in flist:
if fod.startswith('https://'):
fod = download(fod, force=force, verbose=False)
if os.path.isfile(fod): ### it's a file
if fod.endswith('.gz'):
fod = gunzip(fod)
a = _load_file(fod, unpack)
acts.append(a)
elif os.path.isdir(fod):### it's a directory or DICOM
flist = os.listdir(fod)
if '.dcm' in flist[0]: ### it's DICOM
reader = vtk.vtkDICOMImageReader()
reader.SetDirectoryName(fod)
reader.Update()
image = reader.GetOutput()
actor = Volume(image)
actor.info['PixelSpacing'] = reader.GetPixelSpacing()
actor.info['Width'] = reader.GetWidth()
actor.info['Height'] = reader.GetHeight()
actor.info['PositionPatient'] = reader.GetImagePositionPatient()
actor.info['OrientationPatient'] = reader.GetImageOrientationPatient()
actor.info['BitsAllocated'] = reader.GetBitsAllocated()
actor.info['PixelRepresentation'] = reader.GetPixelRepresentation()
actor.info['NumberOfComponents'] = reader.GetNumberOfComponents()
actor.info['TransferSyntaxUID'] = reader.GetTransferSyntaxUID()
actor.info['RescaleSlope'] = reader.GetRescaleSlope()
actor.info['RescaleOffset'] = reader.GetRescaleOffset()
actor.info['PatientName'] = reader.GetPatientName()
actor.info['StudyUID'] = reader.GetStudyUID()
actor.info['StudyID'] = reader.GetStudyID()
actor.info['GantryAngle'] = reader.GetGantryAngle()
acts.append(actor)
else: ### it's a normal directory
utils.humansort(flist)
for ifile in flist:
a = _load_file(fod+'/'+ifile, unpack)
acts.append(a)
else:
vedo.logger.error(f"in load(), cannot find {fod}")
if len(acts) == 1:
if "numpy" in str(type(acts[0])):
return acts[0]
if not acts[0]:
vedo.logger.error(f"in load(), cannot load {inputobj}")
return acts[0]
elif len(acts) == 0:
vedo.logger.error(f"in load(), cannot load {inputobj}")
return None
else:
return acts
def _load_file(filename, unpack):
fl = filename.lower()
################################################################# other formats:
if fl.endswith(".xml") or fl.endswith(".xml.gz") or fl.endswith(".xdmf"):
# Fenics tetrahedral file
actor = loadDolfin(filename)
elif fl.endswith(".neutral") or fl.endswith(".neu"): # neutral tetrahedral file
actor = loadNeutral(filename)
elif fl.endswith(".gmsh"): # gmesh file
actor = loadGmesh(filename)
elif fl.endswith(".pcd"): # PCL point-cloud format
actor = loadPCD(filename)
actor.GetProperty().SetPointSize(2)
elif fl.endswith(".off"):
actor = loadOFF(filename)
elif fl.endswith(".3ds"): # 3ds format
actor = load3DS(filename)
elif fl.endswith(".wrl"):
importer = vtk.vtkVRMLImporter()
importer.SetFileName(filename)
importer.Read()
importer.Update()
actors = importer.GetRenderer().GetActors() #vtkActorCollection
actors.InitTraversal()
wacts = []
for i in range(actors.GetNumberOfItems()):
act = actors.GetNextActor()
wacts.append(act)
actor = Assembly(wacts)
################################################################# volumetric:
elif fl.endswith(".tif") or fl.endswith(".tiff") or fl.endswith(".slc") or fl.endswith(".vti") \
or fl.endswith(".mhd") or fl.endswith(".nrrd") or fl.endswith(".nii") \
or fl.endswith(".dem"):
img = loadImageData(filename)
actor = Volume(img)
################################################################# 2D images:
elif fl.endswith(".png") or fl.endswith(".jpg") \
or fl.endswith(".bmp") or fl.endswith(".jpeg") or fl.endswith(".gif"):
if ".png" in fl:
picr = vtk.vtkPNGReader()
elif ".jpg" in fl or ".jpeg" in fl:
picr = vtk.vtkJPEGReader()
elif ".bmp" in fl:
picr = vtk.vtkBMPReader()
elif ".gif" in fl:
from PIL import Image, ImageSequence
img = Image.open(filename)
frames = []
for frame in ImageSequence.Iterator(img):
a = np.array(frame.convert('RGB').getdata(), dtype=np.uint8)
a = a.reshape(frame.size[1], frame.size[0],3)
frames.append(Picture(a))
return frames
picr.SetFileName(filename)
picr.Update()
actor = Picture(picr.GetOutput()) # object derived from vtk.vtkImageActor()
################################################################# multiblock:
elif fl.endswith(".vtm") or fl.endswith(".vtmb"):
read = vtk.vtkXMLMultiBlockDataReader()
read.SetFileName(filename)
read.Update()
mb = read.GetOutput()
if unpack:
acts = []
for i in range(mb.GetNumberOfBlocks()):
b = mb.GetBlock(i)
if isinstance(b, (vtk.vtkPolyData,
vtk.vtkUnstructuredGrid,
vtk.vtkStructuredGrid,
vtk.vtkRectilinearGrid)):
acts.append(Mesh(b))
elif isinstance(b, vtk.vtkImageData):
acts.append(Volume(b))
elif isinstance(b, vtk.vtkUnstructuredGrid):
acts.append(vedo.UGrid(b))
return acts
else:
return mb
################################################################# numpy:
elif fl.endswith(".npy") or fl.endswith(".npz"):
acts = loadnumpy(filename)
if unpack is False:
return Assembly(acts)
return acts
elif fl.endswith(".geojson"):
return loadGeoJSON(filename)
elif fl.endswith(".pvd"):
return loadPVD(filename)
elif fl.endswith(".pdb"):
return loadPDB(filename)
################################################################# polygonal mesh:
else:
if fl.endswith(".vtk"): # read all legacy vtk types
#output can be:
# PolyData, StructuredGrid, StructuredPoints, UnstructuredGrid, RectilinearGrid
reader = vtk.vtkDataSetReader()
reader.ReadAllScalarsOn()
reader.ReadAllVectorsOn()
reader.ReadAllTensorsOn()
reader.ReadAllFieldsOn()
reader.ReadAllNormalsOn()
reader.ReadAllColorScalarsOn()
elif fl.endswith(".ply"):
reader = vtk.vtkPLYReader()
elif fl.endswith(".obj"):
reader = vtk.vtkOBJReader()
elif fl.endswith(".stl"):
reader = vtk.vtkSTLReader()
elif fl.endswith(".byu") or fl.endswith(".g"):
reader = vtk.vtkBYUReader()
elif fl.endswith(".foam"): # OpenFoam
reader = vtk.vtkOpenFOAMReader()
elif fl.endswith(".pvd"):
reader = vtk.vtkXMLGenericDataObjectReader()
elif fl.endswith(".vtp"):
reader = vtk.vtkXMLPolyDataReader()
elif fl.endswith(".vts"):
reader = vtk.vtkXMLStructuredGridReader()
elif fl.endswith(".vtu"):
reader = vtk.vtkXMLUnstructuredGridReader()
elif fl.endswith(".vtr"):
reader = vtk.vtkXMLRectilinearGridReader()
elif fl.endswith(".pvtk"):
reader = vtk.vtkPDataSetReader()
elif fl.endswith(".pvtr"):
reader = vtk.vtkXMLPRectilinearGridReader()
elif fl.endswith("pvtu"):
reader = vtk.vtkXMLPUnstructuredGridReader()
elif fl.endswith(".txt") or fl.endswith(".xyz"):
reader = vtk.vtkParticleReader() # (format is x, y, z, scalar)
elif fl.endswith(".facet"):
reader = vtk.vtkFacetReader()
else:
return None
reader.SetFileName(filename)
reader.Update()
routput = reader.GetOutput()
if not routput:
vedo.logger.error(f"unable to load {filename}")
return None
if isinstance(routput, vtk.vtkUnstructuredGrid):
actor = vedo.TetMesh(routput)
else:
actor = Mesh(routput)
if fl.endswith(".txt") or fl.endswith(".xyz"):
actor.GetProperty().SetPointSize(4)
actor.filename = filename
actor.fileSize, actor.created = fileInfo(filename)
return actor
def download(url, force=False, verbose=True):
"""Retrieve a file from a url, save it locally and return its path.
Use ``force`` to force reload and discard cache copies."""
if not url.startswith('https://'):
vedo.logger.error(f"Invalid URL (must start with https):\n{url}")
return url
url = url.replace('www.dropbox', 'dl.dropbox')
if "github.com" in url:
url = url.replace('/blob/', '/raw/')
basename = os.path.basename(url)
if '?' in basename:
basename = basename.split('?')[0]
from tempfile import NamedTemporaryFile
tmp_file = NamedTemporaryFile(delete=False)
tmp_file.name = os.path.join(os.path.dirname(tmp_file.name),
os.path.basename(basename))
if force==False and os.path.exists(tmp_file.name):
if verbose:
colors.printc("reusing cached file:", tmp_file.name)
#colors.printc(" (use force=True to force a new download)")
return tmp_file.name
try:
from urllib.request import urlopen, Request
req = Request(url, headers={'User-Agent': 'Mozilla/5.0'})
if verbose:
colors.printc('reading', basename, 'from',
url.split('/')[2][:40],'...', end='')
except ImportError:
import urllib2
import contextlib
urlopen = lambda url_: contextlib.closing(urllib2.urlopen(url_))
req = url
if verbose:
colors.printc('reading', basename, 'from',
url.split('/')[2][:40],'...', end='')
with urlopen(req) as response, open(tmp_file.name, 'wb') as output:
output.write(response.read())
if verbose: colors.printc(' done.')
return tmp_file.name
def gunzip(filename):
"""Unzip a `.gz` file to a temporary file and returns its path."""
if not filename.endswith('.gz'):
#colors.printc("gunzip() error: file must end with .gz", c='r')
return filename
from tempfile import NamedTemporaryFile
import gzip
tmp_file = NamedTemporaryFile(delete=False)
tmp_file.name = os.path.join(os.path.dirname(tmp_file.name),
os.path.basename(filename).replace('.gz',''))
inF = gzip.open(filename, "rb")
with open(tmp_file.name, "wb") as outF:
outF.write(inF.read())
inF.close()
return tmp_file.name
def fileInfo(file_path):
sz, created= "", ""
if os.path.isfile(file_path):
file_info = os.stat(file_path)
num = file_info.st_size
for x in ['B', 'KB', 'MB', 'GB', 'TB']:
if num < 1024.0:
break
num /= 1024.0
sz = "%3.1f%s" % (num, x)
created = time.ctime(os.path.getmtime(file_path))
return sz, created
###################################################################
def loadStructuredPoints(filename):
"""Load and return a ``vtkStructuredPoints`` object from file."""
reader = vtk.vtkStructuredPointsReader()
reader.SetFileName(filename)
reader.Update()
return reader.GetOutput()
def loadStructuredGrid(filename):
"""Load and return a ``vtkStructuredGrid`` object from file."""
if filename.endswith(".vts"):
reader = vtk.vtkXMLStructuredGridReader()
else:
reader = vtk.vtkStructuredGridReader()
reader.SetFileName(filename)
reader.Update()
return reader.GetOutput()
def loadUnStructuredGrid(filename):
"""Load and return a ``vtkunStructuredGrid`` object from file."""
if filename.endswith(".vtu"):
reader = vtk.vtkXMLUnstructuredGridReader()
else:
reader = vtk.vtkUnstructuredGridReader()
reader.SetFileName(filename)
reader.Update()
return reader.GetOutput()
def loadRectilinearGrid(filename):
"""Load and return a ``vtkRectilinearGrid`` object from file."""
if filename.endswith(".vtr"):
reader = vtk.vtkXMLRectilinearGridReader()
else:
reader = vtk.vtkRectilinearGridReader()
reader.SetFileName(filename)
reader.Update()
return reader.GetOutput()
def loadXMLData(filename):
"""Read any type of vtk data object encoded in XML format."""
reader = vtk.vtkXMLGenericDataObjectReader()
reader.SetFileName(filename)
reader.Update()
return reader.GetOutput()
###################################################################
def load3DS(filename):
"""Load ``3DS`` file format from file. Return an ``Assembly(vtkAssembly)`` object."""
renderer = vtk.vtkRenderer()
renWin = vtk.vtkRenderWindow()
renWin.AddRenderer(renderer)
importer = vtk.vtk3DSImporter()
importer.SetFileName(filename)
importer.ComputeNormalsOn()
importer.SetRenderWindow(renWin)
importer.Update()
actors = renderer.GetActors() # vtkActorCollection
acts = []
for i in range(actors.GetNumberOfItems()):
a = actors.GetItemAsObject(i)
acts.append(a)
del renWin
return Assembly(acts)
def loadOFF(filename):
"""Read the OFF file format (polygonal mesh)."""
with open(filename, "r") as f:
lines = f.readlines()
vertices = []
faces = []
NumberOfVertices = None
i = -1
for text in lines:
if len(text) == 0:
continue
if text == '\n':
continue
if "#" in text:
continue
if "OFF" in text:
continue
ts = text.split()
n = len(ts)
if not NumberOfVertices and n > 1:
NumberOfVertices, NumberOfFaces = int(ts[0]), int(ts[1])
continue
i += 1
if i < NumberOfVertices and n == 3:
x, y, z = float(ts[0]), float(ts[1]), float(ts[2])
vertices.append([x, y, z])
ids = []
if NumberOfVertices <= i < (NumberOfVertices + NumberOfFaces + 1) and n > 2:
ids += [int(xx) for xx in ts[1:]]
faces.append(ids)
return Mesh(utils.buildPolyData(vertices, faces))
def loadGeoJSON(filename):
"""Load GeoJSON files."""
jr = vtk.vtkGeoJSONReader()
jr.SetFileName(filename)
jr.Update()
return Mesh(jr.GetOutput())
def loadDolfin(filename, exterior=False):
"""Reads a `Fenics/Dolfin` file format (.xml or .xdmf).
Return an ``Mesh`` object."""
import sys
if sys.version_info[0] < 3:
return _loadDolfin_old(filename)
import dolfin
if filename.lower().endswith('.xdmf'):
f = dolfin.XDMFFile(filename)
m = dolfin.Mesh()
f.read(m)
else:
m = dolfin.Mesh(filename)
bm = dolfin.BoundaryMesh(m, "exterior")
if exterior:
poly = utils.buildPolyData(bm.coordinates(), bm.cells(), fast=True, tetras=True)
else:
polyb = utils.buildPolyData(bm.coordinates(), bm.cells(), fast=True, tetras=True)
polym = utils.buildPolyData(m.coordinates(), m.cells(), fast=True, tetras=True)
app = vtk.vtkAppendPolyData()
app.AddInputData(polym)
app.AddInputData(polyb)
app.Update()
poly = app.GetOutput()
return Mesh(poly).lw(0.1)
def _loadDolfin_old(filename, exterior='dummy'):
import xml.etree.ElementTree as et
if filename.endswith(".gz"):
import gzip
inF = gzip.open(filename, "rb")
with open("/tmp/filename.xml", "wb") as outF:
outF.write(inF.read())
inF.close()
tree = et.parse("/tmp/filename.xml")
else:
tree = et.parse(filename)
coords, faces = [], []
for mesh in tree.getroot():
for elem in mesh:
for e in elem.findall("vertex"):
x = float(e.get("x"))
y = float(e.get("y"))
ez = e.get("z")
if ez is None:
coords.append([x, y])
else:
z = float(ez)
coords.append([x, y, z])
tets = elem.findall("tetrahedron")
if not len(tets):
tris = elem.findall("triangle")
for e in tris:
v0 = int(e.get("v0"))
v1 = int(e.get("v1"))
v2 = int(e.get("v2"))
faces.append([v0, v1, v2])
else:
for e in tets:
v0 = int(e.get("v0"))
v1 = int(e.get("v1"))
v2 = int(e.get("v2"))
v3 = int(e.get("v3"))
faces.append([v0, v1, v2, v3])
poly = utils.buildPolyData(coords, faces)
return Mesh(poly)
def loadPVD(filename):
"""Reads a paraview set of files."""
import xml.etree.ElementTree as et
tree = et.parse(filename)
dname = os.path.dirname(filename)
if not dname:
dname = '.'
listofobjs = []
for coll in tree.getroot():
for dataset in coll:
fname = dataset.get("file")
ob = load(dname+'/'+fname)
tm = dataset.get("timestep")
if tm:
ob.time(tm)
listofobjs.append(ob)
if len(listofobjs) == 1:
return listofobjs[0]
elif len(listofobjs) == 0:
return None
else:
return listofobjs
def loadPDB(filename, bondScale=1, hydrogenBondScale=1, coilWidth=0.3, helixWidth=1.3):
"""Reads a molecule Protein Data Bank file."""
rr = vtk.vtkPDBReader()
rr.SetFileName('1btn.pdb')
rr.SetBScale(bondScale)
rr.SetHBScale(hydrogenBondScale)
rr.Update()
prf = vtk.vtkProteinRibbonFilter()
prf.SetCoilWidth(coilWidth)
prf.SetHelixWidth(helixWidth)
prf.SetInputData(rr.GetOutput())
prf.Update()
return Mesh(prf.GetOutput())
def loadNeutral(filename):
"""Reads a `Neutral` tetrahedral file format. Return an ``Mesh`` object."""
with open(filename, "r") as f:
lines = f.readlines()
ncoords = int(lines[0])
coords = []
for i in range(1, ncoords + 1):
x, y, z = lines[i].split()
coords.append([float(x), float(y), float(z)])
ntets = int(lines[ncoords + 1])
idolf_tets = []
for i in range(ncoords + 2, ncoords + ntets + 2):
text = lines[i].split()
v0, v1, v2, v3 = int(text[1])-1, int(text[2])-1, int(text[3])-1, int(text[4])-1
# p0, p1, p2, p3 = np.array(coords[v1]), np.array(coords[v0]), coords[v3], coords[v2]
# d10 = p1-p0
# d21 = p2-p1
# dc = np.cross(d10, d21)
# print(np.dot(dc,p3-p0))
idolf_tets.append([v0, v1, v2, v3])
poly = utils.buildPolyData(coords, idolf_tets)
return Mesh(poly)
def loadGmesh(filename):
"""Reads a `gmesh` file format. Return an ``Mesh`` object."""
with open(filename, "r") as f:
lines = f.readlines()
nnodes = 0
index_nodes = 0
for i, line in enumerate(lines):
if "$Nodes" in line:
index_nodes = i + 1
nnodes = int(lines[index_nodes])
break
node_coords = []
for i in range(index_nodes + 1, index_nodes + 1 + nnodes):
cn = lines[i].split()
node_coords.append([float(cn[1]), float(cn[2]), float(cn[3])])
nelements = 0
index_elements = 0
for i, line in enumerate(lines):
if "$Elements" in line:
index_elements = i + 1
nelements = int(lines[index_elements])
break
elements = []
for i in range(index_elements + 1, index_elements + 1 + nelements):
ele = lines[i].split()
elements.append([int(ele[-3]), int(ele[-2]), int(ele[-1])])
poly = utils.buildPolyData(node_coords, elements, indexOffset=1)
return Mesh(poly)
def loadPCD(filename):
"""Return a ``Mesh`` made of only vertex points
from `Point Cloud` file format. Return an ``Points`` object."""
with open(filename, "r") as f:
lines = f.readlines()
start = False
pts = []
N, expN = 0, 0
for text in lines:
if start:
if N >= expN:
break
l = text.split()
pts.append([float(l[0]), float(l[1]), float(l[2])])
N += 1
if not start and "POINTS" in text:
expN = int(text.split()[1])
if not start and "DATA ascii" in text:
start = True
if expN != N:
vedo.logger.warning(f"Mismatch in PCD file {expN} != {len(pts)}")
poly = utils.buildPolyData(pts)
return Points(poly).pointSize(4)
def tonumpy(obj):
'''Dump a vedo object to numpy format.'''
adict = dict()
adict['type'] = 'unknown'
########################################################
def _fillcommon(obj, adict):
adict['filename'] = obj.filename
adict['name'] = obj.name
adict['time'] = obj.time()
adict['rendered_at'] = obj.renderedAt
adict['position'] = obj.pos()
adict['info'] = obj.info
m = np.eye(4)
vm = obj.getTransform().GetMatrix()
for i in [0, 1, 2, 3]:
for j in [0, 1, 2, 3]:
m[i,j] = vm.GetElement(i, j)
adict['transform'] = m
minv = np.eye(4)
vm.Invert()
for i in [0, 1, 2, 3]:
for j in [0, 1, 2, 3]:
minv[i,j] = vm.GetElement(i, j)
adict['transform_inverse'] = minv
########################################################
def _fillmesh(obj, adict):
adict['points'] = obj.points(transformed=False).astype(np.float32)
poly = obj.polydata()
adict['flagText'] = obj.flagText
adict['cells'] = None
if poly.GetNumberOfPolys():
try:
adict['cells'] = np.array(obj.faces(), dtype=np.uint32)
except ValueError:
adict['cells'] = obj.faces()
adict['lines'] = None
if poly.GetNumberOfLines():
adict['lines'] = obj.lines()
adict['pointdata'] = []
for iname in obj.pointdata.keys():
if not iname:
continue
if 'Normals' in iname.lower():
continue
arr = poly.GetPointData().GetArray(iname)
adict['pointdata'].append([utils.vtk2numpy(arr), iname])
adict['celldata'] = []
for iname in obj.celldata.keys():
if not iname:
continue
if 'Normals' in iname.lower():
continue
arr = poly.GetCellData().GetArray(iname)
adict['celldata'].append([utils.vtk2numpy(arr), iname])
adict['activedata'] = None
if poly.GetPointData().GetScalars():
adict['activedata'] = ['pointdata', poly.GetPointData().GetScalars().GetName()]
elif poly.GetCellData().GetScalars():
adict['activedata'] = ['celldata', poly.GetCellData().GetScalars().GetName()]
adict['LUT'] = None
adict['LUT_range'] = None
lut = obj._mapper.GetLookupTable()
if lut:
nlut = lut.GetNumberOfTableValues()
lutvals=[]
for i in range(nlut):
v4 = lut.GetTableValue(i) # r, g, b, alpha
lutvals.append(v4)
adict['LUT'] = lutvals
adict['LUT_range'] = lut.GetRange()
prp = obj.GetProperty()
adict['alpha'] = prp.GetOpacity()
adict['representation'] = prp.GetRepresentation()
adict['pointsize'] = prp.GetPointSize()
adict['linecolor'] = None
adict['linewidth'] = None
if prp.GetEdgeVisibility():
adict['linewidth'] = obj.lineWidth()
adict['linecolor'] = prp.GetEdgeColor()
adict['ambient'] = prp.GetAmbient()
adict['diffuse'] = prp.GetDiffuse()
adict['specular'] = prp.GetSpecular()
adict['specularpower'] = prp.GetSpecularPower()
adict['specularcolor'] = prp.GetSpecularColor()
adict['shading'] = prp.GetInterpolation() # flat phong..:
adict['color'] = prp.GetColor()
adict['lightingIsOn'] = prp.GetLighting()
adict['backColor'] = None
if obj.GetBackfaceProperty():
adict['backColor'] = obj.GetBackfaceProperty().GetColor()
adict['scalarvisibility'] = obj.mapper().GetScalarVisibility()
adict['texture'] = None
######################################################## Assembly
if isinstance(obj, Assembly):
pass
# adict['type'] = 'Assembly'
# _fillcommon(obj, adict)
# adict['actors'] = []
# for a in obj.unpack():
# assdict = dict()
# if isinstance(a, Mesh):
# _fillmesh(a, assdict)
# adict['actors'].append(assdict)
######################################################## Points/Mesh
elif isinstance(obj, Points):
adict['type'] = 'Mesh'
_fillcommon(obj, adict)
_fillmesh(obj, adict)
######################################################## Volume
elif isinstance(obj, Volume):
adict['type'] = 'Volume'
_fillcommon(obj, adict)
imgdata = obj.inputdata()
arr = utils.vtk2numpy(imgdata.GetPointData().GetScalars())
adict['array'] = arr.reshape(imgdata.GetDimensions())
adict['mode'] = obj.mode()
#adict['jittering'] = obj.mapper().GetUseJittering()
prp = obj.GetProperty()
ctf = prp.GetRGBTransferFunction()
otf = prp.GetScalarOpacity()
gotf = prp.GetGradientOpacity()
smin, smax = ctf.GetRange()
xs = np.linspace(smin, smax, num=100, endpoint=True)
cols, als, algrs = [], [], []
for x in xs:
cols.append(ctf.GetColor(x))
als.append(otf.GetValue(x))
if gotf:
algrs.append(gotf.GetValue(x))
adict['color'] = cols
adict['alpha'] = als
adict['alphagrad'] = algrs
######################################################## Picture
elif isinstance(obj, Picture):
adict['type'] = 'Picture'
_fillcommon(obj, adict)
adict['array'] = obj.tonumpy()
######################################################## Text2D
elif isinstance(obj, vedo.Text2D):
adict['type'] = 'Text2D'
adict['rendered_at'] = obj.renderedAt
adict['text'] = obj.text()
adict['position'] = obj.GetPosition()
adict['color'] = obj.property.GetColor()
adict['font'] = obj.fontname
adict['size'] = obj.property.GetFontSize()/22.5
adict['bgcol'] = obj.property.GetBackgroundColor()
adict['alpha'] = obj.property.GetBackgroundOpacity()
adict['frame'] = obj.property.GetFrame()
# print('tonumpy(): vedo.Text2D', obj.text()[:10], obj.font(), obj.GetPosition())
else:
pass
#colors.printc('Unknown object type in tonumpy()', [obj], c='r')
return adict
def loadnumpy(inobj):
"""Load a vedo format file or scene."""
# make sure the numpy file is not containing a scene
if isinstance(inobj, str): # user passing a file
if inobj.endswith('.npy'):
data = np.load(inobj, allow_pickle=True, encoding='latin1')#.flatten()
elif inobj.endswith('.npz'):
data = np.load(inobj, allow_pickle=True)['vedo_scenes']
isdict = hasattr(data[0], "keys")
if isdict and "objects" in data[0].keys(): # loading a full scene!!
return importWindow(data[0])
# it's a very normal numpy data object? just return it!
if not isdict:
return data
if 'type' not in data[0].keys():
return data
else:
data = inobj
######################################################
def _loadcommon(obj, d):
keys = d.keys()
if 'time' in keys: obj.time(d['time'])
if 'name' in keys: obj.name = d['name']
if 'filename' in keys: obj.filename = d['filename']
if 'info' in keys: obj.info = d['info']
if 'transform' in keys and len(d['transform']) == 4:
vm = vtk.vtkMatrix4x4()
for i in [0, 1, 2, 3]:
for j in [0, 1, 2, 3]:
vm.SetElement(i, j, d['transform'][i,j])
obj.applyTransform(vm)
elif 'position' in keys:
obj.pos(d['position'])
######################################################
def _buildmesh(d):
keys = d.keys()
vertices = d['points']
if not len(vertices):
return None
cells = None
if 'cells' in keys:
cells = d['cells']
lines = None
if 'lines' in keys:
lines = d['lines']
poly = utils.buildPolyData(vertices, cells, lines)
msh = Mesh(poly)
_loadcommon(msh, d)
prp = msh.GetProperty()
if 'ambient' in keys: prp.SetAmbient(d['ambient'])
if 'diffuse' in keys: prp.SetDiffuse(d['diffuse'])
if 'specular' in keys: prp.SetSpecular(d['specular'])
if 'specularpower' in keys: prp.SetSpecularPower(d['specularpower'])
if 'specularcolor' in keys: prp.SetSpecularColor(d['specularcolor'])
if 'lightingIsOn' in keys: prp.SetLighting(d['lightingIsOn'])
if 'shading' in keys: prp.SetInterpolation(d['shading'])
if 'alpha' in keys: prp.SetOpacity(d['alpha'])
if 'opacity' in keys: prp.SetOpacity(d['opacity']) # synonym
if 'representation' in keys: prp.SetRepresentation(d['representation'])
if 'pointsize' in keys and d['pointsize']: prp.SetPointSize(d['pointsize'])
if 'linewidth' in keys and d['linewidth']: msh.lineWidth(d['linewidth'])
if 'linecolor' in keys and d['linecolor']: msh.lineColor(d['linecolor'])
if 'color' in keys and d['color'] is not None:
msh.color(d['color'])
if 'backColor' in keys and d['backColor'] is not None:
msh.backColor(d['backColor'])
if 'flagText' in keys and d['flagText']: msh.flag(d['flagText'])
if 'celldata' in keys:
for csc, cscname in d['celldata']:
msh.addCellArray(csc, cscname)
if 'pointdata' in keys:
for psc, pscname in d['pointdata']:
msh.addPointArray(psc, pscname)
msh.mapper().ScalarVisibilityOff() # deactivate scalars
if 'LUT' in keys and 'activedata' in keys and d['activedata']:
# print(d['activedata'],'', msh.filename)
lut_list = d['LUT']
ncols = len(lut_list)
lut = vtk.vtkLookupTable()
lut.SetNumberOfTableValues(ncols)
lut.SetRange(d['LUT_range'])
for i in range(ncols):
r, g, b, a = lut_list[i]
lut.SetTableValue(i, r, g, b, a)
lut.Build()
msh.mapper().SetLookupTable(lut)
msh.mapper().ScalarVisibilityOn() # activate scalars
msh.mapper().SetScalarRange(d['LUT_range'])
if d['activedata'][0] == 'celldata':
poly.GetCellData().SetActiveScalars(d['activedata'][1])
if d['activedata'][0] == 'pointdata':
poly.GetPointData().SetActiveScalars(d['activedata'][1])
if 'shading' in keys and int(d['shading'])>0:
msh.computeNormals(cells=0) # otherwise cannot renderer phong
if 'scalarvisibility' in keys:
if d['scalarvisibility']:
msh.mapper().ScalarVisibilityOn()
else:
msh.mapper().ScalarVisibilityOff()
if 'texture' in keys and d['texture']:
msh.texture(d['texture'])
return msh
######################################################
objs = []
for d in data:
#print('loadnumpy:', d['type'], d)
### Mesh
if 'mesh' == d['type'].lower():
a = _buildmesh(d)
if a: objs.append(a)
### Assembly
elif 'assembly' == d['type'].lower():
assacts = []
for ad in d['actors']:
assacts.append(_buildmesh(ad))
asse = Assembly(assacts)
_loadcommon(asse, d)
objs.append(asse)
### Volume
elif 'volume' == d['type'].lower():
vol = Volume(d['array'])
_loadcommon(vol, d)
if 'jittering' in d.keys(): vol.jittering(d['jittering'])
#print(d['mode'])
vol.mode(d['mode'])
vol.color(d['color'])
vol.alpha(d['alpha'])
vol.alphaGradient(d['alphagrad'])
objs.append(vol)
### Picture
elif 'picture' == d['type'].lower():
vimg = Picture(d['array'])
_loadcommon(vimg, d)
objs.append(vimg)
### Text2D
elif 'text2d' == d['type'].lower():
t = vedo.shapes.Text2D(d['text'], font=d['font'], c=d['color'])
t.pos(d['position']).size(d['size'])
t.background(d['bgcol'], d['alpha'])
if d['frame']:
t.frame(d['bgcol'])
objs.append(t)
### Annotation ## backward compatibility - will disappear
elif 'annotation' == d['type'].lower():
from vedo.shapes import Text2D
pos = d['position']
if isinstance(pos, int):
pos = "top-left"
d['size'] *= 2.7
t = Text2D(d['text'], font=d['font'], c=d['color']).pos(pos)
t.background(d['bgcol'], d['alpha']).size(d['size']).frame(d['bgcol'])
objs.append(t) ## backward compatibility
if len(objs) == 1:
return objs[0]
elif len(objs) == 0:
return None
else:
return objs
def loadImageData(filename):
"""Read and return a ``vtkImageData`` object from file."""
if ".tif" in filename.lower():
reader = vtk.vtkTIFFReader()
# print("GetOrientationType ", reader.GetOrientationType())
reader.SetOrientationType(settings.tiffOrientationType)
elif ".slc" in filename.lower():
reader = vtk.vtkSLCReader()
if not reader.CanReadFile(filename):
vedo.logger.error(f"sorry, bad SLC file {filename}")
return None
elif ".vti" in filename.lower():
reader = vtk.vtkXMLImageDataReader()
elif ".mhd" in filename.lower():
reader = vtk.vtkMetaImageReader()
elif ".dem" in filename.lower():
reader = vtk.vtkDEMReader()
elif ".nii" in filename.lower():
reader = vtk.vtkNIFTIImageReader()
elif ".nrrd" in filename.lower():
reader = vtk.vtkNrrdReader()
if not reader.CanReadFile(filename):
vedo.logger.error(f"sorry, bad NRRD file {filename}")
return None
reader.SetFileName(filename)
reader.Update()
image = reader.GetOutput()
return image
###########################################################
def write(objct, fileoutput, binary=True):
"""
Write 3D object to file. (same as `save()`).
Possile extensions are:
- vtk, vti, npy, npz, ply, obj, stl, byu, vtp, vti, mhd, xyz, tif, png, bmp.
"""
obj = objct
if isinstance(obj, Points): # picks transformation
obj = objct.polydata(True)
elif isinstance(obj, (vtk.vtkActor, vtk.vtkVolume)):
obj = objct.GetMapper().GetInput()
elif isinstance(obj, (vtk.vtkPolyData, vtk.vtkImageData)):
obj = objct
if hasattr(obj, 'filename'):
obj.filename = fileoutput
fr = fileoutput.lower()
if fr.endswith(".vtk"):
writer = vtk.vtkDataSetWriter()
elif fr.endswith(".ply"):
writer = vtk.vtkPLYWriter()
writer.AddComment("PLY file generated by vedo")
lut = objct.GetMapper().GetLookupTable()
if lut:
pscal = obj.GetPointData().GetScalars()
if not pscal:
pscal = obj.GetCellData().GetScalars()
if pscal and pscal.GetName():
writer.SetArrayName(pscal.GetName())
writer.SetLookupTable(lut)
elif fr.endswith(".stl"):
writer = vtk.vtkSTLWriter()
elif fr.endswith(".vtp"):
writer = vtk.vtkXMLPolyDataWriter()
elif fr.endswith(".vtu"):
writer = vtk.vtkXMLUnstructuredGridWriter()
elif fr.endswith(".vtm"):
g = vtk.vtkMultiBlockDataGroupFilter()
for ob in objct:
if isinstance(ob, (Points, Volume)): # picks transformation
ob = ob.polydata(True)
g.AddInputData(ob)
# elif isinstance(ob, (vtk.vtkActor, vtk.vtkVolume)):
# ob = ob.GetMapper().GetInput()
# g.AddInputData(ob)
g.Update()
mb = g.GetOutputDataObject(0)
wri = vtk.vtkXMLMultiBlockDataWriter()
wri.SetInputData(mb)
wri.SetFileName(fileoutput)
wri.Write()
return mb
elif fr.endswith(".xyz"):
writer = vtk.vtkSimplePointsWriter()
elif fr.endswith(".facet"):
writer = vtk.vtkFacetWriter()
elif fr.endswith(".vti"):
writer = vtk.vtkXMLImageDataWriter()
elif fr.endswith(".mhd"):
writer = vtk.vtkMetaImageWriter()
elif fr.endswith(".nii"):
writer = vtk.vtkNIFTIImageWriter()
elif fr.endswith(".png"):
writer = vtk.vtkPNGWriter()
elif fr.endswith(".jpg"):
writer = vtk.vtkJPEGWriter()
elif fr.endswith(".bmp"):
writer = vtk.vtkBMPWriter()
elif fr.endswith(".tif") or fr.endswith(".tiff"):
writer = vtk.vtkTIFFWriter()
# print("GetCompression ", writer.GetCompression()) # basically uncompressed..
writer.SetFileDimensionality(len(obj.GetDimensions()))
elif fr.endswith(".npy") or fr.endswith(".npz"):
if utils.isSequence(objct):
objslist = objct
else:
objslist = [objct]
dicts2save = []
for obj in objslist:
dicts2save.append( tonumpy(obj) )
np.save(fileoutput, dicts2save)
return dicts2save
elif fr.endswith(".obj"):
with open(fileoutput, "w") as outF:
outF.write('# OBJ file format with ext .obj\n')
outF.write('# File generated by vedo\n')
for p in objct.points():
outF.write("v {:.5g} {:.5g} {:.5g}\n".format(*p))
# pdata = objct.polydata().GetPointData().GetScalars()
# if pdata:
# ndata = vtk_to_numpy(pdata)
# for vd in ndata:
# outF.write('vp '+ str(vd) +'\n')
#ptxt = objct.polydata().GetPointData().GetTCoords() # not working
#if ptxt:
# ntxt = vtk_to_numpy(ptxt)
# print(len(objct.faces()), objct.points().shape, ntxt.shape)
# for vt in ntxt:
# outF.write('vt '+ str(vt[0]) +" "+ str(vt[1])+ ' 0\n')
for i,f in enumerate(objct.faces()):
fs = ''
for fi in f:
fs += " {:d}".format(fi+1)
outF.write('f' + fs + '\n')
for l in objct.lines():
ls = ''
for li in l:
ls += str(li+1)+" "
outF.write('l '+ ls + '\n')
return objct
elif fr.endswith(".xml"): # write tetrahedral dolfin xml
vertices = objct.points().astype(str)
faces = np.array(objct.faces()).astype(str)
ncoords = vertices.shape[0]
with open(fileoutput, "w") as outF:
outF.write('<?xml version="1.0" encoding="UTF-8"?>\n')
outF.write('<dolfin xmlns:dolfin="http://www.fenicsproject.org">\n')
if len(faces[0]) == 4:# write tetrahedral mesh
ntets = faces.shape[0]
outF.write(' <mesh celltype="tetrahedron" dim="3">\n')
outF.write(' <vertices size="' + str(ncoords) + '">\n')
for i in range(ncoords):
x, y, z = vertices[i]
outF.write(' <vertex index="'+str(i)+'" x="'+x+'" y="'+y+'" z="'+z+'"/>\n')
outF.write(' </vertices>\n')
outF.write(' <cells size="' + str(ntets) + '">\n')
for i in range(ntets):
v0, v1, v2, v3 = faces[i]
outF.write(' <tetrahedron index="'+str(i)
+ '" v0="'+v0+'" v1="'+v1+'" v2="'+v2+'" v3="'+v3+'"/>\n')
elif len(faces[0]) == 3:# write triangle mesh
ntri = faces.shape[0]
outF.write(' <mesh celltype="triangle" dim="2">\n')
outF.write(' <vertices size="' + str(ncoords) + '">\n')
for i in range(ncoords):
x, y, dummy_z = vertices[i]
outF.write(' <vertex index="'+str(i)+'" x="'+x+'" y="'+y+'"/>\n')
outF.write(' </vertices>\n')
outF.write(' <cells size="' + str(ntri) + '">\n')
for i in range(ntri):
v0, v1, v2 = faces[i]
outF.write(' <triangle index="'+str(i)+'" v0="'+v0+'" v1="'+v1+'" v2="'+v2+'"/>\n')
outF.write(' </cells>\n')
outF.write(" </mesh>\n")
outF.write("</dolfin>\n")
return objct
else:
vedo.logger.error(f"Unknown format {fileoutput}, file not saved")
return objct
try:
if hasattr(writer, 'SetFileTypeToBinary'):
if binary:
writer.SetFileTypeToBinary()
else:
writer.SetFileTypeToASCII()
writer.SetInputData(obj)
writer.SetFileName(fileoutput)
writer.Write()
except:
vedo.logger.error(f"could not save {fileoutput}")
return objct
def writeTransform(inobj, filename='transform.mat', comment=''):
"""
Save a transformation for a mesh or pointcloud to ASCII file.
Parameters
----------
filename : str, optional
output file name. The default is 'transform.mat'.
comment : str, optional
some optional comment. The default is ''.
"""
if isinstance(inobj, Points):
M = inobj.getTransform().GetMatrix()
elif isinstance(inobj, vtk.vtkTransform):
M = inobj.GetMatrix()
elif isinstance(inobj, vtk.vtkMatrix4x4):
M = inobj
else:
vedo.logger.error(f"in writeTransform(), cannot understand input type {type(inobj)}")
with open(filename,'w') as f:
if comment:
f.write('# '+comment+'\n')
for i in range(4):
f.write( str(M.GetElement(i,0))+' '+
str(M.GetElement(i,1))+' '+
str(M.GetElement(i,2))+' '+
str(M.GetElement(i,3))+'\n',
)
f.write('\n')
return
def loadTransform(filename):
"""
Load a ``vtkTransform`` from a file.mat.
Returns
-------
T : vtkTransform
The transformation to be applied to some object (``use applyTransform()``).
comment : str
a comment string associated to this transformation file.
"""
with open(filename,'r') as f:
lines = f.readlines()
M = vtk.vtkMatrix4x4()
i=0
comment = ''
for l in lines:
if l.startswith('#'):
comment = l.replace('#', "").replace('\n', "")
continue
vals = l.split(' ')
if len(vals)==4:
for j in range(4):
v = vals[j].replace('\n', '')
M.SetElement(i,j, float(v))
i+=1
T = vtk.vtkTransform()
T.SetMatrix(M)
return (T, comment)
###############################################################################
def exportWindow(fileoutput, binary=False):
'''
Exporter which writes out the renderered scene into an HTML, X3D
or Numpy file.
.. hint:: examples/other/export_x3d.py
Check out the HTML generated webpage [here](https://vedo.embl.es/examples/embryo.html).
See also: FEniCS [test webpage](https://vedo.embl.es/examples/fenics_elasticity.html).
.. note:: the rendering window can also be exported to `numpy` file `scene.npz`
by pressing ``E`` keyboard at any moment during visualization.
.. image:: https://user-images.githubusercontent.com/32848391/57160341-c6ffbd80-6de8-11e9-95ff-7215ce642bc5.jpg
'''
fr = fileoutput.lower()
####################################################################
if fr.endswith(".npy") or fr.endswith(".npz"):
sdict = dict()
plt = vedo.plotter_instance
sdict['shape'] = plt.shape #todo
sdict['sharecam'] = plt.sharecam #todo
sdict['camera'] = dict( pos=plt.camera.GetPosition(),
focalPoint=plt.camera.GetFocalPoint(),
viewup=plt.camera.GetViewUp(),
distance=plt.camera.GetDistance(),
clippingRange=plt.camera.GetClippingRange() )
sdict['position'] = plt.pos
sdict['size'] = plt.size
sdict['axes'] = plt.axes
sdict['title'] = plt.title
sdict['backgrcol'] = colors.getColor(plt.backgrcol)
sdict['backgrcol2'] = None
if plt.renderer.GetGradientBackground():
sdict['backgrcol2'] = plt.renderer.GetBackground2()
sdict['useDepthPeeling'] = settings.useDepthPeeling
sdict['renderLinesAsTubes'] = settings.renderLinesAsTubes
sdict['hiddenLineRemoval'] = settings.hiddenLineRemoval
sdict['visibleGridEdges'] = settings.visibleGridEdges
# sdict['interactorStyle'] = 0
sdict['useParallelProjection'] = settings.useParallelProjection
sdict['defaultFont'] = settings.defaultFont
sdict['objects'] = []
allobjs = plt.getMeshes(includeNonPickables=True) + plt.getVolumes(includeNonPickables=True)
acts2d = plt.renderer.GetActors2D()
acts2d.InitTraversal()
for i in range(acts2d.GetNumberOfItems()):
a = acts2d.GetNextItem()
if isinstance(a, vedo.Text2D):
allobjs.append(a)
allobjs += plt.actors
allobjs = list(set(allobjs)) # make sure its unique
for a in allobjs:
sdict['objects'].append(tonumpy(a))
if fr.endswith(".npz"):
np.savez_compressed(fileoutput, vedo_scenes=[sdict])
else:
np.save(fileoutput, [sdict])
####################################################################
elif fr.endswith(".x3d"):
obj = list(set(vedo.plotter_instance.getMeshes()+
vedo.plotter_instance.actors))
if len(vedo.plotter_instance.axes_instances):
obj.append(vedo.plotter_instance.axes_instances[0])
for a in obj:
if isinstance(a, Mesh):
newa = a.clone(transformed=True)
vedo.plotter_instance.remove(a, render=False).add(newa, render=False)
elif isinstance(a, Assembly):
for b in a.unpack():
if b:
newb = b.clone()
vedo.plotter_instance.add(newb, render=False)
vedo.plotter_instance.remove(a)
vedo.plotter_instance.render()
exporter = vtk.vtkX3DExporter()
exporter.SetBinary(binary)
exporter.FastestOff()
exporter.SetInput(vedo.plotter_instance.window)
exporter.SetFileName(fileoutput)
# exporter.WriteToOutputStringOn() # see below
exporter.Update()
exporter.Write()
# this can reduce the size by more than half...
# outstring = exporter.GetOutputString().decode("utf-8") # this fails though
# from vedo.utils import isInteger, isNumber, precision
# newlines = []
# for l in outstring.splitlines(True):
# ls = l.lstrip()
# content = ls.split()
# newls = ""
# for c in content:
# c2 = c.replace(',','')
# if isNumber(c2) and not isInteger(c2):
# newc = precision(float(c2), 4)
# if ',' in c:
# newls += newc + ','
# else:
# newls += newc + ' '
# else:
# newls += c + ' '
# newlines.append(newls.lstrip()+'\n')
# with open("fileoutput", 'w') as f:
# l = "".join(newlines)
# f.write(l)
x3d_html = _x3d_html.replace("~fileoutput", fileoutput)
wsize = vedo.plotter_instance.window.GetSize()
x3d_html = x3d_html.replace("~width", str(wsize[0]))
x3d_html = x3d_html.replace("~height", str(wsize[1]))
with open(fileoutput.replace('.x3d', '.html'), "w") as outF:
outF.write(x3d_html)
vedo.logger.info(f"Saved files {fileoutput} and {fileoutput.replace('.x3d','.html')}")
####################################################################
elif fr.endswith(".html"):
from vedo.backends import getNotebookBackend
savebk = vedo.notebookBackend
vedo.notebookBackend='k3d'
plt = getNotebookBackend(vedo.plotter_instance.actors, 1.5, '')
with open(fileoutput,'w') as fp:
fp.write(plt.get_snapshot())
vedo.notebookBackend = savebk
else:
vedo.logger.error(f"export extension {fr.split('.')[-1]} is not supported")
return vedo.plotter_instance
def importWindow(fileinput, mtlFile=None, texturePath=None):
"""Import a whole scene from a Numpy or OBJ wavefront file.
Return a ``Plotter`` instance.
Parameters
----------
mtlFile : str
MTL file for OBJ wavefront files.
texturePath : str
path of the texture files directory.
"""
from vedo import Plotter
data = None
if isinstance(fileinput, dict):
data = fileinput
elif fileinput.endswith('.npy'):
data = np.load(fileinput, allow_pickle=True, encoding="latin1").flatten()[0]
elif fileinput.endswith('.npz'):
data = np.load(fileinput, allow_pickle=True)['vedo_scenes'][0]
if data is not None:
if 'renderLinesAsTubes' in data.keys():
settings.renderLinesAsTubes = data['renderLinesAsTubes']
if 'hiddenLineRemoval' in data.keys():
settings.hiddenLineRemoval = data['hiddenLineRemoval']
if 'visibleGridEdges' in data.keys():
settings.visibleGridEdges = data['visibleGridEdges']
if 'interactorStyle' in data.keys():
vedo.interactorStyle = data['interactorStyle']
if 'useParallelProjection' in data.keys():
settings.useParallelProjection = data['useParallelProjection']
if 'usePolygonOffset' in data.keys():
settings.usePolygonOffset = data['usePolygonOffset']
if 'polygonOffsetFactor' in data.keys():
settings.polygonOffsetFactor = data['polygonOffsetFactor']
if 'polygonOffsetUnits' in data.keys():
settings.polygonOffsetUnits = data['polygonOffsetUnits']
if 'interpolateScalarsBeforeMapping' in data.keys():
settings.interpolateScalarsBeforeMapping = data['interpolateScalarsBeforeMapping']
if 'defaultFont' in data.keys():
settings.defaultFont = data['defaultFont']
if 'useDepthPeeling' in data.keys():
settings.useDepthPeeling = data['useDepthPeeling']
axes = data.pop('axes', 4)
title = data.pop('title', '')
backgrcol = data.pop('backgrcol', "white")
backgrcol2 = data.pop('backgrcol2', None)
cam = data.pop('camera', None)
if data['shape'] != (1,1): data['size']="auto" # disable size
plt = Plotter(size=data['size'], # not necessarily a good idea to set it
#shape=data['shape'], # will need to create a Renderer class first
axes=axes,
title=title,
bg=backgrcol,
bg2=backgrcol2,
)
if cam:
if 'pos' in cam.keys(): plt.camera.SetPosition( cam['pos'] )
if 'focalPoint' in cam.keys(): plt.camera.SetFocalPoint( cam['focalPoint'] )
if 'viewup' in cam.keys(): plt.camera.SetViewUp(cam['viewup'] )
if 'distance' in cam.keys(): plt.camera.SetDistance( cam['distance'] )
if 'clippingRange' in cam.keys(): plt.camera.SetClippingRange( cam['clippingRange'] )
plt.resetcam = False
if 'objects' in data.keys():
objs = loadnumpy(data['objects'])
if not utils.isSequence(objs):
objs = [objs]
else:
#colors.printc("Trying to import a single mesh.. use load() instead.", c='r')
#colors.printc(" -> try to load a single object with load().", c='r')
objs = [loadnumpy(fileinput)]
plt.actors = objs
plt.add(objs, render=False)
return plt
elif '.obj' in fileinput.lower():
plt = Plotter()
importer = vtk.vtkOBJImporter()
importer.SetFileName(fileinput)
if mtlFile is not False:
if mtlFile is None:
mtlFile = fileinput.replace('.obj', '.mtl').replace('.OBJ', '.MTL')
importer.SetFileNameMTL(mtlFile)
if texturePath is not False:
if texturePath is None:
texturePath = fileinput.replace('.obj', '.txt').replace('.OBJ', '.TXT')
importer.SetTexturePath(texturePath)
importer.SetRenderWindow(plt.window)
importer.Update()
actors = plt.renderer.GetActors()
actors.InitTraversal()
for i in range(actors.GetNumberOfItems()):
vactor = actors.GetNextActor()
act = Mesh(vactor)
act_tu = vactor.GetTexture()
if act_tu:
act_tu.InterpolateOn()
act.texture(act_tu)
plt.actors.append( act )
return plt
##########################################################
def screenshot(filename="screenshot.png", scale=None, asarray=False):
"""
Save a screenshot of the current rendering window.
Parameters
----------
scale : int
set image magnification as an integer multiplicative factor
asarray : bool
return a numpy array of the image
"""
if not vedo.plotter_instance or not vedo.plotter_instance.window:
vedo.logger.error("in screenshot(), rendering window is not present, skip.")
return vedo.plotter_instance
if filename.endswith('.pdf'):
writer = vtk.vtkGL2PSExporter()
writer.SetRenderWindow(vedo.plotter_instance.window)
writer.Write3DPropsAsRasterImageOff()
writer.SilentOn()
writer.SetSortToBSP()
writer.SetFileFormatToPDF()
writer.SetFilePrefix(filename.replace('.pdf',''))
writer.Write()
return vedo.plotter_instance ##########
elif filename.endswith('.svg'):
writer = vtk.vtkGL2PSExporter()
writer.SetRenderWindow(vedo.plotter_instance.window)
writer.Write3DPropsAsRasterImageOff()
writer.SilentOn()
writer.SetSortToBSP()
writer.SetFileFormatToSVG()
writer.SetFilePrefix(filename.replace('.svg',''))
writer.Write()
return vedo.plotter_instance ##########
elif filename.endswith('.eps'):
writer = vtk.vtkGL2PSExporter()
writer.SetRenderWindow(vedo.plotter_instance.window)
writer.Write3DPropsAsRasterImageOff()
writer.SilentOn()
writer.SetSortToBSP()
writer.SetFileFormatToEPS()
writer.SetFilePrefix(filename.replace('.eps',''))
writer.Write()
return vedo.plotter_instance ##########
if scale is None:
scale = settings.screeshotScale
if settings.screeshotLargeImage:
w2if = vtk.vtkRenderLargeImage()
w2if.SetInput(vedo.plotter_instance.renderer)
w2if.SetMagnification(scale)
else:
w2if = vtk.vtkWindowToImageFilter()
w2if.SetInput(vedo.plotter_instance.window)
if hasattr(w2if, 'SetScale'):
w2if.SetScale(scale, scale)
if settings.screenshotTransparentBackground:
w2if.SetInputBufferTypeToRGBA()
w2if.ReadFrontBufferOff() # read from the back buffer
w2if.Update()
if asarray:
w2ifout = w2if.GetOutput()
npdata = utils.vtk2numpy(w2ifout.GetPointData().GetArray("ImageScalars"))
npdata = npdata[:,[0,1,2]]
ydim, xdim, _ = w2ifout.GetDimensions()
npdata = npdata.reshape([xdim, ydim, -1])
npdata = np.flip(npdata, axis=0)
return npdata
if filename.lower().endswith('.png'):
writer = vtk.vtkPNGWriter()
writer.SetFileName(filename)
writer.SetInputData(w2if.GetOutput())
writer.Write()
elif filename.lower().endswith('.jpg') or filename.lower().endswith('.jpeg'):
writer = vtk.vtkJPEGWriter()
writer.SetFileName(filename)
writer.SetInputData(w2if.GetOutput())
writer.Write()
else: #add .png
writer = vtk.vtkPNGWriter()
writer.SetFileName(filename+'.png')
writer.SetInputData(w2if.GetOutput())
writer.Write()
return vedo.plotter_instance
def ask(*question, **kwarg):
"""
Ask a question from command line. Return the answer as a string.
See function `printc()` for the description of the options.
Parameters
----------
options : list
a python list of possible answers to choose from.
default : str
the default answer when just hitting return.
Example:
.. code-block:: python
import vedo
res = vedo.io.ask("Continue?", options=['Y','n'], default='Y', c='g')
print(res)
"""
kwarg.update({'end': ' '})
if 'invert' not in kwarg.keys():
kwarg.update({'invert': True})
if 'box' in kwarg.keys():
kwarg.update({'box': ''})
options = kwarg.pop("options", [])
default = kwarg.pop("default", '')
if options:
opt = '['
for o in options:
opt += o + '/'
opt = opt[:-1]+']'
colors.printc(*question, opt, **kwarg)
else:
colors.printc(*question, **kwarg)
resp = input()
if options:
if resp not in options:
if default and str(repr(resp))=="''":
return default
colors.printc("Please choose one option in:", opt, italic=True, bold=False)
kwarg["options"] = options
return ask(*question, **kwarg) # ask again
return resp
##############################################################################################
class Video:
"""
Class to generate a video from the specified rendering window.
Program `ffmpeg` is used to create video from each generated frame.
Parameters
----------
name : str
name of the output file.
fps : int
set the number of frames per second.
duration : float
set the total `duration` of the video and recalculates `fps` accordingly.
ffmpeg : str
set path to ffmpeg program. Default value assumes ffmpeg command is in the path.
.. hint:: examples/other/makeVideo.py
.. image:: https://user-images.githubusercontent.com/32848391/50739007-2bfc2b80-11da-11e9-97e6-620a3541a6fa.jpg
"""
def __init__(self,
name="movie.mp4",
duration=None,
fps=24,
backend='ffmpeg',
):
from tempfile import TemporaryDirectory
self.name = name
self.duration = duration
self.backend = backend
self.fps = float(fps)
self.command = "ffmpeg -loglevel panic -y -r"
self.options = "-b:v 8000k"
self.frames = []
self.tmp_dir = TemporaryDirectory()
self.get_filename = lambda x: os.path.join(self.tmp_dir.name, x)
colors.printc("\video Video", self.name, "is open... ", c="m", end='')
def addFrame(self):
"""Add frame to current video."""
fr = self.get_filename(str(len(self.frames)) + ".png")
screenshot(fr)
self.frames.append(fr)
return self
def pause(self, pause=0):
"""Insert a `pause`, in seconds."""
fr = self.frames[-1]
n = int(self.fps * pause)
for _ in range(n):
fr2 = self.get_filename(str(len(self.frames)) + ".png")
self.frames.append(fr2)
os.system("cp -f %s %s" % (fr, fr2))
return self
def action(self,
elevation=(0,80),
azimuth=(0,359),
cameras=(),
resetcam=False,
):
"""
Automatic shooting of a static scene by specifying rotation and elevation ranges.
:param list elevation: initial and final elevation angles
:param list azimuth_range: initial and final azimuth angles
:param list cameras: list of cameras to go through, each camera can be dictionary or a vtkCamera
"""
if not self.duration:
self.duration = 5
def build_vtk_cam(cm_input):
cm = dict(cm_input)
cm_pos = cm.pop("pos", None)
cm_focalPoint = cm.pop("focalPoint", None)
cm_viewup = cm.pop("viewup", None)
cm_distance = cm.pop("distance", None)
cm_clippingRange = cm.pop("clippingRange", None)
cm_parallelScale = cm.pop("parallelScale", None)
cm_thickness = cm.pop("thickness", None)
cm_viewAngle = cm.pop("viewAngle", None)
cm = vtk.vtkCamera()
if cm_pos is not None: cm.SetPosition(cm_pos)
if cm_focalPoint is not None: cm.SetFocalPoint(cm_focalPoint)
if cm_viewup is not None: cm.SetViewUp(cm_viewup)
if cm_distance is not None: cm.SetDistance(cm_distance)
if cm_clippingRange is not None: cm.SetClippingRange(cm_clippingRange)
if cm_parallelScale is not None: cm.SetParallelScale(cm_parallelScale)
if cm_thickness is not None: cm.SetThickness(cm_thickness)
if cm_viewAngle is not None: cm.SetViewAngle(cm_viewAngle)
return cm
plt = vedo.plotter_instance
n = int(self.fps * self.duration)
cams = []
for cm in cameras:
cams.append(build_vtk_cam(cm))
nc = len(cams)
plt.show(resetcam=resetcam, interactive=False)
if nc:
for icam, cam in enumerate(cams):
if cam==cams[-1]:
break
for i in range(n):
plt.moveCamera(cams[icam], cams[icam+1], i/n)
plt.show()
self.addFrame()
else: ########################################
for i in range(n):
plt.camera.Elevation((elevation[1]-elevation[0])/n)
plt.camera.Azimuth((azimuth[1]-azimuth[0])/n)
plt.show()
self.addFrame()
return self
def close(self):
"""
Render the video and write to file.
Return the current ``Plotter`` instance.
"""
if self.duration:
self.fps = len(self.frames) / float(self.duration)
colors.printc("Recalculated video FPS to", round(self.fps, 3), c="m")
else:
self.fps = int(self.fps)
########################################
if self.backend == 'ffmpeg':
out = os.system(self.command + " " + str(self.fps)
+ " -i " + self.tmp_dir.name + os.sep
+ "%01d.png " + self.options + " " + self.name)
if out:
vedo.logger.error(f"backend {self.backend} returning error")
else:
colors.printc(f" \save video saved as {self.name}", c="m")
########################################
elif 'cv' in self.backend:
try:
import cv2
except:
vedo.logger.error("opencv is not installed")
return
cap = cv2.VideoCapture(os.path.join(self.tmp_dir.name, "%1d.png"))
fourcc = cv2.VideoWriter_fourcc(*"mp4v")
w,h = vedo.plotter_instance.window.GetSize()
w,h = w*settings.screeshotScale, h*settings.screeshotScale
writer = cv2.VideoWriter(self.name, fourcc, self.fps, (w, h), True)
found = False
while True:
ret, frame = cap.read()
if not ret: break
writer.write(frame)
found = True
cap.release()
writer.release()
if found:
vedo.logger.info("video saved as {self.name}")
else:
vedo.logger.error("could not find snapshots")
self.tmp_dir.cleanup()
return vedo.plotter_instance
Functions
def ask(*question, **kwarg)
-
Ask a question from command line. Return the answer as a string. See function
printc()
for the description of the options.Parameters
options
:list
- a python list of possible answers to choose from.
default
:str
- the default answer when just hitting return.
Example
.. code-block:: python
import vedo res = vedo.io.ask("Continue?", options=['Y','n'], default='Y', c='g') print(res)
Expand source code
def ask(*question, **kwarg): """ Ask a question from command line. Return the answer as a string. See function `printc()` for the description of the options. Parameters ---------- options : list a python list of possible answers to choose from. default : str the default answer when just hitting return. Example: .. code-block:: python import vedo res = vedo.io.ask("Continue?", options=['Y','n'], default='Y', c='g') print(res) """ kwarg.update({'end': ' '}) if 'invert' not in kwarg.keys(): kwarg.update({'invert': True}) if 'box' in kwarg.keys(): kwarg.update({'box': ''}) options = kwarg.pop("options", []) default = kwarg.pop("default", '') if options: opt = '[' for o in options: opt += o + '/' opt = opt[:-1]+']' colors.printc(*question, opt, **kwarg) else: colors.printc(*question, **kwarg) resp = input() if options: if resp not in options: if default and str(repr(resp))=="''": return default colors.printc("Please choose one option in:", opt, italic=True, bold=False) kwarg["options"] = options return ask(*question, **kwarg) # ask again return resp
def download(url, force=False, verbose=True)
-
Retrieve a file from a url, save it locally and return its path. Use
force
to force reload and discard cache copies.Expand source code
def download(url, force=False, verbose=True): """Retrieve a file from a url, save it locally and return its path. Use ``force`` to force reload and discard cache copies.""" if not url.startswith('https://'): vedo.logger.error(f"Invalid URL (must start with https):\n{url}") return url url = url.replace('www.dropbox', 'dl.dropbox') if "github.com" in url: url = url.replace('/blob/', '/raw/') basename = os.path.basename(url) if '?' in basename: basename = basename.split('?')[0] from tempfile import NamedTemporaryFile tmp_file = NamedTemporaryFile(delete=False) tmp_file.name = os.path.join(os.path.dirname(tmp_file.name), os.path.basename(basename)) if force==False and os.path.exists(tmp_file.name): if verbose: colors.printc("reusing cached file:", tmp_file.name) #colors.printc(" (use force=True to force a new download)") return tmp_file.name try: from urllib.request import urlopen, Request req = Request(url, headers={'User-Agent': 'Mozilla/5.0'}) if verbose: colors.printc('reading', basename, 'from', url.split('/')[2][:40],'...', end='') except ImportError: import urllib2 import contextlib urlopen = lambda url_: contextlib.closing(urllib2.urlopen(url_)) req = url if verbose: colors.printc('reading', basename, 'from', url.split('/')[2][:40],'...', end='') with urlopen(req) as response, open(tmp_file.name, 'wb') as output: output.write(response.read()) if verbose: colors.printc(' done.') return tmp_file.name
def exportWindow(fileoutput, binary=False)
-
Exporter which writes out the renderered scene into an HTML, X3D or Numpy file.
Hint: examples/other/export_x3d.py
Check out the HTML generated webpage here.
See also: FEniCS test webpage.
Note: the rendering window can also be exported to
numpy
filescene.npz
by pressing
E
keyboard at any moment during visualization.Expand source code
def exportWindow(fileoutput, binary=False): ''' Exporter which writes out the renderered scene into an HTML, X3D or Numpy file. .. hint:: examples/other/export_x3d.py Check out the HTML generated webpage [here](https://vedo.embl.es/examples/embryo.html). See also: FEniCS [test webpage](https://vedo.embl.es/examples/fenics_elasticity.html). .. note:: the rendering window can also be exported to `numpy` file `scene.npz` by pressing ``E`` keyboard at any moment during visualization. .. image:: https://user-images.githubusercontent.com/32848391/57160341-c6ffbd80-6de8-11e9-95ff-7215ce642bc5.jpg ''' fr = fileoutput.lower() #################################################################### if fr.endswith(".npy") or fr.endswith(".npz"): sdict = dict() plt = vedo.plotter_instance sdict['shape'] = plt.shape #todo sdict['sharecam'] = plt.sharecam #todo sdict['camera'] = dict( pos=plt.camera.GetPosition(), focalPoint=plt.camera.GetFocalPoint(), viewup=plt.camera.GetViewUp(), distance=plt.camera.GetDistance(), clippingRange=plt.camera.GetClippingRange() ) sdict['position'] = plt.pos sdict['size'] = plt.size sdict['axes'] = plt.axes sdict['title'] = plt.title sdict['backgrcol'] = colors.getColor(plt.backgrcol) sdict['backgrcol2'] = None if plt.renderer.GetGradientBackground(): sdict['backgrcol2'] = plt.renderer.GetBackground2() sdict['useDepthPeeling'] = settings.useDepthPeeling sdict['renderLinesAsTubes'] = settings.renderLinesAsTubes sdict['hiddenLineRemoval'] = settings.hiddenLineRemoval sdict['visibleGridEdges'] = settings.visibleGridEdges # sdict['interactorStyle'] = 0 sdict['useParallelProjection'] = settings.useParallelProjection sdict['defaultFont'] = settings.defaultFont sdict['objects'] = [] allobjs = plt.getMeshes(includeNonPickables=True) + plt.getVolumes(includeNonPickables=True) acts2d = plt.renderer.GetActors2D() acts2d.InitTraversal() for i in range(acts2d.GetNumberOfItems()): a = acts2d.GetNextItem() if isinstance(a, vedo.Text2D): allobjs.append(a) allobjs += plt.actors allobjs = list(set(allobjs)) # make sure its unique for a in allobjs: sdict['objects'].append(tonumpy(a)) if fr.endswith(".npz"): np.savez_compressed(fileoutput, vedo_scenes=[sdict]) else: np.save(fileoutput, [sdict]) #################################################################### elif fr.endswith(".x3d"): obj = list(set(vedo.plotter_instance.getMeshes()+ vedo.plotter_instance.actors)) if len(vedo.plotter_instance.axes_instances): obj.append(vedo.plotter_instance.axes_instances[0]) for a in obj: if isinstance(a, Mesh): newa = a.clone(transformed=True) vedo.plotter_instance.remove(a, render=False).add(newa, render=False) elif isinstance(a, Assembly): for b in a.unpack(): if b: newb = b.clone() vedo.plotter_instance.add(newb, render=False) vedo.plotter_instance.remove(a) vedo.plotter_instance.render() exporter = vtk.vtkX3DExporter() exporter.SetBinary(binary) exporter.FastestOff() exporter.SetInput(vedo.plotter_instance.window) exporter.SetFileName(fileoutput) # exporter.WriteToOutputStringOn() # see below exporter.Update() exporter.Write() # this can reduce the size by more than half... # outstring = exporter.GetOutputString().decode("utf-8") # this fails though # from vedo.utils import isInteger, isNumber, precision # newlines = [] # for l in outstring.splitlines(True): # ls = l.lstrip() # content = ls.split() # newls = "" # for c in content: # c2 = c.replace(',','') # if isNumber(c2) and not isInteger(c2): # newc = precision(float(c2), 4) # if ',' in c: # newls += newc + ',' # else: # newls += newc + ' ' # else: # newls += c + ' ' # newlines.append(newls.lstrip()+'\n') # with open("fileoutput", 'w') as f: # l = "".join(newlines) # f.write(l) x3d_html = _x3d_html.replace("~fileoutput", fileoutput) wsize = vedo.plotter_instance.window.GetSize() x3d_html = x3d_html.replace("~width", str(wsize[0])) x3d_html = x3d_html.replace("~height", str(wsize[1])) with open(fileoutput.replace('.x3d', '.html'), "w") as outF: outF.write(x3d_html) vedo.logger.info(f"Saved files {fileoutput} and {fileoutput.replace('.x3d','.html')}") #################################################################### elif fr.endswith(".html"): from vedo.backends import getNotebookBackend savebk = vedo.notebookBackend vedo.notebookBackend='k3d' plt = getNotebookBackend(vedo.plotter_instance.actors, 1.5, '') with open(fileoutput,'w') as fp: fp.write(plt.get_snapshot()) vedo.notebookBackend = savebk else: vedo.logger.error(f"export extension {fr.split('.')[-1]} is not supported") return vedo.plotter_instance
def gunzip(filename)
-
Unzip a
.gz
file to a temporary file and returns its path.Expand source code
def gunzip(filename): """Unzip a `.gz` file to a temporary file and returns its path.""" if not filename.endswith('.gz'): #colors.printc("gunzip() error: file must end with .gz", c='r') return filename from tempfile import NamedTemporaryFile import gzip tmp_file = NamedTemporaryFile(delete=False) tmp_file.name = os.path.join(os.path.dirname(tmp_file.name), os.path.basename(filename).replace('.gz','')) inF = gzip.open(filename, "rb") with open(tmp_file.name, "wb") as outF: outF.write(inF.read()) inF.close() return tmp_file.name
def importWindow(fileinput, mtlFile=None, texturePath=None)
-
Import a whole scene from a Numpy or OBJ wavefront file. Return a
Plotter
instance.Parameters
mtlFile
:str
- MTL file for OBJ wavefront files.
texturePath
:str
- path of the texture files directory.
Expand source code
def importWindow(fileinput, mtlFile=None, texturePath=None): """Import a whole scene from a Numpy or OBJ wavefront file. Return a ``Plotter`` instance. Parameters ---------- mtlFile : str MTL file for OBJ wavefront files. texturePath : str path of the texture files directory. """ from vedo import Plotter data = None if isinstance(fileinput, dict): data = fileinput elif fileinput.endswith('.npy'): data = np.load(fileinput, allow_pickle=True, encoding="latin1").flatten()[0] elif fileinput.endswith('.npz'): data = np.load(fileinput, allow_pickle=True)['vedo_scenes'][0] if data is not None: if 'renderLinesAsTubes' in data.keys(): settings.renderLinesAsTubes = data['renderLinesAsTubes'] if 'hiddenLineRemoval' in data.keys(): settings.hiddenLineRemoval = data['hiddenLineRemoval'] if 'visibleGridEdges' in data.keys(): settings.visibleGridEdges = data['visibleGridEdges'] if 'interactorStyle' in data.keys(): vedo.interactorStyle = data['interactorStyle'] if 'useParallelProjection' in data.keys(): settings.useParallelProjection = data['useParallelProjection'] if 'usePolygonOffset' in data.keys(): settings.usePolygonOffset = data['usePolygonOffset'] if 'polygonOffsetFactor' in data.keys(): settings.polygonOffsetFactor = data['polygonOffsetFactor'] if 'polygonOffsetUnits' in data.keys(): settings.polygonOffsetUnits = data['polygonOffsetUnits'] if 'interpolateScalarsBeforeMapping' in data.keys(): settings.interpolateScalarsBeforeMapping = data['interpolateScalarsBeforeMapping'] if 'defaultFont' in data.keys(): settings.defaultFont = data['defaultFont'] if 'useDepthPeeling' in data.keys(): settings.useDepthPeeling = data['useDepthPeeling'] axes = data.pop('axes', 4) title = data.pop('title', '') backgrcol = data.pop('backgrcol', "white") backgrcol2 = data.pop('backgrcol2', None) cam = data.pop('camera', None) if data['shape'] != (1,1): data['size']="auto" # disable size plt = Plotter(size=data['size'], # not necessarily a good idea to set it #shape=data['shape'], # will need to create a Renderer class first axes=axes, title=title, bg=backgrcol, bg2=backgrcol2, ) if cam: if 'pos' in cam.keys(): plt.camera.SetPosition( cam['pos'] ) if 'focalPoint' in cam.keys(): plt.camera.SetFocalPoint( cam['focalPoint'] ) if 'viewup' in cam.keys(): plt.camera.SetViewUp(cam['viewup'] ) if 'distance' in cam.keys(): plt.camera.SetDistance( cam['distance'] ) if 'clippingRange' in cam.keys(): plt.camera.SetClippingRange( cam['clippingRange'] ) plt.resetcam = False if 'objects' in data.keys(): objs = loadnumpy(data['objects']) if not utils.isSequence(objs): objs = [objs] else: #colors.printc("Trying to import a single mesh.. use load() instead.", c='r') #colors.printc(" -> try to load a single object with load().", c='r') objs = [loadnumpy(fileinput)] plt.actors = objs plt.add(objs, render=False) return plt elif '.obj' in fileinput.lower(): plt = Plotter() importer = vtk.vtkOBJImporter() importer.SetFileName(fileinput) if mtlFile is not False: if mtlFile is None: mtlFile = fileinput.replace('.obj', '.mtl').replace('.OBJ', '.MTL') importer.SetFileNameMTL(mtlFile) if texturePath is not False: if texturePath is None: texturePath = fileinput.replace('.obj', '.txt').replace('.OBJ', '.TXT') importer.SetTexturePath(texturePath) importer.SetRenderWindow(plt.window) importer.Update() actors = plt.renderer.GetActors() actors.InitTraversal() for i in range(actors.GetNumberOfItems()): vactor = actors.GetNextActor() act = Mesh(vactor) act_tu = vactor.GetTexture() if act_tu: act_tu.InterpolateOn() act.texture(act_tu) plt.actors.append( act ) return plt
def load(inputobj, unpack=True, force=False)
-
Load
Mesh
,Volume
andPicture
objects from file or from the web.The output will depend on the file extension. See examples below. Unzip is made on the fly, if file ends with
.gz
. Can load an object directly from a URL address.Parameters
unpack
:bool
- unpack MultiBlockData into a flat list of objects.
force
:bool
- when downloading a file ignore any previous cached downloads and force a new one.
Example
.. code-block:: python
from vedo import dataurl, load, show # Return a Mesh object g = load(dataurl+'250.vtk') show(g) # Return a list of 2 meshes g = load([dataurl+'250.vtk', dataurl+'270.vtk']) show(g) # Return a list of meshes by reading all files in a directory # (if directory contains DICOM files then a Volume is returned) g = load('mydicomdir/') show(g) # Download a file from a URL address and unzip it on the fly g = load('https://vedo.embl.es/examples/panther.stl.gz') show(g)
Expand source code
def load(inputobj, unpack=True, force=False): """ Load ``Mesh``, ``Volume`` and ``Picture`` objects from file or from the web. The output will depend on the file extension. See examples below. Unzip is made on the fly, if file ends with `.gz`. Can load an object directly from a URL address. Parameters ---------- unpack : bool unpack MultiBlockData into a flat list of objects. force : bool when downloading a file ignore any previous cached downloads and force a new one. Example: .. code-block:: python from vedo import dataurl, load, show # Return a Mesh object g = load(dataurl+'250.vtk') show(g) # Return a list of 2 meshes g = load([dataurl+'250.vtk', dataurl+'270.vtk']) show(g) # Return a list of meshes by reading all files in a directory # (if directory contains DICOM files then a Volume is returned) g = load('mydicomdir/') show(g) # Download a file from a URL address and unzip it on the fly g = load('https://vedo.embl.es/examples/panther.stl.gz') show(g) """ acts = [] if utils.isSequence(inputobj): flist = inputobj elif isinstance(inputobj, str) and inputobj.startswith('https://'): flist = [inputobj] else: flist = sorted(glob.glob(inputobj)) for fod in flist: if fod.startswith('https://'): fod = download(fod, force=force, verbose=False) if os.path.isfile(fod): ### it's a file if fod.endswith('.gz'): fod = gunzip(fod) a = _load_file(fod, unpack) acts.append(a) elif os.path.isdir(fod):### it's a directory or DICOM flist = os.listdir(fod) if '.dcm' in flist[0]: ### it's DICOM reader = vtk.vtkDICOMImageReader() reader.SetDirectoryName(fod) reader.Update() image = reader.GetOutput() actor = Volume(image) actor.info['PixelSpacing'] = reader.GetPixelSpacing() actor.info['Width'] = reader.GetWidth() actor.info['Height'] = reader.GetHeight() actor.info['PositionPatient'] = reader.GetImagePositionPatient() actor.info['OrientationPatient'] = reader.GetImageOrientationPatient() actor.info['BitsAllocated'] = reader.GetBitsAllocated() actor.info['PixelRepresentation'] = reader.GetPixelRepresentation() actor.info['NumberOfComponents'] = reader.GetNumberOfComponents() actor.info['TransferSyntaxUID'] = reader.GetTransferSyntaxUID() actor.info['RescaleSlope'] = reader.GetRescaleSlope() actor.info['RescaleOffset'] = reader.GetRescaleOffset() actor.info['PatientName'] = reader.GetPatientName() actor.info['StudyUID'] = reader.GetStudyUID() actor.info['StudyID'] = reader.GetStudyID() actor.info['GantryAngle'] = reader.GetGantryAngle() acts.append(actor) else: ### it's a normal directory utils.humansort(flist) for ifile in flist: a = _load_file(fod+'/'+ifile, unpack) acts.append(a) else: vedo.logger.error(f"in load(), cannot find {fod}") if len(acts) == 1: if "numpy" in str(type(acts[0])): return acts[0] if not acts[0]: vedo.logger.error(f"in load(), cannot load {inputobj}") return acts[0] elif len(acts) == 0: vedo.logger.error(f"in load(), cannot load {inputobj}") return None else: return acts
def loadRectilinearGrid(filename)
-
Load and return a
vtkRectilinearGrid
object from file.Expand source code
def loadRectilinearGrid(filename): """Load and return a ``vtkRectilinearGrid`` object from file.""" if filename.endswith(".vtr"): reader = vtk.vtkXMLRectilinearGridReader() else: reader = vtk.vtkRectilinearGridReader() reader.SetFileName(filename) reader.Update() return reader.GetOutput()
def loadStructuredGrid(filename)
-
Load and return a
vtkStructuredGrid
object from file.Expand source code
def loadStructuredGrid(filename): """Load and return a ``vtkStructuredGrid`` object from file.""" if filename.endswith(".vts"): reader = vtk.vtkXMLStructuredGridReader() else: reader = vtk.vtkStructuredGridReader() reader.SetFileName(filename) reader.Update() return reader.GetOutput()
def loadStructuredPoints(filename)
-
Load and return a
vtkStructuredPoints
object from file.Expand source code
def loadStructuredPoints(filename): """Load and return a ``vtkStructuredPoints`` object from file.""" reader = vtk.vtkStructuredPointsReader() reader.SetFileName(filename) reader.Update() return reader.GetOutput()
def loadTransform(filename)
-
Load a
vtkTransform
from a file.mat.Returns
T
:vtkTransform
- The transformation to be applied to some object (
use applyTransform()
). comment
:str
- a comment string associated to this transformation file.
Expand source code
def loadTransform(filename): """ Load a ``vtkTransform`` from a file.mat. Returns ------- T : vtkTransform The transformation to be applied to some object (``use applyTransform()``). comment : str a comment string associated to this transformation file. """ with open(filename,'r') as f: lines = f.readlines() M = vtk.vtkMatrix4x4() i=0 comment = '' for l in lines: if l.startswith('#'): comment = l.replace('#', "").replace('\n', "") continue vals = l.split(' ') if len(vals)==4: for j in range(4): v = vals[j].replace('\n', '') M.SetElement(i,j, float(v)) i+=1 T = vtk.vtkTransform() T.SetMatrix(M) return (T, comment)
def loadUnStructuredGrid(filename)
-
Load and return a
vtkunStructuredGrid
object from file.Expand source code
def loadUnStructuredGrid(filename): """Load and return a ``vtkunStructuredGrid`` object from file.""" if filename.endswith(".vtu"): reader = vtk.vtkXMLUnstructuredGridReader() else: reader = vtk.vtkUnstructuredGridReader() reader.SetFileName(filename) reader.Update() return reader.GetOutput()
def screenshot(filename='screenshot.png', scale=None, asarray=False)
-
Save a screenshot of the current rendering window.
Parameters
scale
:int
- set image magnification as an integer multiplicative factor
asarray
:bool
- return a numpy array of the image
Expand source code
def screenshot(filename="screenshot.png", scale=None, asarray=False): """ Save a screenshot of the current rendering window. Parameters ---------- scale : int set image magnification as an integer multiplicative factor asarray : bool return a numpy array of the image """ if not vedo.plotter_instance or not vedo.plotter_instance.window: vedo.logger.error("in screenshot(), rendering window is not present, skip.") return vedo.plotter_instance if filename.endswith('.pdf'): writer = vtk.vtkGL2PSExporter() writer.SetRenderWindow(vedo.plotter_instance.window) writer.Write3DPropsAsRasterImageOff() writer.SilentOn() writer.SetSortToBSP() writer.SetFileFormatToPDF() writer.SetFilePrefix(filename.replace('.pdf','')) writer.Write() return vedo.plotter_instance ########## elif filename.endswith('.svg'): writer = vtk.vtkGL2PSExporter() writer.SetRenderWindow(vedo.plotter_instance.window) writer.Write3DPropsAsRasterImageOff() writer.SilentOn() writer.SetSortToBSP() writer.SetFileFormatToSVG() writer.SetFilePrefix(filename.replace('.svg','')) writer.Write() return vedo.plotter_instance ########## elif filename.endswith('.eps'): writer = vtk.vtkGL2PSExporter() writer.SetRenderWindow(vedo.plotter_instance.window) writer.Write3DPropsAsRasterImageOff() writer.SilentOn() writer.SetSortToBSP() writer.SetFileFormatToEPS() writer.SetFilePrefix(filename.replace('.eps','')) writer.Write() return vedo.plotter_instance ########## if scale is None: scale = settings.screeshotScale if settings.screeshotLargeImage: w2if = vtk.vtkRenderLargeImage() w2if.SetInput(vedo.plotter_instance.renderer) w2if.SetMagnification(scale) else: w2if = vtk.vtkWindowToImageFilter() w2if.SetInput(vedo.plotter_instance.window) if hasattr(w2if, 'SetScale'): w2if.SetScale(scale, scale) if settings.screenshotTransparentBackground: w2if.SetInputBufferTypeToRGBA() w2if.ReadFrontBufferOff() # read from the back buffer w2if.Update() if asarray: w2ifout = w2if.GetOutput() npdata = utils.vtk2numpy(w2ifout.GetPointData().GetArray("ImageScalars")) npdata = npdata[:,[0,1,2]] ydim, xdim, _ = w2ifout.GetDimensions() npdata = npdata.reshape([xdim, ydim, -1]) npdata = np.flip(npdata, axis=0) return npdata if filename.lower().endswith('.png'): writer = vtk.vtkPNGWriter() writer.SetFileName(filename) writer.SetInputData(w2if.GetOutput()) writer.Write() elif filename.lower().endswith('.jpg') or filename.lower().endswith('.jpeg'): writer = vtk.vtkJPEGWriter() writer.SetFileName(filename) writer.SetInputData(w2if.GetOutput()) writer.Write() else: #add .png writer = vtk.vtkPNGWriter() writer.SetFileName(filename+'.png') writer.SetInputData(w2if.GetOutput()) writer.Write() return vedo.plotter_instance
def write(objct, fileoutput, binary=True)
-
Write 3D object to file. (same as
save()
).Possile extensions are: - vtk, vti, npy, npz, ply, obj, stl, byu, vtp, vti, mhd, xyz, tif, png, bmp.
Expand source code
def write(objct, fileoutput, binary=True): """ Write 3D object to file. (same as `save()`). Possile extensions are: - vtk, vti, npy, npz, ply, obj, stl, byu, vtp, vti, mhd, xyz, tif, png, bmp. """ obj = objct if isinstance(obj, Points): # picks transformation obj = objct.polydata(True) elif isinstance(obj, (vtk.vtkActor, vtk.vtkVolume)): obj = objct.GetMapper().GetInput() elif isinstance(obj, (vtk.vtkPolyData, vtk.vtkImageData)): obj = objct if hasattr(obj, 'filename'): obj.filename = fileoutput fr = fileoutput.lower() if fr.endswith(".vtk"): writer = vtk.vtkDataSetWriter() elif fr.endswith(".ply"): writer = vtk.vtkPLYWriter() writer.AddComment("PLY file generated by vedo") lut = objct.GetMapper().GetLookupTable() if lut: pscal = obj.GetPointData().GetScalars() if not pscal: pscal = obj.GetCellData().GetScalars() if pscal and pscal.GetName(): writer.SetArrayName(pscal.GetName()) writer.SetLookupTable(lut) elif fr.endswith(".stl"): writer = vtk.vtkSTLWriter() elif fr.endswith(".vtp"): writer = vtk.vtkXMLPolyDataWriter() elif fr.endswith(".vtu"): writer = vtk.vtkXMLUnstructuredGridWriter() elif fr.endswith(".vtm"): g = vtk.vtkMultiBlockDataGroupFilter() for ob in objct: if isinstance(ob, (Points, Volume)): # picks transformation ob = ob.polydata(True) g.AddInputData(ob) # elif isinstance(ob, (vtk.vtkActor, vtk.vtkVolume)): # ob = ob.GetMapper().GetInput() # g.AddInputData(ob) g.Update() mb = g.GetOutputDataObject(0) wri = vtk.vtkXMLMultiBlockDataWriter() wri.SetInputData(mb) wri.SetFileName(fileoutput) wri.Write() return mb elif fr.endswith(".xyz"): writer = vtk.vtkSimplePointsWriter() elif fr.endswith(".facet"): writer = vtk.vtkFacetWriter() elif fr.endswith(".vti"): writer = vtk.vtkXMLImageDataWriter() elif fr.endswith(".mhd"): writer = vtk.vtkMetaImageWriter() elif fr.endswith(".nii"): writer = vtk.vtkNIFTIImageWriter() elif fr.endswith(".png"): writer = vtk.vtkPNGWriter() elif fr.endswith(".jpg"): writer = vtk.vtkJPEGWriter() elif fr.endswith(".bmp"): writer = vtk.vtkBMPWriter() elif fr.endswith(".tif") or fr.endswith(".tiff"): writer = vtk.vtkTIFFWriter() # print("GetCompression ", writer.GetCompression()) # basically uncompressed.. writer.SetFileDimensionality(len(obj.GetDimensions())) elif fr.endswith(".npy") or fr.endswith(".npz"): if utils.isSequence(objct): objslist = objct else: objslist = [objct] dicts2save = [] for obj in objslist: dicts2save.append( tonumpy(obj) ) np.save(fileoutput, dicts2save) return dicts2save elif fr.endswith(".obj"): with open(fileoutput, "w") as outF: outF.write('# OBJ file format with ext .obj\n') outF.write('# File generated by vedo\n') for p in objct.points(): outF.write("v {:.5g} {:.5g} {:.5g}\n".format(*p)) # pdata = objct.polydata().GetPointData().GetScalars() # if pdata: # ndata = vtk_to_numpy(pdata) # for vd in ndata: # outF.write('vp '+ str(vd) +'\n') #ptxt = objct.polydata().GetPointData().GetTCoords() # not working #if ptxt: # ntxt = vtk_to_numpy(ptxt) # print(len(objct.faces()), objct.points().shape, ntxt.shape) # for vt in ntxt: # outF.write('vt '+ str(vt[0]) +" "+ str(vt[1])+ ' 0\n') for i,f in enumerate(objct.faces()): fs = '' for fi in f: fs += " {:d}".format(fi+1) outF.write('f' + fs + '\n') for l in objct.lines(): ls = '' for li in l: ls += str(li+1)+" " outF.write('l '+ ls + '\n') return objct elif fr.endswith(".xml"): # write tetrahedral dolfin xml vertices = objct.points().astype(str) faces = np.array(objct.faces()).astype(str) ncoords = vertices.shape[0] with open(fileoutput, "w") as outF: outF.write('<?xml version="1.0" encoding="UTF-8"?>\n') outF.write('<dolfin xmlns:dolfin="http://www.fenicsproject.org">\n') if len(faces[0]) == 4:# write tetrahedral mesh ntets = faces.shape[0] outF.write(' <mesh celltype="tetrahedron" dim="3">\n') outF.write(' <vertices size="' + str(ncoords) + '">\n') for i in range(ncoords): x, y, z = vertices[i] outF.write(' <vertex index="'+str(i)+'" x="'+x+'" y="'+y+'" z="'+z+'"/>\n') outF.write(' </vertices>\n') outF.write(' <cells size="' + str(ntets) + '">\n') for i in range(ntets): v0, v1, v2, v3 = faces[i] outF.write(' <tetrahedron index="'+str(i) + '" v0="'+v0+'" v1="'+v1+'" v2="'+v2+'" v3="'+v3+'"/>\n') elif len(faces[0]) == 3:# write triangle mesh ntri = faces.shape[0] outF.write(' <mesh celltype="triangle" dim="2">\n') outF.write(' <vertices size="' + str(ncoords) + '">\n') for i in range(ncoords): x, y, dummy_z = vertices[i] outF.write(' <vertex index="'+str(i)+'" x="'+x+'" y="'+y+'"/>\n') outF.write(' </vertices>\n') outF.write(' <cells size="' + str(ntri) + '">\n') for i in range(ntri): v0, v1, v2 = faces[i] outF.write(' <triangle index="'+str(i)+'" v0="'+v0+'" v1="'+v1+'" v2="'+v2+'"/>\n') outF.write(' </cells>\n') outF.write(" </mesh>\n") outF.write("</dolfin>\n") return objct else: vedo.logger.error(f"Unknown format {fileoutput}, file not saved") return objct try: if hasattr(writer, 'SetFileTypeToBinary'): if binary: writer.SetFileTypeToBinary() else: writer.SetFileTypeToASCII() writer.SetInputData(obj) writer.SetFileName(fileoutput) writer.Write() except: vedo.logger.error(f"could not save {fileoutput}") return objct
def writeTransform(inobj, filename='transform.mat', comment='')
-
Save a transformation for a mesh or pointcloud to ASCII file.
Parameters
filename
:str
, optional- output file name. The default is 'transform.mat'.
comment
:str
, optional- some optional comment. The default is ''.
Expand source code
def writeTransform(inobj, filename='transform.mat', comment=''): """ Save a transformation for a mesh or pointcloud to ASCII file. Parameters ---------- filename : str, optional output file name. The default is 'transform.mat'. comment : str, optional some optional comment. The default is ''. """ if isinstance(inobj, Points): M = inobj.getTransform().GetMatrix() elif isinstance(inobj, vtk.vtkTransform): M = inobj.GetMatrix() elif isinstance(inobj, vtk.vtkMatrix4x4): M = inobj else: vedo.logger.error(f"in writeTransform(), cannot understand input type {type(inobj)}") with open(filename,'w') as f: if comment: f.write('# '+comment+'\n') for i in range(4): f.write( str(M.GetElement(i,0))+' '+ str(M.GetElement(i,1))+' '+ str(M.GetElement(i,2))+' '+ str(M.GetElement(i,3))+'\n', ) f.write('\n') return
Classes
class Video (name='movie.mp4', duration=None, fps=24, backend='ffmpeg')
-
Class to generate a video from the specified rendering window. Program
ffmpeg
is used to create video from each generated frame.Parameters
name
:str
- name of the output file.
fps
:int
- set the number of frames per second.
duration
:float
- set the total
duration
of the video and recalculatesfps
accordingly.
ffmpeg : str set path to ffmpeg program. Default value assumes ffmpeg command is in the path.
Hint: examples/other/makeVideo.py
Expand source code
class Video: """ Class to generate a video from the specified rendering window. Program `ffmpeg` is used to create video from each generated frame. Parameters ---------- name : str name of the output file. fps : int set the number of frames per second. duration : float set the total `duration` of the video and recalculates `fps` accordingly. ffmpeg : str set path to ffmpeg program. Default value assumes ffmpeg command is in the path. .. hint:: examples/other/makeVideo.py .. image:: https://user-images.githubusercontent.com/32848391/50739007-2bfc2b80-11da-11e9-97e6-620a3541a6fa.jpg """ def __init__(self, name="movie.mp4", duration=None, fps=24, backend='ffmpeg', ): from tempfile import TemporaryDirectory self.name = name self.duration = duration self.backend = backend self.fps = float(fps) self.command = "ffmpeg -loglevel panic -y -r" self.options = "-b:v 8000k" self.frames = [] self.tmp_dir = TemporaryDirectory() self.get_filename = lambda x: os.path.join(self.tmp_dir.name, x) colors.printc("\video Video", self.name, "is open... ", c="m", end='') def addFrame(self): """Add frame to current video.""" fr = self.get_filename(str(len(self.frames)) + ".png") screenshot(fr) self.frames.append(fr) return self def pause(self, pause=0): """Insert a `pause`, in seconds.""" fr = self.frames[-1] n = int(self.fps * pause) for _ in range(n): fr2 = self.get_filename(str(len(self.frames)) + ".png") self.frames.append(fr2) os.system("cp -f %s %s" % (fr, fr2)) return self def action(self, elevation=(0,80), azimuth=(0,359), cameras=(), resetcam=False, ): """ Automatic shooting of a static scene by specifying rotation and elevation ranges. :param list elevation: initial and final elevation angles :param list azimuth_range: initial and final azimuth angles :param list cameras: list of cameras to go through, each camera can be dictionary or a vtkCamera """ if not self.duration: self.duration = 5 def build_vtk_cam(cm_input): cm = dict(cm_input) cm_pos = cm.pop("pos", None) cm_focalPoint = cm.pop("focalPoint", None) cm_viewup = cm.pop("viewup", None) cm_distance = cm.pop("distance", None) cm_clippingRange = cm.pop("clippingRange", None) cm_parallelScale = cm.pop("parallelScale", None) cm_thickness = cm.pop("thickness", None) cm_viewAngle = cm.pop("viewAngle", None) cm = vtk.vtkCamera() if cm_pos is not None: cm.SetPosition(cm_pos) if cm_focalPoint is not None: cm.SetFocalPoint(cm_focalPoint) if cm_viewup is not None: cm.SetViewUp(cm_viewup) if cm_distance is not None: cm.SetDistance(cm_distance) if cm_clippingRange is not None: cm.SetClippingRange(cm_clippingRange) if cm_parallelScale is not None: cm.SetParallelScale(cm_parallelScale) if cm_thickness is not None: cm.SetThickness(cm_thickness) if cm_viewAngle is not None: cm.SetViewAngle(cm_viewAngle) return cm plt = vedo.plotter_instance n = int(self.fps * self.duration) cams = [] for cm in cameras: cams.append(build_vtk_cam(cm)) nc = len(cams) plt.show(resetcam=resetcam, interactive=False) if nc: for icam, cam in enumerate(cams): if cam==cams[-1]: break for i in range(n): plt.moveCamera(cams[icam], cams[icam+1], i/n) plt.show() self.addFrame() else: ######################################## for i in range(n): plt.camera.Elevation((elevation[1]-elevation[0])/n) plt.camera.Azimuth((azimuth[1]-azimuth[0])/n) plt.show() self.addFrame() return self def close(self): """ Render the video and write to file. Return the current ``Plotter`` instance. """ if self.duration: self.fps = len(self.frames) / float(self.duration) colors.printc("Recalculated video FPS to", round(self.fps, 3), c="m") else: self.fps = int(self.fps) ######################################## if self.backend == 'ffmpeg': out = os.system(self.command + " " + str(self.fps) + " -i " + self.tmp_dir.name + os.sep + "%01d.png " + self.options + " " + self.name) if out: vedo.logger.error(f"backend {self.backend} returning error") else: colors.printc(f" \save video saved as {self.name}", c="m") ######################################## elif 'cv' in self.backend: try: import cv2 except: vedo.logger.error("opencv is not installed") return cap = cv2.VideoCapture(os.path.join(self.tmp_dir.name, "%1d.png")) fourcc = cv2.VideoWriter_fourcc(*"mp4v") w,h = vedo.plotter_instance.window.GetSize() w,h = w*settings.screeshotScale, h*settings.screeshotScale writer = cv2.VideoWriter(self.name, fourcc, self.fps, (w, h), True) found = False while True: ret, frame = cap.read() if not ret: break writer.write(frame) found = True cap.release() writer.release() if found: vedo.logger.info("video saved as {self.name}") else: vedo.logger.error("could not find snapshots") self.tmp_dir.cleanup() return vedo.plotter_instance
Methods
def action(self, elevation=(0, 80), azimuth=(0, 359), cameras=(), resetcam=False)
-
Automatic shooting of a static scene by specifying rotation and elevation ranges.
:param list elevation: initial and final elevation angles :param list azimuth_range: initial and final azimuth angles :param list cameras: list of cameras to go through, each camera can be dictionary or a vtkCamera
Expand source code
def action(self, elevation=(0,80), azimuth=(0,359), cameras=(), resetcam=False, ): """ Automatic shooting of a static scene by specifying rotation and elevation ranges. :param list elevation: initial and final elevation angles :param list azimuth_range: initial and final azimuth angles :param list cameras: list of cameras to go through, each camera can be dictionary or a vtkCamera """ if not self.duration: self.duration = 5 def build_vtk_cam(cm_input): cm = dict(cm_input) cm_pos = cm.pop("pos", None) cm_focalPoint = cm.pop("focalPoint", None) cm_viewup = cm.pop("viewup", None) cm_distance = cm.pop("distance", None) cm_clippingRange = cm.pop("clippingRange", None) cm_parallelScale = cm.pop("parallelScale", None) cm_thickness = cm.pop("thickness", None) cm_viewAngle = cm.pop("viewAngle", None) cm = vtk.vtkCamera() if cm_pos is not None: cm.SetPosition(cm_pos) if cm_focalPoint is not None: cm.SetFocalPoint(cm_focalPoint) if cm_viewup is not None: cm.SetViewUp(cm_viewup) if cm_distance is not None: cm.SetDistance(cm_distance) if cm_clippingRange is not None: cm.SetClippingRange(cm_clippingRange) if cm_parallelScale is not None: cm.SetParallelScale(cm_parallelScale) if cm_thickness is not None: cm.SetThickness(cm_thickness) if cm_viewAngle is not None: cm.SetViewAngle(cm_viewAngle) return cm plt = vedo.plotter_instance n = int(self.fps * self.duration) cams = [] for cm in cameras: cams.append(build_vtk_cam(cm)) nc = len(cams) plt.show(resetcam=resetcam, interactive=False) if nc: for icam, cam in enumerate(cams): if cam==cams[-1]: break for i in range(n): plt.moveCamera(cams[icam], cams[icam+1], i/n) plt.show() self.addFrame() else: ######################################## for i in range(n): plt.camera.Elevation((elevation[1]-elevation[0])/n) plt.camera.Azimuth((azimuth[1]-azimuth[0])/n) plt.show() self.addFrame() return self
def addFrame(self)
-
Add frame to current video.
Expand source code
def addFrame(self): """Add frame to current video.""" fr = self.get_filename(str(len(self.frames)) + ".png") screenshot(fr) self.frames.append(fr) return self
def close(self)
-
Render the video and write to file. Return the current
Plotter
instance.Expand source code
def close(self): """ Render the video and write to file. Return the current ``Plotter`` instance. """ if self.duration: self.fps = len(self.frames) / float(self.duration) colors.printc("Recalculated video FPS to", round(self.fps, 3), c="m") else: self.fps = int(self.fps) ######################################## if self.backend == 'ffmpeg': out = os.system(self.command + " " + str(self.fps) + " -i " + self.tmp_dir.name + os.sep + "%01d.png " + self.options + " " + self.name) if out: vedo.logger.error(f"backend {self.backend} returning error") else: colors.printc(f" \save video saved as {self.name}", c="m") ######################################## elif 'cv' in self.backend: try: import cv2 except: vedo.logger.error("opencv is not installed") return cap = cv2.VideoCapture(os.path.join(self.tmp_dir.name, "%1d.png")) fourcc = cv2.VideoWriter_fourcc(*"mp4v") w,h = vedo.plotter_instance.window.GetSize() w,h = w*settings.screeshotScale, h*settings.screeshotScale writer = cv2.VideoWriter(self.name, fourcc, self.fps, (w, h), True) found = False while True: ret, frame = cap.read() if not ret: break writer.write(frame) found = True cap.release() writer.release() if found: vedo.logger.info("video saved as {self.name}") else: vedo.logger.error("could not find snapshots") self.tmp_dir.cleanup() return vedo.plotter_instance
def pause(self, pause=0)
-
Insert a
pause
, in seconds.Expand source code
def pause(self, pause=0): """Insert a `pause`, in seconds.""" fr = self.frames[-1] n = int(self.fps * pause) for _ in range(n): fr2 = self.get_filename(str(len(self.frames)) + ".png") self.frames.append(fr2) os.system("cp -f %s %s" % (fr, fr2)) return self