.stl 3D模型文件的读取计算,方法和程序实现(matlab版C++版python版)
0. 背景描述
3D模型、3D打印中很常見的一種文件格式.STL文件,其描述的主要就是其表面點所組成的三角面片的點坐標信息(vertex),和法向量(normal)。 如果單純查看的話,很多軟件都可以打開,諸如SolidWorks、3DMax,win10自帶3d查看器、MITK等。win10自帶的3D查看器,較為推薦。而我最早一般是用基于vtk的醫學可視化軟件,進行讀取查看。
如果想要訪問獲取.STL文件中的點坐標數據,來進行后續更為自由的計算和處理,最好是需要程序實現的。其中須知,.STL文件一般有兩種文件模式,即ASCII文本型和Binary二進制形式。其中ASCII形式的文件,很多文本查看軟件便可打開,如記事本、notepad++、sublime等等。推薦notepad++,其大文件的打開速度很優秀。
想對這兩種文件模式下的STL文件進行統一的讀取計算,讀取stl,返回表面點坐標(可附加計算內部點坐標)。程序操作如下:
1. C++版
基于vtk進行C++的簡單實現即可,vtk的大牛們已經寫好了vtkSTLReader的類,程序實現demo見另外的link:URL.
2. Python版
同樣類似于C++版,還是基于vtk實現,程序實現demo見另外的link:URL.
3. Matlab版
MATLAB的版本程序最麻煩,詳見下面的stlGeneralAccess函數,支持二進制和文本型兩類STL的訪問和計算,并且幫項目中的同門實現了計算內部點坐標的程序部分(方法參考了程序注釋中的REFS.2),如果沒有這個需要的,把對應的代碼(計算inNodes部分)刪掉或者注釋掉即可。
NOTE: 程序邏輯也比較簡單,就是讀入STL文件,判斷是ASCII還是Binary,然后分別處理,先獲得三角面片的關系和點坐標,然后針對點坐標進行去重整理,得到純粹的表面點即邊界點的坐標,之后利用文獻2(REFS.2)的方法計算得到內部點。
function [x, y, z, bdNodes, inNodes] = stlGeneralAccess(filename, dimSize) % This function reads an STL file in binary format or ascii format. % It returns the coordinates of both boundary nodes and interior nodes % of the 3D polyhedron. % % partial REFs: % 1. stlread() by Doron Harlev for binary STL file via mathswork.com % 2. https://wrf.ecse.rpi.edu//Research/Short_Notes/pnpoly.html % % Examples: % close all % clear all % clc% [x, y, z, bdNodes, inNodes] = stlGeneralAccess('./Region1.stl'); % if 0 % patch(x, y, z); % else % scatter3(bdNodes(:,1), bdNodes(:,2), bdNodes(:,3), 'MarkerFaceColor',[.75 .75 .0]); % hold on % scatter3(inNodes(:,1), inNodes(:,2), inNodes(:,3),'MarkerFaceColor',[.5 .0 .0]); % end % % Where % filename --- Full path of the input STL file. % dimSize --- [3*1]. The dimSize of coordinate, covering the 3D polyhedron. % x --- [3*numFacet]. X Position of three points in a triangular facet. % y --- [3*numFacet]. Y Position of three points in a triangular facet. % z --- [3*numFacet]. Z Position of three points in a triangular facet. % bdNodes --- [num_bdNodes*3]. The coordinates of boundary nodes. % inNodes --- [num_inNodes*3]. The coordinates of interior nodes. % % FUNCTION stlGeneralAccess. Version 6.1 Written by JeffZhang. AUG,2020. % E-mail: jfsufeng@gmail.com || jfzhang2018@zju.edu.cn % Dsp.CN:該函數支持STL文件的二進制形式和文本形式的通用訪問和計算,可計算得到邊界點和內部點if nargin < 1error('Too few input arguments') end if nargout > 5error('Too many output arguments') end fid=fopen(filename); if fid == -1error('File could not be opened, pls check the path.') end disp('>>>stlGeneralAccess.[START]') %tic;toc; nline = 0; binary = -1; ascii = -1; binaryOrText = -1; stlLines={}; while ~feof(fid)tline = fgetl(fid);nline = nline + 1;if contains(tline, 'endfacet')ascii = 1;binary = 0;endif nline >= 50break;end endif ascii == 1 && binary ==0disp('FileMode: text');binaryOrText = 0; elsedisp('FileMode: binary');binaryOrText = 1; endnum_facet = 0; frewind(fid) switch binaryOrTextcase 0while ~feof(fid)stlLines=[stlLines;fgets(fid)];end%norm = [];ver1=[];ver2=[];ver3=[];x=[];y=[];z=[];nmRow=[];%x=zeros(3,num_facet); y=zeros(3,num_facet); z=zeros(3,num_facet);for i = 1:size(stlLines,1)%tline = fgetl(fid);tl = stlLines{i};id = strfind(tl,'facet normal');if ~isempty(id)%if contains(tl,'facet normal')nmRow=[nmRow;i];num_facet = num_facet+1;%tk = tl(id+13:end); %15 or id+13(better&more stable)%nm = strsplit(tk,' ');%norm = [norm; str2double(nm)]; %% skipif i+2 <= size(stlLines,1) && i+3 <= size(stlLines,1) && i+4 <= size(stlLines,1)ver1Str = stlLines{i+2};ver2Str = stlLines{i+3};ver3Str = stlLines{i+4};id1 = strfind(ver1Str,'vertex');id2 = strfind(ver2Str,'vertex');id3 = strfind(ver3Str,'vertex');if isempty(id1) || isempty(id2) || isempty(id3)%if ~contains(ver1Str,'vertex')||~contains(ver2Str,'vertex')||~contains(ver3Str,'vertex')error('Exception0x0815 of vertex.')end tk1 = ver1Str(id1+7:end); %11 or id1+7(better&more stable)tk2 = ver2Str(id2+7:end);tk3 = ver3Str(id3+7:end);nm1 = strsplit(tk1,' ');nm2 = strsplit(tk2,' ');nm3 = strsplit(tk3,' ');ver1 = [ver1; str2double(nm1)];ver2 = [ver2; str2double(nm2)];ver3 = [ver3; str2double(nm3)];x=[x;ver1(end,1) ver2(end,1) ver3(end,1)]; %% For patch validationy=[y;ver1(end,2) ver2(end,2) ver3(end,2)];z=[z;ver1(end,3) ver2(end,3) ver3(end,3)];endendendx=x';y=y';z=z';case 1ftitle=fread(fid,80,'uchar=>schar'); % Read file titlenum_facet=fread(fid,1,'int32'); % Read number of Facetsfprintf('\nTitle: %s\n', char(ftitle'));fprintf('Num Facets: %d\n', num_facet);x=zeros(3,num_facet); y=zeros(3,num_facet); z=zeros(3,num_facet);ver1=zeros(num_facet,3); ver2=zeros(num_facet,3); ver3=zeros(num_facet,3);for i=1:num_facetnorm=fread(fid,3,'float32'); % normal coordinates, ignored for nowver1(i,:)=fread(fid,3,'float32'); % vertex 1ver2(i,:)=fread(fid,3,'float32'); % vertex 2ver3(i,:)=fread(fid,3,'float32'); % vertex 3col=fread(fid,1,'uint16'); % color bytesx(:,i)=[ver1(i,1); ver2(i,1); ver3(i,1)]; % convert to matlab "patch" compatible formaty(:,i)=[ver1(i,2); ver2(i,2); ver3(i,2)];z(:,i)=[ver1(i,3); ver2(i,3); ver3(i,3)];endend fclose(fid);bdVertexes=[ver1;ver2;ver3]; bdNodes=unique(bdVertexes, 'rows');%% 缺少圖像Size,Spacing等信息,所以這里只是做個臨時demo,屆時傳入實際的Size[3]和Spacing[3]即可如果posCrd坐標計算的話 % 如果直接只用idxCrd坐標的話,直接完美使用,不用傳參. imSize = [512 512 300]; imSpacing = [1 1 1]; % [.5 0.5 0.5] origin = [0. 0. 0.]; if nargin < 2disp('Default dimSize below.')dimSize = imSize endminX_bd = min(bdNodes(:,1)); maxX_bd = max(bdNodes(:,1)); minY_bd = min(bdNodes(:,2)); maxY_bd = max(bdNodes(:,2)); minZ_bd = min(bdNodes(:,3)); maxZ_bd = max(bdNodes(:,3)); % posCrd = origin + idxCrd*imSpacing, for medical image computing; % 如果用posCrd來計算;則步長h(i)=imSpacing(i);rangeSz(i) = imSize(i)*imSpacing(i); inNodesPre=[]; inNodes=[]; for i= 1:1:dimSize(1)for j= 1:1:dimSize(2)for k= 1:1:dimSize(3)tmpX = origin(1) + i * imSpacing(1);tmpY = origin(2) + j * imSpacing(2);tmpZ = origin(3) + k * imSpacing(3);if tmpX >= minX_bd && tmpX <= maxX_bd && ...tmpY >= minY_bd && tmpY <= maxY_bd && ...tmpZ >= minZ_bd && tmpZ <= maxZ_bdinNodesPre = [inNodesPre; tmpX tmpY tmpZ];endendend endif isempty(inNodesPre)fprintf('\n!ERROR!Warning:The dimSize may be not suitable.\n') end%% fix x,evaluate y,z. (pnpoly method) for i= 1:1:size(inNodesPre,1)polyEdge2dId = find((bdNodes(:,1)>= inNodesPre(i, 1)-.5)&(bdNodes(:,1)<= inNodesPre(i, 1)+.4));vertY = bdNodes(polyEdge2dId,2);vertZ = bdNodes(polyEdge2dId,3);testY = inNodesPre(i,2);testZ = inNodesPre(i,3);c = 0;for k=1:1:length(vertY)if k == 1j = length(vertY);elsej = k - 1;endif ( ((vertZ(k)>testZ) ~= (vertZ(j)>testZ)) && ...(testY < (vertY(j)-vertY(k)) * (testZ-vertZ(k)) / (vertZ(j)-vertZ(k)) + vertY(k)) )c = ~c;endendif ~mod(c,2)%disp('even -- outside')else%disp('uneven -- inside')inNodes = [inNodes; inNodesPre(i,:)];end end disp('>>>stlGeneralAccess.[OVER]')end最后附兩個該函數調用的demo效果:
?
?
?
此文暫結~ 后續的更新可以到我的mathworks-FileExchange去check&update.
URL1:?https://www.mathworks.com/matlabcentral/fileexchange/79191-stlgeneralaccess
URL2:?https://github.com/JeffJFZ/stlGeneralAccess
?
總結
以上是生活随笔為你收集整理的.stl 3D模型文件的读取计算,方法和程序实现(matlab版C++版python版)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: cap流程图_工艺流程图(中英文标准模板
- 下一篇: Android 更换字体和改变字重