MATLAB对ply文件格式的读取和显示
转自:https://blog.csdn.net/lafengxiaoyu/article/details/60574150
在网上搜索这个题目可以找到一些类似的文章,其来源大致都是http://people.sc.fsu.edu/~jburkardt/m_src/ply_io/ply_io.html。但是并没有说明怎样运行和显示,因此我做这篇博客详细讲解一下。首先是这个ply_read.m文件
function [ Elements, varargout ] = PLY_READ ( Path, Str )
%*****************************************************************************80
%
%% PLY_READ reads a PLY 3D data file.
%
% [DATA,COMMENTS] = PLY_READ(FILENAME) reads a version 1.0 PLY file
% FILENAME and returns a structure DATA. The fields in this structure
% are defined by the PLY header; each element type is a field and each
% element property is a subfield. If the file contains any comments,
% they are returned in a cell string array COMMENTS.
%
% [TRI,PTS] = PLY_READ(FILENAME,\'tri\') or
% [TRI,PTS,DATA,COMMENTS] = PLY_READ(FILENAME,\'tri\') converts vertex
% and face data into triangular connectivity and vertex arrays. The
% mesh can then be displayed using the TRISURF command.
%
% Note: This function is slow for large mesh files (+50K faces),
% especially when reading data with list type properties.
%
% Example:
% [Tri,Pts] = PLY_READ(\'cow.ply\',\'tri\');
% [Tri,Pts] = PLY_READ(\'bunny.ply\',\'tri\');
% trisurf(Tri,Pts(:,1),Pts(:,2),Pts(:,3));
% colormap(gray); axis equal;
%
% Discussion:
%
% The original version of this program had a mistake that meant it
% did not properly triangulate files whose faces were not already triangular.
% This has been corrected (JVB, 25 February 2007).
%
% Glenn Ramsey pointed out and corrected a problem that occurred
% with an uninitialized value of Type2, 27 August 2012.
%
% Licensing:
%
% This code is distributed under the GNU LGPL license.
%
% Modified:
%
% 27 August 2012
%
% Author:
%
% Pascal Getreuer 2004
%
% Parameters:
%
% Local Parameters:
%
% COMMENTS, any comments from the file.
%
% ELEMENTCOUNT, the number of each type of element in file.
%
% ELEMENTS, the element data.
%
% PROPERTYTYPES, the element property types.
%
% SIZEOF, size in bytes of each type.
%
%
% Open the input file in "read text" mode.
%
[ fid, Msg ] = fopen ( Path, \'rt\' );
if ( fid == -1 )
error ( Msg );
end
Buf = fscanf ( fid, \'%s\', 1 );
if ( ~strcmp ( Buf, \'ply\' ) )
fclose ( fid );
error(\'Not a PLY file.\');
end
%
% Read the header.
%
Position = ftell(fid);
Format = \'\';
NumComments = 0;
Comments = {};
NumElements = 0;
NumProperties = 0;
Elements = [];
ElementCount = [];
PropertyTypes = [];
ElementNames = {}; % list of element names in the order they are stored in the file
PropertyNames = []; % structure of lists of property names
while ( 1 )
%
% Read a line from the file.
%
Buf = fgetl ( fid );
BufRem = Buf;
Token = {};
Count = 0;
%
% Split the line into tokens.
%
while ( ~isempty(BufRem) )
[ tmp, BufRem ] = strtok(BufRem);
%
% Count the tokens.
%
if ( ~isempty ( tmp ) )
Count = Count + 1;
Token{Count} = tmp;
end
end
%
% Parse the line.
%
if ( Count )
switch lower ( Token{1} )
%
% Read the data format.
%
case \'format\'
if ( 2 <= Count )
Format = lower ( Token{2} );
if ( Count == 3 & ~strcmp ( Token{3}, \'1.0\' ) )
fclose ( fid );
error(\'Only PLY format version 1.0 supported.\');
end
end
%
% Read a comment.
%
case \'comment\'
NumComments = NumComments + 1;
Comments{NumComments} = \'\';
for i = 2 : Count
Comments{NumComments} = [Comments{NumComments},Token{i},\' \'];
end
%
% Read an element name.
%
case \'element\'
if ( 3 <= Count )
if ( isfield(Elements,Token{2}) )
fclose ( fid );
error([\'Duplicate element name, \'\'\',Token{2},\'\'\'.\']);
end
NumElements = NumElements + 1;
NumProperties = 0;
Elements = setfield(Elements,Token{2},[]);
PropertyTypes = setfield(PropertyTypes,Token{2},[]);
ElementNames{NumElements} = Token{2};
PropertyNames = setfield(PropertyNames,Token{2},{});
CurElement = Token{2};
ElementCount(NumElements) = str2double(Token{3});
if ( isnan(ElementCount(NumElements)) )
fclose ( fid );
error([\'Bad element definition: \',Buf]);
end
else
error([\'Bad element definition: \',Buf]);
end
%
% Read an element property.
%
case \'property\'
if ( ~isempty(CurElement) & Count >= 3 )
NumProperties = NumProperties + 1;
eval([\'tmp=isfield(Elements.\',CurElement,\',Token{Count});\'],...
\'fclose(fid);error([\'\'Error reading property: \'\',Buf])\');
if ( tmp )
error([\'Duplicate property name, \'\'\',CurElement,\'.\',Token{2},\'\'\'.\']);
end
%
% Add property subfield to Elements.
%
eval([\'Elements.\',CurElement,\'.\',Token{Count},\'=[];\'], ...
\'fclose(fid);error([\'\'Error reading property: \'\',Buf])\');
%
% Add property subfield to PropertyTypes and save type.
%
eval([\'PropertyTypes.\',CurElement,\'.\',Token{Count},\'={Token{2:Count-1}};\'], ...
\'fclose(fid);error([\'\'Error reading property: \'\',Buf])\');
%
% Record property name order.
%
eval([\'PropertyNames.\',CurElement,\'{NumProperties}=Token{Count};\'], ...
\'fclose(fid);error([\'\'Error reading property: \'\',Buf])\');
else
fclose ( fid );
if ( isempty(CurElement) )
error([\'Property definition without element definition: \',Buf]);
else
error([\'Bad property definition: \',Buf]);
end
end
%
% End of header.
%
case \'end_header\'
break;
end
end
end
%
% Set reading for specified data format.
%
if ( isempty ( Format ) )
warning(\'Data format unspecified, assuming ASCII.\');
Format = \'ascii\';
end
switch Format
case \'ascii\'
Format = 0;
case \'binary_little_endian\'
Format = 1;
case \'binary_big_endian\'
Format = 2;
otherwise
fclose ( fid );
error([\'Data format \'\'\',Format,\'\'\' not supported.\']);
end
%
% Read the rest of the file as ASCII data...
%
if ( ~Format )
Buf = fscanf ( fid, \'%f\' );
BufOff = 1;
else
%
% ...or, close the file, and reopen in "read binary" mode.
%
fclose ( fid );
%
% Reopen the binary file as LITTLE_ENDIAN or BIG_ENDIAN.
%
if ( Format == 1 )
fid = fopen ( Path, \'r\', \'ieee-le.l64\' );
else
fid = fopen ( Path, \'r\', \'ieee-be.l64\' );
end
%
% Find the end of the header again.
% Using ftell on the old handle doesn\'t give the correct position.
%
BufSize = 8192;
Buf = [ blanks(10), char(fread(fid,BufSize,\'uchar\')\') ];
i = [];
tmp = -11;
while ( isempty(i) )
i = findstr(Buf,[\'end_header\',13,10]); % look for end_header + CR/LF
i = [i,findstr(Buf,[\'end_header\',10])]; % look for end_header + LF
if ( isempty(i) )
tmp = tmp + BufSize;
Buf = [Buf(BufSize+1:BufSize+10),char(fread(fid,BufSize,\'uchar\')\')];
end
end
%
% seek to just after the line feed
%
fseek ( fid, i + tmp + 11 + (Buf(i + 10) == 13), -1 );
end
%
% Read element data.
%
% PLY and MATLAB data types (for fread)
%
PlyTypeNames = {\'char\',\'uchar\',\'short\',\'ushort\',\'int\',\'uint\',\'float\',\'double\', ...
\'char8\',\'uchar8\',\'short16\',\'ushort16\',\'int32\',\'uint32\',\'float32\',\'double64\'};
MatlabTypeNames = {\'schar\',\'uchar\',\'int16\',\'uint16\',\'int32\',\'uint32\',\'single\',\'double\'};
SizeOf = [1,1,2,2,4,4,4,8];
for i = 1 : NumElements
%
% get current element property information
%
eval([\'CurPropertyNames=PropertyNames.\',ElementNames{i},\';\']);
eval([\'CurPropertyTypes=PropertyTypes.\',ElementNames{i},\';\']);
NumProperties = size(CurPropertyNames,2);
% fprintf(\'Reading %s...\n\',ElementNames{i});
%
% Read ASCII data.
%
if ( ~Format )
for j = 1 : NumProperties
Token = getfield(CurPropertyTypes,CurPropertyNames{j});
if ( strcmpi(Token{1},\'list\') )
Type(j) = 1;
else
Type(j) = 0;
end
%
% Glenn Ramsey 20120827
% Initialise Type2{} to prevent uninitialised value error.
%
Type2{j} = \'\';
end
%
% Parse the buffer.
%
if ( ~any(Type) )
% no list types
Data = reshape ( ...
Buf(BufOff:BufOff+ElementCount(i)*NumProperties-1), ...
NumProperties, ElementCount(i) )\';
BufOff = BufOff + ElementCount(i) * NumProperties;
else
ListData = cell(NumProperties,1);
for k = 1 : NumProperties
ListData{k} = cell(ElementCount(i),1);
end
%
% list type
%
for j = 1 : ElementCount(i)
for k = 1 : NumProperties
if ( ~Type(k) )
Data(j,k) = Buf(BufOff);
BufOff = BufOff + 1;
else
tmp = Buf(BufOff);
ListData{k}{j} = Buf(BufOff+(1:tmp))\';
BufOff = BufOff + tmp + 1;
end
end
end
end
%
% Read binary data.
%
else
% translate PLY data type names to MATLAB data type names
ListFlag = 0; % = 1 if there is a list type
SameFlag = 1; % = 1 if all types are the same
for j = 1 : NumProperties
Token = getfield(CurPropertyTypes,CurPropertyNames{j});
%
% Non-list type.
%
if ( ~strcmp(Token{1},\'list\' ) )
tmp = rem(strmatch(Token{1},PlyTypeNames,\'exact\')-1,8)+1;
if ( ~isempty(tmp) )
TypeSize(j) = SizeOf(tmp);
Type{j} = MatlabTypeNames{tmp};
TypeSize2(j) = 0;
Type2{j} = \'\';
SameFlag = SameFlag & strcmp(Type{1},Type{j});
else
fclose(fid);
error([\'Unknown property data type, \'\'\',Token{1},\'\'\', in \', ...
ElementNames{i},\'.\',CurPropertyNames{j},\'.\']);
end
else % list type
if ( length(Token) == 3 )
ListFlag = 1;
SameFlag = 0;
tmp = rem(strmatch(Token{2},PlyTypeNames,\'exact\')-1,8)+1;
tmp2 = rem(strmatch(Token{3},PlyTypeNames,\'exact\')-1,8)+1;
if ( ~isempty(tmp) & ~isempty(tmp2) )
TypeSize(j) = SizeOf(tmp);
Type{j} = MatlabTypeNames{tmp};
TypeSize2(j) = SizeOf(tmp2);
Type2{j} = MatlabTypeNames{tmp2};
else
fclose(fid);
error([\'Unknown property data type, \'\'list \',Token{2},\' \',Token{3},\'\'\', in \', ...
ElementNames{i},\'.\',CurPropertyNames{j},\'.\']);
end
else
fclose(fid);
error([\'Invalid list syntax in \',ElementNames{i},\'.\',CurPropertyNames{j},\'.\']);
end
end
end
% read file
if ( ~ListFlag )
%
% No list types, all the same type (fast)
%
if ( SameFlag )
Data = fread(fid,[NumProperties,ElementCount(i)],Type{1})\';
%
% No list types, mixed type.
%
else
Data = zeros(ElementCount(i),NumProperties);
for j = 1 : ElementCount(i)
for k = 1 : NumProperties
Data(j,k) = fread(fid,1,Type{k});
end
end
end
else
ListData = cell(NumProperties,1);
for k = 1 : NumProperties
ListData{k} = cell(ElementCount(i),1);
end
if ( NumProperties == 1 )
BufSize = 512;
SkipNum = 4;
j = 0;
%
% List type, one property (fast if lists are usually the same length)
%
while ( j < ElementCount(i) )
BufSize = min(ElementCount(i)-j,BufSize);
Position = ftell(fid);
%
% Read in BufSize count values, assuming all counts = SkipNum
%
[Buf,BufSize] = fread(fid,BufSize,Type{1},SkipNum*TypeSize2(1));
Miss = find(Buf ~= SkipNum); % find first count that is not SkipNum
fseek(fid,Position + TypeSize(1),-1); % seek back to after first count
if ( isempty(Miss) )
% all counts are SkipNum
Buf = fread(fid,[SkipNum,BufSize],[int2str(SkipNum),\'*\',Type2{1}],TypeSize(1))\';
fseek(fid,-TypeSize(1),0); % undo last skip
for k = 1:BufSize
ListData{1}{j+k} = Buf(k,:);
end
j = j + BufSize;
BufSize = floor(1.5*BufSize);
else
%
% Some counts are SkipNum.
%
if ( 1 < Miss(1) )
Buf2 = fread(fid,[SkipNum,Miss(1)-1],[int2str(SkipNum),\'*\',Type2{1}],TypeSize(1))\';
for k = 1:Miss(1)-1
ListData{1}{j+k} = Buf2(k,:);
end
j = j + k;
end
%
% Read in the list with the missed count.
%
SkipNum = Buf(Miss(1));
j = j + 1;
ListData{1}{j} = fread(fid,[1,SkipNum],Type2{1});
BufSize = ceil(0.6*BufSize);
end
end
else
%
% List type(s), multiple properties (slow)
%
Data = zeros(ElementCount(i),NumProperties);
for j = 1:ElementCount(i)
for k = 1:NumProperties
if ( isempty(Type2{k}) )
Data(j,k) = fread(fid,1,Type{k});
else
tmp = fread(fid,1,Type{k});
ListData{k}{j} = fread(fid,[1,tmp],Type2{k});
end
end
end
end
end
end
%
% Put data into Elements structure
%
for k = 1 : NumProperties
if ( ( ~Format && ~Type(k) ) || (Format && isempty(Type2{k})) )
eval([\'Elements.\',ElementNames{i},\'.\',CurPropertyNames{k},\'=Data(:,k);\']);
else
eval([\'Elements.\',ElementNames{i},\'.\',CurPropertyNames{k},\'=ListData{k};\']);
end
end
end
clear Data
clear ListData;
fclose ( fid );
%
% Output the data as a triangular mesh pair.
%
if ( ( nargin > 1 & strcmpi(Str,\'Tri\') ) || nargout > 2 )
%
% Find vertex element field
%
Name = {\'vertex\',\'Vertex\',\'point\',\'Point\',\'pts\',\'Pts\'};
Names = [];
for i = 1 : length(Name)
if ( any ( strcmp ( ElementNames, Name{i} ) ) )
Names = getfield(PropertyNames,Name{i});
Name = Name{i};
break;
end
end
if ( any(strcmp(Names,\'x\')) & any(strcmp(Names,\'y\')) & any(strcmp(Names,\'z\')) )
eval([\'varargout{1}=[Elements.\',Name,\'.x,Elements.\',Name,\'.y,Elements.\',Name,\'.z];\']);
else
varargout{1} = zeros(1,3);
end
varargout{1} = varargout{1}\';
varargout{2} = Elements;
varargout{3} = Comments;
Elements = [];
%
% Find face element field
%
Name = {\'face\',\'Face\',\'poly\',\'Poly\',\'tri\',\'Tri\'};
Names = [];
for i = 1 : length(Name)
if ( any(strcmp(ElementNames,Name{i})) )
Names = getfield(PropertyNames,Name{i});
Name = Name{i};
break;
end
end
if ( ~isempty(Names) )
% find vertex indices property subfield
PropertyName = {\'vertex_indices\',\'vertex_indexes\',\'vertex_index\',\'indices\',\'indexes\'};
for i = 1 : length(PropertyName)
if ( any(strcmp(Names,PropertyName{i})) )
PropertyName = PropertyName{i};
break;
end
end
%
% Convert face index list to triangular connectivity.
%
if ( ~iscell(PropertyName) )
eval([\'FaceIndices=varargout{2}.\',Name,\'.\',PropertyName,\';\']);
N = length(FaceIndices);
Elements = zeros(3,N*2);
Extra = 0;
for k = 1 : N
Elements(1:3,k) = FaceIndices{k}(1:3)\';
%
% The original code had an error in the following loop.
%
for j = 4 : length(FaceIndices{k})
Extra = Extra + 1;
Elements(1,N + Extra) = FaceIndices{k}(1);
Elements(2,N + Extra) = FaceIndices{k}(j-1);
Elements(3,N + Extra) = FaceIndices{k}(j);
end
end
%
% Add 1 to each vertex value; PLY vertices are zero based.
%
Elements = Elements(:,1:N+Extra) + 1;
end
end
else
varargout{1} = Comments;
end
return
end
输入的参数是两个,一个是ply文件的位置,另一个是打开方式,一般为\’tri\’
对于ply格式的解答这里有几篇文章可以参考:PLY文件格式剖析(一),PLY文件格式剖析(二)
PLY_DISPLAY这个函数也是类似的
但是事实上用txt直接读取ply格式确是有可能出现乱码的
但是某些只有点和面的信息的格式是被允许的
例如下图的棱锥
ply
format ascii 1.0
comment created by MATLAB ply_write
element vertex 5
property float x
property float y
property float z
element face 6
property list uchar char vertex_indices
end_header
0.000000 0.000000 0.000000
1.000000 0.000000 0.000000
1.000000 1.000000 0.000000
0.000000 1.000000 0.000000
0.500000 0.500000 1.600000
3 1 0 3
3 1 3 2
3 0 1 4
3 0 4 3
3 3 4 2
3 1 2 4
此外,更为复杂的cow可以在我的资源页处下载,下面就是读取ply格式的文件了,这里文中给定的例子略微有点问题,应该调用的是矩阵的转置
[Tri,Pts] = PLY_READ(\'coww.ply\',\'tri\');
Pts=Pts\';
trisurf(Tri\',Pts(:,1),Pts(:,2),Pts(:,3));