您的位置:首页 > 编程语言 > MATLAB

PLY文件格式及其MATLAB读写操作

2016-07-13 19:52 507 查看
PLY是一种电脑档案格式,全名为多边形档案(Polygon File Format)或 斯坦福三角形档案(Stanford Triangle Format)。

史丹佛大学的 The Digital Michelangelo Project计划采用PLY格式储存极高分辨率之米开朗基罗的作品"大卫"雕塑。

该格式主要用以储存立体扫描结果的三维数值,透过多边形片面的集合描述三维物体,与其他格式相较之下这是较为简单的方法。它可以储存的资讯包含颜色、透明度、表面法向量、材质座标与资料可信度,并能对多边形的正反两面设定不同的属性。

在档案内容的储存上PLY有两种版本,分别是纯文字(ASCII)版本与二元码(binary)版本,其差异在储存时是否以ASCII编码表示元素资讯。


档案格式

(本文并未提供完整的格式描述,以下仅介绍PLY的基本概念与格式)

每个PLY档都包含档头(header),用以设定网格模型的“元素”与“属性”,以及在档头下方接着一连串的元素“数值资料”。一般而言,网格模型的“元素”就是顶点(vertices)、面(faces),另外还可能包含有边(edges)、深度图样本(samples of range maps)与三角带(triangle strips)等元素。无论是纯文字与二元码的PLY档,档头资讯都是以ASCII编码编写,接续其后的数值资料才有编码之分。PLY档案以此行:


?

ply

开头作为PLY格式的识别。接着第二行是版本资讯,目前有三种写法:

?

format ascii 1.0

format binary_little_endian 1.0

format binary_big_endian 1.0

其中ascii, binary_little_endian, binary_big_endian是档案储存的编码方式,而1.0是遵循的标准版本(现阶段仅有PLY 1.0版)。在档头中可使用’comment’作为一行的开头以编写注解,例如:

?

comment Thisisa comment!

描述元素及属性,必须使用’element’及’property’的关键字,一般的格式为element下方接着属性列表,例如:

?

element

property

property

property

‘property’不仅定义了资料的型态,其出现顺序亦定义了资料的顺序。内定的资料形态有两种写法:一种是char uchar short ushort int uint float double,另外一种是具有位元长度的int8 uint8 int16 uint16 int32 uint32 float32 float64。 例如,描述一个包含12个顶点的物体,每个顶点使用3个单精度浮点数 (x,y,z)代表点的座标,使用3个unsigned char代表顶点颜色,颜色顺序为 (B, G, R),则档头的写法为:

?

element vertex 12

propertyfloatx

propertyfloaty

propertyfloatz

property uchar blue

property uchar green

property uchar red

其中vertex是内定的元素类型,接续的6行property描述构成vertex元素的数值字段顺序代表的意义,及其资料形态。

另一个常使用的元素是面。由于一个面是由3个以上的顶点所组成,因此使用一个“顶点列表”即可描述一个面, PLY格式使用一个特殊关键字’property list’定义之。 例如,一个具有10个面的物体,其PLY档头可能包含:

?

element face 10

property list ucharintvertex_indices

‘property list’表示该元素face的特性是由一行的顶点列表来描述。列表开头以uchar型态的数值表示列表的项目数,后面接着资料型态为int的顶点索引值(vertex_indices),顶点索引值从0开始。

最后,标头必须以此行结尾:

?

end_header

档头后接着的是元素资料(端点座标、拓朴连结等)。在ASCII格式中各个端点与面的资讯都是以独立的一行描述,而二元编码格式则连续储存这些资料,加载时须以’element’定义的元素数目以及’property’中设定的资料形态计算各笔字段的长度。

范例

一个典型的PLY档案结构分成三部分:

?

档头 (从ply开始到end_header)

顶点元素列表

面元素列表

其中的顶点元素列表一般以x y z方式排列,形态如档头所定义;而面元素列表是以下列格式表示。

?

<組成面的端點數N> <端點#1的索引> <端點#2的索引> … <端點#N的索引>

例如画出一个有4个顶点,4个面的四面体,档案内容为:

?

ply

format ascii 1.0

comment這是一個正四面體

element vertex 4

propertyfloatx

propertyfloaty

propertyfloatz

element face 4

property list ucharintvertex_index

end_header

0 3 0

2.449 -1.0 -1.414

0 -1 2.828

-2.449 -1.0 -1.414

3 0 1 3

3 0 2 1

3 0 3 2

3 1 2 3

其中1~10行是档头, 11~14行是顶点元素列表, 15~18行则是面元素列表。

