vedo.file_io
Submodule to read/write meshes and other objects in different formats, and other I/O functionalities.
1import glob 2import os 3import time 4from tempfile import NamedTemporaryFile, TemporaryDirectory 5from typing import Any, List, Tuple, Union 6 7import numpy as np 8 9import vedo.vtkclasses as vtki # a wrapper for lazy imports 10 11import vedo 12from vedo import settings 13from vedo import colors 14from vedo import utils 15from vedo.assembly import Assembly 16from vedo.image import Image 17from vedo.pointcloud import Points 18from vedo.mesh import Mesh 19from vedo.volume import Volume 20 21__docformat__ = "google" 22 23__doc__ = """ 24Submodule to read/write meshes and other objects in different formats, 25and other I/O functionalities. 26""" 27 28__all__ = [ 29 "load", 30 "read", 31 "download", 32 "gunzip", 33 "loadStructuredPoints", 34 "loadStructuredGrid", 35 "write", 36 "save", 37 "export_window", 38 "import_window", 39 "load_obj", 40 "screenshot", 41 "ask", 42 "Video", 43] 44 45 46# example web page for X3D 47_x3d_html_template = """ 48<!DOCTYPE html> 49<html lang="en"> 50<head> 51 <meta charset="UTF-8"> 52 <meta name="viewport" content="width=device-width, initial-scale=1.0"> 53 <title> vedo with x3d </title> 54 55 <!-- THESE ARE THE RELEVANT LINES: --> 56 <script src='https://www.x3dom.org/download/x3dom.js'> </script> 57 <link rel='stylesheet' type='text/css' href='https://www.x3dom.org/download/x3dom.css'/> 58 59 <style> 60 table, td, th { border: 1px solid black; background-color: powderblue;} 61 table {width: 70%; border-collapse: collapse;} 62 table th {width: 35%;} 63 </style> 64</head> 65 66<body style="font-family: Verdana"> 67 <h1>Example html generated by vedo</h1> 68 This example loads a 3D scene from file ~fileoutput generated by 69 <a href="https://github.com/marcomusy/vedo">vedo</a> 70 (see <a href="https://github.com/marcomusy/vedo/tree/master/examples/other/export_x3d.py">export_x3d.py</a>). 71 <br><br> 72 73 74 <!-- THESE ARE THE RELEVANT LINES: --> 75 <x3d width='~widthpx' height='~heightpx'> 76 <scene> 77 <Inline url="~fileoutput"> </Inline> 78 </scene> 79 </x3d> 80 81 <h3>Nothing shows up above this line?</h3> 82 Enable your browser to load local files: 83 <br><b>Firefox</b>: type <code>about:config</code> in the URL bar and 84 change <code>privacy.file_unique_origin</code> from <code>True</code> to <code>False</code> 85 <br><b>Chrome</b>: from terminal type: 86 <code>google-chrome --enable-webgl --allow-file-access-from-files</code> 87 (see <a href="https://cmatskas.com/interacting-with-local-data-files-using-chrome/">here</a>) 88 89 <br> 90 <h3>Controls:</h3> 91 <h4><strong>Examine Mode (activate with key 'e'):</strong></h4> 92 <table> 93 <tbody> 94 <tr class="even description"> 95 <th>Button</th> 96 <th>Function</th> 97 </tr> 98 <tr> 99 <td>Left Button / Left Button + Shift</td> 100 <td>Rotate</td> 101 </tr> 102 <tr> 103 <td>Mid Button / Left Button + Ctl</td> 104 <td>Pan</td> 105 </tr> 106 <tr> 107 <td>Right Button / Wheel / Left Button + Alt</td> 108 <td>Zoom</td> 109 </tr> 110 <tr> 111 <td>Left double click</td> 112 <td>Set center of rotation</td> 113 </tr> 114 </tbody> 115 </table> 116 <h4><strong>Walk Mode (activate with key 'w'):</strong></h4> 117 <table> 118 <tbody> 119 <tr class="even description"> 120 <th>Button</th> 121 <th>Function</th> 122 </tr> 123 <tr> 124 <td>Left Button</td> 125 <td>Move forward</td> 126 </tr> 127 <tr> 128 <td>Right Button</td> 129 <td>Move backward</td> 130 </tr> 131 </tbody> 132 </table> 133 <h4><strong>Fly Mode (activate with key 'f'):</strong></h4> 134 <table> 135 <tbody> 136 <tr class="even description"> 137 <th>Button</th> 138 <th>Function</th> 139 </tr> 140 <tr> 141 <td>Left Button</td> 142 <td>Move forward</td> 143 </tr> 144 <tr> 145 <td>Right Button</td> 146 <td>Move backward</td> 147 </tr> 148 </tbody> 149 </table> 150 <h3>Non-interactive camera movement</h3> 151 <table> 152 <tbody> 153 <tr class="even description"> 154 <th>Key</th> 155 <th>Function</th> 156 </tr> 157 <tr> 158 <td>r</td> 159 <td>reset view</td> 160 </tr> 161 <tr> 162 <td>a</td> 163 <td>show all</td> 164 </tr> 165 <tr> 166 <td>u</td> 167 <td>upright</td> 168 </tr> 169 </tbody> 170 </table> 171</body> 172</html> 173""" 174 175######################################################################## 176def load(inputobj: Union[list, str], unpack=True, force=False) -> Any: 177 """ 178 Load any vedo objects from file or from the web. 179 180 The output will depend on the file extension. See examples below. 181 Unzip is made on the fly, if file ends with `.gz`. 182 Can load an object directly from a URL address. 183 184 Arguments: 185 unpack : (bool) 186 unpack MultiBlockData into a flat list of objects. 187 force : (bool) 188 when downloading a file ignore any previous cached downloads and force a new one. 189 190 Example: 191 ```python 192 from vedo import dataurl, load, show 193 # Return a list of 2 meshes 194 g = load([dataurl+'250.vtk', dataurl+'270.vtk']) 195 show(g) 196 # Return a list of meshes by reading all files in a directory 197 # (if directory contains DICOM files then a Volume is returned) 198 g = load('mydicomdir/') 199 show(g) 200 ``` 201 """ 202 acts = [] 203 if utils.is_sequence(inputobj): 204 flist = inputobj 205 elif isinstance(inputobj, str) and inputobj.startswith("https://"): 206 flist = [inputobj] 207 else: 208 flist = utils.humansort(glob.glob(inputobj)) 209 210 for fod in flist: 211 212 if fod.startswith("https://"): 213 fod = download(fod, force=force, verbose=False) 214 215 if os.path.isfile(fod): ### it's a file 216 217 if fod.endswith(".gz"): 218 fod = gunzip(fod) 219 220 a = _load_file(fod, unpack) 221 acts.append(a) 222 223 elif os.path.isdir(fod): ### it's a directory or DICOM 224 flist = os.listdir(fod) 225 if ".dcm" in flist[0]: ### it's DICOM 226 reader = vtki.new("DICOMImageReader") 227 reader.SetDirectoryName(fod) 228 reader.Update() 229 image = reader.GetOutput() 230 vol = Volume(image) 231 try: 232 vol.metadata["PixelSpacing"] = reader.GetPixelSpacing() 233 vol.metadata["Width"] = reader.GetWidth() 234 vol.metadata["Height"] = reader.GetHeight() 235 vol.metadata["PositionPatient"] = reader.GetImagePositionPatient() 236 vol.metadata["OrientationPatient"] = reader.GetImageOrientationPatient() 237 vol.metadata["BitsAllocated"] = reader.GetBitsAllocated() 238 vol.metadata["PixelRepresentation"] = reader.GetPixelRepresentation() 239 vol.metadata["NumberOfComponents"] = reader.GetNumberOfComponents() 240 vol.metadata["TransferSyntaxUID"] = reader.GetTransferSyntaxUID() 241 vol.metadata["RescaleSlope"] = reader.GetRescaleSlope() 242 vol.metadata["RescaleOffset"] = reader.GetRescaleOffset() 243 vol.metadata["PatientName"] = reader.GetPatientName() 244 vol.metadata["StudyUID"] = reader.GetStudyUID() 245 vol.metadata["StudyID"] = reader.GetStudyID() 246 vol.metadata["GantryAngle"] = reader.GetGantryAngle() 247 except Exception as e: 248 vedo.logger.warning(f"Cannot read DICOM metadata: {e}") 249 acts.append(vol) 250 251 else: ### it's a normal directory 252 utils.humansort(flist) 253 for ifile in flist: 254 a = _load_file(fod + "/" + ifile, unpack) 255 acts.append(a) 256 else: 257 vedo.logger.error(f"in load(), cannot find {fod}") 258 259 if len(acts) == 1: 260 if "numpy" in str(type(acts[0])): 261 return acts[0] 262 if not acts[0]: 263 vedo.logger.error(f"in load(), cannot load {inputobj}") 264 return acts[0] 265 266 if len(acts) == 0: 267 vedo.logger.error(f"in load(), cannot load {inputobj}") 268 return None 269 270 else: 271 return acts 272 273######################################################################## 274def _load_file(filename, unpack): 275 fl = filename.lower() 276 277 ########################################################## other formats: 278 if fl.endswith(".xml") or fl.endswith(".xml.gz") or fl.endswith(".xdmf"): 279 # Fenics tetrahedral file 280 objt = loadDolfin(filename) 281 elif fl.endswith(".neutral") or fl.endswith(".neu"): # neutral tets 282 objt = loadNeutral(filename) 283 elif fl.endswith(".gmsh"): # gmesh file 284 objt = loadGmesh(filename) 285 elif fl.endswith(".pcd"): # PCL point-cloud format 286 objt = loadPCD(filename) 287 objt.properties.SetPointSize(2) 288 elif fl.endswith(".off"): 289 objt = loadOFF(filename) 290 elif fl.endswith(".3ds"): # 3ds format 291 objt = load3DS(filename) 292 elif fl.endswith(".wrl"): 293 importer = vtki.new("VRMLImporter") 294 importer.SetFileName(filename) 295 importer.Read() 296 importer.Update() 297 actors = importer.GetRenderer().GetActors() # vtkActorCollection 298 actors.InitTraversal() 299 wacts = [] 300 for i in range(actors.GetNumberOfItems()): 301 act = actors.GetNextActor() 302 m = Mesh(act.GetMapper().GetInput()) 303 m.actor = act 304 wacts.append(m) 305 objt = Assembly(wacts) 306 307 ######################################################## volumetric: 308 elif ( 309 fl.endswith(".tif") 310 or fl.endswith(".tiff") 311 or fl.endswith(".slc") 312 or fl.endswith(".vti") 313 or fl.endswith(".mhd") 314 or fl.endswith(".nrrd") 315 or fl.endswith(".nii") 316 or fl.endswith(".dem") 317 ): 318 img = loadImageData(filename) 319 objt = Volume(img) 320 321 ######################################################### 2D images: 322 elif ( 323 fl.endswith(".png") 324 or fl.endswith(".jpg") 325 or fl.endswith(".bmp") 326 or fl.endswith(".jpeg") 327 or fl.endswith(".gif") 328 ): 329 if ".png" in fl: 330 picr = vtki.new("PNGReader") 331 elif ".jpg" in fl or ".jpeg" in fl: 332 picr = vtki.new("JPEGReader") 333 elif ".bmp" in fl: 334 picr = vtki.new("BMPReader") 335 elif ".gif" in fl: 336 from PIL import Image as PILImage, ImageSequence 337 338 img = PILImage.open(filename) 339 frames = [] 340 for frame in ImageSequence.Iterator(img): 341 a = np.array(frame.convert("RGB").getdata(), dtype=np.uint8) 342 a = a.reshape([frame.size[1], frame.size[0], 3]) 343 frames.append(Image(a)) 344 return frames 345 346 picr.SetFileName(filename) 347 picr.Update() 348 objt = Image(picr.GetOutput()) 349 350 ######################################################### multiblock: 351 elif fl.endswith(".vtm") or fl.endswith(".vtmb"): 352 read = vtki.new("XMLMultiBlockDataReader") 353 read.SetFileName(filename) 354 read.Update() 355 mb = read.GetOutput() 356 if unpack: 357 acts = [] 358 for i in range(mb.GetNumberOfBlocks()): 359 b = mb.GetBlock(i) 360 if isinstance( 361 b, 362 ( 363 vtki.vtkPolyData, 364 vtki.vtkStructuredGrid, 365 vtki.vtkRectilinearGrid, 366 ), 367 ): 368 acts.append(Mesh(b)) 369 elif isinstance(b, vtki.vtkImageData): 370 acts.append(Volume(b)) 371 elif isinstance(b, vtki.vtkUnstructuredGrid): 372 acts.append(vedo.UnstructuredGrid(b)) 373 return acts 374 return mb 375 376 ########################################################### 377 elif fl.endswith(".geojson"): 378 return loadGeoJSON(filename) 379 380 elif fl.endswith(".pvd"): 381 return loadPVD(filename) 382 383 ########################################################### polygonal mesh: 384 else: 385 if fl.endswith(".vtk"): # read all legacy vtk types 386 reader = vtki.new("DataSetReader") 387 reader.ReadAllScalarsOn() 388 reader.ReadAllVectorsOn() 389 reader.ReadAllTensorsOn() 390 reader.ReadAllFieldsOn() 391 reader.ReadAllNormalsOn() 392 reader.ReadAllColorScalarsOn() 393 elif fl.endswith(".ply"): 394 reader = vtki.new("PLYReader") 395 elif fl.endswith(".obj"): 396 reader = vtki.new("OBJReader") 397 reader.SetGlobalWarningDisplay(0) # suppress warnings issue #980 398 elif fl.endswith(".stl"): 399 reader = vtki.new("STLReader") 400 elif fl.endswith(".byu") or fl.endswith(".g"): 401 reader = vtki.new("BYUReader") 402 elif fl.endswith(".foam"): # OpenFoam 403 reader = vtki.new("OpenFOAMReader") 404 elif fl.endswith(".pvd"): 405 reader = vtki.new("XMLGenericDataObjectReader") 406 elif fl.endswith(".vtp"): 407 reader = vtki.new("XMLPolyDataReader") 408 elif fl.endswith(".vts"): 409 reader = vtki.new("XMLStructuredGridReader") 410 elif fl.endswith(".vtu"): 411 reader = vtki.new("XMLUnstructuredGridReader") 412 elif fl.endswith(".vtr"): 413 reader = vtki.new("XMLRectilinearGridReader") 414 elif fl.endswith(".pvtr"): 415 reader = vtki.new("XMLPRectilinearGridReader") 416 elif fl.endswith("pvtu"): 417 reader = vtki.new("XMLPUnstructuredGridReader") 418 elif fl.endswith(".txt") or fl.endswith(".xyz") or fl.endswith(".dat"): 419 reader = vtki.new("ParticleReader") # (format is x, y, z, scalar) 420 elif fl.endswith(".facet"): 421 reader = vtki.new("FacetReader") 422 else: 423 return None 424 425 reader.SetFileName(filename) 426 reader.Update() 427 routput = reader.GetOutput() 428 429 if not routput: 430 vedo.logger.error(f"unable to load {filename}") 431 return None 432 433 if isinstance(routput, vtki.vtkUnstructuredGrid): 434 objt = vedo.UnstructuredGrid(routput) 435 436 else: 437 objt = Mesh(routput) 438 if fl.endswith(".txt") or fl.endswith(".xyz") or fl.endswith(".dat"): 439 objt.point_size(4) 440 441 objt.filename = filename 442 objt.file_size, objt.created = file_info(filename) 443 return objt 444 445 446def download(url: str, force=False, verbose=True) -> str: 447 """ 448 Retrieve a file from a URL, save it locally and return its path. 449 Use `force=True` to force a reload and discard cached copies. 450 """ 451 if not url.startswith("https://"): 452 # assume it's a file so no need to download 453 return url 454 url = url.replace("www.dropbox", "dl.dropbox") 455 456 if "github.com" in url: 457 url = url.replace("/blob/", "/raw/") 458 459 basename = os.path.basename(url) 460 461 if "?" in basename: 462 basename = basename.split("?")[0] 463 464 home_directory = os.path.expanduser("~") 465 cachedir = os.path.join(home_directory, settings.cache_directory, "vedo") 466 fname = os.path.join(cachedir, basename) 467 # Create the directory if it does not exist 468 if not os.path.exists(cachedir): 469 os.makedirs(cachedir) 470 471 if not force and os.path.exists(fname): 472 if verbose: 473 colors.printc("reusing cached file:", fname) 474 return fname 475 476 try: 477 from urllib.request import urlopen, Request 478 req = Request(url, headers={"User-Agent": "Mozilla/5.0"}) 479 if verbose: 480 colors.printc("reading", basename, "from", url.split("/")[2][:40], "...", end="") 481 482 except ImportError: 483 import urllib2 484 import contextlib 485 urlopen = lambda url_: contextlib.closing(urllib2.urlopen(url_)) 486 req = url 487 if verbose: 488 colors.printc("reading", basename, "from", url.split("/")[2][:40], "...", end="") 489 490 with urlopen(req) as response, open(fname, "wb") as output: 491 output.write(response.read()) 492 493 if verbose: 494 colors.printc(" done.") 495 return fname 496 497 498######################################################################## 499# def download_new(url, to_local_file="", force=False, verbose=True): 500# """ 501# Downloads a file from `url` to `to_local_file` if the local copy is outdated. 502 503# Arguments: 504# url : (str) 505# The URL to download the file from. 506# to_local_file : (str) 507# The local file name to save the file to. 508# If not specified, the file name will be the same as the remote file name 509# in the directory specified by `settings.cache_directory + "/vedo"`. 510# force : (bool) 511# Force a new download even if the local file is up to date. 512# verbose : (bool) 513# Print verbose messages. 514# """ 515# if not url.startswith("https://"): 516# if os.path.exists(url): 517# # Assume the url is already the local file path 518# return url 519# else: 520# raise FileNotFoundError(f"File not found: {url}") 521 522# from datetime import datetime 523# import requests 524 525# url = url.replace("www.dropbox", "dl.dropbox") 526 527# if "github.com" in url: 528# url = url.replace("/blob/", "/raw/") 529 530# # Get the user's home directory 531# home_directory = os.path.expanduser("~") 532 533# # Define the path for the cache directory 534# cachedir = os.path.join(home_directory, settings.cache_directory, "vedo") 535 536# # Create the directory if it does not exist 537# if not os.path.exists(cachedir): 538# os.makedirs(cachedir) 539 540# if not to_local_file: 541# basename = os.path.basename(url) 542# if "?" in basename: 543# basename = basename.split("?")[0] 544# to_local_file = os.path.join(cachedir, basename) 545# if verbose: print(f"Using local file name: {to_local_file}") 546 547# # Check if the local file exists and get its last modified time 548# if os.path.exists(to_local_file): 549# to_local_file_modified_time = os.path.getmtime(to_local_file) 550# else: 551# to_local_file_modified_time = 0 552 553# # Send a HEAD request to get last modified time of the remote file 554# response = requests.head(url) 555# if 'Last-Modified' in response.headers: 556# remote_file_modified_time = datetime.strptime( 557# response.headers['Last-Modified'], '%a, %d %b %Y %H:%M:%S GMT' 558# ).timestamp() 559# else: 560# # If the Last-Modified header not available, assume file needs to be downloaded 561# remote_file_modified_time = float('inf') 562 563# # Download the file if the remote file is newer 564# if force or remote_file_modified_time > to_local_file_modified_time: 565# response = requests.get(url) 566# with open(to_local_file, 'wb') as file: 567# file.write(response.content) 568# if verbose: print(f"Downloaded file from {url} -> {to_local_file}") 569# else: 570# if verbose: print("Local file is up to date.") 571# return to_local_file 572 573 574######################################################################## 575def gunzip(filename: str) -> str: 576 """Unzip a `.gz` file to a temporary file and returns its path.""" 577 if not filename.endswith(".gz"): 578 # colors.printc("gunzip() error: file must end with .gz", c='r') 579 return filename 580 581 import gzip 582 583 tmp_file = NamedTemporaryFile(delete=False) 584 tmp_file.name = os.path.join( 585 os.path.dirname(tmp_file.name), os.path.basename(filename).replace(".gz", "") 586 ) 587 inF = gzip.open(filename, "rb") 588 with open(tmp_file.name, "wb") as outF: 589 outF.write(inF.read()) 590 inF.close() 591 return tmp_file.name 592 593######################################################################## 594def file_info(file_path: str) -> Tuple[str, str]: 595 """Return the file size and creation time of input file""" 596 siz, created = "", "" 597 if os.path.isfile(file_path): 598 f_info = os.stat(file_path) 599 num = f_info.st_size 600 for x in ["B", "KB", "MB", "GB", "TB"]: 601 if num < 1024.0: 602 break 603 num /= 1024.0 604 siz = "%3.1f%s" % (num, x) 605 created = time.ctime(os.path.getmtime(file_path)) 606 return siz, created 607 608 609################################################################### 610def loadStructuredPoints(filename, as_points=True): 611 """ 612 Load and return a `vtkStructuredPoints` object from file. 613 614 If `as_points` is True, return a `Points` object 615 instead of a `vtkStructuredPoints`. 616 """ 617 reader = vtki.new("StructuredPointsReader") 618 reader.SetFileName(filename) 619 reader.Update() 620 if as_points: 621 v2p = vtki.new("ImageToPoints") 622 v2p.SetInputData(reader.GetOutput()) 623 v2p.Update() 624 pts = Points(v2p.GetOutput()) 625 return pts 626 return reader.GetOutput() 627 628######################################################################## 629def loadStructuredGrid(filename): 630 """Load and return a `vtkStructuredGrid` object from file.""" 631 if filename.endswith(".vts"): 632 reader = vtki.new("XMLStructuredGridReader") 633 else: 634 reader = vtki.new("StructuredGridReader") 635 reader.SetFileName(filename) 636 reader.Update() 637 return reader.GetOutput() 638 639 640################################################################### 641def load3DS(filename: str) -> Assembly: 642 """Load `3DS` file format from file.""" 643 renderer = vtki.vtkRenderer() 644 renWin = vtki.vtkRenderWindow() 645 renWin.AddRenderer(renderer) 646 647 importer = vtki.new("3DSImporter") 648 importer.SetFileName(filename) 649 importer.ComputeNormalsOn() 650 importer.SetRenderWindow(renWin) 651 importer.Update() 652 653 actors = renderer.GetActors() # vtkActorCollection 654 acts = [] 655 for i in range(actors.GetNumberOfItems()): 656 a = actors.GetItemAsObject(i) 657 acts.append(a) 658 del renWin 659 660 wrapped_acts = [] 661 for a in acts: 662 try: 663 newa = Mesh(a.GetMapper().GetInput()) 664 newa.actor = a 665 wrapped_acts.append(newa) 666 # print("loaded 3DS object", [a]) 667 except: 668 print("ERROR: cannot load 3DS object part", [a]) 669 return vedo.Assembly(wrapped_acts) 670 671######################################################################## 672def loadOFF(filename: str) -> Mesh: 673 """Read the OFF file format (polygonal mesh).""" 674 with open(filename, "r", encoding="UTF-8") as f: 675 lines = f.readlines() 676 677 vertices = [] 678 faces = [] 679 NumberOfVertices = 0 680 i = -1 681 for text in lines: 682 if len(text) == 0: 683 continue 684 if text == "\n": 685 continue 686 if "#" in text: 687 continue 688 if "OFF" in text: 689 continue 690 691 ts = text.split() 692 n = len(ts) 693 694 if not NumberOfVertices and n > 1: 695 NumberOfVertices, NumberOfFaces = int(ts[0]), int(ts[1]) 696 continue 697 i += 1 698 699 if i < NumberOfVertices and n == 3: 700 x, y, z = float(ts[0]), float(ts[1]), float(ts[2]) 701 vertices.append([x, y, z]) 702 703 ids = [] 704 if NumberOfVertices <= i < (NumberOfVertices + NumberOfFaces + 1) and n > 2: 705 ids += [int(xx) for xx in ts[1:]] 706 faces.append(ids) 707 708 return Mesh(utils.buildPolyData(vertices, faces)) 709 710######################################################################## 711def loadGeoJSON(filename: str) -> Mesh: 712 """Load GeoJSON files.""" 713 jr = vtki.new("GeoJSONReader") 714 jr.SetFileName(filename) 715 jr.Update() 716 return Mesh(jr.GetOutput()) 717 718######################################################################## 719def loadDolfin(filename: str) -> Union[Mesh, "vedo.TetMesh", None]: 720 """ 721 Reads a `Fenics/Dolfin` file format (.xml or .xdmf). 722 723 Return a `Mesh` or a `TetMesh` object. 724 """ 725 try: 726 import dolfin 727 except ImportError: 728 vedo.logger.error("loadDolfin(): dolfin module not found. Install with:") 729 vedo.logger.error(" conda create -n fenics -c conda-forge fenics") 730 vedo.logger.error(" conda install conda-forge::mshr") 731 vedo.logger.error(" conda activate fenics") 732 return None 733 734 if filename.lower().endswith(".xdmf"): 735 f = dolfin.XDMFFile(filename) 736 m = dolfin.Mesh() 737 f.read(m) 738 else: 739 m = dolfin.Mesh(filename) 740 741 cells = m.cells() 742 verts = m.coordinates() 743 744 if cells.size and verts.size: 745 if len(cells[0]) == 4: # tetrahedral mesh 746 return vedo.TetMesh([verts, cells]) 747 elif len(cells[0]) == 3: # triangular mesh 748 return Mesh([verts, cells]) 749 750 return None 751 752 753######################################################################## 754def loadPVD(filename: str) -> Union[List[Any], None]: 755 """Read paraview files.""" 756 import xml.etree.ElementTree as et 757 758 tree = et.parse(filename) 759 760 dname = os.path.dirname(filename) 761 if not dname: 762 dname = "." 763 764 listofobjs = [] 765 for coll in tree.getroot(): 766 for dataset in coll: 767 fname = dataset.get("file") 768 if not fname: 769 continue 770 ob = load(dname + "/" + fname) 771 tm = dataset.get("timestep") 772 if tm: 773 ob.time = tm 774 listofobjs.append(ob) 775 if len(listofobjs) == 1: 776 return listofobjs[0] 777 if len(listofobjs) == 0: 778 return None 779 return listofobjs 780 781######################################################################## 782def loadNeutral(filename:str) -> "vedo.TetMesh": 783 """ 784 Reads a `Neutral` tetrahedral file format. 785 786 Returns an `TetMesh` object. 787 """ 788 with open(filename, "r", encoding="UTF-8") as f: 789 lines = f.readlines() 790 791 ncoords = int(lines[0]) 792 coords = [] 793 for i in range(1, ncoords + 1): 794 x, y, z = lines[i].split() 795 coords.append([float(x), float(y), float(z)]) 796 797 ntets = int(lines[ncoords + 1]) 798 idolf_tets = [] 799 for i in range(ncoords + 2, ncoords + ntets + 2): 800 text = lines[i].split() 801 v0, v1, v2, v3 = int(text[1])-1, int(text[2])-1, int(text[3])-1, int(text[4])-1 802 idolf_tets.append([v0, v1, v2, v3]) 803 804 return vedo.TetMesh([coords, idolf_tets]) 805 806######################################################################## 807def loadGmesh(filename: str) -> Mesh: 808 """Reads a `gmesh` file format. Return an `Mesh` object.""" 809 with open(filename, "r", encoding="UTF-8") as f: 810 lines = f.readlines() 811 812 nnodes = 0 813 index_nodes = 0 814 for i, line in enumerate(lines): 815 if "$Nodes" in line: 816 index_nodes = i + 1 817 nnodes = int(lines[index_nodes]) 818 break 819 node_coords = [] 820 for i in range(index_nodes + 1, index_nodes + 1 + nnodes): 821 cn = lines[i].split() 822 node_coords.append([float(cn[1]), float(cn[2]), float(cn[3])]) 823 824 nelements = 0 825 index_elements = 0 826 for i, line in enumerate(lines): 827 if "$Elements" in line: 828 index_elements = i + 1 829 nelements = int(lines[index_elements]) 830 break 831 elements = [] 832 for i in range(index_elements + 1, index_elements + 1 + nelements): 833 ele = lines[i].split() 834 elements.append([int(ele[-3]), int(ele[-2]), int(ele[-1])]) 835 836 poly = utils.buildPolyData(node_coords, elements, index_offset=1) 837 return Mesh(poly) 838 839######################################################################## 840def loadPCD(filename: str) -> Points: 841 """Return a `Mesh` made of only vertex points 842 from the `PointCloud` library file format. 843 844 Returns an `Points` object. 845 """ 846 with open(filename, "r", encoding="UTF-8") as f: 847 lines = f.readlines() 848 849 start = False 850 pts = [] 851 N, expN = 0, 0 852 for text in lines: 853 if start: 854 if N >= expN: 855 break 856 l = text.split() 857 pts.append([float(l[0]), float(l[1]), float(l[2])]) 858 N += 1 859 if not start and "POINTS" in text: 860 expN = int(text.split()[1]) 861 if not start and "DATA ascii" in text: 862 start = True 863 if expN != N: 864 vedo.logger.warning(f"Mismatch in PCD file {expN} != {len(pts)}") 865 poly = utils.buildPolyData(pts) 866 return Points(poly).point_size(4) 867 868######################################################################### 869def _from_numpy(d: dict) -> Mesh: 870 # recreate a mesh from numpy arrays 871 keys = d.keys() 872 873 points = d["points"] 874 cells = d["cells"] if "cells" in keys else None 875 lines = d["lines"] if "lines" in keys else None 876 877 msh = Mesh([points, cells, lines]) 878 879 if "pointdata" in keys and isinstance(d["pointdata"], dict): 880 for arrname, arr in d["pointdata"].items(): 881 msh.pointdata[arrname] = arr 882 if "celldata" in keys and isinstance(d["celldata"], dict): 883 for arrname, arr in d["celldata"].items(): 884 msh.celldata[arrname] = arr 885 if "metadata" in keys and isinstance(d["metadata"], dict): 886 for arrname, arr in d["metadata"].items(): 887 msh.metadata[arrname] = arr 888 889 prp = msh.properties 890 prp.SetAmbient(d['ambient']) 891 prp.SetDiffuse(d['diffuse']) 892 prp.SetSpecular(d['specular']) 893 prp.SetSpecularPower(d['specularpower']) 894 prp.SetSpecularColor(d['specularcolor']) 895 896 prp.SetInterpolation(0) 897 # prp.SetInterpolation(d['shading']) 898 899 prp.SetOpacity(d['alpha']) 900 prp.SetRepresentation(d['representation']) 901 prp.SetPointSize(d['pointsize']) 902 if d['color'] is not None: 903 msh.color(d['color']) 904 if "lighting_is_on" in d.keys(): 905 prp.SetLighting(d['lighting_is_on']) 906 # Must check keys for backwards compatibility: 907 if "linecolor" in d.keys() and d['linecolor'] is not None: 908 msh.linecolor(d['linecolor']) 909 if "backcolor" in d.keys() and d['backcolor'] is not None: 910 msh.backcolor(d['backcolor']) 911 912 if d['linewidth'] is not None: 913 msh.linewidth(d['linewidth']) 914 if "edge_visibility" in d.keys(): 915 prp.SetEdgeVisibility(d['edge_visibility']) # new 916 917 lut_list = d["LUT"] 918 lut_range = d["LUT_range"] 919 ncols = len(lut_list) 920 lut = vtki.vtkLookupTable() 921 lut.SetNumberOfTableValues(ncols) 922 lut.SetRange(lut_range) 923 for i in range(ncols): 924 r, g, b, a = lut_list[i] 925 lut.SetTableValue(i, r, g, b, a) 926 lut.Build() 927 msh.mapper.SetLookupTable(lut) 928 msh.mapper.SetScalarRange(lut_range) 929 930 try: # NEW in vedo 5.0 931 arname = d["array_name_to_color_by"] 932 msh.mapper.SetArrayName(arname) 933 msh.mapper.SetInterpolateScalarsBeforeMapping( 934 d["interpolate_scalars_before_mapping"]) 935 msh.mapper.SetUseLookupTableScalarRange( 936 d["use_lookup_table_scalar_range"]) 937 msh.mapper.SetScalarRange(d["scalar_range"]) 938 msh.mapper.SetScalarVisibility(d["scalar_visibility"]) 939 msh.mapper.SetScalarMode(d["scalar_mode"]) 940 msh.mapper.SetColorMode(d["color_mode"]) 941 if d["scalar_visibility"]: 942 if d["scalar_mode"] == 1: 943 msh.dataset.GetPointData().SetActiveScalars(arname) 944 if d["scalar_mode"] == 2: 945 msh.dataset.GetCellData().SetActiveScalars(arname) 946 947 if "texture_array" in keys and d["texture_array"] is not None: 948 # recreate a vtkTexture object from numpy arrays: 949 t = vtki.vtkTexture() 950 t.SetInterpolate(d["texture_interpolate"]) 951 t.SetRepeat(d["texture_repeat"]) 952 t.SetQuality(d["texture_quality"]) 953 t.SetColorMode(d["texture_color_mode"]) 954 t.SetMipmap(d["texture_mipmap"]) 955 t.SetBlendingMode(d["texture_blending_mode"]) 956 t.SetEdgeClamp(d["texture_edge_clamp"]) 957 t.SetBorderColor(d["texture_border_color"]) 958 msh.actor.SetTexture(t) 959 tcarray = None 960 for arrname in msh.pointdata.keys(): 961 if "Texture" in arrname or "TCoord" in arrname: 962 tcarray = arrname 963 break 964 if tcarray is not None: 965 t.SetInputData(vedo.Image(d["texture_array"]).dataset) 966 msh.pointdata.select_texture_coords(tcarray) 967 968 # print("color_mode", d["color_mode"]) 969 # print("scalar_mode", d["scalar_mode"]) 970 # print("scalar_range", d["scalar_range"]) 971 # print("scalar_visibility", d["scalar_visibility"]) 972 # print("array_name_to_color_by", arname) 973 except KeyError: 974 pass 975 976 if "time" in keys: msh.time = d["time"] 977 if "name" in keys: msh.name = d["name"] 978 # if "info" in keys: msh.info = d["info"] 979 if "filename" in keys: msh.filename = d["filename"] 980 if "pickable" in keys: msh.pickable(d["pickable"]) 981 if "dragable" in keys: msh.draggable(d["dragable"]) 982 return msh 983 984############################################################################# 985def _import_npy(fileinput: str) -> "vedo.Plotter": 986 """Import a vedo scene from numpy format.""" 987 988 fileinput = download(fileinput, verbose=False, force=True) 989 if fileinput.endswith(".npy"): 990 data = np.load(fileinput, allow_pickle=True, encoding="latin1").flatten()[0] 991 elif fileinput.endswith(".npz"): 992 data = np.load(fileinput, allow_pickle=True)["vedo_scenes"][0] 993 994 if "use_parallel_projection" in data.keys(): 995 vedo.settings.use_parallel_projection = data["use_parallel_projection"] 996 if "use_polygon_offset" in data.keys(): 997 vedo.settings.use_polygon_offset = data["use_polygon_offset"] 998 if "polygon_offset_factor" in data.keys(): 999 vedo.settings.polygon_offset_factor = data["polygon_offset_factor"] 1000 if "polygon_offset_units" in data.keys(): 1001 vedo.settings.polygon_offset_units = data["polygon_offset_units"] 1002 if "interpolate_scalars_before_mapping" in data.keys(): 1003 vedo.settings.interpolate_scalars_before_mapping = data["interpolate_scalars_before_mapping"] 1004 if "default_font" in data.keys(): 1005 vedo.settings.default_font = data["default_font"] 1006 if "use_depth_peeling" in data.keys(): 1007 vedo.settings.use_depth_peeling = data["use_depth_peeling"] 1008 1009 axes = data.pop("axes", 4) # UNUSED 1010 title = data.pop("title", "") 1011 backgrcol = data.pop("backgrcol", "white") 1012 backgrcol2 = data.pop("backgrcol2", None) 1013 cam = data.pop("camera", None) 1014 1015 if data["shape"] != (1, 1): 1016 data["size"] = "auto" # disable size 1017 1018 plt = vedo.Plotter( 1019 size=data["size"], # not necessarily a good idea to set it 1020 axes=axes, # must be zero to avoid recreating the axes 1021 title=title, 1022 bg=backgrcol, 1023 bg2=backgrcol2, 1024 ) 1025 1026 if cam: 1027 if "pos" in cam.keys(): 1028 plt.camera.SetPosition(cam["pos"]) 1029 if "focalPoint" in cam.keys(): # obsolete 1030 plt.camera.SetFocalPoint(cam["focalPoint"]) 1031 if "focal_point" in cam.keys(): 1032 plt.camera.SetFocalPoint(cam["focal_point"]) 1033 if "viewup" in cam.keys(): 1034 plt.camera.SetViewUp(cam["viewup"]) 1035 if "distance" in cam.keys(): 1036 plt.camera.SetDistance(cam["distance"]) 1037 if "clippingRange" in cam.keys(): # obsolete 1038 plt.camera.SetClippingRange(cam["clippingRange"]) 1039 if "clipping_range" in cam.keys(): 1040 plt.camera.SetClippingRange(cam["clipping_range"]) 1041 if "parallel_scale" in cam.keys(): 1042 plt.camera.SetParallelScale(cam["parallel_scale"]) 1043 1044 ############################################## 1045 objs = [] 1046 for d in data["objects"]: 1047 ### Mesh 1048 if d['type'].lower() == 'mesh': 1049 obj = _from_numpy(d) 1050 1051 ### Assembly 1052 elif d['type'].lower() == 'assembly': 1053 assacts = [] 1054 for ad in d["actors"]: 1055 assacts.append(_from_numpy(ad)) 1056 obj = Assembly(assacts) 1057 obj.SetScale(d["scale"]) 1058 obj.SetPosition(d["position"]) 1059 obj.SetOrientation(d["orientation"]) 1060 obj.SetOrigin(d["origin"]) 1061 1062 ### Volume 1063 elif d['type'].lower() == 'volume': 1064 obj = Volume(d["array"]) 1065 obj.spacing(d["spacing"]) 1066 obj.origin(d["origin"]) 1067 if "jittering" in d.keys(): obj.jittering(d["jittering"]) 1068 obj.mode(d["mode"]) 1069 obj.color(d["color"]) 1070 obj.alpha(d["alpha"]) 1071 obj.alpha_gradient(d["alphagrad"]) 1072 1073 ### TetMesh 1074 elif d['type'].lower() == 'tetmesh': 1075 raise NotImplementedError("TetMesh not supported yet") 1076 1077 ### ScalarBar2D 1078 elif d['type'].lower() == 'scalarbar2d': 1079 raise NotImplementedError("ScalarBar2D not supported yet") 1080 1081 ### Image 1082 elif d['type'].lower() == 'image': 1083 obj = Image(d["array"]) 1084 obj.alpha(d["alpha"]) 1085 obj.actor.SetScale(d["scale"]) 1086 obj.actor.SetPosition(d["position"]) 1087 obj.actor.SetOrientation(d["orientation"]) 1088 obj.actor.SetOrigin(d["origin"]) 1089 1090 ### Text2D 1091 elif d['type'].lower() == 'text2d': 1092 obj = vedo.shapes.Text2D(d["text"], font=d["font"], c=d["color"]) 1093 obj.pos(d["position"]).size(d["size"]) 1094 obj.background(d["bgcol"], d["alpha"]) 1095 if d["frame"]: 1096 obj.frame(d["bgcol"]) 1097 1098 else: 1099 obj = None 1100 # vedo.logger.warning(f"Cannot import object {d}") 1101 pass 1102 1103 if obj: 1104 keys = d.keys() 1105 if "time" in keys: obj.time = d["time"] 1106 if "name" in keys: obj.name = d["name"] 1107 # if "info" in keys: obj.info = d["info"] 1108 if "filename" in keys: obj.filename = d["filename"] 1109 objs.append(obj) 1110 1111 plt.add(objs) 1112 plt.resetcam = False 1113 return plt 1114 1115########################################################### 1116def loadImageData(filename: str) -> Union[vtki.vtkImageData, None]: 1117 """Read and return a `vtkImageData` object from file.""" 1118 if ".tif" in filename.lower(): 1119 reader = vtki.new("TIFFReader") 1120 # print("GetOrientationType ", reader.GetOrientationType()) 1121 reader.SetOrientationType(vedo.settings.tiff_orientation_type) 1122 elif ".slc" in filename.lower(): 1123 reader = vtki.new("SLCReader") 1124 if not reader.CanReadFile(filename): 1125 vedo.logger.error(f"sorry, bad SLC file {filename}") 1126 return None 1127 elif ".vti" in filename.lower(): 1128 reader = vtki.new("XMLImageDataReader") 1129 elif ".mhd" in filename.lower(): 1130 reader = vtki.new("MetaImageReader") 1131 elif ".dem" in filename.lower(): 1132 reader = vtki.new("DEMReader") 1133 elif ".nii" in filename.lower(): 1134 reader = vtki.new("NIFTIImageReader") 1135 elif ".nrrd" in filename.lower(): 1136 reader = vtki.new("NrrdReader") 1137 if not reader.CanReadFile(filename): 1138 vedo.logger.error(f"sorry, bad NRRD file {filename}") 1139 return None 1140 else: 1141 vedo.logger.error(f"cannot read file {filename}") 1142 return None 1143 reader.SetFileName(filename) 1144 reader.Update() 1145 return reader.GetOutput() 1146 1147########################################################### 1148def write(objct: Any, fileoutput: str, binary=True) -> Any: 1149 """ 1150 Write object to file. Same as `save()`. 1151 1152 Supported extensions are: 1153 1154 - `vtk, vti, ply, obj, stl, byu, vtp, vti, mhd, xyz, xml, tif, png, bmp` 1155 """ 1156 obj = objct.dataset 1157 1158 try: 1159 # check if obj is a Mesh.actor and has a transform 1160 M = objct.actor.GetMatrix() 1161 if M and not M.IsIdentity(): 1162 obj = objct.apply_transform_from_actor() 1163 obj = objct.dataset 1164 vedo.logger.info( 1165 f"object '{objct.name}' " 1166 "was manually moved. Writing uses current position." 1167 ) 1168 except: 1169 pass 1170 1171 fr = fileoutput.lower() 1172 if fr.endswith(".vtk"): 1173 writer = vtki.new("DataSetWriter") 1174 elif fr.endswith(".ply"): 1175 writer = vtki.new("PLYWriter") 1176 writer.AddComment("PLY file generated by vedo") 1177 lut = objct.mapper.GetLookupTable() 1178 if lut: 1179 pscal = obj.GetPointData().GetScalars() 1180 if not pscal: 1181 pscal = obj.GetCellData().GetScalars() 1182 if pscal and pscal.GetName(): 1183 writer.SetArrayName(pscal.GetName()) 1184 writer.SetLookupTable(lut) 1185 elif fr.endswith(".stl"): 1186 writer = vtki.new("STLWriter") 1187 elif fr.endswith(".vtp"): 1188 writer = vtki.new("XMLPolyDataWriter") 1189 elif fr.endswith(".vtu"): 1190 writer = vtki.new("XMLUnstructuredGridWriter") 1191 elif fr.endswith(".xyz"): 1192 writer = vtki.new("SimplePointsWriter") 1193 elif fr.endswith(".facet"): 1194 writer = vtki.new("FacetWriter") 1195 elif fr.endswith(".vti"): 1196 writer = vtki.new("XMLImageDataWriter") 1197 elif fr.endswith(".vtr"): 1198 writer = vtki.new("XMLRectilinearGridWriter") 1199 elif fr.endswith(".vtm"): 1200 g = vtki.new("MultiBlockDataGroupFilter") 1201 for ob in objct: 1202 try: 1203 g.AddInputData(ob) 1204 except TypeError: 1205 vedo.logger.warning("cannot save object of type", type(ob)) 1206 g.Update() 1207 mb = g.GetOutputDataObject(0) 1208 wri = vtki.new("vtkXMLMultiBlockDataWriter") 1209 wri.SetInputData(mb) 1210 wri.SetFileName(fileoutput) 1211 wri.Write() 1212 return objct 1213 elif fr.endswith(".mhd"): 1214 writer = vtki.new("MetaImageWriter") 1215 elif fr.endswith(".nii"): 1216 writer = vtki.new("NIFTIImageWriter") 1217 elif fr.endswith(".png"): 1218 writer = vtki.new("PNGWriter") 1219 elif fr.endswith(".jpg"): 1220 writer = vtki.new("JPEGWriter") 1221 elif fr.endswith(".bmp"): 1222 writer = vtki.new("BMPWriter") 1223 elif fr.endswith(".tif") or fr.endswith(".tiff"): 1224 writer = vtki.new("TIFFWriter") 1225 writer.SetFileDimensionality(len(obj.GetDimensions())) 1226 elif fr.endswith(".obj"): 1227 with open(fileoutput, "w", encoding="UTF-8") as outF: 1228 outF.write("# OBJ file format with ext .obj\n") 1229 outF.write("# File generated by vedo\n") 1230 1231 for p in objct.vertices: 1232 outF.write("v {:.5g} {:.5g} {:.5g}\n".format(*p)) 1233 1234 ptxt = objct.dataset.GetPointData().GetTCoords() 1235 if ptxt: 1236 ntxt = utils.vtk2numpy(ptxt) 1237 for vt in ntxt: 1238 outF.write("vt " + str(vt[0]) + " " + str(vt[1]) + " 0.0\n") 1239 1240 if isinstance(objct, Mesh): 1241 for i, f in enumerate(objct.cells): 1242 fs = "" 1243 for fi in f: 1244 if ptxt: 1245 fs += f" {fi+1}/{fi+1}" 1246 else: 1247 fs += f" {fi+1}" 1248 outF.write(f"f{fs}\n") 1249 1250 for l in objct.lines: 1251 ls = "" 1252 for li in l: 1253 ls += str(li + 1) + " " 1254 outF.write(f"l {ls}\n") 1255 return objct 1256 1257 elif fr.endswith(".xml"): # write tetrahedral dolfin xml 1258 vertices = objct.vertices.astype(str) 1259 faces = np.array(objct.cells).astype(str) 1260 ncoords = vertices.shape[0] 1261 with open(fileoutput, "w", encoding="UTF-8") as outF: 1262 outF.write('<?xml version="1.0" encoding="UTF-8"?>\n') 1263 outF.write('<dolfin xmlns:dolfin="http://www.fenicsproject.org">\n') 1264 1265 if len(faces[0]) == 4: # write tetrahedral mesh 1266 ntets = faces.shape[0] 1267 outF.write(' <mesh celltype="tetrahedron" dim="3">\n') 1268 outF.write(' <vertices size="' + str(ncoords) + '">\n') 1269 for i in range(ncoords): 1270 x, y, z = vertices[i] 1271 outF.write(' <vertex index="'+str(i)+'" x="'+x+'" y="'+y+'" z="'+z+'"/>\n') 1272 outF.write(' </vertices>\n') 1273 outF.write(' <cells size="' + str(ntets) + '">\n') 1274 for i in range(ntets): 1275 v0, v1, v2, v3 = faces[i] 1276 outF.write(' <tetrahedron index="'+str(i) 1277 + '" v0="'+v0+'" v1="'+v1+'" v2="'+v2+'" v3="'+v3+'"/>\n') 1278 1279 elif len(faces[0]) == 3: # write triangle mesh 1280 ntri = faces.shape[0] 1281 outF.write(' <mesh celltype="triangle" dim="2">\n') 1282 outF.write(' <vertices size="' + str(ncoords) + '">\n') 1283 for i in range(ncoords): 1284 x, y, _ = vertices[i] 1285 outF.write(' <vertex index="'+str(i)+'" x="'+x+'" y="'+y+'"/>\n') 1286 outF.write(' </vertices>\n') 1287 outF.write(' <cells size="' + str(ntri) + '">\n') 1288 for i in range(ntri): 1289 v0, v1, v2 = faces[i] 1290 outF.write(' <triangle index="'+str(i)+'" v0="'+v0+'" v1="'+v1+'" v2="'+v2+'"/>\n') 1291 1292 outF.write(" </cells>\n") 1293 outF.write(" </mesh>\n") 1294 outF.write("</dolfin>\n") 1295 return objct 1296 1297 else: 1298 vedo.logger.error(f"Unknown format {fileoutput}, file not saved") 1299 return objct 1300 1301 try: 1302 if binary: 1303 writer.SetFileTypeToBinary() 1304 else: 1305 writer.SetFileTypeToASCII() 1306 except AttributeError: 1307 pass 1308 1309 try: 1310 writer.SetInputData(obj) 1311 writer.SetFileName(fileoutput) 1312 writer.Write() 1313 except: 1314 vedo.logger.error(f"could not save {fileoutput}") 1315 return objct 1316 1317def save(obj: Any, fileoutput="out.png", binary=True) -> Any: 1318 """Save an object to file. Same as `write()`.""" 1319 return write(obj, fileoutput, binary) 1320 1321def read(obj: Any, unpack=True, force=False) -> Any: 1322 """Read an object from file. Same as `load()`.""" 1323 return load(obj, unpack, force) 1324 1325############################################################################### 1326def export_window(fileoutput: str, binary=False, plt=None) -> "vedo.Plotter": 1327 """ 1328 Exporter which writes out the rendered scene into an HTML, X3D or Numpy file. 1329 1330 Example: 1331 - [export_x3d.py](https://github.com/marcomusy/vedo/tree/master/examples/other/export_x3d.py) 1332 1333 Check out the HTML generated webpage [here](https://vedo.embl.es/examples/embryo.html). 1334 1335 <img src='https://user-images.githubusercontent.com/32848391/57160341-c6ffbd80-6de8-11e9-95ff-7215ce642bc5.jpg' width="600"/> 1336 1337 .. note:: 1338 the rendering window can also be exported to `numpy` file `scene.npz` 1339 by pressing `E` key at any moment during visualization. 1340 """ 1341 if plt is None: 1342 plt = vedo.plotter_instance 1343 1344 fr = fileoutput.lower() 1345 #################################################################### 1346 if fr.endswith(".npy") or fr.endswith(".npz"): 1347 _export_npy(plt, fileoutput) 1348 1349 #################################################################### 1350 elif fr.endswith(".x3d"): 1351 # obj = plt.get_actors() 1352 # if plt.axes_instances: 1353 # obj.append(plt.axes_instances[0]) 1354 1355 # for a in obj: 1356 # if isinstance(a, Assembly): 1357 # plt.remove(a) 1358 # plt.add(a.unpack()) 1359 1360 plt.render() 1361 1362 exporter = vtki.new("X3DExporter") 1363 exporter.SetBinary(binary) 1364 exporter.FastestOff() 1365 exporter.SetInput(plt.window) 1366 exporter.SetFileName(fileoutput) 1367 # exporter.WriteToOutputStringOn() 1368 exporter.Update() 1369 exporter.Write() 1370 1371 wsize = plt.window.GetSize() 1372 x3d_html = _x3d_html_template.replace("~fileoutput", fileoutput) 1373 x3d_html = x3d_html.replace("~width", str(wsize[0])) 1374 x3d_html = x3d_html.replace("~height", str(wsize[1])) 1375 with open(fileoutput.replace(".x3d", ".html"), "w", encoding="UTF-8") as outF: 1376 outF.write(x3d_html) 1377 1378 #################################################################### 1379 elif fr.endswith(".html"): 1380 savebk = vedo.notebook_backend 1381 vedo.notebook_backend = "k3d" 1382 vedo.settings.default_backend = "k3d" 1383 # acts = plt.get_actors() 1384 plt = vedo.backends.get_notebook_backend(plt.objects) 1385 1386 with open(fileoutput, "w", encoding="UTF-8") as fp: 1387 fp.write(plt.get_snapshot()) 1388 1389 vedo.notebook_backend = savebk 1390 vedo.settings.default_backend = savebk 1391 1392 else: 1393 vedo.logger.error(f"export extension {fr.split('.')[-1]} is not supported") 1394 1395 return plt 1396 1397######################################################################### 1398def _to_numpy(act: Any) -> dict: 1399 """Encode a vedo object to numpy format.""" 1400 1401 ######################################################## 1402 def _fillcommon(obj, adict): 1403 adict["filename"] = obj.filename 1404 adict["name"] = obj.name 1405 adict["time"] = obj.time 1406 adict["rendered_at"] = obj.rendered_at 1407 try: 1408 adict["transform"] = obj.transform.matrix 1409 except AttributeError: 1410 adict["transform"] = np.eye(4) 1411 1412 #################################################################### 1413 try: 1414 obj = act.retrieve_object() 1415 except AttributeError: 1416 obj = act 1417 1418 adict = {} 1419 adict["type"] = "unknown" 1420 1421 ######################################################## Points/Mesh 1422 if isinstance(obj, (Points, vedo.UnstructuredGrid)): 1423 adict["type"] = "Mesh" 1424 _fillcommon(obj, adict) 1425 1426 if isinstance(obj, vedo.UnstructuredGrid): 1427 # adict["type"] = "UnstructuredGrid" 1428 # adict["cells"] = obj.cells_as_flat_array 1429 poly = obj._actor.GetMapper().GetInput() 1430 mapper = obj._actor.GetMapper() 1431 else: 1432 poly = obj.dataset 1433 mapper = obj.mapper 1434 1435 adict["points"] = obj.vertices.astype(float) 1436 1437 adict["cells"] = None 1438 if poly.GetNumberOfPolys(): 1439 adict["cells"] = obj.cells_as_flat_array 1440 1441 adict["lines"] = None 1442 if poly.GetNumberOfLines(): 1443 adict["lines"] = obj.lines#_as_flat_array 1444 1445 adict["pointdata"] = {} 1446 for iname in obj.pointdata.keys(): 1447 if "normals" in iname.lower(): 1448 continue 1449 adict["pointdata"][iname] = obj.pointdata[iname] 1450 1451 adict["celldata"] = {} 1452 for iname in obj.celldata.keys(): 1453 if "normals" in iname.lower(): 1454 continue 1455 adict["celldata"][iname] = obj.celldata[iname] 1456 1457 adict["metadata"] = {} 1458 for iname in obj.metadata.keys(): 1459 adict["metadata"][iname] = obj.metadata[iname] 1460 1461 # NEW in vedo 5.0 1462 adict["scalar_mode"] = mapper.GetScalarMode() 1463 adict["array_name_to_color_by"] = mapper.GetArrayName() 1464 adict["color_mode"] = mapper.GetColorMode() 1465 adict["interpolate_scalars_before_mapping"] = mapper.GetInterpolateScalarsBeforeMapping() 1466 adict["use_lookup_table_scalar_range"] = mapper.GetUseLookupTableScalarRange() 1467 adict["scalar_range"] = mapper.GetScalarRange() 1468 adict["scalar_visibility"] = mapper.GetScalarVisibility() 1469 adict["pickable"] = obj.actor.GetPickable() 1470 adict["dragable"] = obj.actor.GetDragable() 1471 1472 # adict["color_map_colors"] = mapper.GetColorMapColors() #vtkUnsignedCharArray 1473 # adict["color_coordinates"] = mapper.GetColorCoordinates() #vtkFloatArray 1474 texmap = mapper.GetColorTextureMap() #vtkImageData 1475 if texmap: 1476 adict["color_texture_map"] = vedo.Image(texmap).tonumpy() 1477 # print("color_texture_map", adict["color_texture_map"].shape) 1478 1479 adict["texture_array"] = None 1480 texture = obj.actor.GetTexture() 1481 if texture: 1482 adict["texture_array"] = vedo.Image(texture.GetInput()).tonumpy() 1483 adict["texture_interpolate"] = texture.GetInterpolate() 1484 adict["texture_repeat"] = texture.GetRepeat() 1485 adict["texture_quality"] = texture.GetQuality() 1486 adict["texture_color_mode"] = texture.GetColorMode() 1487 adict["texture_mipmap"] = texture.GetMipmap() 1488 adict["texture_blending_mode"] = texture.GetBlendingMode() 1489 adict["texture_edge_clamp"] = texture.GetEdgeClamp() 1490 adict["texture_border_color"] = texture.GetBorderColor() 1491 # print("tonumpy: texture", obj.name, adict["texture_array"].shape) 1492 1493 adict["LUT"] = None 1494 adict["LUT_range"] = None 1495 lut = mapper.GetLookupTable() 1496 if lut: 1497 nlut = lut.GetNumberOfTableValues() 1498 lutvals = [] 1499 for i in range(nlut): 1500 v4 = lut.GetTableValue(i) # (r, g, b, alpha) 1501 lutvals.append(v4) 1502 adict["LUT"] = np.array(lutvals, dtype=np.float32) 1503 adict["LUT_range"] = np.array(lut.GetRange()) 1504 1505 prp = obj.properties 1506 adict["alpha"] = prp.GetOpacity() 1507 adict["representation"] = prp.GetRepresentation() 1508 adict["pointsize"] = prp.GetPointSize() 1509 1510 adict["linecolor"] = None 1511 adict["linewidth"] = None 1512 adict["edge_visibility"] = prp.GetEdgeVisibility() # new in vedo 5.0 1513 if prp.GetEdgeVisibility(): 1514 adict["linewidth"] = prp.GetLineWidth() 1515 adict["linecolor"] = prp.GetEdgeColor() 1516 1517 adict["ambient"] = prp.GetAmbient() 1518 adict["diffuse"] = prp.GetDiffuse() 1519 adict["specular"] = prp.GetSpecular() 1520 adict["specularpower"] = prp.GetSpecularPower() 1521 adict["specularcolor"] = prp.GetSpecularColor() 1522 adict["shading"] = prp.GetInterpolation() # flat phong..: 1523 adict["color"] = prp.GetColor() 1524 adict["lighting_is_on"] = prp.GetLighting() 1525 adict["backcolor"] = None 1526 if obj.actor.GetBackfaceProperty(): 1527 adict["backcolor"] = obj.actor.GetBackfaceProperty().GetColor() 1528 1529 ######################################################## Volume 1530 elif isinstance(obj, Volume): 1531 adict["type"] = "Volume" 1532 _fillcommon(obj, adict) 1533 adict["array"] = obj.tonumpy() 1534 adict["mode"] = obj.mode() 1535 adict["spacing"] = obj.spacing() 1536 adict["origin"] = obj.origin() 1537 1538 prp = obj.properties 1539 ctf = prp.GetRGBTransferFunction() 1540 otf = prp.GetScalarOpacity() 1541 gotf = prp.GetGradientOpacity() 1542 smin, smax = ctf.GetRange() 1543 xs = np.linspace(smin, smax, num=256, endpoint=True) 1544 cols, als, algrs = [], [], [] 1545 for x in xs: 1546 cols.append(ctf.GetColor(x)) 1547 als.append(otf.GetValue(x)) 1548 if gotf: 1549 algrs.append(gotf.GetValue(x)) 1550 adict["color"] = cols 1551 adict["alpha"] = als 1552 adict["alphagrad"] = algrs 1553 1554 ######################################################## Image 1555 elif isinstance(obj, Image): 1556 adict["type"] = "Image" 1557 _fillcommon(obj, adict) 1558 adict["array"] = obj.tonumpy() 1559 adict["scale"] = obj.actor.GetScale() 1560 adict["position"] = obj.actor.GetPosition() 1561 adict["orientation"] = obj.actor.GetOrientation() 1562 adict['origin'] = obj.actor.GetOrigin() 1563 adict["alpha"] = obj.alpha() 1564 1565 ######################################################## Text2D 1566 elif isinstance(obj, vedo.Text2D): 1567 adict["type"] = "Text2D" 1568 adict["rendered_at"] = obj.rendered_at 1569 adict["text"] = obj.text() 1570 adict["position"] = obj.GetPosition() 1571 adict["color"] = obj.properties.GetColor() 1572 adict["font"] = obj.fontname 1573 adict["size"] = obj.properties.GetFontSize() / 22.5 1574 adict["bgcol"] = obj.properties.GetBackgroundColor() 1575 adict["alpha"] = obj.properties.GetBackgroundOpacity() 1576 adict["frame"] = obj.properties.GetFrame() 1577 1578 else: 1579 # vedo.logger.warning(f"to_numpy: cannot export object of type {type(obj)}") 1580 pass 1581 1582 return adict 1583 1584 1585######################################################################### 1586def _export_npy(plt, fileoutput="scene.npz") -> None: 1587 1588 sdict = {} 1589 sdict["shape"] = plt.shape 1590 sdict["sharecam"] = plt.sharecam 1591 sdict["camera"] = dict( 1592 pos=plt.camera.GetPosition(), 1593 focal_point=plt.camera.GetFocalPoint(), 1594 viewup=plt.camera.GetViewUp(), 1595 distance=plt.camera.GetDistance(), 1596 clipping_range=plt.camera.GetClippingRange(), 1597 parallel_scale=plt.camera.GetParallelScale(), 1598 ) 1599 sdict["position"] = plt.pos 1600 sdict["size"] = plt.size 1601 sdict["axes"] = 0 1602 sdict["title"] = plt.title 1603 sdict["backgrcol"] = colors.get_color(plt.renderer.GetBackground()) 1604 sdict["backgrcol2"] = None 1605 if plt.renderer.GetGradientBackground(): 1606 sdict["backgrcol2"] = plt.renderer.GetBackground2() 1607 sdict["use_depth_peeling"] = plt.renderer.GetUseDepthPeeling() 1608 sdict["use_parallel_projection"] = plt.camera.GetParallelProjection() 1609 sdict["default_font"] = vedo.settings.default_font 1610 1611 sdict["objects"] = [] 1612 1613 actors = plt.get_actors(include_non_pickables=True) 1614 # this ^ also retrieves Actors2D 1615 allobjs = [] 1616 for i, a in enumerate(actors): 1617 1618 if not a.GetVisibility(): 1619 continue 1620 1621 try: 1622 ob = a.retrieve_object() 1623 # print("get_actors",[ob], ob.name) 1624 if isinstance(ob, Assembly): 1625 asse_scale = ob.GetScale() 1626 asse_pos = ob.GetPosition() 1627 asse_ori = ob.GetOrientation() 1628 asse_org = ob.GetOrigin() 1629 for elem in ob.unpack(): 1630 elem.name = f"ASSEMBLY{i}_{ob.name}_{elem.name}" 1631 # elem.info.update({"assembly": ob.name}) # TODO 1632 # elem.info.update({"assembly_scale": asse_scale}) 1633 # elem.info.update({"assembly_position": asse_pos}) 1634 # elem.info.update({"assembly_orientation": asse_ori}) 1635 # elem.info.update({"assembly_origin": asse_org}) 1636 elem.metadata["assembly"] = ob.name 1637 elem.metadata["assembly_scale"] = asse_scale 1638 elem.metadata["assembly_position"] = asse_pos 1639 elem.metadata["assembly_orientation"] = asse_ori 1640 elem.metadata["assembly_origin"] = asse_org 1641 allobjs.append(elem) 1642 else: 1643 allobjs.append(ob) 1644 1645 except AttributeError: 1646 # print() 1647 # vedo.logger.warning(f"Cannot retrieve object of type {type(a)}") 1648 pass 1649 1650 for a in allobjs: 1651 # print("to_numpy(): dumping", [a], a.name) 1652 # try: 1653 npobj = _to_numpy(a) 1654 sdict["objects"].append(npobj) 1655 # except AttributeError: 1656 # vedo.logger.warning(f"Cannot export object of type {type(a)}") 1657 1658 if fileoutput.endswith(".npz"): 1659 np.savez_compressed(fileoutput, vedo_scenes=[sdict]) 1660 else: 1661 np.save(fileoutput, [sdict]) 1662 1663 1664######################################################################## 1665def import_window(fileinput: str) -> Union["vedo.Plotter", None]: 1666 """ 1667 Import a whole scene from a Numpy NPZ file. 1668 1669 Returns: 1670 `vedo.Plotter` instance 1671 """ 1672 if fileinput.endswith(".npy") or fileinput.endswith(".npz"): 1673 return _import_npy(fileinput) 1674 1675 # elif ".obj" in fileinput.lower(): 1676 # meshes = load_obj(fileinput, mtl_file, texture_path) 1677 # plt = vedo.Plotter() 1678 # plt.add(meshes) 1679 # return plt 1680 1681 # elif fileinput.endswith(".h5") or fileinput.endswith(".hdf5"): 1682 # return _import_hdf5(fileinput) # in store/file_io_HDF5.py 1683 1684 return None 1685 1686 1687def load_obj(fileinput: str, mtl_file=None, texture_path=None) -> List[Mesh]: 1688 """ 1689 Import a set of meshes from a OBJ wavefront file. 1690 1691 Arguments: 1692 mtl_file : (str) 1693 MTL file for OBJ wavefront files 1694 texture_path : (str) 1695 path of the texture files directory 1696 1697 Returns: 1698 `list(Mesh)` 1699 """ 1700 window = vtki.vtkRenderWindow() 1701 window.SetOffScreenRendering(1) 1702 renderer = vtki.vtkRenderer() 1703 window.AddRenderer(renderer) 1704 1705 importer = vtki.new("OBJImporter") 1706 importer.SetFileName(fileinput) 1707 if mtl_file is None: 1708 mtl_file = fileinput.replace(".obj", ".mtl").replace(".OBJ", ".MTL") 1709 if os.path.isfile(mtl_file): 1710 importer.SetFileNameMTL(mtl_file) 1711 if texture_path is None: 1712 texture_path = fileinput.replace(".obj", ".txt").replace(".OBJ", ".TXT") 1713 # since the texture_path may be a directory which contains textures 1714 if os.path.exists(texture_path): 1715 importer.SetTexturePath(texture_path) 1716 importer.SetRenderWindow(window) 1717 importer.Update() 1718 1719 actors = renderer.GetActors() 1720 actors.InitTraversal() 1721 objs = [] 1722 for _ in range(actors.GetNumberOfItems()): 1723 vactor = actors.GetNextActor() 1724 msh = Mesh(vactor) 1725 msh.name = "OBJMesh" 1726 tx = vactor.GetTexture() 1727 if tx: 1728 msh.texture(tx) 1729 objs.append(msh) 1730 return objs 1731 1732 1733########################################################## 1734def screenshot(filename="screenshot.png", scale=1, asarray=False) -> Union["vedo.Plotter", np.ndarray, None]: 1735 """ 1736 Save a screenshot of the current rendering window. 1737 1738 Alternatively, press key `Shift-S` in the rendering window to save a screenshot. 1739 You can also use keyword `screenshot` in `show(..., screenshot="pic.png")`. 1740 1741 Arguments: 1742 scale : (int) 1743 Set image magnification as an integer multiplicative factor. 1744 E.g. setting a magnification of 2 produces an image twice as large, 1745 but 10x slower to generate. 1746 asarray : (bool) 1747 Return a numpy array of the image 1748 """ 1749 # print("calling screenshot", filename, scale, asarray) 1750 1751 if not vedo.plotter_instance or not vedo.plotter_instance.window: 1752 # vedo.logger.error("in screenshot(), rendering window is not present, skip.") 1753 return vedo.plotter_instance ########## 1754 1755 if asarray and scale == 1 and not vedo.plotter_instance.offscreen: 1756 nx, ny = vedo.plotter_instance.window.GetSize() 1757 arr = vtki.vtkUnsignedCharArray() 1758 vedo.plotter_instance.window.GetRGBACharPixelData(0, 0, nx-1, ny-1, 0, arr) 1759 narr = vedo.vtk2numpy(arr).T[:3].T.reshape([ny, nx, 3]) 1760 narr = np.flip(narr, axis=0) 1761 return narr ########## 1762 1763 filename = str(filename) 1764 1765 if filename.endswith(".pdf"): 1766 writer = vtki.new("GL2PSExporter") 1767 writer.SetRenderWindow(vedo.plotter_instance.window) 1768 writer.Write3DPropsAsRasterImageOff() 1769 writer.SilentOn() 1770 writer.SetSortToBSP() 1771 writer.SetFileFormatToPDF() 1772 writer.SetFilePrefix(filename.replace(".pdf", "")) 1773 writer.Write() 1774 return vedo.plotter_instance ########## 1775 1776 elif filename.endswith(".svg"): 1777 writer = vtki.new("GL2PSExporter") 1778 writer.SetRenderWindow(vedo.plotter_instance.window) 1779 writer.Write3DPropsAsRasterImageOff() 1780 writer.SilentOn() 1781 writer.SetSortToBSP() 1782 writer.SetFileFormatToSVG() 1783 writer.SetFilePrefix(filename.replace(".svg", "")) 1784 writer.Write() 1785 return vedo.plotter_instance ########## 1786 1787 elif filename.endswith(".eps"): 1788 writer = vtki.new("GL2PSExporter") 1789 writer.SetRenderWindow(vedo.plotter_instance.window) 1790 writer.Write3DPropsAsRasterImageOff() 1791 writer.SilentOn() 1792 writer.SetSortToBSP() 1793 writer.SetFileFormatToEPS() 1794 writer.SetFilePrefix(filename.replace(".eps", "")) 1795 writer.Write() 1796 return vedo.plotter_instance ########## 1797 1798 if settings.screeshot_large_image: 1799 w2if = vtki.new("RenderLargeImage") 1800 w2if.SetInput(vedo.plotter_instance.renderer) 1801 w2if.SetMagnification(scale) 1802 else: 1803 w2if = vtki.new("WindowToImageFilter") 1804 w2if.SetInput(vedo.plotter_instance.window) 1805 if hasattr(w2if, "SetScale"): 1806 w2if.SetScale(int(scale), int(scale)) 1807 if settings.screenshot_transparent_background: 1808 w2if.SetInputBufferTypeToRGBA() 1809 w2if.ReadFrontBufferOff() # read from the back buffer 1810 w2if.Update() 1811 1812 if asarray: 1813 pd = w2if.GetOutput().GetPointData() 1814 npdata = utils.vtk2numpy(pd.GetArray("ImageScalars")) 1815 npdata = npdata[:, [0, 1, 2]] 1816 ydim, xdim, _ = w2if.GetOutput().GetDimensions() 1817 npdata = npdata.reshape([xdim, ydim, -1]) 1818 npdata = np.flip(npdata, axis=0) 1819 return npdata ########################### 1820 1821 # elif settings.default_backend == "2d" and vedo.notebook_plotter: 1822 # vedo.notebook_plotter.save(filename) # a PIL Image 1823 # return vedo.notebook_plotter ########## 1824 1825 if filename.lower().endswith(".png"): 1826 writer = vtki.new("PNGWriter") 1827 writer.SetFileName(filename) 1828 writer.SetInputData(w2if.GetOutput()) 1829 writer.Write() 1830 elif filename.lower().endswith(".jpg") or filename.lower().endswith(".jpeg"): 1831 writer = vtki.new("JPEGWriter") 1832 writer.SetFileName(filename) 1833 writer.SetInputData(w2if.GetOutput()) 1834 writer.Write() 1835 else: # add .png 1836 writer = vtki.new("PNGWriter") 1837 writer.SetFileName(filename + ".png") 1838 writer.SetInputData(w2if.GetOutput()) 1839 writer.Write() 1840 return vedo.plotter_instance 1841 1842 1843def ask(*question, **kwarg) -> str: 1844 """ 1845 Ask a question from command line. Return the answer as a string. 1846 See function `colors.printc()` for the description of the keyword options. 1847 1848 Arguments: 1849 options : (list) 1850 a python list of possible answers to choose from. 1851 default : (str) 1852 the default answer when just hitting return. 1853 1854 Example: 1855 ```python 1856 import vedo 1857 res = vedo.ask("Continue?", options=['Y','n'], default='Y', c='g') 1858 print(res) 1859 ``` 1860 """ 1861 kwarg.update({"end": " "}) 1862 if "invert" not in kwarg: 1863 kwarg.update({"invert": True}) 1864 if "box" in kwarg: 1865 kwarg.update({"box": ""}) 1866 1867 options = kwarg.pop("options", []) 1868 default = kwarg.pop("default", "") 1869 if options: 1870 opt = "[" 1871 for o in options: 1872 opt += o + "/" 1873 opt = opt[:-1] + "]" 1874 colors.printc(*question, opt, **kwarg) 1875 else: 1876 colors.printc(*question, **kwarg) 1877 1878 try: 1879 resp = input() 1880 except Exception: 1881 resp = "" 1882 return resp 1883 1884 if options: 1885 if resp not in options: 1886 if default and str(repr(resp)) == "''": 1887 return default 1888 colors.printc("Please choose one option in:", opt, italic=True, bold=False) 1889 kwarg["options"] = options 1890 return ask(*question, **kwarg) # ask again 1891 return resp 1892 1893 1894############################################################################################## 1895class Video: 1896 """ 1897 Generate a video from a rendering window. 1898 """ 1899 1900 def __init__(self, name="movie.mp4", duration=None, fps=24, backend="imageio"): 1901 """ 1902 Class to generate a video from the specified rendering window. 1903 Program `ffmpeg` is used to create video from each generated frame. 1904 1905 Arguments: 1906 name : (str) 1907 name of the output file. 1908 fps : (int) 1909 set the number of frames per second. 1910 duration : (float) 1911 set the total `duration` of the video and recalculates `fps` accordingly. 1912 backend : (str) 1913 the backend engine to be used `['imageio', 'ffmpeg', 'cv']` 1914 1915 Examples: 1916 - [make_video.py](https://github.com/marcomusy/vedo/tree/master/examples/other/make_video.py) 1917 1918 ![](https://user-images.githubusercontent.com/32848391/50739007-2bfc2b80-11da-11e9-97e6-620a3541a6fa.jpg) 1919 """ 1920 self.name = name 1921 self.duration = duration 1922 self.backend = backend 1923 self.fps = float(fps) 1924 self.command = "ffmpeg -loglevel panic -y -r" 1925 self.options = "-b:v 8000k" 1926 1927 self.frames = [] 1928 self.tmp_dir = TemporaryDirectory() 1929 self.get_filename = lambda x: os.path.join(self.tmp_dir.name, x) 1930 colors.printc(":video: Video file", self.name, "is open... ", c="m", end="") 1931 1932 def add_frame(self) -> "Video": 1933 """Add frame to current video.""" 1934 fr = self.get_filename(str(len(self.frames)) + ".png") 1935 screenshot(fr) 1936 self.frames.append(fr) 1937 return self 1938 1939 def pause(self, pause=0) -> "Video": 1940 """Insert a `pause`, in seconds.""" 1941 fr = self.frames[-1] 1942 n = int(self.fps * pause) 1943 for _ in range(n): 1944 fr2 = self.get_filename(str(len(self.frames)) + ".png") 1945 self.frames.append(fr2) 1946 os.system("cp -f %s %s" % (fr, fr2)) 1947 return self 1948 1949 def action(self, elevation=(0, 80), azimuth=(0, 359), cameras=(), resetcam=False) -> "Video": 1950 """ 1951 Automatic shooting of a static scene by specifying rotation and elevation ranges. 1952 1953 Arguments: 1954 elevation : list 1955 initial and final elevation angles 1956 azimuth_range : list 1957 initial and final azimuth angles 1958 cameras : list 1959 list of cameras to go through, each camera can be dictionary or a vtkCamera 1960 """ 1961 if not self.duration: 1962 self.duration = 5 1963 1964 plt = vedo.plotter_instance 1965 if not plt: 1966 vedo.logger.error("No vedo plotter found, cannot make video.") 1967 return self 1968 n = int(self.fps * self.duration) 1969 1970 cams = [] 1971 for cm in cameras: 1972 cams.append(utils.camera_from_dict(cm)) 1973 nc = len(cams) 1974 1975 plt.show(resetcam=resetcam, interactive=False) 1976 1977 if nc: 1978 for i in range(n): 1979 plt.move_camera(cams, i / n) 1980 plt.show() 1981 self.add_frame() 1982 1983 else: ######################################## 1984 1985 for i in range(n): 1986 plt.camera.Elevation((elevation[1] - elevation[0]) / n) 1987 plt.camera.Azimuth((azimuth[1] - azimuth[0]) / n) 1988 plt.show() 1989 self.add_frame() 1990 1991 return self 1992 1993 def close(self) -> None: 1994 """ 1995 Render the video and write it to file. 1996 """ 1997 if self.duration: 1998 self.fps = int(len(self.frames) / float(self.duration) + 0.5) 1999 colors.printc("recalculated fps:", self.fps, c="m", end="") 2000 else: 2001 self.fps = int(self.fps) 2002 2003 ######################################## 2004 if self.backend == "ffmpeg": 2005 out = os.system( 2006 self.command 2007 + " " 2008 + str(self.fps) 2009 + " -i " 2010 + f"'{self.tmp_dir.name}'" 2011 + os.sep 2012 + "%01d.png " 2013 + self.options 2014 + " " 2015 + f"'{self.name}'" 2016 ) 2017 if out: 2018 vedo.logger.error(f":noentry: backend {self.backend} returning error: {out}") 2019 else: 2020 colors.printc(f":save: saved to {self.name}", c="m") 2021 2022 ######################################## 2023 elif "cv" in self.backend: 2024 try: 2025 import cv2 2026 except ImportError: 2027 vedo.logger.error("opencv is not installed") 2028 return 2029 2030 cap = cv2.VideoCapture(os.path.join(self.tmp_dir.name, "%1d.png")) 2031 fourcc = cv2.VideoWriter_fourcc(*"mp4v") 2032 if vedo.plotter_instance: 2033 w, h = vedo.plotter_instance.window.GetSize() 2034 writer = cv2.VideoWriter(self.name, fourcc, self.fps, (w, h), True) 2035 else: 2036 vedo.logger.error("No vedo plotter found, cannot make video.") 2037 return 2038 2039 while True: 2040 ret, frame = cap.read() 2041 if not ret: 2042 break 2043 writer.write(frame) 2044 2045 cap.release() 2046 writer.release() 2047 2048 ######################################## 2049 elif "imageio" in self.backend: 2050 try: 2051 import imageio 2052 except ImportError: 2053 vedo.logger.error("Please install imageio with:\n pip install imageio[ffmpeg]") 2054 return 2055 2056 if self.name.endswith(".mp4"): 2057 writer = imageio.get_writer(self.name, fps=self.fps) 2058 elif self.name.endswith(".gif"): 2059 writer = imageio.get_writer(self.name, mode="I", duration=1 / self.fps) 2060 elif self.name.endswith(".webm"): 2061 writer = imageio.get_writer(self.name, format="webm", fps=self.fps) 2062 else: 2063 vedo.logger.error(f"Unknown format of {self.name}.") 2064 return 2065 2066 for f in utils.humansort(self.frames): 2067 image = imageio.v3.imread(f) 2068 writer.append_data(image) 2069 try: 2070 writer.close() 2071 colors.printc(f"... saved as {self.name}", c="m") 2072 except: 2073 colors.printc(f":noentry: Could not save video {self.name}", c="r") 2074 2075 # finalize cleanup 2076 self.tmp_dir.cleanup() 2077 2078 def split_frames(self, output_dir="video_frames", prefix="frame_", format="png") -> None: 2079 """Split an existing video file into frames.""" 2080 try: 2081 import imageio 2082 except ImportError: 2083 vedo.logger.error("\nPlease install imageio with:\n pip install imageio") 2084 return 2085 2086 # Create the output directory if it doesn't exist 2087 if not os.path.exists(output_dir): 2088 os.makedirs(output_dir) 2089 2090 # Create a reader object to read the video 2091 reader = imageio.get_reader(self.name) 2092 2093 # Loop through each frame of the video and save it as image 2094 print() 2095 for i, frame in utils.progressbar( 2096 enumerate(reader), title=f"writing {format} frames", c="m", width=20 2097 ): 2098 output_file = os.path.join(output_dir, f"{prefix}{str(i).zfill(5)}.{format}") 2099 imageio.imwrite(output_file, frame, format=format)
177def load(inputobj: Union[list, str], unpack=True, force=False) -> Any: 178 """ 179 Load any vedo objects from file or from the web. 180 181 The output will depend on the file extension. See examples below. 182 Unzip is made on the fly, if file ends with `.gz`. 183 Can load an object directly from a URL address. 184 185 Arguments: 186 unpack : (bool) 187 unpack MultiBlockData into a flat list of objects. 188 force : (bool) 189 when downloading a file ignore any previous cached downloads and force a new one. 190 191 Example: 192 ```python 193 from vedo import dataurl, load, show 194 # Return a list of 2 meshes 195 g = load([dataurl+'250.vtk', dataurl+'270.vtk']) 196 show(g) 197 # Return a list of meshes by reading all files in a directory 198 # (if directory contains DICOM files then a Volume is returned) 199 g = load('mydicomdir/') 200 show(g) 201 ``` 202 """ 203 acts = [] 204 if utils.is_sequence(inputobj): 205 flist = inputobj 206 elif isinstance(inputobj, str) and inputobj.startswith("https://"): 207 flist = [inputobj] 208 else: 209 flist = utils.humansort(glob.glob(inputobj)) 210 211 for fod in flist: 212 213 if fod.startswith("https://"): 214 fod = download(fod, force=force, verbose=False) 215 216 if os.path.isfile(fod): ### it's a file 217 218 if fod.endswith(".gz"): 219 fod = gunzip(fod) 220 221 a = _load_file(fod, unpack) 222 acts.append(a) 223 224 elif os.path.isdir(fod): ### it's a directory or DICOM 225 flist = os.listdir(fod) 226 if ".dcm" in flist[0]: ### it's DICOM 227 reader = vtki.new("DICOMImageReader") 228 reader.SetDirectoryName(fod) 229 reader.Update() 230 image = reader.GetOutput() 231 vol = Volume(image) 232 try: 233 vol.metadata["PixelSpacing"] = reader.GetPixelSpacing() 234 vol.metadata["Width"] = reader.GetWidth() 235 vol.metadata["Height"] = reader.GetHeight() 236 vol.metadata["PositionPatient"] = reader.GetImagePositionPatient() 237 vol.metadata["OrientationPatient"] = reader.GetImageOrientationPatient() 238 vol.metadata["BitsAllocated"] = reader.GetBitsAllocated() 239 vol.metadata["PixelRepresentation"] = reader.GetPixelRepresentation() 240 vol.metadata["NumberOfComponents"] = reader.GetNumberOfComponents() 241 vol.metadata["TransferSyntaxUID"] = reader.GetTransferSyntaxUID() 242 vol.metadata["RescaleSlope"] = reader.GetRescaleSlope() 243 vol.metadata["RescaleOffset"] = reader.GetRescaleOffset() 244 vol.metadata["PatientName"] = reader.GetPatientName() 245 vol.metadata["StudyUID"] = reader.GetStudyUID() 246 vol.metadata["StudyID"] = reader.GetStudyID() 247 vol.metadata["GantryAngle"] = reader.GetGantryAngle() 248 except Exception as e: 249 vedo.logger.warning(f"Cannot read DICOM metadata: {e}") 250 acts.append(vol) 251 252 else: ### it's a normal directory 253 utils.humansort(flist) 254 for ifile in flist: 255 a = _load_file(fod + "/" + ifile, unpack) 256 acts.append(a) 257 else: 258 vedo.logger.error(f"in load(), cannot find {fod}") 259 260 if len(acts) == 1: 261 if "numpy" in str(type(acts[0])): 262 return acts[0] 263 if not acts[0]: 264 vedo.logger.error(f"in load(), cannot load {inputobj}") 265 return acts[0] 266 267 if len(acts) == 0: 268 vedo.logger.error(f"in load(), cannot load {inputobj}") 269 return None 270 271 else: 272 return acts
Load any vedo 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.
Arguments:
- 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:
from vedo import dataurl, load, show # 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)
1322def read(obj: Any, unpack=True, force=False) -> Any: 1323 """Read an object from file. Same as `load()`.""" 1324 return load(obj, unpack, force)
Read an object from file. Same as load()
.
447def download(url: str, force=False, verbose=True) -> str: 448 """ 449 Retrieve a file from a URL, save it locally and return its path. 450 Use `force=True` to force a reload and discard cached copies. 451 """ 452 if not url.startswith("https://"): 453 # assume it's a file so no need to download 454 return url 455 url = url.replace("www.dropbox", "dl.dropbox") 456 457 if "github.com" in url: 458 url = url.replace("/blob/", "/raw/") 459 460 basename = os.path.basename(url) 461 462 if "?" in basename: 463 basename = basename.split("?")[0] 464 465 home_directory = os.path.expanduser("~") 466 cachedir = os.path.join(home_directory, settings.cache_directory, "vedo") 467 fname = os.path.join(cachedir, basename) 468 # Create the directory if it does not exist 469 if not os.path.exists(cachedir): 470 os.makedirs(cachedir) 471 472 if not force and os.path.exists(fname): 473 if verbose: 474 colors.printc("reusing cached file:", fname) 475 return fname 476 477 try: 478 from urllib.request import urlopen, Request 479 req = Request(url, headers={"User-Agent": "Mozilla/5.0"}) 480 if verbose: 481 colors.printc("reading", basename, "from", url.split("/")[2][:40], "...", end="") 482 483 except ImportError: 484 import urllib2 485 import contextlib 486 urlopen = lambda url_: contextlib.closing(urllib2.urlopen(url_)) 487 req = url 488 if verbose: 489 colors.printc("reading", basename, "from", url.split("/")[2][:40], "...", end="") 490 491 with urlopen(req) as response, open(fname, "wb") as output: 492 output.write(response.read()) 493 494 if verbose: 495 colors.printc(" done.") 496 return fname
Retrieve a file from a URL, save it locally and return its path.
Use force=True
to force a reload and discard cached copies.
576def gunzip(filename: str) -> str: 577 """Unzip a `.gz` file to a temporary file and returns its path.""" 578 if not filename.endswith(".gz"): 579 # colors.printc("gunzip() error: file must end with .gz", c='r') 580 return filename 581 582 import gzip 583 584 tmp_file = NamedTemporaryFile(delete=False) 585 tmp_file.name = os.path.join( 586 os.path.dirname(tmp_file.name), os.path.basename(filename).replace(".gz", "") 587 ) 588 inF = gzip.open(filename, "rb") 589 with open(tmp_file.name, "wb") as outF: 590 outF.write(inF.read()) 591 inF.close() 592 return tmp_file.name
Unzip a .gz
file to a temporary file and returns its path.
611def loadStructuredPoints(filename, as_points=True): 612 """ 613 Load and return a `vtkStructuredPoints` object from file. 614 615 If `as_points` is True, return a `Points` object 616 instead of a `vtkStructuredPoints`. 617 """ 618 reader = vtki.new("StructuredPointsReader") 619 reader.SetFileName(filename) 620 reader.Update() 621 if as_points: 622 v2p = vtki.new("ImageToPoints") 623 v2p.SetInputData(reader.GetOutput()) 624 v2p.Update() 625 pts = Points(v2p.GetOutput()) 626 return pts 627 return reader.GetOutput()
Load and return a vtkStructuredPoints
object from file.
If as_points
is True, return a Points
object
instead of a vtkStructuredPoints
.
630def loadStructuredGrid(filename): 631 """Load and return a `vtkStructuredGrid` object from file.""" 632 if filename.endswith(".vts"): 633 reader = vtki.new("XMLStructuredGridReader") 634 else: 635 reader = vtki.new("StructuredGridReader") 636 reader.SetFileName(filename) 637 reader.Update() 638 return reader.GetOutput()
Load and return a vtkStructuredGrid
object from file.
1149def write(objct: Any, fileoutput: str, binary=True) -> Any: 1150 """ 1151 Write object to file. Same as `save()`. 1152 1153 Supported extensions are: 1154 1155 - `vtk, vti, ply, obj, stl, byu, vtp, vti, mhd, xyz, xml, tif, png, bmp` 1156 """ 1157 obj = objct.dataset 1158 1159 try: 1160 # check if obj is a Mesh.actor and has a transform 1161 M = objct.actor.GetMatrix() 1162 if M and not M.IsIdentity(): 1163 obj = objct.apply_transform_from_actor() 1164 obj = objct.dataset 1165 vedo.logger.info( 1166 f"object '{objct.name}' " 1167 "was manually moved. Writing uses current position." 1168 ) 1169 except: 1170 pass 1171 1172 fr = fileoutput.lower() 1173 if fr.endswith(".vtk"): 1174 writer = vtki.new("DataSetWriter") 1175 elif fr.endswith(".ply"): 1176 writer = vtki.new("PLYWriter") 1177 writer.AddComment("PLY file generated by vedo") 1178 lut = objct.mapper.GetLookupTable() 1179 if lut: 1180 pscal = obj.GetPointData().GetScalars() 1181 if not pscal: 1182 pscal = obj.GetCellData().GetScalars() 1183 if pscal and pscal.GetName(): 1184 writer.SetArrayName(pscal.GetName()) 1185 writer.SetLookupTable(lut) 1186 elif fr.endswith(".stl"): 1187 writer = vtki.new("STLWriter") 1188 elif fr.endswith(".vtp"): 1189 writer = vtki.new("XMLPolyDataWriter") 1190 elif fr.endswith(".vtu"): 1191 writer = vtki.new("XMLUnstructuredGridWriter") 1192 elif fr.endswith(".xyz"): 1193 writer = vtki.new("SimplePointsWriter") 1194 elif fr.endswith(".facet"): 1195 writer = vtki.new("FacetWriter") 1196 elif fr.endswith(".vti"): 1197 writer = vtki.new("XMLImageDataWriter") 1198 elif fr.endswith(".vtr"): 1199 writer = vtki.new("XMLRectilinearGridWriter") 1200 elif fr.endswith(".vtm"): 1201 g = vtki.new("MultiBlockDataGroupFilter") 1202 for ob in objct: 1203 try: 1204 g.AddInputData(ob) 1205 except TypeError: 1206 vedo.logger.warning("cannot save object of type", type(ob)) 1207 g.Update() 1208 mb = g.GetOutputDataObject(0) 1209 wri = vtki.new("vtkXMLMultiBlockDataWriter") 1210 wri.SetInputData(mb) 1211 wri.SetFileName(fileoutput) 1212 wri.Write() 1213 return objct 1214 elif fr.endswith(".mhd"): 1215 writer = vtki.new("MetaImageWriter") 1216 elif fr.endswith(".nii"): 1217 writer = vtki.new("NIFTIImageWriter") 1218 elif fr.endswith(".png"): 1219 writer = vtki.new("PNGWriter") 1220 elif fr.endswith(".jpg"): 1221 writer = vtki.new("JPEGWriter") 1222 elif fr.endswith(".bmp"): 1223 writer = vtki.new("BMPWriter") 1224 elif fr.endswith(".tif") or fr.endswith(".tiff"): 1225 writer = vtki.new("TIFFWriter") 1226 writer.SetFileDimensionality(len(obj.GetDimensions())) 1227 elif fr.endswith(".obj"): 1228 with open(fileoutput, "w", encoding="UTF-8") as outF: 1229 outF.write("# OBJ file format with ext .obj\n") 1230 outF.write("# File generated by vedo\n") 1231 1232 for p in objct.vertices: 1233 outF.write("v {:.5g} {:.5g} {:.5g}\n".format(*p)) 1234 1235 ptxt = objct.dataset.GetPointData().GetTCoords() 1236 if ptxt: 1237 ntxt = utils.vtk2numpy(ptxt) 1238 for vt in ntxt: 1239 outF.write("vt " + str(vt[0]) + " " + str(vt[1]) + " 0.0\n") 1240 1241 if isinstance(objct, Mesh): 1242 for i, f in enumerate(objct.cells): 1243 fs = "" 1244 for fi in f: 1245 if ptxt: 1246 fs += f" {fi+1}/{fi+1}" 1247 else: 1248 fs += f" {fi+1}" 1249 outF.write(f"f{fs}\n") 1250 1251 for l in objct.lines: 1252 ls = "" 1253 for li in l: 1254 ls += str(li + 1) + " " 1255 outF.write(f"l {ls}\n") 1256 return objct 1257 1258 elif fr.endswith(".xml"): # write tetrahedral dolfin xml 1259 vertices = objct.vertices.astype(str) 1260 faces = np.array(objct.cells).astype(str) 1261 ncoords = vertices.shape[0] 1262 with open(fileoutput, "w", encoding="UTF-8") as outF: 1263 outF.write('<?xml version="1.0" encoding="UTF-8"?>\n') 1264 outF.write('<dolfin xmlns:dolfin="http://www.fenicsproject.org">\n') 1265 1266 if len(faces[0]) == 4: # write tetrahedral mesh 1267 ntets = faces.shape[0] 1268 outF.write(' <mesh celltype="tetrahedron" dim="3">\n') 1269 outF.write(' <vertices size="' + str(ncoords) + '">\n') 1270 for i in range(ncoords): 1271 x, y, z = vertices[i] 1272 outF.write(' <vertex index="'+str(i)+'" x="'+x+'" y="'+y+'" z="'+z+'"/>\n') 1273 outF.write(' </vertices>\n') 1274 outF.write(' <cells size="' + str(ntets) + '">\n') 1275 for i in range(ntets): 1276 v0, v1, v2, v3 = faces[i] 1277 outF.write(' <tetrahedron index="'+str(i) 1278 + '" v0="'+v0+'" v1="'+v1+'" v2="'+v2+'" v3="'+v3+'"/>\n') 1279 1280 elif len(faces[0]) == 3: # write triangle mesh 1281 ntri = faces.shape[0] 1282 outF.write(' <mesh celltype="triangle" dim="2">\n') 1283 outF.write(' <vertices size="' + str(ncoords) + '">\n') 1284 for i in range(ncoords): 1285 x, y, _ = vertices[i] 1286 outF.write(' <vertex index="'+str(i)+'" x="'+x+'" y="'+y+'"/>\n') 1287 outF.write(' </vertices>\n') 1288 outF.write(' <cells size="' + str(ntri) + '">\n') 1289 for i in range(ntri): 1290 v0, v1, v2 = faces[i] 1291 outF.write(' <triangle index="'+str(i)+'" v0="'+v0+'" v1="'+v1+'" v2="'+v2+'"/>\n') 1292 1293 outF.write(" </cells>\n") 1294 outF.write(" </mesh>\n") 1295 outF.write("</dolfin>\n") 1296 return objct 1297 1298 else: 1299 vedo.logger.error(f"Unknown format {fileoutput}, file not saved") 1300 return objct 1301 1302 try: 1303 if binary: 1304 writer.SetFileTypeToBinary() 1305 else: 1306 writer.SetFileTypeToASCII() 1307 except AttributeError: 1308 pass 1309 1310 try: 1311 writer.SetInputData(obj) 1312 writer.SetFileName(fileoutput) 1313 writer.Write() 1314 except: 1315 vedo.logger.error(f"could not save {fileoutput}") 1316 return objct
Write object to file. Same as save()
.
Supported extensions are:
vtk, vti, ply, obj, stl, byu, vtp, vti, mhd, xyz, xml, tif, png, bmp
1318def save(obj: Any, fileoutput="out.png", binary=True) -> Any: 1319 """Save an object to file. Same as `write()`.""" 1320 return write(obj, fileoutput, binary)
Save an object to file. Same as write()
.
1327def export_window(fileoutput: str, binary=False, plt=None) -> "vedo.Plotter": 1328 """ 1329 Exporter which writes out the rendered scene into an HTML, X3D or Numpy file. 1330 1331 Example: 1332 - [export_x3d.py](https://github.com/marcomusy/vedo/tree/master/examples/other/export_x3d.py) 1333 1334 Check out the HTML generated webpage [here](https://vedo.embl.es/examples/embryo.html). 1335 1336 <img src='https://user-images.githubusercontent.com/32848391/57160341-c6ffbd80-6de8-11e9-95ff-7215ce642bc5.jpg' width="600"/> 1337 1338 .. note:: 1339 the rendering window can also be exported to `numpy` file `scene.npz` 1340 by pressing `E` key at any moment during visualization. 1341 """ 1342 if plt is None: 1343 plt = vedo.plotter_instance 1344 1345 fr = fileoutput.lower() 1346 #################################################################### 1347 if fr.endswith(".npy") or fr.endswith(".npz"): 1348 _export_npy(plt, fileoutput) 1349 1350 #################################################################### 1351 elif fr.endswith(".x3d"): 1352 # obj = plt.get_actors() 1353 # if plt.axes_instances: 1354 # obj.append(plt.axes_instances[0]) 1355 1356 # for a in obj: 1357 # if isinstance(a, Assembly): 1358 # plt.remove(a) 1359 # plt.add(a.unpack()) 1360 1361 plt.render() 1362 1363 exporter = vtki.new("X3DExporter") 1364 exporter.SetBinary(binary) 1365 exporter.FastestOff() 1366 exporter.SetInput(plt.window) 1367 exporter.SetFileName(fileoutput) 1368 # exporter.WriteToOutputStringOn() 1369 exporter.Update() 1370 exporter.Write() 1371 1372 wsize = plt.window.GetSize() 1373 x3d_html = _x3d_html_template.replace("~fileoutput", fileoutput) 1374 x3d_html = x3d_html.replace("~width", str(wsize[0])) 1375 x3d_html = x3d_html.replace("~height", str(wsize[1])) 1376 with open(fileoutput.replace(".x3d", ".html"), "w", encoding="UTF-8") as outF: 1377 outF.write(x3d_html) 1378 1379 #################################################################### 1380 elif fr.endswith(".html"): 1381 savebk = vedo.notebook_backend 1382 vedo.notebook_backend = "k3d" 1383 vedo.settings.default_backend = "k3d" 1384 # acts = plt.get_actors() 1385 plt = vedo.backends.get_notebook_backend(plt.objects) 1386 1387 with open(fileoutput, "w", encoding="UTF-8") as fp: 1388 fp.write(plt.get_snapshot()) 1389 1390 vedo.notebook_backend = savebk 1391 vedo.settings.default_backend = savebk 1392 1393 else: 1394 vedo.logger.error(f"export extension {fr.split('.')[-1]} is not supported") 1395 1396 return plt
Exporter which writes out the rendered scene into an HTML, X3D or Numpy file.
Example:
Check out the HTML generated webpage here.
the rendering window can also be exported to numpy
file scene.npz
by pressing E
key at any moment during visualization.
1666def import_window(fileinput: str) -> Union["vedo.Plotter", None]: 1667 """ 1668 Import a whole scene from a Numpy NPZ file. 1669 1670 Returns: 1671 `vedo.Plotter` instance 1672 """ 1673 if fileinput.endswith(".npy") or fileinput.endswith(".npz"): 1674 return _import_npy(fileinput) 1675 1676 # elif ".obj" in fileinput.lower(): 1677 # meshes = load_obj(fileinput, mtl_file, texture_path) 1678 # plt = vedo.Plotter() 1679 # plt.add(meshes) 1680 # return plt 1681 1682 # elif fileinput.endswith(".h5") or fileinput.endswith(".hdf5"): 1683 # return _import_hdf5(fileinput) # in store/file_io_HDF5.py 1684 1685 return None
Import a whole scene from a Numpy NPZ file.
Returns:
vedo.Plotter
instance
1688def load_obj(fileinput: str, mtl_file=None, texture_path=None) -> List[Mesh]: 1689 """ 1690 Import a set of meshes from a OBJ wavefront file. 1691 1692 Arguments: 1693 mtl_file : (str) 1694 MTL file for OBJ wavefront files 1695 texture_path : (str) 1696 path of the texture files directory 1697 1698 Returns: 1699 `list(Mesh)` 1700 """ 1701 window = vtki.vtkRenderWindow() 1702 window.SetOffScreenRendering(1) 1703 renderer = vtki.vtkRenderer() 1704 window.AddRenderer(renderer) 1705 1706 importer = vtki.new("OBJImporter") 1707 importer.SetFileName(fileinput) 1708 if mtl_file is None: 1709 mtl_file = fileinput.replace(".obj", ".mtl").replace(".OBJ", ".MTL") 1710 if os.path.isfile(mtl_file): 1711 importer.SetFileNameMTL(mtl_file) 1712 if texture_path is None: 1713 texture_path = fileinput.replace(".obj", ".txt").replace(".OBJ", ".TXT") 1714 # since the texture_path may be a directory which contains textures 1715 if os.path.exists(texture_path): 1716 importer.SetTexturePath(texture_path) 1717 importer.SetRenderWindow(window) 1718 importer.Update() 1719 1720 actors = renderer.GetActors() 1721 actors.InitTraversal() 1722 objs = [] 1723 for _ in range(actors.GetNumberOfItems()): 1724 vactor = actors.GetNextActor() 1725 msh = Mesh(vactor) 1726 msh.name = "OBJMesh" 1727 tx = vactor.GetTexture() 1728 if tx: 1729 msh.texture(tx) 1730 objs.append(msh) 1731 return objs
Import a set of meshes from a OBJ wavefront file.
Arguments:
- mtl_file : (str) MTL file for OBJ wavefront files
- texture_path : (str) path of the texture files directory
Returns:
list(Mesh)
1735def screenshot(filename="screenshot.png", scale=1, asarray=False) -> Union["vedo.Plotter", np.ndarray, None]: 1736 """ 1737 Save a screenshot of the current rendering window. 1738 1739 Alternatively, press key `Shift-S` in the rendering window to save a screenshot. 1740 You can also use keyword `screenshot` in `show(..., screenshot="pic.png")`. 1741 1742 Arguments: 1743 scale : (int) 1744 Set image magnification as an integer multiplicative factor. 1745 E.g. setting a magnification of 2 produces an image twice as large, 1746 but 10x slower to generate. 1747 asarray : (bool) 1748 Return a numpy array of the image 1749 """ 1750 # print("calling screenshot", filename, scale, asarray) 1751 1752 if not vedo.plotter_instance or not vedo.plotter_instance.window: 1753 # vedo.logger.error("in screenshot(), rendering window is not present, skip.") 1754 return vedo.plotter_instance ########## 1755 1756 if asarray and scale == 1 and not vedo.plotter_instance.offscreen: 1757 nx, ny = vedo.plotter_instance.window.GetSize() 1758 arr = vtki.vtkUnsignedCharArray() 1759 vedo.plotter_instance.window.GetRGBACharPixelData(0, 0, nx-1, ny-1, 0, arr) 1760 narr = vedo.vtk2numpy(arr).T[:3].T.reshape([ny, nx, 3]) 1761 narr = np.flip(narr, axis=0) 1762 return narr ########## 1763 1764 filename = str(filename) 1765 1766 if filename.endswith(".pdf"): 1767 writer = vtki.new("GL2PSExporter") 1768 writer.SetRenderWindow(vedo.plotter_instance.window) 1769 writer.Write3DPropsAsRasterImageOff() 1770 writer.SilentOn() 1771 writer.SetSortToBSP() 1772 writer.SetFileFormatToPDF() 1773 writer.SetFilePrefix(filename.replace(".pdf", "")) 1774 writer.Write() 1775 return vedo.plotter_instance ########## 1776 1777 elif filename.endswith(".svg"): 1778 writer = vtki.new("GL2PSExporter") 1779 writer.SetRenderWindow(vedo.plotter_instance.window) 1780 writer.Write3DPropsAsRasterImageOff() 1781 writer.SilentOn() 1782 writer.SetSortToBSP() 1783 writer.SetFileFormatToSVG() 1784 writer.SetFilePrefix(filename.replace(".svg", "")) 1785 writer.Write() 1786 return vedo.plotter_instance ########## 1787 1788 elif filename.endswith(".eps"): 1789 writer = vtki.new("GL2PSExporter") 1790 writer.SetRenderWindow(vedo.plotter_instance.window) 1791 writer.Write3DPropsAsRasterImageOff() 1792 writer.SilentOn() 1793 writer.SetSortToBSP() 1794 writer.SetFileFormatToEPS() 1795 writer.SetFilePrefix(filename.replace(".eps", "")) 1796 writer.Write() 1797 return vedo.plotter_instance ########## 1798 1799 if settings.screeshot_large_image: 1800 w2if = vtki.new("RenderLargeImage") 1801 w2if.SetInput(vedo.plotter_instance.renderer) 1802 w2if.SetMagnification(scale) 1803 else: 1804 w2if = vtki.new("WindowToImageFilter") 1805 w2if.SetInput(vedo.plotter_instance.window) 1806 if hasattr(w2if, "SetScale"): 1807 w2if.SetScale(int(scale), int(scale)) 1808 if settings.screenshot_transparent_background: 1809 w2if.SetInputBufferTypeToRGBA() 1810 w2if.ReadFrontBufferOff() # read from the back buffer 1811 w2if.Update() 1812 1813 if asarray: 1814 pd = w2if.GetOutput().GetPointData() 1815 npdata = utils.vtk2numpy(pd.GetArray("ImageScalars")) 1816 npdata = npdata[:, [0, 1, 2]] 1817 ydim, xdim, _ = w2if.GetOutput().GetDimensions() 1818 npdata = npdata.reshape([xdim, ydim, -1]) 1819 npdata = np.flip(npdata, axis=0) 1820 return npdata ########################### 1821 1822 # elif settings.default_backend == "2d" and vedo.notebook_plotter: 1823 # vedo.notebook_plotter.save(filename) # a PIL Image 1824 # return vedo.notebook_plotter ########## 1825 1826 if filename.lower().endswith(".png"): 1827 writer = vtki.new("PNGWriter") 1828 writer.SetFileName(filename) 1829 writer.SetInputData(w2if.GetOutput()) 1830 writer.Write() 1831 elif filename.lower().endswith(".jpg") or filename.lower().endswith(".jpeg"): 1832 writer = vtki.new("JPEGWriter") 1833 writer.SetFileName(filename) 1834 writer.SetInputData(w2if.GetOutput()) 1835 writer.Write() 1836 else: # add .png 1837 writer = vtki.new("PNGWriter") 1838 writer.SetFileName(filename + ".png") 1839 writer.SetInputData(w2if.GetOutput()) 1840 writer.Write() 1841 return vedo.plotter_instance
Save a screenshot of the current rendering window.
Alternatively, press key Shift-S
in the rendering window to save a screenshot.
You can also use keyword screenshot
in show(..., screenshot="pic.png")
.
Arguments:
- scale : (int) Set image magnification as an integer multiplicative factor. E.g. setting a magnification of 2 produces an image twice as large, but 10x slower to generate.
- asarray : (bool) Return a numpy array of the image
1844def ask(*question, **kwarg) -> str: 1845 """ 1846 Ask a question from command line. Return the answer as a string. 1847 See function `colors.printc()` for the description of the keyword options. 1848 1849 Arguments: 1850 options : (list) 1851 a python list of possible answers to choose from. 1852 default : (str) 1853 the default answer when just hitting return. 1854 1855 Example: 1856 ```python 1857 import vedo 1858 res = vedo.ask("Continue?", options=['Y','n'], default='Y', c='g') 1859 print(res) 1860 ``` 1861 """ 1862 kwarg.update({"end": " "}) 1863 if "invert" not in kwarg: 1864 kwarg.update({"invert": True}) 1865 if "box" in kwarg: 1866 kwarg.update({"box": ""}) 1867 1868 options = kwarg.pop("options", []) 1869 default = kwarg.pop("default", "") 1870 if options: 1871 opt = "[" 1872 for o in options: 1873 opt += o + "/" 1874 opt = opt[:-1] + "]" 1875 colors.printc(*question, opt, **kwarg) 1876 else: 1877 colors.printc(*question, **kwarg) 1878 1879 try: 1880 resp = input() 1881 except Exception: 1882 resp = "" 1883 return resp 1884 1885 if options: 1886 if resp not in options: 1887 if default and str(repr(resp)) == "''": 1888 return default 1889 colors.printc("Please choose one option in:", opt, italic=True, bold=False) 1890 kwarg["options"] = options 1891 return ask(*question, **kwarg) # ask again 1892 return resp
Ask a question from command line. Return the answer as a string.
See function colors.printc()
for the description of the keyword options.
Arguments:
- options : (list) a python list of possible answers to choose from.
- default : (str) the default answer when just hitting return.
Example:
import vedo
res = vedo.ask("Continue?", options=['Y','n'], default='Y', c='g')
print(res)
1896class Video: 1897 """ 1898 Generate a video from a rendering window. 1899 """ 1900 1901 def __init__(self, name="movie.mp4", duration=None, fps=24, backend="imageio"): 1902 """ 1903 Class to generate a video from the specified rendering window. 1904 Program `ffmpeg` is used to create video from each generated frame. 1905 1906 Arguments: 1907 name : (str) 1908 name of the output file. 1909 fps : (int) 1910 set the number of frames per second. 1911 duration : (float) 1912 set the total `duration` of the video and recalculates `fps` accordingly. 1913 backend : (str) 1914 the backend engine to be used `['imageio', 'ffmpeg', 'cv']` 1915 1916 Examples: 1917 - [make_video.py](https://github.com/marcomusy/vedo/tree/master/examples/other/make_video.py) 1918 1919 ![](https://user-images.githubusercontent.com/32848391/50739007-2bfc2b80-11da-11e9-97e6-620a3541a6fa.jpg) 1920 """ 1921 self.name = name 1922 self.duration = duration 1923 self.backend = backend 1924 self.fps = float(fps) 1925 self.command = "ffmpeg -loglevel panic -y -r" 1926 self.options = "-b:v 8000k" 1927 1928 self.frames = [] 1929 self.tmp_dir = TemporaryDirectory() 1930 self.get_filename = lambda x: os.path.join(self.tmp_dir.name, x) 1931 colors.printc(":video: Video file", self.name, "is open... ", c="m", end="") 1932 1933 def add_frame(self) -> "Video": 1934 """Add frame to current video.""" 1935 fr = self.get_filename(str(len(self.frames)) + ".png") 1936 screenshot(fr) 1937 self.frames.append(fr) 1938 return self 1939 1940 def pause(self, pause=0) -> "Video": 1941 """Insert a `pause`, in seconds.""" 1942 fr = self.frames[-1] 1943 n = int(self.fps * pause) 1944 for _ in range(n): 1945 fr2 = self.get_filename(str(len(self.frames)) + ".png") 1946 self.frames.append(fr2) 1947 os.system("cp -f %s %s" % (fr, fr2)) 1948 return self 1949 1950 def action(self, elevation=(0, 80), azimuth=(0, 359), cameras=(), resetcam=False) -> "Video": 1951 """ 1952 Automatic shooting of a static scene by specifying rotation and elevation ranges. 1953 1954 Arguments: 1955 elevation : list 1956 initial and final elevation angles 1957 azimuth_range : list 1958 initial and final azimuth angles 1959 cameras : list 1960 list of cameras to go through, each camera can be dictionary or a vtkCamera 1961 """ 1962 if not self.duration: 1963 self.duration = 5 1964 1965 plt = vedo.plotter_instance 1966 if not plt: 1967 vedo.logger.error("No vedo plotter found, cannot make video.") 1968 return self 1969 n = int(self.fps * self.duration) 1970 1971 cams = [] 1972 for cm in cameras: 1973 cams.append(utils.camera_from_dict(cm)) 1974 nc = len(cams) 1975 1976 plt.show(resetcam=resetcam, interactive=False) 1977 1978 if nc: 1979 for i in range(n): 1980 plt.move_camera(cams, i / n) 1981 plt.show() 1982 self.add_frame() 1983 1984 else: ######################################## 1985 1986 for i in range(n): 1987 plt.camera.Elevation((elevation[1] - elevation[0]) / n) 1988 plt.camera.Azimuth((azimuth[1] - azimuth[0]) / n) 1989 plt.show() 1990 self.add_frame() 1991 1992 return self 1993 1994 def close(self) -> None: 1995 """ 1996 Render the video and write it to file. 1997 """ 1998 if self.duration: 1999 self.fps = int(len(self.frames) / float(self.duration) + 0.5) 2000 colors.printc("recalculated fps:", self.fps, c="m", end="") 2001 else: 2002 self.fps = int(self.fps) 2003 2004 ######################################## 2005 if self.backend == "ffmpeg": 2006 out = os.system( 2007 self.command 2008 + " " 2009 + str(self.fps) 2010 + " -i " 2011 + f"'{self.tmp_dir.name}'" 2012 + os.sep 2013 + "%01d.png " 2014 + self.options 2015 + " " 2016 + f"'{self.name}'" 2017 ) 2018 if out: 2019 vedo.logger.error(f":noentry: backend {self.backend} returning error: {out}") 2020 else: 2021 colors.printc(f":save: saved to {self.name}", c="m") 2022 2023 ######################################## 2024 elif "cv" in self.backend: 2025 try: 2026 import cv2 2027 except ImportError: 2028 vedo.logger.error("opencv is not installed") 2029 return 2030 2031 cap = cv2.VideoCapture(os.path.join(self.tmp_dir.name, "%1d.png")) 2032 fourcc = cv2.VideoWriter_fourcc(*"mp4v") 2033 if vedo.plotter_instance: 2034 w, h = vedo.plotter_instance.window.GetSize() 2035 writer = cv2.VideoWriter(self.name, fourcc, self.fps, (w, h), True) 2036 else: 2037 vedo.logger.error("No vedo plotter found, cannot make video.") 2038 return 2039 2040 while True: 2041 ret, frame = cap.read() 2042 if not ret: 2043 break 2044 writer.write(frame) 2045 2046 cap.release() 2047 writer.release() 2048 2049 ######################################## 2050 elif "imageio" in self.backend: 2051 try: 2052 import imageio 2053 except ImportError: 2054 vedo.logger.error("Please install imageio with:\n pip install imageio[ffmpeg]") 2055 return 2056 2057 if self.name.endswith(".mp4"): 2058 writer = imageio.get_writer(self.name, fps=self.fps) 2059 elif self.name.endswith(".gif"): 2060 writer = imageio.get_writer(self.name, mode="I", duration=1 / self.fps) 2061 elif self.name.endswith(".webm"): 2062 writer = imageio.get_writer(self.name, format="webm", fps=self.fps) 2063 else: 2064 vedo.logger.error(f"Unknown format of {self.name}.") 2065 return 2066 2067 for f in utils.humansort(self.frames): 2068 image = imageio.v3.imread(f) 2069 writer.append_data(image) 2070 try: 2071 writer.close() 2072 colors.printc(f"... saved as {self.name}", c="m") 2073 except: 2074 colors.printc(f":noentry: Could not save video {self.name}", c="r") 2075 2076 # finalize cleanup 2077 self.tmp_dir.cleanup() 2078 2079 def split_frames(self, output_dir="video_frames", prefix="frame_", format="png") -> None: 2080 """Split an existing video file into frames.""" 2081 try: 2082 import imageio 2083 except ImportError: 2084 vedo.logger.error("\nPlease install imageio with:\n pip install imageio") 2085 return 2086 2087 # Create the output directory if it doesn't exist 2088 if not os.path.exists(output_dir): 2089 os.makedirs(output_dir) 2090 2091 # Create a reader object to read the video 2092 reader = imageio.get_reader(self.name) 2093 2094 # Loop through each frame of the video and save it as image 2095 print() 2096 for i, frame in utils.progressbar( 2097 enumerate(reader), title=f"writing {format} frames", c="m", width=20 2098 ): 2099 output_file = os.path.join(output_dir, f"{prefix}{str(i).zfill(5)}.{format}") 2100 imageio.imwrite(output_file, frame, format=format)
Generate a video from a rendering window.
1901 def __init__(self, name="movie.mp4", duration=None, fps=24, backend="imageio"): 1902 """ 1903 Class to generate a video from the specified rendering window. 1904 Program `ffmpeg` is used to create video from each generated frame. 1905 1906 Arguments: 1907 name : (str) 1908 name of the output file. 1909 fps : (int) 1910 set the number of frames per second. 1911 duration : (float) 1912 set the total `duration` of the video and recalculates `fps` accordingly. 1913 backend : (str) 1914 the backend engine to be used `['imageio', 'ffmpeg', 'cv']` 1915 1916 Examples: 1917 - [make_video.py](https://github.com/marcomusy/vedo/tree/master/examples/other/make_video.py) 1918 1919 ![](https://user-images.githubusercontent.com/32848391/50739007-2bfc2b80-11da-11e9-97e6-620a3541a6fa.jpg) 1920 """ 1921 self.name = name 1922 self.duration = duration 1923 self.backend = backend 1924 self.fps = float(fps) 1925 self.command = "ffmpeg -loglevel panic -y -r" 1926 self.options = "-b:v 8000k" 1927 1928 self.frames = [] 1929 self.tmp_dir = TemporaryDirectory() 1930 self.get_filename = lambda x: os.path.join(self.tmp_dir.name, x) 1931 colors.printc(":video: Video file", self.name, "is open... ", c="m", end="")
Class to generate a video from the specified rendering window.
Program ffmpeg
is used to create video from each generated frame.
Arguments:
- 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. - backend : (str)
the backend engine to be used
['imageio', 'ffmpeg', 'cv']
Examples:
1933 def add_frame(self) -> "Video": 1934 """Add frame to current video.""" 1935 fr = self.get_filename(str(len(self.frames)) + ".png") 1936 screenshot(fr) 1937 self.frames.append(fr) 1938 return self
Add frame to current video.
1940 def pause(self, pause=0) -> "Video": 1941 """Insert a `pause`, in seconds.""" 1942 fr = self.frames[-1] 1943 n = int(self.fps * pause) 1944 for _ in range(n): 1945 fr2 = self.get_filename(str(len(self.frames)) + ".png") 1946 self.frames.append(fr2) 1947 os.system("cp -f %s %s" % (fr, fr2)) 1948 return self
Insert a pause
, in seconds.
1950 def action(self, elevation=(0, 80), azimuth=(0, 359), cameras=(), resetcam=False) -> "Video": 1951 """ 1952 Automatic shooting of a static scene by specifying rotation and elevation ranges. 1953 1954 Arguments: 1955 elevation : list 1956 initial and final elevation angles 1957 azimuth_range : list 1958 initial and final azimuth angles 1959 cameras : list 1960 list of cameras to go through, each camera can be dictionary or a vtkCamera 1961 """ 1962 if not self.duration: 1963 self.duration = 5 1964 1965 plt = vedo.plotter_instance 1966 if not plt: 1967 vedo.logger.error("No vedo plotter found, cannot make video.") 1968 return self 1969 n = int(self.fps * self.duration) 1970 1971 cams = [] 1972 for cm in cameras: 1973 cams.append(utils.camera_from_dict(cm)) 1974 nc = len(cams) 1975 1976 plt.show(resetcam=resetcam, interactive=False) 1977 1978 if nc: 1979 for i in range(n): 1980 plt.move_camera(cams, i / n) 1981 plt.show() 1982 self.add_frame() 1983 1984 else: ######################################## 1985 1986 for i in range(n): 1987 plt.camera.Elevation((elevation[1] - elevation[0]) / n) 1988 plt.camera.Azimuth((azimuth[1] - azimuth[0]) / n) 1989 plt.show() 1990 self.add_frame() 1991 1992 return self
Automatic shooting of a static scene by specifying rotation and elevation ranges.
Arguments:
- elevation : list initial and final elevation angles
- azimuth_range : list initial and final azimuth angles
- cameras : list list of cameras to go through, each camera can be dictionary or a vtkCamera
1994 def close(self) -> None: 1995 """ 1996 Render the video and write it to file. 1997 """ 1998 if self.duration: 1999 self.fps = int(len(self.frames) / float(self.duration) + 0.5) 2000 colors.printc("recalculated fps:", self.fps, c="m", end="") 2001 else: 2002 self.fps = int(self.fps) 2003 2004 ######################################## 2005 if self.backend == "ffmpeg": 2006 out = os.system( 2007 self.command 2008 + " " 2009 + str(self.fps) 2010 + " -i " 2011 + f"'{self.tmp_dir.name}'" 2012 + os.sep 2013 + "%01d.png " 2014 + self.options 2015 + " " 2016 + f"'{self.name}'" 2017 ) 2018 if out: 2019 vedo.logger.error(f":noentry: backend {self.backend} returning error: {out}") 2020 else: 2021 colors.printc(f":save: saved to {self.name}", c="m") 2022 2023 ######################################## 2024 elif "cv" in self.backend: 2025 try: 2026 import cv2 2027 except ImportError: 2028 vedo.logger.error("opencv is not installed") 2029 return 2030 2031 cap = cv2.VideoCapture(os.path.join(self.tmp_dir.name, "%1d.png")) 2032 fourcc = cv2.VideoWriter_fourcc(*"mp4v") 2033 if vedo.plotter_instance: 2034 w, h = vedo.plotter_instance.window.GetSize() 2035 writer = cv2.VideoWriter(self.name, fourcc, self.fps, (w, h), True) 2036 else: 2037 vedo.logger.error("No vedo plotter found, cannot make video.") 2038 return 2039 2040 while True: 2041 ret, frame = cap.read() 2042 if not ret: 2043 break 2044 writer.write(frame) 2045 2046 cap.release() 2047 writer.release() 2048 2049 ######################################## 2050 elif "imageio" in self.backend: 2051 try: 2052 import imageio 2053 except ImportError: 2054 vedo.logger.error("Please install imageio with:\n pip install imageio[ffmpeg]") 2055 return 2056 2057 if self.name.endswith(".mp4"): 2058 writer = imageio.get_writer(self.name, fps=self.fps) 2059 elif self.name.endswith(".gif"): 2060 writer = imageio.get_writer(self.name, mode="I", duration=1 / self.fps) 2061 elif self.name.endswith(".webm"): 2062 writer = imageio.get_writer(self.name, format="webm", fps=self.fps) 2063 else: 2064 vedo.logger.error(f"Unknown format of {self.name}.") 2065 return 2066 2067 for f in utils.humansort(self.frames): 2068 image = imageio.v3.imread(f) 2069 writer.append_data(image) 2070 try: 2071 writer.close() 2072 colors.printc(f"... saved as {self.name}", c="m") 2073 except: 2074 colors.printc(f":noentry: Could not save video {self.name}", c="r") 2075 2076 # finalize cleanup 2077 self.tmp_dir.cleanup()
Render the video and write it to file.
2079 def split_frames(self, output_dir="video_frames", prefix="frame_", format="png") -> None: 2080 """Split an existing video file into frames.""" 2081 try: 2082 import imageio 2083 except ImportError: 2084 vedo.logger.error("\nPlease install imageio with:\n pip install imageio") 2085 return 2086 2087 # Create the output directory if it doesn't exist 2088 if not os.path.exists(output_dir): 2089 os.makedirs(output_dir) 2090 2091 # Create a reader object to read the video 2092 reader = imageio.get_reader(self.name) 2093 2094 # Loop through each frame of the video and save it as image 2095 print() 2096 for i, frame in utils.progressbar( 2097 enumerate(reader), title=f"writing {format} frames", c="m", width=20 2098 ): 2099 output_file = os.path.join(output_dir, f"{prefix}{str(i).zfill(5)}.{format}") 2100 imageio.imwrite(output_file, frame, format=format)
Split an existing video file into frames.