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 {:.8g} {:.8g} {:.8g}\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(".off"): 1258 with open(fileoutput, "w", encoding="UTF-8") as outF: 1259 outF.write("OFF\n") 1260 outF.write(str(objct.npoints) + " " + str(objct.ncells) + " 0\n\n") 1261 for p in objct.vertices: 1262 outF.write(" ".join([str(i) for i in p]) + "\n") 1263 for c in objct.cells: 1264 outF.write(str(len(c)) + " " + " ".join([str(i) for i in c]) + "\n") 1265 return objct 1266 1267 elif fr.endswith(".xml"): # write tetrahedral dolfin xml 1268 vertices = objct.vertices.astype(str) 1269 faces = np.array(objct.cells).astype(str) 1270 ncoords = vertices.shape[0] 1271 with open(fileoutput, "w", encoding="UTF-8") as outF: 1272 outF.write('<?xml version="1.0" encoding="UTF-8"?>\n') 1273 outF.write('<dolfin xmlns:dolfin="http://www.fenicsproject.org">\n') 1274 1275 if len(faces[0]) == 4: # write tetrahedral mesh 1276 ntets = faces.shape[0] 1277 outF.write(' <mesh celltype="tetrahedron" dim="3">\n') 1278 outF.write(' <vertices size="' + str(ncoords) + '">\n') 1279 for i in range(ncoords): 1280 x, y, z = vertices[i] 1281 outF.write(' <vertex index="'+str(i)+'" x="'+x+'" y="'+y+'" z="'+z+'"/>\n') 1282 outF.write(' </vertices>\n') 1283 outF.write(' <cells size="' + str(ntets) + '">\n') 1284 for i in range(ntets): 1285 v0, v1, v2, v3 = faces[i] 1286 outF.write(' <tetrahedron index="'+str(i) 1287 + '" v0="'+v0+'" v1="'+v1+'" v2="'+v2+'" v3="'+v3+'"/>\n') 1288 1289 elif len(faces[0]) == 3: # write triangle mesh 1290 ntri = faces.shape[0] 1291 outF.write(' <mesh celltype="triangle" dim="2">\n') 1292 outF.write(' <vertices size="' + str(ncoords) + '">\n') 1293 for i in range(ncoords): 1294 x, y, _ = vertices[i] 1295 outF.write(' <vertex index="'+str(i)+'" x="'+x+'" y="'+y+'"/>\n') 1296 outF.write(' </vertices>\n') 1297 outF.write(' <cells size="' + str(ntri) + '">\n') 1298 for i in range(ntri): 1299 v0, v1, v2 = faces[i] 1300 outF.write(' <triangle index="'+str(i)+'" v0="'+v0+'" v1="'+v1+'" v2="'+v2+'"/>\n') 1301 1302 outF.write(" </cells>\n") 1303 outF.write(" </mesh>\n") 1304 outF.write("</dolfin>\n") 1305 return objct 1306 1307 else: 1308 vedo.logger.error(f"Unknown format {fileoutput}, file not saved") 1309 return objct 1310 1311 try: 1312 if binary: 1313 writer.SetFileTypeToBinary() 1314 else: 1315 writer.SetFileTypeToASCII() 1316 except AttributeError: 1317 pass 1318 1319 try: 1320 writer.SetInputData(obj) 1321 writer.SetFileName(fileoutput) 1322 writer.Write() 1323 except: 1324 vedo.logger.error(f"could not save {fileoutput}") 1325 return objct 1326 1327def save(obj: Any, fileoutput="out.png", binary=True) -> Any: 1328 """Save an object to file. Same as `write()`.""" 1329 return write(obj, fileoutput, binary) 1330 1331def read(obj: Any, unpack=True, force=False) -> Any: 1332 """Read an object from file. Same as `load()`.""" 1333 return load(obj, unpack, force) 1334 1335############################################################################### 1336def export_window(fileoutput: str, binary=False, plt=None) -> "vedo.Plotter": 1337 """ 1338 Exporter which writes out the rendered scene into an HTML, X3D or Numpy file. 1339 1340 Example: 1341 - [export_x3d.py](https://github.com/marcomusy/vedo/tree/master/examples/other/export_x3d.py) 1342 1343 Check out the HTML generated webpage [here](https://vedo.embl.es/examples/embryo.html). 1344 1345 <img src='https://user-images.githubusercontent.com/32848391/57160341-c6ffbd80-6de8-11e9-95ff-7215ce642bc5.jpg' width="600"/> 1346 1347 .. note:: 1348 the rendering window can also be exported to `numpy` file `scene.npz` 1349 by pressing `E` key at any moment during visualization. 1350 """ 1351 if plt is None: 1352 plt = vedo.plotter_instance 1353 1354 fr = fileoutput.lower() 1355 #################################################################### 1356 if fr.endswith(".npy") or fr.endswith(".npz"): 1357 _export_npy(plt, fileoutput) 1358 1359 #################################################################### 1360 elif fr.endswith(".x3d"): 1361 # obj = plt.get_actors() 1362 # if plt.axes_instances: 1363 # obj.append(plt.axes_instances[0]) 1364 1365 # for a in obj: 1366 # if isinstance(a, Assembly): 1367 # plt.remove(a) 1368 # plt.add(a.unpack()) 1369 1370 plt.render() 1371 1372 exporter = vtki.new("X3DExporter") 1373 exporter.SetBinary(binary) 1374 exporter.FastestOff() 1375 exporter.SetInput(plt.window) 1376 exporter.SetFileName(fileoutput) 1377 # exporter.WriteToOutputStringOn() 1378 exporter.Update() 1379 exporter.Write() 1380 1381 wsize = plt.window.GetSize() 1382 x3d_html = _x3d_html_template.replace("~fileoutput", fileoutput) 1383 x3d_html = x3d_html.replace("~width", str(wsize[0])) 1384 x3d_html = x3d_html.replace("~height", str(wsize[1])) 1385 with open(fileoutput.replace(".x3d", ".html"), "w", encoding="UTF-8") as outF: 1386 outF.write(x3d_html) 1387 1388 #################################################################### 1389 elif fr.endswith(".html"): 1390 savebk = vedo.notebook_backend 1391 vedo.notebook_backend = "k3d" 1392 vedo.settings.default_backend = "k3d" 1393 # acts = plt.get_actors() 1394 plt = vedo.backends.get_notebook_backend(plt.objects) 1395 1396 with open(fileoutput, "w", encoding="UTF-8") as fp: 1397 fp.write(plt.get_snapshot()) 1398 1399 vedo.notebook_backend = savebk 1400 vedo.settings.default_backend = savebk 1401 1402 else: 1403 vedo.logger.error(f"export extension {fr.split('.')[-1]} is not supported") 1404 1405 return plt 1406 1407######################################################################### 1408def _to_numpy(act: Any) -> dict: 1409 """Encode a vedo object to numpy format.""" 1410 1411 ######################################################## 1412 def _fillcommon(obj, adict): 1413 adict["filename"] = obj.filename 1414 adict["name"] = obj.name 1415 adict["time"] = obj.time 1416 adict["rendered_at"] = obj.rendered_at 1417 try: 1418 adict["transform"] = obj.transform.matrix 1419 except AttributeError: 1420 adict["transform"] = np.eye(4) 1421 1422 #################################################################### 1423 try: 1424 obj = act.retrieve_object() 1425 except AttributeError: 1426 obj = act 1427 1428 adict = {} 1429 adict["type"] = "unknown" 1430 1431 ######################################################## Points/Mesh 1432 if isinstance(obj, (Points, vedo.UnstructuredGrid)): 1433 adict["type"] = "Mesh" 1434 _fillcommon(obj, adict) 1435 1436 if isinstance(obj, vedo.UnstructuredGrid): 1437 # adict["type"] = "UnstructuredGrid" 1438 # adict["cells"] = obj.cells_as_flat_array 1439 poly = obj._actor.GetMapper().GetInput() 1440 mapper = obj._actor.GetMapper() 1441 else: 1442 poly = obj.dataset 1443 mapper = obj.mapper 1444 1445 adict["points"] = obj.vertices.astype(float) 1446 1447 adict["cells"] = None 1448 if poly.GetNumberOfPolys(): 1449 adict["cells"] = obj.cells_as_flat_array 1450 1451 adict["lines"] = None 1452 if poly.GetNumberOfLines(): 1453 adict["lines"] = obj.lines#_as_flat_array 1454 1455 adict["pointdata"] = {} 1456 for iname in obj.pointdata.keys(): 1457 if "normals" in iname.lower(): 1458 continue 1459 adict["pointdata"][iname] = obj.pointdata[iname] 1460 1461 adict["celldata"] = {} 1462 for iname in obj.celldata.keys(): 1463 if "normals" in iname.lower(): 1464 continue 1465 adict["celldata"][iname] = obj.celldata[iname] 1466 1467 adict["metadata"] = {} 1468 for iname in obj.metadata.keys(): 1469 adict["metadata"][iname] = obj.metadata[iname] 1470 1471 # NEW in vedo 5.0 1472 adict["scalar_mode"] = mapper.GetScalarMode() 1473 adict["array_name_to_color_by"] = mapper.GetArrayName() 1474 adict["color_mode"] = mapper.GetColorMode() 1475 adict["interpolate_scalars_before_mapping"] = mapper.GetInterpolateScalarsBeforeMapping() 1476 adict["use_lookup_table_scalar_range"] = mapper.GetUseLookupTableScalarRange() 1477 adict["scalar_range"] = mapper.GetScalarRange() 1478 adict["scalar_visibility"] = mapper.GetScalarVisibility() 1479 adict["pickable"] = obj.actor.GetPickable() 1480 adict["dragable"] = obj.actor.GetDragable() 1481 1482 # adict["color_map_colors"] = mapper.GetColorMapColors() #vtkUnsignedCharArray 1483 # adict["color_coordinates"] = mapper.GetColorCoordinates() #vtkFloatArray 1484 texmap = mapper.GetColorTextureMap() #vtkImageData 1485 if texmap: 1486 adict["color_texture_map"] = vedo.Image(texmap).tonumpy() 1487 # print("color_texture_map", adict["color_texture_map"].shape) 1488 1489 adict["texture_array"] = None 1490 texture = obj.actor.GetTexture() 1491 if texture: 1492 adict["texture_array"] = vedo.Image(texture.GetInput()).tonumpy() 1493 adict["texture_interpolate"] = texture.GetInterpolate() 1494 adict["texture_repeat"] = texture.GetRepeat() 1495 adict["texture_quality"] = texture.GetQuality() 1496 adict["texture_color_mode"] = texture.GetColorMode() 1497 adict["texture_mipmap"] = texture.GetMipmap() 1498 adict["texture_blending_mode"] = texture.GetBlendingMode() 1499 adict["texture_edge_clamp"] = texture.GetEdgeClamp() 1500 adict["texture_border_color"] = texture.GetBorderColor() 1501 # print("tonumpy: texture", obj.name, adict["texture_array"].shape) 1502 1503 adict["LUT"] = None 1504 adict["LUT_range"] = None 1505 lut = mapper.GetLookupTable() 1506 if lut: 1507 nlut = lut.GetNumberOfTableValues() 1508 lutvals = [] 1509 for i in range(nlut): 1510 v4 = lut.GetTableValue(i) # (r, g, b, alpha) 1511 lutvals.append(v4) 1512 adict["LUT"] = np.array(lutvals, dtype=np.float32) 1513 adict["LUT_range"] = np.array(lut.GetRange()) 1514 1515 prp = obj.properties 1516 adict["alpha"] = prp.GetOpacity() 1517 adict["representation"] = prp.GetRepresentation() 1518 adict["pointsize"] = prp.GetPointSize() 1519 1520 adict["linecolor"] = None 1521 adict["linewidth"] = None 1522 adict["edge_visibility"] = prp.GetEdgeVisibility() # new in vedo 5.0 1523 if prp.GetEdgeVisibility(): 1524 adict["linewidth"] = prp.GetLineWidth() 1525 adict["linecolor"] = prp.GetEdgeColor() 1526 1527 adict["ambient"] = prp.GetAmbient() 1528 adict["diffuse"] = prp.GetDiffuse() 1529 adict["specular"] = prp.GetSpecular() 1530 adict["specularpower"] = prp.GetSpecularPower() 1531 adict["specularcolor"] = prp.GetSpecularColor() 1532 adict["shading"] = prp.GetInterpolation() # flat phong..: 1533 adict["color"] = prp.GetColor() 1534 adict["lighting_is_on"] = prp.GetLighting() 1535 adict["backcolor"] = None 1536 if obj.actor.GetBackfaceProperty(): 1537 adict["backcolor"] = obj.actor.GetBackfaceProperty().GetColor() 1538 1539 ######################################################## Volume 1540 elif isinstance(obj, Volume): 1541 adict["type"] = "Volume" 1542 _fillcommon(obj, adict) 1543 adict["array"] = obj.tonumpy() 1544 adict["mode"] = obj.mode() 1545 adict["spacing"] = obj.spacing() 1546 adict["origin"] = obj.origin() 1547 1548 prp = obj.properties 1549 ctf = prp.GetRGBTransferFunction() 1550 otf = prp.GetScalarOpacity() 1551 gotf = prp.GetGradientOpacity() 1552 smin, smax = ctf.GetRange() 1553 xs = np.linspace(smin, smax, num=256, endpoint=True) 1554 cols, als, algrs = [], [], [] 1555 for x in xs: 1556 cols.append(ctf.GetColor(x)) 1557 als.append(otf.GetValue(x)) 1558 if gotf: 1559 algrs.append(gotf.GetValue(x)) 1560 adict["color"] = cols 1561 adict["alpha"] = als 1562 adict["alphagrad"] = algrs 1563 1564 ######################################################## Image 1565 elif isinstance(obj, Image): 1566 adict["type"] = "Image" 1567 _fillcommon(obj, adict) 1568 adict["array"] = obj.tonumpy() 1569 adict["scale"] = obj.actor.GetScale() 1570 adict["position"] = obj.actor.GetPosition() 1571 adict["orientation"] = obj.actor.GetOrientation() 1572 adict['origin'] = obj.actor.GetOrigin() 1573 adict["alpha"] = obj.alpha() 1574 1575 ######################################################## Text2D 1576 elif isinstance(obj, vedo.Text2D): 1577 adict["type"] = "Text2D" 1578 adict["rendered_at"] = obj.rendered_at 1579 adict["text"] = obj.text() 1580 adict["position"] = obj.GetPosition() 1581 adict["color"] = obj.properties.GetColor() 1582 adict["font"] = obj.fontname 1583 adict["size"] = obj.properties.GetFontSize() / 22.5 1584 adict["bgcol"] = obj.properties.GetBackgroundColor() 1585 adict["alpha"] = obj.properties.GetBackgroundOpacity() 1586 adict["frame"] = obj.properties.GetFrame() 1587 1588 else: 1589 # vedo.logger.warning(f"to_numpy: cannot export object of type {type(obj)}") 1590 pass 1591 1592 return adict 1593 1594 1595######################################################################### 1596def _export_npy(plt, fileoutput="scene.npz") -> None: 1597 1598 sdict = {} 1599 sdict["shape"] = plt.shape 1600 sdict["sharecam"] = plt.sharecam 1601 sdict["camera"] = dict( 1602 pos=plt.camera.GetPosition(), 1603 focal_point=plt.camera.GetFocalPoint(), 1604 viewup=plt.camera.GetViewUp(), 1605 distance=plt.camera.GetDistance(), 1606 clipping_range=plt.camera.GetClippingRange(), 1607 parallel_scale=plt.camera.GetParallelScale(), 1608 ) 1609 sdict["position"] = plt.pos 1610 sdict["size"] = plt.size 1611 sdict["axes"] = 0 1612 sdict["title"] = plt.title 1613 sdict["backgrcol"] = colors.get_color(plt.renderer.GetBackground()) 1614 sdict["backgrcol2"] = None 1615 if plt.renderer.GetGradientBackground(): 1616 sdict["backgrcol2"] = plt.renderer.GetBackground2() 1617 sdict["use_depth_peeling"] = plt.renderer.GetUseDepthPeeling() 1618 sdict["use_parallel_projection"] = plt.camera.GetParallelProjection() 1619 sdict["default_font"] = vedo.settings.default_font 1620 1621 sdict["objects"] = [] 1622 1623 actors = plt.get_actors(include_non_pickables=True) 1624 # this ^ also retrieves Actors2D 1625 allobjs = [] 1626 for i, a in enumerate(actors): 1627 1628 if not a.GetVisibility(): 1629 continue 1630 1631 try: 1632 ob = a.retrieve_object() 1633 # print("get_actors",[ob], ob.name) 1634 if isinstance(ob, Assembly): 1635 asse_scale = ob.GetScale() 1636 asse_pos = ob.GetPosition() 1637 asse_ori = ob.GetOrientation() 1638 asse_org = ob.GetOrigin() 1639 for elem in ob.unpack(): 1640 elem.name = f"ASSEMBLY{i}_{ob.name}_{elem.name}" 1641 # elem.info.update({"assembly": ob.name}) # TODO 1642 # elem.info.update({"assembly_scale": asse_scale}) 1643 # elem.info.update({"assembly_position": asse_pos}) 1644 # elem.info.update({"assembly_orientation": asse_ori}) 1645 # elem.info.update({"assembly_origin": asse_org}) 1646 elem.metadata["assembly"] = ob.name 1647 elem.metadata["assembly_scale"] = asse_scale 1648 elem.metadata["assembly_position"] = asse_pos 1649 elem.metadata["assembly_orientation"] = asse_ori 1650 elem.metadata["assembly_origin"] = asse_org 1651 allobjs.append(elem) 1652 else: 1653 allobjs.append(ob) 1654 1655 except AttributeError: 1656 # print() 1657 # vedo.logger.warning(f"Cannot retrieve object of type {type(a)}") 1658 pass 1659 1660 for a in allobjs: 1661 # print("to_numpy(): dumping", [a], a.name) 1662 # try: 1663 npobj = _to_numpy(a) 1664 sdict["objects"].append(npobj) 1665 # except AttributeError: 1666 # vedo.logger.warning(f"Cannot export object of type {type(a)}") 1667 1668 if fileoutput.endswith(".npz"): 1669 np.savez_compressed(fileoutput, vedo_scenes=[sdict]) 1670 else: 1671 np.save(fileoutput, [sdict]) 1672 1673 1674######################################################################## 1675def import_window(fileinput: str) -> Union["vedo.Plotter", None]: 1676 """ 1677 Import a whole scene from a Numpy NPZ file. 1678 1679 Returns: 1680 `vedo.Plotter` instance 1681 """ 1682 if fileinput.endswith(".npy") or fileinput.endswith(".npz"): 1683 return _import_npy(fileinput) 1684 1685 # elif ".obj" in fileinput.lower(): 1686 # meshes = load_obj(fileinput, mtl_file, texture_path) 1687 # plt = vedo.Plotter() 1688 # plt.add(meshes) 1689 # return plt 1690 1691 # elif fileinput.endswith(".h5") or fileinput.endswith(".hdf5"): 1692 # return _import_hdf5(fileinput) # in store/file_io_HDF5.py 1693 1694 return None 1695 1696 1697def load_obj(fileinput: str, mtl_file=None, texture_path=None) -> List[Mesh]: 1698 """ 1699 Import a set of meshes from a OBJ wavefront file. 1700 1701 Arguments: 1702 mtl_file : (str) 1703 MTL file for OBJ wavefront files 1704 texture_path : (str) 1705 path of the texture files directory 1706 1707 Returns: 1708 `list(Mesh)` 1709 """ 1710 window = vtki.vtkRenderWindow() 1711 window.SetOffScreenRendering(1) 1712 renderer = vtki.vtkRenderer() 1713 window.AddRenderer(renderer) 1714 1715 importer = vtki.new("OBJImporter") 1716 importer.SetFileName(fileinput) 1717 if mtl_file is None: 1718 mtl_file = fileinput.replace(".obj", ".mtl").replace(".OBJ", ".MTL") 1719 if os.path.isfile(mtl_file): 1720 importer.SetFileNameMTL(mtl_file) 1721 if texture_path is None: 1722 texture_path = fileinput.replace(".obj", ".txt").replace(".OBJ", ".TXT") 1723 # since the texture_path may be a directory which contains textures 1724 if os.path.exists(texture_path): 1725 importer.SetTexturePath(texture_path) 1726 importer.SetRenderWindow(window) 1727 importer.Update() 1728 1729 actors = renderer.GetActors() 1730 actors.InitTraversal() 1731 objs = [] 1732 for _ in range(actors.GetNumberOfItems()): 1733 vactor = actors.GetNextActor() 1734 msh = Mesh(vactor) 1735 msh.name = "OBJMesh" 1736 tx = vactor.GetTexture() 1737 if tx: 1738 msh.texture(tx) 1739 objs.append(msh) 1740 return objs 1741 1742 1743########################################################## 1744def screenshot(filename="screenshot.png", scale=1, asarray=False) -> Union["vedo.Plotter", np.ndarray, None]: 1745 """ 1746 Save a screenshot of the current rendering window. 1747 1748 Alternatively, press key `Shift-S` in the rendering window to save a screenshot. 1749 You can also use keyword `screenshot` in `show(..., screenshot="pic.png")`. 1750 1751 Arguments: 1752 scale : (int) 1753 Set image magnification as an integer multiplicative factor. 1754 E.g. setting a magnification of 2 produces an image twice as large, 1755 but 10x slower to generate. 1756 asarray : (bool) 1757 Return a numpy array of the image 1758 """ 1759 # print("calling screenshot", filename, scale, asarray) 1760 1761 if not vedo.plotter_instance or not vedo.plotter_instance.window: 1762 # vedo.logger.error("in screenshot(), rendering window is not present, skip.") 1763 return vedo.plotter_instance ########## 1764 1765 if asarray and scale == 1 and not vedo.plotter_instance.offscreen: 1766 nx, ny = vedo.plotter_instance.window.GetSize() 1767 arr = vtki.vtkUnsignedCharArray() 1768 vedo.plotter_instance.window.GetRGBACharPixelData(0, 0, nx-1, ny-1, 0, arr) 1769 narr = vedo.vtk2numpy(arr).T[:3].T.reshape([ny, nx, 3]) 1770 narr = np.flip(narr, axis=0) 1771 return narr ########## 1772 1773 filename = str(filename) 1774 1775 if filename.endswith(".pdf"): 1776 writer = vtki.new("GL2PSExporter") 1777 writer.SetRenderWindow(vedo.plotter_instance.window) 1778 writer.Write3DPropsAsRasterImageOff() 1779 writer.SilentOn() 1780 writer.SetSortToBSP() 1781 writer.SetFileFormatToPDF() 1782 writer.SetFilePrefix(filename.replace(".pdf", "")) 1783 writer.Write() 1784 return vedo.plotter_instance ########## 1785 1786 elif filename.endswith(".svg"): 1787 writer = vtki.new("GL2PSExporter") 1788 writer.SetRenderWindow(vedo.plotter_instance.window) 1789 writer.Write3DPropsAsRasterImageOff() 1790 writer.SilentOn() 1791 writer.SetSortToBSP() 1792 writer.SetFileFormatToSVG() 1793 writer.SetFilePrefix(filename.replace(".svg", "")) 1794 writer.Write() 1795 return vedo.plotter_instance ########## 1796 1797 elif filename.endswith(".eps"): 1798 writer = vtki.new("GL2PSExporter") 1799 writer.SetRenderWindow(vedo.plotter_instance.window) 1800 writer.Write3DPropsAsRasterImageOff() 1801 writer.SilentOn() 1802 writer.SetSortToBSP() 1803 writer.SetFileFormatToEPS() 1804 writer.SetFilePrefix(filename.replace(".eps", "")) 1805 writer.Write() 1806 return vedo.plotter_instance ########## 1807 1808 if settings.screeshot_large_image: 1809 w2if = vtki.new("RenderLargeImage") 1810 w2if.SetInput(vedo.plotter_instance.renderer) 1811 w2if.SetMagnification(scale) 1812 else: 1813 w2if = vtki.new("WindowToImageFilter") 1814 w2if.SetInput(vedo.plotter_instance.window) 1815 if hasattr(w2if, "SetScale"): 1816 w2if.SetScale(int(scale), int(scale)) 1817 if settings.screenshot_transparent_background: 1818 w2if.SetInputBufferTypeToRGBA() 1819 w2if.ReadFrontBufferOff() # read from the back buffer 1820 w2if.Update() 1821 1822 if asarray: 1823 pd = w2if.GetOutput().GetPointData() 1824 npdata = utils.vtk2numpy(pd.GetArray("ImageScalars")) 1825 npdata = npdata[:, [0, 1, 2]] 1826 ydim, xdim, _ = w2if.GetOutput().GetDimensions() 1827 npdata = npdata.reshape([xdim, ydim, -1]) 1828 npdata = np.flip(npdata, axis=0) 1829 return npdata ########################### 1830 1831 # elif settings.default_backend == "2d" and vedo.notebook_plotter: 1832 # vedo.notebook_plotter.save(filename) # a PIL Image 1833 # return vedo.notebook_plotter ########## 1834 1835 if filename.lower().endswith(".png"): 1836 writer = vtki.new("PNGWriter") 1837 writer.SetFileName(filename) 1838 writer.SetInputData(w2if.GetOutput()) 1839 writer.Write() 1840 elif filename.lower().endswith(".jpg") or filename.lower().endswith(".jpeg"): 1841 writer = vtki.new("JPEGWriter") 1842 writer.SetFileName(filename) 1843 writer.SetInputData(w2if.GetOutput()) 1844 writer.Write() 1845 else: # add .png 1846 writer = vtki.new("PNGWriter") 1847 writer.SetFileName(filename + ".png") 1848 writer.SetInputData(w2if.GetOutput()) 1849 writer.Write() 1850 return vedo.plotter_instance 1851 1852 1853def ask(*question, **kwarg) -> str: 1854 """ 1855 Ask a question from command line. Return the answer as a string. 1856 See function `colors.printc()` for the description of the keyword options. 1857 1858 Arguments: 1859 options : (list) 1860 a python list of possible answers to choose from. 1861 default : (str) 1862 the default answer when just hitting return. 1863 1864 Example: 1865 ```python 1866 import vedo 1867 res = vedo.ask("Continue?", options=['Y','n'], default='Y', c='g') 1868 print(res) 1869 ``` 1870 """ 1871 kwarg.update({"end": " "}) 1872 if "invert" not in kwarg: 1873 kwarg.update({"invert": True}) 1874 if "box" in kwarg: 1875 kwarg.update({"box": ""}) 1876 1877 options = kwarg.pop("options", []) 1878 default = kwarg.pop("default", "") 1879 if options: 1880 opt = "[" 1881 for o in options: 1882 opt += o + "/" 1883 opt = opt[:-1] + "]" 1884 colors.printc(*question, opt, **kwarg) 1885 else: 1886 colors.printc(*question, **kwarg) 1887 1888 try: 1889 resp = input() 1890 except Exception: 1891 resp = "" 1892 return resp 1893 1894 if options: 1895 if resp not in options: 1896 if default and str(repr(resp)) == "''": 1897 return default 1898 colors.printc("Please choose one option in:", opt, italic=True, bold=False) 1899 kwarg["options"] = options 1900 return ask(*question, **kwarg) # ask again 1901 return resp 1902 1903 1904############################################################################################## 1905class Video: 1906 """ 1907 Generate a video from a rendering window. 1908 """ 1909 1910 def __init__(self, name="movie.mp4", duration=None, fps=24, backend="imageio"): 1911 """ 1912 Class to generate a video from the specified rendering window. 1913 Program `ffmpeg` is used to create video from each generated frame. 1914 1915 Arguments: 1916 name : (str) 1917 name of the output file. 1918 fps : (int) 1919 set the number of frames per second. 1920 duration : (float) 1921 set the total `duration` of the video and recalculates `fps` accordingly. 1922 backend : (str) 1923 the backend engine to be used `['imageio', 'ffmpeg', 'cv']` 1924 1925 Examples: 1926 - [make_video.py](https://github.com/marcomusy/vedo/tree/master/examples/other/make_video.py) 1927 1928 ![](https://user-images.githubusercontent.com/32848391/50739007-2bfc2b80-11da-11e9-97e6-620a3541a6fa.jpg) 1929 """ 1930 self.name = name 1931 self.duration = duration 1932 self.backend = backend 1933 self.fps = float(fps) 1934 self.command = "ffmpeg -loglevel panic -y -r" 1935 self.options = "-b:v 8000k" 1936 1937 self.frames = [] 1938 self.tmp_dir = TemporaryDirectory() 1939 self.get_filename = lambda x: os.path.join(self.tmp_dir.name, x) 1940 colors.printc(":video: Video file", self.name, "is open... ", c="m", end="") 1941 1942 def add_frame(self) -> "Video": 1943 """Add frame to current video.""" 1944 fr = self.get_filename(str(len(self.frames)) + ".png") 1945 screenshot(fr) 1946 self.frames.append(fr) 1947 return self 1948 1949 def pause(self, pause=0) -> "Video": 1950 """Insert a `pause`, in seconds.""" 1951 fr = self.frames[-1] 1952 n = int(self.fps * pause) 1953 for _ in range(n): 1954 fr2 = self.get_filename(str(len(self.frames)) + ".png") 1955 self.frames.append(fr2) 1956 os.system("cp -f %s %s" % (fr, fr2)) 1957 return self 1958 1959 def action(self, elevation=(0, 80), azimuth=(0, 359), cameras=(), resetcam=False) -> "Video": 1960 """ 1961 Automatic shooting of a static scene by specifying rotation and elevation ranges. 1962 1963 Arguments: 1964 elevation : list 1965 initial and final elevation angles 1966 azimuth_range : list 1967 initial and final azimuth angles 1968 cameras : list 1969 list of cameras to go through, each camera can be dictionary or a vtkCamera 1970 """ 1971 if not self.duration: 1972 self.duration = 5 1973 1974 plt = vedo.plotter_instance 1975 if not plt: 1976 vedo.logger.error("No vedo plotter found, cannot make video.") 1977 return self 1978 n = int(self.fps * self.duration) 1979 1980 cams = [] 1981 for cm in cameras: 1982 cams.append(utils.camera_from_dict(cm)) 1983 nc = len(cams) 1984 1985 plt.show(resetcam=resetcam, interactive=False) 1986 1987 if nc: 1988 for i in range(n): 1989 plt.move_camera(cams, i / n) 1990 plt.show() 1991 self.add_frame() 1992 1993 else: ######################################## 1994 1995 for i in range(n): 1996 plt.camera.Elevation((elevation[1] - elevation[0]) / n) 1997 plt.camera.Azimuth((azimuth[1] - azimuth[0]) / n) 1998 plt.show() 1999 self.add_frame() 2000 2001 return self 2002 2003 def close(self) -> None: 2004 """ 2005 Render the video and write it to file. 2006 """ 2007 if self.duration: 2008 self.fps = int(len(self.frames) / float(self.duration) + 0.5) 2009 colors.printc("recalculated fps:", self.fps, c="m", end="") 2010 else: 2011 self.fps = int(self.fps) 2012 2013 ######################################## 2014 if self.backend == "ffmpeg": 2015 out = os.system( 2016 self.command 2017 + " " 2018 + str(self.fps) 2019 + " -i " 2020 + f"'{self.tmp_dir.name}'" 2021 + os.sep 2022 + "%01d.png " 2023 + self.options 2024 + " " 2025 + f"'{self.name}'" 2026 ) 2027 if out: 2028 vedo.logger.error(f":noentry: backend {self.backend} returning error: {out}") 2029 else: 2030 colors.printc(f":save: saved to {self.name}", c="m") 2031 2032 ######################################## 2033 elif "cv" in self.backend: 2034 try: 2035 import cv2 2036 except ImportError: 2037 vedo.logger.error("opencv is not installed") 2038 return 2039 2040 cap = cv2.VideoCapture(os.path.join(self.tmp_dir.name, "%1d.png")) 2041 fourcc = cv2.VideoWriter_fourcc(*"mp4v") 2042 if vedo.plotter_instance: 2043 w, h = vedo.plotter_instance.window.GetSize() 2044 writer = cv2.VideoWriter(self.name, fourcc, self.fps, (w, h), True) 2045 else: 2046 vedo.logger.error("No vedo plotter found, cannot make video.") 2047 return 2048 2049 while True: 2050 ret, frame = cap.read() 2051 if not ret: 2052 break 2053 writer.write(frame) 2054 2055 cap.release() 2056 writer.release() 2057 2058 ######################################## 2059 elif "imageio" in self.backend: 2060 try: 2061 import imageio 2062 except ImportError: 2063 vedo.logger.error("Please install imageio with:\n pip install imageio[ffmpeg]") 2064 return 2065 2066 if self.name.endswith(".mp4"): 2067 writer = imageio.get_writer(self.name, fps=self.fps) 2068 elif self.name.endswith(".gif"): 2069 writer = imageio.get_writer(self.name, mode="I", duration=1 / self.fps) 2070 elif self.name.endswith(".webm"): 2071 writer = imageio.get_writer(self.name, format="webm", fps=self.fps) 2072 else: 2073 vedo.logger.error(f"Unknown format of {self.name}.") 2074 return 2075 2076 for f in utils.humansort(self.frames): 2077 image = imageio.v3.imread(f) 2078 try: 2079 writer.append_data(image) 2080 except TypeError: 2081 vedo.logger.error(f"Could not append data to video {self.name}") 2082 vedo.logger.error("Please install imageio with: pip install imageio[ffmpeg]") 2083 break 2084 try: 2085 writer.close() 2086 colors.printc(f"... saved as {self.name}", c="m") 2087 except: 2088 colors.printc(f":noentry: Could not save video {self.name}", c="r") 2089 2090 # finalize cleanup 2091 self.tmp_dir.cleanup() 2092 2093 def split_frames(self, output_dir="video_frames", prefix="frame_", format="png") -> None: 2094 """Split an existing video file into frames.""" 2095 try: 2096 import imageio 2097 except ImportError: 2098 vedo.logger.error("\nPlease install imageio with:\n pip install imageio") 2099 return 2100 2101 # Create the output directory if it doesn't exist 2102 if not os.path.exists(output_dir): 2103 os.makedirs(output_dir) 2104 2105 # Create a reader object to read the video 2106 reader = imageio.get_reader(self.name) 2107 2108 # Loop through each frame of the video and save it as image 2109 print() 2110 for i, frame in utils.progressbar( 2111 enumerate(reader), title=f"writing {format} frames", c="m", width=20 2112 ): 2113 output_file = os.path.join(output_dir, f"{prefix}{str(i).zfill(5)}.{format}") 2114 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)
1332def read(obj: Any, unpack=True, force=False) -> Any: 1333 """Read an object from file. Same as `load()`.""" 1334 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 {:.8g} {:.8g} {:.8g}\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(".off"): 1259 with open(fileoutput, "w", encoding="UTF-8") as outF: 1260 outF.write("OFF\n") 1261 outF.write(str(objct.npoints) + " " + str(objct.ncells) + " 0\n\n") 1262 for p in objct.vertices: 1263 outF.write(" ".join([str(i) for i in p]) + "\n") 1264 for c in objct.cells: 1265 outF.write(str(len(c)) + " " + " ".join([str(i) for i in c]) + "\n") 1266 return objct 1267 1268 elif fr.endswith(".xml"): # write tetrahedral dolfin xml 1269 vertices = objct.vertices.astype(str) 1270 faces = np.array(objct.cells).astype(str) 1271 ncoords = vertices.shape[0] 1272 with open(fileoutput, "w", encoding="UTF-8") as outF: 1273 outF.write('<?xml version="1.0" encoding="UTF-8"?>\n') 1274 outF.write('<dolfin xmlns:dolfin="http://www.fenicsproject.org">\n') 1275 1276 if len(faces[0]) == 4: # write tetrahedral mesh 1277 ntets = faces.shape[0] 1278 outF.write(' <mesh celltype="tetrahedron" dim="3">\n') 1279 outF.write(' <vertices size="' + str(ncoords) + '">\n') 1280 for i in range(ncoords): 1281 x, y, z = vertices[i] 1282 outF.write(' <vertex index="'+str(i)+'" x="'+x+'" y="'+y+'" z="'+z+'"/>\n') 1283 outF.write(' </vertices>\n') 1284 outF.write(' <cells size="' + str(ntets) + '">\n') 1285 for i in range(ntets): 1286 v0, v1, v2, v3 = faces[i] 1287 outF.write(' <tetrahedron index="'+str(i) 1288 + '" v0="'+v0+'" v1="'+v1+'" v2="'+v2+'" v3="'+v3+'"/>\n') 1289 1290 elif len(faces[0]) == 3: # write triangle mesh 1291 ntri = faces.shape[0] 1292 outF.write(' <mesh celltype="triangle" dim="2">\n') 1293 outF.write(' <vertices size="' + str(ncoords) + '">\n') 1294 for i in range(ncoords): 1295 x, y, _ = vertices[i] 1296 outF.write(' <vertex index="'+str(i)+'" x="'+x+'" y="'+y+'"/>\n') 1297 outF.write(' </vertices>\n') 1298 outF.write(' <cells size="' + str(ntri) + '">\n') 1299 for i in range(ntri): 1300 v0, v1, v2 = faces[i] 1301 outF.write(' <triangle index="'+str(i)+'" v0="'+v0+'" v1="'+v1+'" v2="'+v2+'"/>\n') 1302 1303 outF.write(" </cells>\n") 1304 outF.write(" </mesh>\n") 1305 outF.write("</dolfin>\n") 1306 return objct 1307 1308 else: 1309 vedo.logger.error(f"Unknown format {fileoutput}, file not saved") 1310 return objct 1311 1312 try: 1313 if binary: 1314 writer.SetFileTypeToBinary() 1315 else: 1316 writer.SetFileTypeToASCII() 1317 except AttributeError: 1318 pass 1319 1320 try: 1321 writer.SetInputData(obj) 1322 writer.SetFileName(fileoutput) 1323 writer.Write() 1324 except: 1325 vedo.logger.error(f"could not save {fileoutput}") 1326 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
1328def save(obj: Any, fileoutput="out.png", binary=True) -> Any: 1329 """Save an object to file. Same as `write()`.""" 1330 return write(obj, fileoutput, binary)
Save an object to file. Same as write()
.
1337def export_window(fileoutput: str, binary=False, plt=None) -> "vedo.Plotter": 1338 """ 1339 Exporter which writes out the rendered scene into an HTML, X3D or Numpy file. 1340 1341 Example: 1342 - [export_x3d.py](https://github.com/marcomusy/vedo/tree/master/examples/other/export_x3d.py) 1343 1344 Check out the HTML generated webpage [here](https://vedo.embl.es/examples/embryo.html). 1345 1346 <img src='https://user-images.githubusercontent.com/32848391/57160341-c6ffbd80-6de8-11e9-95ff-7215ce642bc5.jpg' width="600"/> 1347 1348 .. note:: 1349 the rendering window can also be exported to `numpy` file `scene.npz` 1350 by pressing `E` key at any moment during visualization. 1351 """ 1352 if plt is None: 1353 plt = vedo.plotter_instance 1354 1355 fr = fileoutput.lower() 1356 #################################################################### 1357 if fr.endswith(".npy") or fr.endswith(".npz"): 1358 _export_npy(plt, fileoutput) 1359 1360 #################################################################### 1361 elif fr.endswith(".x3d"): 1362 # obj = plt.get_actors() 1363 # if plt.axes_instances: 1364 # obj.append(plt.axes_instances[0]) 1365 1366 # for a in obj: 1367 # if isinstance(a, Assembly): 1368 # plt.remove(a) 1369 # plt.add(a.unpack()) 1370 1371 plt.render() 1372 1373 exporter = vtki.new("X3DExporter") 1374 exporter.SetBinary(binary) 1375 exporter.FastestOff() 1376 exporter.SetInput(plt.window) 1377 exporter.SetFileName(fileoutput) 1378 # exporter.WriteToOutputStringOn() 1379 exporter.Update() 1380 exporter.Write() 1381 1382 wsize = plt.window.GetSize() 1383 x3d_html = _x3d_html_template.replace("~fileoutput", fileoutput) 1384 x3d_html = x3d_html.replace("~width", str(wsize[0])) 1385 x3d_html = x3d_html.replace("~height", str(wsize[1])) 1386 with open(fileoutput.replace(".x3d", ".html"), "w", encoding="UTF-8") as outF: 1387 outF.write(x3d_html) 1388 1389 #################################################################### 1390 elif fr.endswith(".html"): 1391 savebk = vedo.notebook_backend 1392 vedo.notebook_backend = "k3d" 1393 vedo.settings.default_backend = "k3d" 1394 # acts = plt.get_actors() 1395 plt = vedo.backends.get_notebook_backend(plt.objects) 1396 1397 with open(fileoutput, "w", encoding="UTF-8") as fp: 1398 fp.write(plt.get_snapshot()) 1399 1400 vedo.notebook_backend = savebk 1401 vedo.settings.default_backend = savebk 1402 1403 else: 1404 vedo.logger.error(f"export extension {fr.split('.')[-1]} is not supported") 1405 1406 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.
1676def import_window(fileinput: str) -> Union["vedo.Plotter", None]: 1677 """ 1678 Import a whole scene from a Numpy NPZ file. 1679 1680 Returns: 1681 `vedo.Plotter` instance 1682 """ 1683 if fileinput.endswith(".npy") or fileinput.endswith(".npz"): 1684 return _import_npy(fileinput) 1685 1686 # elif ".obj" in fileinput.lower(): 1687 # meshes = load_obj(fileinput, mtl_file, texture_path) 1688 # plt = vedo.Plotter() 1689 # plt.add(meshes) 1690 # return plt 1691 1692 # elif fileinput.endswith(".h5") or fileinput.endswith(".hdf5"): 1693 # return _import_hdf5(fileinput) # in store/file_io_HDF5.py 1694 1695 return None
Import a whole scene from a Numpy NPZ file.
Returns:
vedo.Plotter
instance
1698def load_obj(fileinput: str, mtl_file=None, texture_path=None) -> List[Mesh]: 1699 """ 1700 Import a set of meshes from a OBJ wavefront file. 1701 1702 Arguments: 1703 mtl_file : (str) 1704 MTL file for OBJ wavefront files 1705 texture_path : (str) 1706 path of the texture files directory 1707 1708 Returns: 1709 `list(Mesh)` 1710 """ 1711 window = vtki.vtkRenderWindow() 1712 window.SetOffScreenRendering(1) 1713 renderer = vtki.vtkRenderer() 1714 window.AddRenderer(renderer) 1715 1716 importer = vtki.new("OBJImporter") 1717 importer.SetFileName(fileinput) 1718 if mtl_file is None: 1719 mtl_file = fileinput.replace(".obj", ".mtl").replace(".OBJ", ".MTL") 1720 if os.path.isfile(mtl_file): 1721 importer.SetFileNameMTL(mtl_file) 1722 if texture_path is None: 1723 texture_path = fileinput.replace(".obj", ".txt").replace(".OBJ", ".TXT") 1724 # since the texture_path may be a directory which contains textures 1725 if os.path.exists(texture_path): 1726 importer.SetTexturePath(texture_path) 1727 importer.SetRenderWindow(window) 1728 importer.Update() 1729 1730 actors = renderer.GetActors() 1731 actors.InitTraversal() 1732 objs = [] 1733 for _ in range(actors.GetNumberOfItems()): 1734 vactor = actors.GetNextActor() 1735 msh = Mesh(vactor) 1736 msh.name = "OBJMesh" 1737 tx = vactor.GetTexture() 1738 if tx: 1739 msh.texture(tx) 1740 objs.append(msh) 1741 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)
1745def screenshot(filename="screenshot.png", scale=1, asarray=False) -> Union["vedo.Plotter", np.ndarray, None]: 1746 """ 1747 Save a screenshot of the current rendering window. 1748 1749 Alternatively, press key `Shift-S` in the rendering window to save a screenshot. 1750 You can also use keyword `screenshot` in `show(..., screenshot="pic.png")`. 1751 1752 Arguments: 1753 scale : (int) 1754 Set image magnification as an integer multiplicative factor. 1755 E.g. setting a magnification of 2 produces an image twice as large, 1756 but 10x slower to generate. 1757 asarray : (bool) 1758 Return a numpy array of the image 1759 """ 1760 # print("calling screenshot", filename, scale, asarray) 1761 1762 if not vedo.plotter_instance or not vedo.plotter_instance.window: 1763 # vedo.logger.error("in screenshot(), rendering window is not present, skip.") 1764 return vedo.plotter_instance ########## 1765 1766 if asarray and scale == 1 and not vedo.plotter_instance.offscreen: 1767 nx, ny = vedo.plotter_instance.window.GetSize() 1768 arr = vtki.vtkUnsignedCharArray() 1769 vedo.plotter_instance.window.GetRGBACharPixelData(0, 0, nx-1, ny-1, 0, arr) 1770 narr = vedo.vtk2numpy(arr).T[:3].T.reshape([ny, nx, 3]) 1771 narr = np.flip(narr, axis=0) 1772 return narr ########## 1773 1774 filename = str(filename) 1775 1776 if filename.endswith(".pdf"): 1777 writer = vtki.new("GL2PSExporter") 1778 writer.SetRenderWindow(vedo.plotter_instance.window) 1779 writer.Write3DPropsAsRasterImageOff() 1780 writer.SilentOn() 1781 writer.SetSortToBSP() 1782 writer.SetFileFormatToPDF() 1783 writer.SetFilePrefix(filename.replace(".pdf", "")) 1784 writer.Write() 1785 return vedo.plotter_instance ########## 1786 1787 elif filename.endswith(".svg"): 1788 writer = vtki.new("GL2PSExporter") 1789 writer.SetRenderWindow(vedo.plotter_instance.window) 1790 writer.Write3DPropsAsRasterImageOff() 1791 writer.SilentOn() 1792 writer.SetSortToBSP() 1793 writer.SetFileFormatToSVG() 1794 writer.SetFilePrefix(filename.replace(".svg", "")) 1795 writer.Write() 1796 return vedo.plotter_instance ########## 1797 1798 elif filename.endswith(".eps"): 1799 writer = vtki.new("GL2PSExporter") 1800 writer.SetRenderWindow(vedo.plotter_instance.window) 1801 writer.Write3DPropsAsRasterImageOff() 1802 writer.SilentOn() 1803 writer.SetSortToBSP() 1804 writer.SetFileFormatToEPS() 1805 writer.SetFilePrefix(filename.replace(".eps", "")) 1806 writer.Write() 1807 return vedo.plotter_instance ########## 1808 1809 if settings.screeshot_large_image: 1810 w2if = vtki.new("RenderLargeImage") 1811 w2if.SetInput(vedo.plotter_instance.renderer) 1812 w2if.SetMagnification(scale) 1813 else: 1814 w2if = vtki.new("WindowToImageFilter") 1815 w2if.SetInput(vedo.plotter_instance.window) 1816 if hasattr(w2if, "SetScale"): 1817 w2if.SetScale(int(scale), int(scale)) 1818 if settings.screenshot_transparent_background: 1819 w2if.SetInputBufferTypeToRGBA() 1820 w2if.ReadFrontBufferOff() # read from the back buffer 1821 w2if.Update() 1822 1823 if asarray: 1824 pd = w2if.GetOutput().GetPointData() 1825 npdata = utils.vtk2numpy(pd.GetArray("ImageScalars")) 1826 npdata = npdata[:, [0, 1, 2]] 1827 ydim, xdim, _ = w2if.GetOutput().GetDimensions() 1828 npdata = npdata.reshape([xdim, ydim, -1]) 1829 npdata = np.flip(npdata, axis=0) 1830 return npdata ########################### 1831 1832 # elif settings.default_backend == "2d" and vedo.notebook_plotter: 1833 # vedo.notebook_plotter.save(filename) # a PIL Image 1834 # return vedo.notebook_plotter ########## 1835 1836 if filename.lower().endswith(".png"): 1837 writer = vtki.new("PNGWriter") 1838 writer.SetFileName(filename) 1839 writer.SetInputData(w2if.GetOutput()) 1840 writer.Write() 1841 elif filename.lower().endswith(".jpg") or filename.lower().endswith(".jpeg"): 1842 writer = vtki.new("JPEGWriter") 1843 writer.SetFileName(filename) 1844 writer.SetInputData(w2if.GetOutput()) 1845 writer.Write() 1846 else: # add .png 1847 writer = vtki.new("PNGWriter") 1848 writer.SetFileName(filename + ".png") 1849 writer.SetInputData(w2if.GetOutput()) 1850 writer.Write() 1851 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
1854def ask(*question, **kwarg) -> str: 1855 """ 1856 Ask a question from command line. Return the answer as a string. 1857 See function `colors.printc()` for the description of the keyword options. 1858 1859 Arguments: 1860 options : (list) 1861 a python list of possible answers to choose from. 1862 default : (str) 1863 the default answer when just hitting return. 1864 1865 Example: 1866 ```python 1867 import vedo 1868 res = vedo.ask("Continue?", options=['Y','n'], default='Y', c='g') 1869 print(res) 1870 ``` 1871 """ 1872 kwarg.update({"end": " "}) 1873 if "invert" not in kwarg: 1874 kwarg.update({"invert": True}) 1875 if "box" in kwarg: 1876 kwarg.update({"box": ""}) 1877 1878 options = kwarg.pop("options", []) 1879 default = kwarg.pop("default", "") 1880 if options: 1881 opt = "[" 1882 for o in options: 1883 opt += o + "/" 1884 opt = opt[:-1] + "]" 1885 colors.printc(*question, opt, **kwarg) 1886 else: 1887 colors.printc(*question, **kwarg) 1888 1889 try: 1890 resp = input() 1891 except Exception: 1892 resp = "" 1893 return resp 1894 1895 if options: 1896 if resp not in options: 1897 if default and str(repr(resp)) == "''": 1898 return default 1899 colors.printc("Please choose one option in:", opt, italic=True, bold=False) 1900 kwarg["options"] = options 1901 return ask(*question, **kwarg) # ask again 1902 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)
1906class Video: 1907 """ 1908 Generate a video from a rendering window. 1909 """ 1910 1911 def __init__(self, name="movie.mp4", duration=None, fps=24, backend="imageio"): 1912 """ 1913 Class to generate a video from the specified rendering window. 1914 Program `ffmpeg` is used to create video from each generated frame. 1915 1916 Arguments: 1917 name : (str) 1918 name of the output file. 1919 fps : (int) 1920 set the number of frames per second. 1921 duration : (float) 1922 set the total `duration` of the video and recalculates `fps` accordingly. 1923 backend : (str) 1924 the backend engine to be used `['imageio', 'ffmpeg', 'cv']` 1925 1926 Examples: 1927 - [make_video.py](https://github.com/marcomusy/vedo/tree/master/examples/other/make_video.py) 1928 1929 ![](https://user-images.githubusercontent.com/32848391/50739007-2bfc2b80-11da-11e9-97e6-620a3541a6fa.jpg) 1930 """ 1931 self.name = name 1932 self.duration = duration 1933 self.backend = backend 1934 self.fps = float(fps) 1935 self.command = "ffmpeg -loglevel panic -y -r" 1936 self.options = "-b:v 8000k" 1937 1938 self.frames = [] 1939 self.tmp_dir = TemporaryDirectory() 1940 self.get_filename = lambda x: os.path.join(self.tmp_dir.name, x) 1941 colors.printc(":video: Video file", self.name, "is open... ", c="m", end="") 1942 1943 def add_frame(self) -> "Video": 1944 """Add frame to current video.""" 1945 fr = self.get_filename(str(len(self.frames)) + ".png") 1946 screenshot(fr) 1947 self.frames.append(fr) 1948 return self 1949 1950 def pause(self, pause=0) -> "Video": 1951 """Insert a `pause`, in seconds.""" 1952 fr = self.frames[-1] 1953 n = int(self.fps * pause) 1954 for _ in range(n): 1955 fr2 = self.get_filename(str(len(self.frames)) + ".png") 1956 self.frames.append(fr2) 1957 os.system("cp -f %s %s" % (fr, fr2)) 1958 return self 1959 1960 def action(self, elevation=(0, 80), azimuth=(0, 359), cameras=(), resetcam=False) -> "Video": 1961 """ 1962 Automatic shooting of a static scene by specifying rotation and elevation ranges. 1963 1964 Arguments: 1965 elevation : list 1966 initial and final elevation angles 1967 azimuth_range : list 1968 initial and final azimuth angles 1969 cameras : list 1970 list of cameras to go through, each camera can be dictionary or a vtkCamera 1971 """ 1972 if not self.duration: 1973 self.duration = 5 1974 1975 plt = vedo.plotter_instance 1976 if not plt: 1977 vedo.logger.error("No vedo plotter found, cannot make video.") 1978 return self 1979 n = int(self.fps * self.duration) 1980 1981 cams = [] 1982 for cm in cameras: 1983 cams.append(utils.camera_from_dict(cm)) 1984 nc = len(cams) 1985 1986 plt.show(resetcam=resetcam, interactive=False) 1987 1988 if nc: 1989 for i in range(n): 1990 plt.move_camera(cams, i / n) 1991 plt.show() 1992 self.add_frame() 1993 1994 else: ######################################## 1995 1996 for i in range(n): 1997 plt.camera.Elevation((elevation[1] - elevation[0]) / n) 1998 plt.camera.Azimuth((azimuth[1] - azimuth[0]) / n) 1999 plt.show() 2000 self.add_frame() 2001 2002 return self 2003 2004 def close(self) -> None: 2005 """ 2006 Render the video and write it to file. 2007 """ 2008 if self.duration: 2009 self.fps = int(len(self.frames) / float(self.duration) + 0.5) 2010 colors.printc("recalculated fps:", self.fps, c="m", end="") 2011 else: 2012 self.fps = int(self.fps) 2013 2014 ######################################## 2015 if self.backend == "ffmpeg": 2016 out = os.system( 2017 self.command 2018 + " " 2019 + str(self.fps) 2020 + " -i " 2021 + f"'{self.tmp_dir.name}'" 2022 + os.sep 2023 + "%01d.png " 2024 + self.options 2025 + " " 2026 + f"'{self.name}'" 2027 ) 2028 if out: 2029 vedo.logger.error(f":noentry: backend {self.backend} returning error: {out}") 2030 else: 2031 colors.printc(f":save: saved to {self.name}", c="m") 2032 2033 ######################################## 2034 elif "cv" in self.backend: 2035 try: 2036 import cv2 2037 except ImportError: 2038 vedo.logger.error("opencv is not installed") 2039 return 2040 2041 cap = cv2.VideoCapture(os.path.join(self.tmp_dir.name, "%1d.png")) 2042 fourcc = cv2.VideoWriter_fourcc(*"mp4v") 2043 if vedo.plotter_instance: 2044 w, h = vedo.plotter_instance.window.GetSize() 2045 writer = cv2.VideoWriter(self.name, fourcc, self.fps, (w, h), True) 2046 else: 2047 vedo.logger.error("No vedo plotter found, cannot make video.") 2048 return 2049 2050 while True: 2051 ret, frame = cap.read() 2052 if not ret: 2053 break 2054 writer.write(frame) 2055 2056 cap.release() 2057 writer.release() 2058 2059 ######################################## 2060 elif "imageio" in self.backend: 2061 try: 2062 import imageio 2063 except ImportError: 2064 vedo.logger.error("Please install imageio with:\n pip install imageio[ffmpeg]") 2065 return 2066 2067 if self.name.endswith(".mp4"): 2068 writer = imageio.get_writer(self.name, fps=self.fps) 2069 elif self.name.endswith(".gif"): 2070 writer = imageio.get_writer(self.name, mode="I", duration=1 / self.fps) 2071 elif self.name.endswith(".webm"): 2072 writer = imageio.get_writer(self.name, format="webm", fps=self.fps) 2073 else: 2074 vedo.logger.error(f"Unknown format of {self.name}.") 2075 return 2076 2077 for f in utils.humansort(self.frames): 2078 image = imageio.v3.imread(f) 2079 try: 2080 writer.append_data(image) 2081 except TypeError: 2082 vedo.logger.error(f"Could not append data to video {self.name}") 2083 vedo.logger.error("Please install imageio with: pip install imageio[ffmpeg]") 2084 break 2085 try: 2086 writer.close() 2087 colors.printc(f"... saved as {self.name}", c="m") 2088 except: 2089 colors.printc(f":noentry: Could not save video {self.name}", c="r") 2090 2091 # finalize cleanup 2092 self.tmp_dir.cleanup() 2093 2094 def split_frames(self, output_dir="video_frames", prefix="frame_", format="png") -> None: 2095 """Split an existing video file into frames.""" 2096 try: 2097 import imageio 2098 except ImportError: 2099 vedo.logger.error("\nPlease install imageio with:\n pip install imageio") 2100 return 2101 2102 # Create the output directory if it doesn't exist 2103 if not os.path.exists(output_dir): 2104 os.makedirs(output_dir) 2105 2106 # Create a reader object to read the video 2107 reader = imageio.get_reader(self.name) 2108 2109 # Loop through each frame of the video and save it as image 2110 print() 2111 for i, frame in utils.progressbar( 2112 enumerate(reader), title=f"writing {format} frames", c="m", width=20 2113 ): 2114 output_file = os.path.join(output_dir, f"{prefix}{str(i).zfill(5)}.{format}") 2115 imageio.imwrite(output_file, frame, format=format)
Generate a video from a rendering window.
1911 def __init__(self, name="movie.mp4", duration=None, fps=24, backend="imageio"): 1912 """ 1913 Class to generate a video from the specified rendering window. 1914 Program `ffmpeg` is used to create video from each generated frame. 1915 1916 Arguments: 1917 name : (str) 1918 name of the output file. 1919 fps : (int) 1920 set the number of frames per second. 1921 duration : (float) 1922 set the total `duration` of the video and recalculates `fps` accordingly. 1923 backend : (str) 1924 the backend engine to be used `['imageio', 'ffmpeg', 'cv']` 1925 1926 Examples: 1927 - [make_video.py](https://github.com/marcomusy/vedo/tree/master/examples/other/make_video.py) 1928 1929 ![](https://user-images.githubusercontent.com/32848391/50739007-2bfc2b80-11da-11e9-97e6-620a3541a6fa.jpg) 1930 """ 1931 self.name = name 1932 self.duration = duration 1933 self.backend = backend 1934 self.fps = float(fps) 1935 self.command = "ffmpeg -loglevel panic -y -r" 1936 self.options = "-b:v 8000k" 1937 1938 self.frames = [] 1939 self.tmp_dir = TemporaryDirectory() 1940 self.get_filename = lambda x: os.path.join(self.tmp_dir.name, x) 1941 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:
1943 def add_frame(self) -> "Video": 1944 """Add frame to current video.""" 1945 fr = self.get_filename(str(len(self.frames)) + ".png") 1946 screenshot(fr) 1947 self.frames.append(fr) 1948 return self
Add frame to current video.
1950 def pause(self, pause=0) -> "Video": 1951 """Insert a `pause`, in seconds.""" 1952 fr = self.frames[-1] 1953 n = int(self.fps * pause) 1954 for _ in range(n): 1955 fr2 = self.get_filename(str(len(self.frames)) + ".png") 1956 self.frames.append(fr2) 1957 os.system("cp -f %s %s" % (fr, fr2)) 1958 return self
Insert a pause
, in seconds.
1960 def action(self, elevation=(0, 80), azimuth=(0, 359), cameras=(), resetcam=False) -> "Video": 1961 """ 1962 Automatic shooting of a static scene by specifying rotation and elevation ranges. 1963 1964 Arguments: 1965 elevation : list 1966 initial and final elevation angles 1967 azimuth_range : list 1968 initial and final azimuth angles 1969 cameras : list 1970 list of cameras to go through, each camera can be dictionary or a vtkCamera 1971 """ 1972 if not self.duration: 1973 self.duration = 5 1974 1975 plt = vedo.plotter_instance 1976 if not plt: 1977 vedo.logger.error("No vedo plotter found, cannot make video.") 1978 return self 1979 n = int(self.fps * self.duration) 1980 1981 cams = [] 1982 for cm in cameras: 1983 cams.append(utils.camera_from_dict(cm)) 1984 nc = len(cams) 1985 1986 plt.show(resetcam=resetcam, interactive=False) 1987 1988 if nc: 1989 for i in range(n): 1990 plt.move_camera(cams, i / n) 1991 plt.show() 1992 self.add_frame() 1993 1994 else: ######################################## 1995 1996 for i in range(n): 1997 plt.camera.Elevation((elevation[1] - elevation[0]) / n) 1998 plt.camera.Azimuth((azimuth[1] - azimuth[0]) / n) 1999 plt.show() 2000 self.add_frame() 2001 2002 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
2004 def close(self) -> None: 2005 """ 2006 Render the video and write it to file. 2007 """ 2008 if self.duration: 2009 self.fps = int(len(self.frames) / float(self.duration) + 0.5) 2010 colors.printc("recalculated fps:", self.fps, c="m", end="") 2011 else: 2012 self.fps = int(self.fps) 2013 2014 ######################################## 2015 if self.backend == "ffmpeg": 2016 out = os.system( 2017 self.command 2018 + " " 2019 + str(self.fps) 2020 + " -i " 2021 + f"'{self.tmp_dir.name}'" 2022 + os.sep 2023 + "%01d.png " 2024 + self.options 2025 + " " 2026 + f"'{self.name}'" 2027 ) 2028 if out: 2029 vedo.logger.error(f":noentry: backend {self.backend} returning error: {out}") 2030 else: 2031 colors.printc(f":save: saved to {self.name}", c="m") 2032 2033 ######################################## 2034 elif "cv" in self.backend: 2035 try: 2036 import cv2 2037 except ImportError: 2038 vedo.logger.error("opencv is not installed") 2039 return 2040 2041 cap = cv2.VideoCapture(os.path.join(self.tmp_dir.name, "%1d.png")) 2042 fourcc = cv2.VideoWriter_fourcc(*"mp4v") 2043 if vedo.plotter_instance: 2044 w, h = vedo.plotter_instance.window.GetSize() 2045 writer = cv2.VideoWriter(self.name, fourcc, self.fps, (w, h), True) 2046 else: 2047 vedo.logger.error("No vedo plotter found, cannot make video.") 2048 return 2049 2050 while True: 2051 ret, frame = cap.read() 2052 if not ret: 2053 break 2054 writer.write(frame) 2055 2056 cap.release() 2057 writer.release() 2058 2059 ######################################## 2060 elif "imageio" in self.backend: 2061 try: 2062 import imageio 2063 except ImportError: 2064 vedo.logger.error("Please install imageio with:\n pip install imageio[ffmpeg]") 2065 return 2066 2067 if self.name.endswith(".mp4"): 2068 writer = imageio.get_writer(self.name, fps=self.fps) 2069 elif self.name.endswith(".gif"): 2070 writer = imageio.get_writer(self.name, mode="I", duration=1 / self.fps) 2071 elif self.name.endswith(".webm"): 2072 writer = imageio.get_writer(self.name, format="webm", fps=self.fps) 2073 else: 2074 vedo.logger.error(f"Unknown format of {self.name}.") 2075 return 2076 2077 for f in utils.humansort(self.frames): 2078 image = imageio.v3.imread(f) 2079 try: 2080 writer.append_data(image) 2081 except TypeError: 2082 vedo.logger.error(f"Could not append data to video {self.name}") 2083 vedo.logger.error("Please install imageio with: pip install imageio[ffmpeg]") 2084 break 2085 try: 2086 writer.close() 2087 colors.printc(f"... saved as {self.name}", c="m") 2088 except: 2089 colors.printc(f":noentry: Could not save video {self.name}", c="r") 2090 2091 # finalize cleanup 2092 self.tmp_dir.cleanup()
Render the video and write it to file.
2094 def split_frames(self, output_dir="video_frames", prefix="frame_", format="png") -> None: 2095 """Split an existing video file into frames.""" 2096 try: 2097 import imageio 2098 except ImportError: 2099 vedo.logger.error("\nPlease install imageio with:\n pip install imageio") 2100 return 2101 2102 # Create the output directory if it doesn't exist 2103 if not os.path.exists(output_dir): 2104 os.makedirs(output_dir) 2105 2106 # Create a reader object to read the video 2107 reader = imageio.get_reader(self.name) 2108 2109 # Loop through each frame of the video and save it as image 2110 print() 2111 for i, frame in utils.progressbar( 2112 enumerate(reader), title=f"writing {format} frames", c="m", width=20 2113 ): 2114 output_file = os.path.join(output_dir, f"{prefix}{str(i).zfill(5)}.{format}") 2115 imageio.imwrite(output_file, frame, format=format)
Split an existing video file into frames.