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