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