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