其中: 0 3 0是顶点

历史

PLY格式发展于90年代中期,在史丹佛大学图学实验室的Marc Levoy教授指导下,由Greg Turk及其他成员开发出来。PLY格式受Wavefront .obj格式的启发,但改进了Obj格式所缺少的对任意属性及群组的扩充性。因此PLY格式发明了”property”及”element”这两个关键词,来概括“顶点、面、相关资讯、群组”的概念。

注意

ply文件不支持中文格式的文件名字,所以在使用过程中避免使用中文来命名。

使用MATLAB对PLY文件进行读操作

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

**使用MATLAB对PLY文件进行写操作**
实现将矩阵的内容转换到封装的结构体中


function [Data1,Data2]=change(A,B)

Data1.vertex.x = A(:,1);

Data1.vertex.y = A(:,2);

Data1.vertex.z = A(:,3);

Data2.vertex.x = B(:,1);

Data2.vertex.y = B(:,2);

Data2.vertex.z = B(:,3);

function ply_write ( Elements, Path, Format, Str )

%*****************************************************************************80
%
%% PLY_WRITE writes 3D data as a PLY file.
%
%  Discussion:
%
%    PLY_WRITE(DATA,FILENAME) writes the structure DATA as a binary
%    PLY file.  Every field of DATA is interpreted as an element
%    and every subfield as an element property.  Each subfield of
%    property data must either be an array or a cell array of
%    arrays.  All property data in an element must have the same
%    length.
%
%    A common PLY data structure has the following fields:
%      DATA.vertex.x = x coordinates, [Nx1] real array
%      DATA.vertex.y = y coordinates, [Nx1] real array
%      DATA.vertex.z = z coordinates, [Nx1] real array
%
%      DATA.face.vertex_indices = vertex index lists,
%         an {Mx1} cell array where each cell holds a one-
%         dimesional array (of any length) of vertex indices.
%
%    Some other common data fields:
%
%      DATA.vertex.nx = x coordinate of normal, [Nx1] real array
%      DATA.vertex.ny = y coordinate of normal, [Nx1] real array
%      DATA.vertex.nz = z coordinate of normal, [Nx1] real array
%
%      DATA.edge.vertex1 = index to a vertex, [Px1] integer array
%      DATA.edge.vertex2 = second vertex index, [Px1] integer array
%
%    Many other fields and properties can be added.  The PLY format
%    is not limited to the naming in the examples above -- they are
%    only the conventional naming.
%
%    PLY_WRITE(DATA,FILENAME,FORMAT) write the PLY with a specified
%    data format, where FORMAT is
%      'ascii'                  ASCII text data
%      'binary_little_endian'   binary data, little endian
%      'binary_big_endian'      binary data, big endian (default)
%
%    PLY_WRITE(DATA,FILENAME,FORMAT,'double') or
%
%    PLY_WRITE(DATA,FILENAME,'double') write floating-point data as
%    double precision rather than in the default single precision.
%
%  Example:
%
%    % make a cube
%    clear Data;
%    Data.vertex.x = [0;0;0;0;1;1;1;1];
%    Data.vertex.y = [0;0;1;1;0;0;1;1];
%    Data.vertex.z = [0;1;1;0;0;1;1;0];
%    Data.face.vertex_indices = {[0,1,2,3],[7,6,5,4], ...
%         [0,4,5,1],[1,5,6,2],[2,6,7,3],[3,7,4,0]};
%    ply_write(Data,'cube.ply','ascii');
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    01 July 2016    Seth Billings   bug fix: 'ushort' / 'uint16' data type was
%                                    not included in the min/max value arrays; now
%                                    excluding 'ushort' from the integer data types
%                                    (rather than fixing the min/max arrays), since
%                                    some programs such as MeshLab do not handle the
%                                    'ushort' data type properly anyway
%
%    01 July 2016    Seth Billings   bug fixes for max data value of 'short' / 'int16'
%                                    data type and for selection of output data type
%    01 March 2007
%
%  Author:
%
%    Pascal Getreuer 2004
%    Seth Billings 2016
%
if ( nargin < 4 )

Str = '';

if ( nargin < 3 )
Format = 'binary_big_endian';
elseif ( strcmpi ( Format, 'double' ) )
Str = 'double';
Format = 'binary_big_endian';
end

end

[ fid, Msg ] = fopen ( Path, 'wt' );

