Beware of the STL file format

Many topics on this forum mention the STL format. While it is great that Slicer supports so many formats, I would advocate that Slicer discourage users from exporting to STL (e.g. from the “Save Scene and Unsaved Data” dialog change the file format pull-down to list “STL (.stl) deprecated” versus the current “STL (.stl)”. The only reason to export to STL is if it is the only format supported by the software or 3D printer you are using. Fortunately, modern 3D printing companies like Shapeways now support better formats such as OBJ.

The fundamental issue with STL is that it does not reuse vertices. This means that the resulting meshes will either look faceted (e.g. Slicer) or will be extremely slow to load (if the software attempts to unify vertices, e.g. Surfice). In addition, this feature makes this format exceptionally inefficient yielding large file sizes (about 3 times the file size of a simple uncompressed binary format). Not only are the faceted images ugly, they are slower and more resource intensive to render with modern methods.

The issue is illustrated in the image below. This is a mesh created with Slicer’s Editor function and then the same mesh is saved as both STL and OBJ format. The two meshes are then reloaded in Slicer. Note that the OBJ file has nice per-pixel shading, while the STL file has jagged per-triangle shading.

Given that the format is so popular with Slicer users, the other option is to support vertex-unification when STL files are loaded. This will make the loaded STL images look nicer (though loading times will increase).

1 Like

Hi @Chris_Rorden - I totally agree that STL is a terrible format for storing geometry and should be avoided whenever possible. As you point out, many 3D printer companies don’t seem to realize this and use it for some kind of legacy reasons (even though it must make their lives harder too). Unfortunately the user community seems to have gotten into the habit of saying STL whenever they mean polygon surface mesh and so the cycle continues.

Regarding your specific proposal, maybe instead of adding the word ‘deprecated’ how about adding ‘discouraged’ or some other term. I think we should reserve ‘deprecated’ for things we’ll be removing in an upcoming release, but, alas, I don’t think we will ever be able to remove STL.

Very interesting discussion. I don’t like STL file format either and very surprised that is is still so widely used. However, I find most consumer/gaming mesh formats about equally bad for medical image computing. Yes, STL is the most basic format, and yes, OBJ is better in a couple of things (it supports polygon cells, surface normals, textures), but it has some disadvantages, too, even compared to STL.

Some of the problems with OBJ:

  • it is not possible to specify unit (mm/inches/m) or coordinate system (LPS/RAS/…) in a standard way (same as STL)
  • no way to store arbitrary metadata (same as STL)
  • it is ASCII only, so files are larger and/or lose precision and much slower to parse than binary STL (worse than STL)
  • we cannot claim to be fully compatible with OBJ, as it has some features that would be very difficult to fully support, such as NURBS curves and surfaces, or multi-texture (worse than STL)

VTK clean polydata filter can merge coincident points and compute surface normals in negligible time, so computation time should not be an issue.

We’ve been planning to add an optional normal computation step into the model display pipeline. This could be enabled by default for STL files or for any other surface mesh files that did not happen to have the normals available.

Standard OBJ format is text only, so file sizes are larger than binary STL.

Adding “deprecated” there sounds too harsh to me (and as @pieper pointed out not even accurate). But even if we could find a better word: I would not single out STL as the bad guy. Is PLY any better? Would you always recommend OBJ over STL? We should probably just try to educate users on the forum, documentation, etc. where there is a room for explaining our rationale for preferring one file format over another.

In the near future, I’ll modify the vtk obj so that there is no loss in precision. I used the class vtkNumberToString in the vtkXMLWriter.

1 Like

This will be great, thank you, one issue will be taken care of then!

(Still, writing/reading numbers as text will be much slower and file size will be larger compared to using a binary file format, and other OBJ file issues still remain.)

I just submitted an MR to improve the saved floating point values.

PLY format reuses vertices, so it does not suffer the issues of STL. I for one do single out STL as the worst popular mesh format as it is the only one that does not reuse vertices.

As @lassoan notes, a major disadvantage of OBJ is that it is only ASCII. Both PLY and STL have ASCII and binary variants. With ASCII you need to trade off file size and precision, and file loading is always extremely slow. In my software OBJ (with moderate precision) is about 13 times slower and 7 times larger than a simple gzipped binary format (MZ3).

The MZ3 format can be extended, is very fast and relatively compact. I might be biased here as I developed it, but I would be happy to extend this. The current version was extended to support features for PALM (Anderson Winkler) and SPM (Guillaume Flandin) and I provide Matlab, JavasScript and Python/Blender code. The strength of this format is that is virtually identical to the raw data one sends to the GPU, which makes it fast. However, just like modern GPUs it only represents triangles. The advantage of triangles is that as long as three points are not co-linear they define a single plane, while this is not true of all quads (e.g. all 3-legged stools are stable, but many 4 legged chairs rock between legs). While modern GPUs do not use quads as a base type, quads can be useful for a file format to store if one is interested in subdivision.

The GIfTI format does allow meta data. It is XML-based and so it is human readable. It uses base-64 storage for binary data which adds a bit of a speed and file size hit, but this is pretty modest. It seems like a reasonable interchange format for brain imaging.

The Draco format is very clever, creating extremely compact files. However, it is very complicated and the fact that it re-indexes vertices would probably be a problem for a lot of brain imaging applications.

And of course let’s not forget glTF, which is already being integrated with Slicer. It’s more graphics-oriented than medical, but the features and community support, not to mention compactness and extensibility are impressive. No reason we couldn’t propose an extension to embed some things like coordinate system reference (probably LPS by default) and other useful things like anatomy codes.

I’m just curious, why it is a problem that vertices are not reused? I’ve just tried and VTK loads a mesh that contains 1.5 million points in 2 seconds from .vtk file, and in about 3 seconds from .stl file (including coincident point merging). So, performance should not be a huge issue.

I agree, I always feel that there are already too many file formats and we should rather spend our time with improving existing ones.

@lassoan - the 2 second load for a vtk file reflects a poorly optimized VTK loader. Slicer saves VTK files as triangles in binary VTK format, e.g. the header will report “POLYGONS n sz” where sz = n * 4. This represents a special case: when sz = n * 4 you can read the entire array from disk and ignore the numPointsN as it will always be 3 (if sz > n * 4 you must read the numPointsN for each NGon and reconstruct it).

POLYGONS n size
numPoints0, i0, j0, k0, ...
numPoints1, i1, j1, k1, ...
...
numPointsn-1, in-1, jn-1, kn-1, ...

Here are times for reading meshes with my software (vtk, obj and stl created with Slicer):
mz3 (raw) 14ms
mz3 (gzip) 134ms
vtk (ignore numPoints) 67ms
obj (ascii) 2710ms
vtk (naive) 2857ms
stl (binary, unify) 4717ms

I think it would be terrific if Slicer would automatically unify STL vertices. However, I would still discourage users from saving in STL unless absolutely required, as many tools do not apply this step and therefore the resulting meshes look poor.

VTK’s STL reader merges coincident points by default. Slicer uses this default setting.

Is this a new feature? The STL and OBJ images from the initial comment were both generated by Slicer and loaded by Slicer (version 4.10.2 on MacOS). The STL and OBJ files look identical to each other when I load them with Surfice (which unifies vertices, e.g. the STL looks faceted in Slicer but not Surfice).

One other thought, is it possible that Slicer/VTK estimate the vertex normals before unifying the vertices? This would explain the faceted look. It is important to calculate surface normals after unifying the vertices.

The faceted look of models loaded from STL files is due to that we do not compute the normals automatically. If normals are not stored in the file then model surface will be displayed with flat shading. I noticed this year’s ago but did not want to simply add a few lines to compute normals with some default settings if they are not available in the input file because of potential rendering artifacts (especially when cells are large and highly angled), the extra computation time, and maybe a module that loads the data may want to compute normals in a specific way (splitting at feature edges, etc). Comouting missing surface normals in the display pipeline would not have any of these limitations, but we have not get around to implement it.

Users can always compute surface normals using Surface toolbox module if they need it.

That is understandable. Sounds like a good reason to discourage users from choosing the STL format if they want to render the data in Slicer. As shown above, the per-vertex surface normals of the OBJ files that are created are more accurate than the per-face normals required by the STL format. I appreciate that the vertex unification of slicer will remove my concerns regarding the usage of GPU resources and performance.

I use the STL file a lot for downstream CAD modelling as well as 3d printing. It is a very well recognised file format across many programs.

I appreciate the education regarding the inefficiencies associated with the format as I was not really aware (I do notice the huge file sizes though)

Although I do think this info would be best shared on forums and documentation as mentioned previously. I think it would look very unprofessional to have little comments next to the file formats in the drop down menu. I have never seen other software with this.

I know when I select STL it is because there is no other option for getting slicer models into other CAD software and 3d printers.

in case someone is still interested in considering a new mesh format, I have been working on (text/binary) JSON based mesh data format - JMesh - as part of the work related to my 3D mesh generator Iso2Mesh (http://iso2mesh.sf.net). This summer, I wrote a few JSON/JData based file specifications, including an early draft for JMesh.

More related discussion can be found in my mailing list post in the below link

https://groups.google.com/forum/#!topic/iso2mesh-users/EIG4V7Gzpl0

Essentially, a text-based JMesh file is basically a plain JSON file that can be parsed by nearly all JSON parsers; to save space, it can be converted to a binary-version (and convert back) using the UBJSON specification (http://ubjson.org). Both formats support strongly-typed data and internal compression.

Currently, working parsers/writers for MATLAB already exist, and the support for other languages (without advanced JData features) can be readily added by linking with any JSON/UBJSON parsers available.

Happy to chat more if anyone is interested in this format. Any suggestion is welcome.