The points read with stlread are different from when the same points are saved with stlwrite and read again.

조회 수: 30 (최근 30일)
As written in the title, after reading the stl file with stlread, I saved the points and connectivityList as variables, then recreated the stl file using the stlwrite function.
Obviously, points and connectivityList used the same data, but the points read from the newly created stl file differed from the previously used variable in the fractional part.
Is there any way to solve these problems?

답변 (2개)

DGM
DGM 2025년 7월 3일 3:36
편집: DGM 2025년 7월 3일 4:52
When writing to an STL from vertex data generated in the workspace, you're losing precision. Your vertex data in memory is represented as double-precision (64b) float. An STL file is capable of nominally 32b float.
The question isn't about the loss of precision when writing fresh data to a file, though. It's about transcoding an existing file without deliberate change. We should expect this workflow to be 32b -> 64b -> 32b, with no obvious loss of precision. This is where the "nominally 32b" subtlety comes into play.
We should expect binary encoded STL files to transcode exactly back to binary STL files, but if we're dealing with an ASCII-encoded STL at either end, it depends on the encoder that was used.
% let's generate some F,V data
unzip holypringle.stl.zip
T = stlread('holypringle.stl');
[F V] = t2fv(T);
% let's say it's a large object making full use
% of the resolution of 64b float
V = 1E5*(V + randn(size(V)));
% cram it back in a triangulation object
T = triangulation(F,V);
% a binary STL has only 32b of precision
%stlwrite(T,'testbin.stl','binary') % built-in
stlWrite('testbin.stl',F,V,'mode','binary') % FEX #20922 or #51200
% an ascii STL typically has about 9 digits of precision.
% this is nominally comparable to 32b float,
% but the exact number and formatting depends on the encoder
% this variability means that we might not be losing the exact
% same amount of precision depending on the encoder we used.
%stlwrite(T,'testascii.stl','text') % built-in
stlWrite('testascii.stl',F,V,'mode','ascii') % FEX #20922 or #51200
% compare the error between the written data and the 64b data in memory
% we should expect these errors to be unavoidable, since we're losing precision.
% we should expect these errors to be similar in magnitude, but not necessarily equal.
Tb = stlread('testbin.stl');
Ta = stlread('testascii.stl');
immse(Tb.Points,V)
ans = 9.9531e-06
immse(Ta.Points,V)
ans = 1.2168e-05
% compare the error between the two files themselves (both nominally 32b float)
% this is the error we might see when we recreate a file using a different encoding type.
immse(Tb.Points,Ta.Points)
ans = 1.8929e-06
In this example, there are two encoders that can optionally be used. Both encoders will write the exact same data when encoding as binary, but their text representations vary slightly when ASCII mode is used. If the encoder casts the vertex data as single-precision float and/or writes enough digits to avoid truncation, the ASCII mode representations should be as consistent as the binary encodings, but not all encoders do. I've seen encoders that truncate to as few as 6 digits.
The built-in encoder will produce binary and ASCII STL files which match exactly when decoded, but the encoder from #20922 does not. If you open the encoder and change every instance of '%.7E' to '%.8E', it will write ASCII encodings which decode to match the binary encodings.
Why do some encoders truncate to fewer digits than is necessary for lossless representation of 32b float? I'm sure some cases are just mistakes, but there's a significant incentive to keep file size under control, even if it comes at the loss of a little bit of information. The STL format is going on 40 years old. ASCII representations can be large, and storage/transport wasn't always as cheap as it is now.
Bear in mind also that not all decoders will return the vertex list in the exact same order or format, even if there is no loss of information. That means that you can potentially wind up with huge differences if you try to compare the vertex data. To understand why, it helps to know that an STL file does not store data by referencing a global vertex list. Each facet in the file is a direct representation of the numerical position of its vertices, not a set of indices into a vertex list. That means that each vertex is described redundantly by each of its neighboring facets.
The decoder should discard the redundant vertex entries and remap the faces to match the pruned vertex list -- but many decoders don't. FEX #22409, #29906 (and others) don't prune the vertex list at all, so you can't even compare them directly without pruning it yourself. On the other hand, the readers from FEX #51200 will prune the vertex list, but pruning results in the list being reordered. There is no loss of information, and the vertex list length does not change; it's just reordered due to the default sorting behavior of unique() being used.

akshatsood
akshatsood 2023년 9월 4일
편집: akshatsood 2023년 9월 4일
Hi Jungwu,
I understand that you are experiencing differences in the fractional part of the coordinates when reading and writing an STL file using stlread and stlwrite functions in MATLAB. As per my understanding, it may be due to the floating-point precision used during the process. This can happen due to the inherent limitations of representing real numbers in computers.
One possible workaround involves using the round function to set the coordinates to desired precision followed by specifying the file format parameter while leveraging the stlwrite function. To demonstrate the workaround, an example script can be opened up by entering the following command in the MATLAB command window
>> openExample('matlab/ReadTriangulationFromSTLTextFileExample')
Adjust the precision variable as per your requirements. By doing this, you can ensure that the fractional part of the coordinates is consistent when reading and writing the STL file. Additionally, you can mention the file format as 'text' while calling stlwrite instead of the default binary to mitigate the differences.
TR = stlread('tristltext.stl');
triangulation with properties:
Points: [6×3 double]
ConnectivityList: [4×3 double]
% set the desired precision for the floating-point numbers
precision = 6;
points = round(TR.Points,precision);
% create a new triangulation object
roundedTR = triangulation(TR.ConnectivityList,points);
% write the rounded data to a new STL file with the file format as 'text'
stlwrite(roundedTR, 'new_file.stl', 'text');
% validate that data is consistent using stlread and stlwrite
newTR = stlread('new_file.stl');
disp(newTR.Points - TR.Points);
I hope this helps.

카테고리

Help CenterFile Exchange에서 STL (STereoLithography)에 대해 자세히 알아보기

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!

Translated by