if ( fid == -1 )
error(Msg);
end
%
%  Bug Fix: Seth Billings
%  - correcting max integer value for 'short' / 'int16' from 2^16-1 to 2^15-1
%  - excluding 'ushort' from integer types since MeshLab does not
%    read 'ushort' data type properly.
%
PlyTypeNames = {'char','uchar','short','int','uint','float','double', ...
'char8','uchar8','short16','int32','uint32','float32','double64'};
FWriteTypeNames = {'schar','uchar','int16','int32','uint32','single','double'};
MatlabTypeNames = {'int8','uint8','int16','int32','uint32','single','double'};
PrintfTypeChar = {'%d','%u','%d','%d','%u','%-.6f','%-.14e'};
IntegerDataMin =  [-128,   0, -2^15,  -2^31,   0];
IntegerDataMax =  [ 127, 255,  2^15-1, 2^31-1, 2^32-1];
numTypes = length(MatlabTypeNames);

%PlyTypeNames = {'char','uchar','short','ushort','int','uint','float','double', ...
%  'char8','uchar8','short16','ushort16','int32','uint32','float32','double64'};
%FWriteTypeNames = {'schar','uchar','int16','uint16','int32','uint32','single','double'};
%MatlabTypeNames = {'int8','uint8','int16','uint16','int32','uint32','single','double'};
%PrintfTypeChar = {'%d','%u','%d','%u','%d','%u','%-.6f','%-.14e'};
%IntegerDataMin = [-128, 0, -2^15, -2^31, 0];
%IntegerDataMax = [127, 255, 2^16-1, 2^31-1, 2^32-1];
%
%  Write the PLY header.
%

fprintf(fid,'ply\nformat %s 1.0\ncomment created by MATLAB ply_write\n',Format);
ElementNames = fieldnames(Elements);
NumElements = length(ElementNames);
Data = cell(NumElements,1);

for i = 1 : NumElements

eval(['tmp=isa(Elements.',ElementNames{i},',''struct'');']);

if ( tmp )
eval(['PropertyNames{i}=fieldnames(Elements.',ElementNames{i},');']);
else
PropertyNames{i} = [];
end

if ( ~isempty(PropertyNames{i}) )
eval(['Data{i}{1}=Elements.',ElementNames{i},'.',PropertyNames{i}{1},';']);
ElementCount(i) = prod(size(Data{i}{1}));
Type{i} = zeros(length(PropertyNames{i}),1);
else
ElementCount(i) = 0;
end

fprintf(fid,'element %s %u\n',ElementNames{i},ElementCount(i));

for j = 1 : length(PropertyNames{i})

eval(['Data{i}{j}=Elements.',ElementNames{i},'.',PropertyNames{i}{j},';']);

if ( ElementCount(i) ~= prod(size(Data{i}{j})) )
fclose(fid);
error('All property data in an element must have the same length.');
end

if ( iscell(Data{i}{j}) )
Type{i}(j) = numTypes + 1;  % Seth Billings
%Type{i}(j) = 9;
Data{i}{j} = Data{i}{j}{1};
end

for k = 1 : length(MatlabTypeNames)
if ( isa(Data{i}{j},MatlabTypeNames{k}) )
Type{i}(j) = Type{i}(j) + k;
break;
end
end

if ( ~rem(Type{i}(j),numTypes+1) ) % Seth Billings
%if ( ~rem(Type{i}(j),9) )
fclose(fid);
error('Unsupported data structure.');
end
%
%  Try to convert float data to integer data
%
%  Array data.
%
if ( Type{i}(j) <= numTypes)  % Seth Billings
%if ( Type{i}(j) <= 8 )
if any(strcmp({'single','double'},MatlabTypeNames{Type{i}(j)}))
if ~any(floor(Data{i}{j}) ~= Data{i}{j})  % data is integer
MinValue = min(min(Data{i}{j}));
MaxValue = max(max(Data{i}{j}));

% choose smallest possible integer data format
% Bug Fix: Seth Billings
tmp = min(find(MinValue >= IntegerDataMin & MaxValue <= IntegerDataMax));
%tmp = max(min(find(MinValue >= IntegerDataMin)),min(find(MaxValue <= IntegerDataMax)));

if ~isempty(tmp)
Type{i}(j) = tmp;
end
end
end
else        % cell array data
eval(['Data{i}{j}=Elements.',ElementNames{i},'.',PropertyNames{i}{j},';']);
tmp = 1;

for k = 1:prod(size(Data{i}{j}))
tmp = tmp & all(floor(Data{i}{j}{k}) == Data{i}{j}{k});
end

if tmp  % data is integer
MinValue = inf;
MaxValue = -inf;

for k = 1:prod(size(Data{i}{j}))
MinValue = min(MinValue,min(Data{i}{j}{k}));
MaxValue = max(MaxValue,max(Data{i}{j}{k}));
end
%
%  choose smallest possible integer data format
%  Bug Fix: Seth Billings
%
tmp = min(find(MinValue >= IntegerDataMin & MaxValue <= IntegerDataMax));
%tmp = max(min(find(MinValue >= IntegerDataMin)),min(find(MaxValue <= IntegerDataMax)));

if ( ~ isempty ( tmp ) )
Type{i}(j) = tmp + numTypes + 1;
%Type{i}(j) = tmp + 9;
end

end
end

% convert double to single if specified
if rem(Type{i}(j),numTypes+1) == numTypes & ~strcmpi(Str,'double') % Seth Billings
%if rem(Type{i}(j),9) == 8 & ~strcmpi(Str,'double')
Type{i}(j) = Type{i}(j) - 1;
end

if Type{i}(j) <= numTypes  % Seth Billings
%if Type{i}(j) <= 8
fprintf(fid,'property %s %s\n',PlyTypeNames{Type{i}(j)},PropertyNames{i}{j});
else
fprintf(fid,'property list uchar %s %s\n',PlyTypeNames{Type{i}(j)-(numTypes+1)},PropertyNames{i}{j}); % Seth Billings
%fprintf(fid,'property list uchar %s %s\n',PlyTypeNames{Type{i}(j)-9},PropertyNames{i}{j});
end
end
end

fprintf(fid,'end_header\n');

switch Format
case 'ascii'
Format = 0;
case 'binary_little_endian'
fclose(fid);
fid = fopen(Path,'a','ieee-le');
Format = 1;
case 'binary_big_endian'
fclose(fid);
fid = fopen(Path,'a','ieee-be');
Format = 2;
end

for i = 1 : NumElements
if ~isempty(PropertyNames{i})
if ~Format          % write ASCII data
for k = 1:ElementCount(i)
for j = 1:length(PropertyNames{i})
if Type{i}(j) <= numTypes  % Seth Billings
%if Type{i}(j) <= 8
fprintf(fid,[PrintfTypeChar{Type{i}(j)},' '],Data{i}{j}(k));
else
fprintf(fid,'%u%s ',length(Data{i}{j}{k}),sprintf([' ',PrintfTypeChar{Type{i}(j)-(numTypes+1)}],Data{i}{j}{k})); % Seth Billings
%fprintf(fid,'%u%s ',length(Data{i}{j}{k}),sprintf([' ',PrintfTypeChar{Type{i}(j)-9}],Data{i}{j}{k}));
end
end

fprintf(fid,'\n');
end
else            % write binary data
if all(Type{i} <= numTypes) & all(Type{i} == Type{i}(1))  % Seth Billings
%if all(Type{i} <= 8) & all(Type{i} == Type{i}(1))
% property data without list types (fast)
tmp = zeros(length(PropertyNames{i}),ElementCount(i));

for j = 1:length(PropertyNames{i})
tmp(j,:) = Data{i}{j}(:)';
end

fwrite(fid,tmp,FWriteTypeNames{Type{i}(j)});
elseif all(Type{i} > numTypes) % Seth Billings
%elseif all(Type{i} > 8)
% only list types
Type{i} = Type{i} - (numTypes+1); % Seth Billings
%Type{i} = Type{i} - 9;

if length(PropertyNames{i}) == 1
% only one list property
tmp = FWriteTypeNames{Type{i}(1)};

for k = 1:ElementCount(i)
fwrite(fid,length(Data{i}{1}{k}),'uchar');
fwrite(fid,Data{i}{1}{k},tmp);
end
else
% multiple list properties
for k = 1:ElementCount(i)
for j = 1:length(PropertyNames{i})
fwrite(fid,length(Data{i}{j}{k}),'uchar');
fwrite(fid,Data{i}{j}{k},FWriteTypeNames{Type{i}(j)});
end
end
end
else
% mixed type
for k = 1:ElementCount(i)
for j = 1:length(PropertyNames{i})
if Type{i}(j) <= numTypes % Seth Billings
%if Type{i}(j) <= 8
fwrite(fid,Data{i}{j}(k),FWriteTypeNames{Type{i}(j)});
else
fwrite(fid,length(Data{i}{j}{k}),'uchar');
fwrite(fid,Data{i}{j}{k},FWriteTypeNames{Type{i}(j)-(numTypes+1)}); % Seth Billings
%fwrite(fid,Data{i}{j}{k},FWriteTypeNames{Type{i}(j)-9});
end
end
end
end
end
end
end

fclose ( fid );

return
end
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: