NET平台下对C3D文件的读写

2019-09-15 10:14栏目:澳门萄京官网
TAG:

【题外话】

多年来实验室要自个儿修改C3D(The 3D Biomechanics Data Standard)文件,即使从网络找到了一个叫c3d4sharp的类库,那几个类库单纯读取C3D文件的话还是能够,不过借使要贯彻修改或许创立C3D文件就相比较麻烦了。同期c3d4sharp完结得比较简单,很多C3D文件里有个别数据都不帮忙。幸而C3D文件总体不是很复杂,于是我就起来重新写了一个C3D文件读写的库,以往在codeplex上创设了个品种叫C3D.NET。

 

【文章索引】

  1. C3D文件格式的结构
  2. C3D文件头的布局
  3. C3D文件参数集结的布局
  4. C3D文件数量区域的布局
  5. 动用C3D.NET读写文件示例

 

【一、C3D文件格式的协会】

第一说C3D文件全部不是很复杂,也未有过多错落有致的概念,C3D的文书档案格式可以从其官方网址下载或在线阅读。首先C3D文件是以Section为单位存款和储蓄的,每三个Section固定为512字节。Section一定是按顺序存款和储蓄的,可是有趣的是,Section的序号是从1初阶的,实际不是0。C3D文件分为三片段,分别是Section ID = 1的C3D文件头(固定为多少个Section,512字节),Section ID一般等于2(在文书头内会付出)的C3D参数会集以及Section ID不亮堂等于几(在文件头和参数集合中都会付给)的C3D数据部分。

可是C3D也可以有很复杂的地方,八个是有关整型的选择,能够接纳使用有号子的(Int16),也得以应用无符号的(UInt16),只但是后面一个能积攒的数据量要多一些罢了,既然那样,不知为何当初还要选用有号子的整型。并且最入眼的是,文档内并没有别的标志能提出文书档案使用的是何种整型。官方给出的化解措施是,能够依据例如帧总的数量、帧索引等看清,要是读出负数,则动用无符号的,不然采纳有暗记的。

另二个是C3D文件能在分化类别的CPU上生成,那表现于不一致CPU恐怕行使的字节序(Endian)和浮点数字分化,比如我们用的CPU都是选择Little-Endian以及IEEE754的浮点数标准。从英特网查还发现成DEC (VAX)以及IBM等CPU选择不一样的浮点数规范,详见小编事先一篇文章:。而C3D则是补助3类CPU,速龙CPU采纳Little-Endian以及IEEE754标准的浮点数,DEC (VAX)选拔的Little-Endian以及故意的浮点数,MIPS (SGI)选用的Big-Endian以及IEEE754标准的浮点数,所以在读取文书档案的时候只怕需求十三分开展处理,在第二节会详细表达。

 

【二、C3D文件头的组织】

首先来说第一有的,也正是C3D的文书头,C3D的文本头一定只占1个Section,即定位的512字节,所以只要读取前512字节就能够把整个头数据得到到了。纵然各类Section有512字节之多,但是对于C3D的文本头只占了相当少的一有的,在文书头中有恢宏空白的区域。个中第一片段是文件头参数部分,内容如下:

图片 1

字节 类型 说明
00H Byte 参数集合开始的Section ID(通常为0x02,但也不一定)
01H Byte 文件标识(固定为0x50)
02H-03H Int16 每帧里3D坐标点的个数
04H-05H Int16 每帧里模拟测量的个数
06H-07H Int16 第1帧的序号(最小为1)
08H-09H Int16 最后1帧的序号
0AH-0BH Int16 最大插值差距
0CH-0FH Single 比例因子(正数表示存储的帧为整数帧,负数为浮点帧)
10H-11H Int16  数据区域开始的Section ID
12H-13H Int16 每帧模拟采样个数
14H-17H Single 帧率

在此之后的第二片段,也正是储存的风云,听起来应该占非常多字节,但是由于限制了平地风波数量最多不能够超过拾几个,同临时间事件名称最长为4字节,所以事件部分也仅占很少的长空。由于C3D首若是为了记录运动的数码,大概在里头有多数相当重要的地点,事件正是用来标志出那个地点的。一个风云包涵四个内容,分别是最长四字节的事件名称、一字节的风浪是或不是应该显得的情状以及叁个四字节的单精度浮点数表示事件现身的日子。

字节 类型 说明
12AH-12BH Int16 事件名是否支持4字节(支持为0x3039,不支持为0)
12CH-12DH Int16 事件数量(最大为18)
130H-176H Single[] 按事件顺序存储的每个事件发生的时间(第1个帧为0.0s)
178H-188H Byte[] 按事件顺序存储的每个事件是否应该显示(1为显示,0为不显示)
18CH-1D2H Char[] 按事件顺序存储的每个事件的名称(每个事件占4字节)

 

【三、C3D文件参数集合的布局】 

C3D文件存款和储蓄了汪洋的参数,其选用了就好像目录的措施存款和储蓄了参数,可是幸亏唯有一级。即参数部分唯有参数组和参数,何况每一种参数组里只好有参数无法再包蕴参数组,每一个参数必需在贰个参数组内。参数集合伊始于文件头中的第4个字节表示的Section ID,常常为2,可是也不必然,有的文件会在文书头后留出空白,然后参数集结初步的Section ID就延期了。所以推断是或不是为C3D文件千万不要一同头读进去个Int16然后决断是否0x5002,而确定要认清第叁个字节是或不是0x50,鲜明参数会集的职位也迟早要遵照文件的首先字节来。

而对此参数集结,起始的4字节定义如下:

字节 类型 说明
00H Byte 第一个参数所在的Section在整个参数集合中的位置(通常为0x01,说明开头4字节之后就是第一个参数)
01H Byte 参数集合部分标识(固定为0x50)
02H Byte 参数集合所占Section数量
03H Byte 生成文件的CPU类型(0x54为Intel,0x55为DEC (VAX, PDP-11),0x56为MIPS (SGI/MIPS))

个中前2个字节官方说一贯忽略就行,可是为了合作在写入的时候依旧要写进去的。第3字节其实大家按顺序读到头也不需求那么些数目。那中间第一的是CPU类型,由于不相同CPU类型采取的字节序以及存储的浮点数字有所分裂,所以我们还需求根据CPU类型实行对应的拍卖。

对于AMD和DEC生成的文档,都以行使Little-Endian字节序存款和储蓄的文书档案,所以无可置疑要选用Little-Endian来读取Int16、Single等种类;而MIPS则使用的Big-Endian字节序存款和储蓄文书档案,所以在读取的时候势须要认清当前Computer暗许的字节序以及文书档案选取的字节序。

而对于Intel和MIPS生成的文书档案,对于浮点数字的储存都是利用标准的IEEE754浮点数字,对于.NET来说无需开展其它管理;而DEC生成的文书档案则运用特有浮点数,须要将4个字节全体读取现在再实行特别的转换,转换方法见作者在此以前的篇章:。

在此之下就存款和储蓄着富有的参数了,参数分为两类,分别是参数组和参数。

对于参数组,要存款和储蓄以下6个内容:

字节 类型 说明
00H SByte 参数组名称长度(如果为负数则表示该参数组锁定请不要修改,而长度为绝对值)
01H SByte 参数组ID的负数
02H - ... Char[] 参数组名称(仅包含大写字母、0-9以及下划线_)
... + 1 - ... + 2 Int16 下一参数组/参数的偏移(包含本内容的2字节)
... + 3
Byte 参数组描述长度
... + 4 -  Char[] 参数组描述内容(ASCII码)

C3D文件未有规定一个参数组前边跟另四个参数组依旧跟该参数组里的富有参数,所以读取的时候要注意下。而参数的剧情则与参数组基本雷同,只是在下一参数组/参数的撼动与参数组描述长度之间贮存着该参数的实际上数目罢了,由于地点描述起来太费事了,这里就不写了。

字节

类型

说明

事先的从头到尾的经过

 

Int16

下一参数组/参数的摆荡(满含本内容的2字节)

 

Byte

参数寄放内容的品类(-1 Char,1 Byte,2 Int16,4 Single),相对值即为长度

 

Byte

参数内容维数(0-3)

 

Byte[]

参数每一维大小(要是维数为0,就从未此部分)

 

Byte[] 

参数实际内容

 

Byte

参数组描述长度

然后的内容

那边必要证实的就是,由于参数能够贮存数组,所以扩张了维数的标记,即当维数为0时,寄存的内容为Char、Byte、Int16、Single等转移出的字节数组;而当维数为1时,寄存的为Char[]、Byte[]、Int16[]、Single[]等转移出的字节数组,由此及彼。而对数组的储存,其实就是数组每个成分依次展张开仓库储,而对此多维数组,则是按行优先开展仓库储存的,比方三维数组,先存款和储蓄Data[0,0,1]再存储Data[0,0,2],依次类推。

不过要求证实的是,对于Char[]以及Char[,]那三种,若是表示的话实际应当相应的是String以及String[]。

 

【四、C3D文件数量区域的结构】

C3D数据区域以帧为单位寄放的,其实一定于这一个区域便是一个帧的集中。而C3D帧其实分为二种,一种是整数帧,而另一种是浮点帧。这两侧的不一样在于,前者存款和储蓄的装有内容都是Int16,而前者则为Single,除此而外,后面一个的3D坐标点(X、Y、Z)还必要倍Gaby例因子才得以,而后面一个存款和储蓄的剧情也正是已经乘以了百分比因子了。

多少区域初步于参数群集中的"POINT"参数组中的"DATA_START"参数,其代表数据区域伊始的Section ID,除外,在文件头中也可能有一份别本。可是依照法定的说法,要是文件头和参数集结中都部分内容,优先读取参数集结中的数据。

对此各样帧,又带有八个部分,第一有个别为3D坐标点部分,第二有的为参考采样部分。

  • 对此每帧的3D坐标点部分,存款和储蓄着该帧全数3D坐标点的数据,各类3D坐标点富含4个Int16或Single数据,分别是X坐标、Y坐标、Z坐标以及Residual和Camera Mask,个中Residual和Camera Mask共占二个Int16。比较风趣的是,对于浮点帧,Residual和Camera Mask如故也仍然叁个Int16,只不过存款和储蓄的时候要将相应的数值转变为Single再展展开旅社储。
    • 对于浮点帧,存款和储蓄的X、Y、Z坐标就是其实的坐标;而对于整数帧,存款和储蓄的X、Y、Z的坐标还索要倍Gaby例因子才方可,比例因子存款和储蓄于参数集结中的"POINT"参数组中的"SCALE"参数。
    • Residual和Camera Mask共占贰个Int16,将其转移为字节数组之后,高位字节(第4个字节)的万丈位表示Residual的符号,即意味着该坐标点是还是不是行得通,假设为0则象征有效,纵然为1则象征无效,而余下的7个字节则为Camera Mask,每一个人表示贰个水墨画机,从未有到高位分别代表7个录制机是不是采纳(为1为使用,为0为未使用)。而Residual的顾名思义数据则为字节数组的第0字节乘以比例因子(浮点帧则为比例因子的相对值)。
  • 而模仿采样部分,则存款和储蓄着该帧全数的上行下效采集样品的数据,不过每一个帧或者包涵八个模拟采集样品,同期每种模仿采集样品恐怕又包含四个channel,存款和储蓄的数目即为该channel下记录的数码。不过存款和储蓄的数额与事实上的数额还要求根据下述公式进行折算,当中data value为存款和储蓄的多寡,real world value为实际的多寡。

    • zero offset能够从"ANALOG"参数组中的"OFFSET"中获取,该数据为Int16的数组,第i位指的正是第i个channel的zero offset。
    • channel scale可以从"ANALOG"参数组中的"SCALE"中赢得,该多少为Single的数组,第i位指的就是dii个channel的scale。
    • general scale是具备模拟采样都亟待倍加的百分比,该数据能够从"ANALOG"参数组中的"GEN_SCALE"中获取,为Single。

    real world value = (data value - zero offset) channel scale general scale

 

【五、使用C3D.NET读写文件示例】

眼下说了那般多,其实只要用C3D.NET来深入分析的话实际是特别轻松的。我们能够从下载C3D.NET的二进制文件可能源码,援引后首要的类都在C3D这些命名空间下。

对于遍历全部的3D坐标可以利用以下的主意,首先能够从文件大概从流中创设C3D文件,然后从文件头中读取存款和储蓄的第1帧的序号,然后读取采集样品点的数额就足以了,当然也得以不从参数组中读取,直接行使file.AllFrames[i].Point3Ds.Length也可以:

 1 C3DFile file = C3DFile.LoadFromFile("文件路径");
 2 Int16 firstFrameIndex = file.Header.FirstFrameIndex;
 3 Int16 pointCount = file.Parameters["POINT:USED"].GetData<Int16>();
 4 
 5 for (Int16 i = 0; i < file.AllFrames.Count; i++)
 6 {
 7     for (Int16 j = 0; j < pointCount; j++)
 8     {
 9         Console.WriteLine("Frame {0} : X = {1}, Y = {2}, Z = {3}",
10             firstFrameIndex + i,
11             file.AllFrames[i].Point3Ds[j].X,
12             file.AllFrames[i].Point3Ds[j].Y ,
13             file.AllFrames[i].Point3Ds[j].Z);
14     }
15 }

而读取模拟采集样品的话,接纳的方法也类似:

 1 Single frameRate = file.Parameters["POINT", "RATE"].GetData<Single>();
 2 Int16 analogChannelCount = file.Parameters["ANALOG", "USED"].GetData<Int16>();
 3 Int16 analogSamplesPerFrame = (Int16)(file.Parameters["ANALOG", "RATE"].GetData<Int16>() / frameRate);
 4 
 5 for (Int16 i = 0; i < file.AllFrames.Count; i++)
 6 {
 7     for (Int16 j = 0; j < analogChannelCount; j++)
 8     {
 9         for (Int16 k = 0; k < analogSamplesPerFrame; k++)
10         {
11             Console.WriteLine("Frame {0}, Sample {1} : {2}",
12                 firstFrameIndex + i, j + 1,
13                 file.AllFrames[i].AnalogSamples[j][k]);
14         }
15     }
16 }

除却三次性将C3D文件内容总体读抽出来的这种措施以外,还足以选取C3DReader来一帧一帧的读取。

 1 using (FileStream fs = new FileStream("文件路径", FileMode.Open, FileAccess.Read))
 2 {
 3     C3DReader reader = new C3DReader(fs);
 4     C3DHeader header = reader.ReadHeader();
 5     C3DParameterDictionary dictionary = reader.ReadParameters();
 6     Int32 index = header.FirstFrameIndex;
 7 
 8     while (true)
 9     {
10         C3DFrame frame = reader.ReadNextFrame(dictionary);
11 
12         if (frame == null)
13         {
14             break;
15         }
16 
17         for (Int16 j = 0; j < frame.Point3Ds.Length; j++)
18         {
19             Console.WriteLine("Frame {0} : X = {1}, Y = {2}, Z = {3}",
20                 index++,
21                 frame.Point3Ds[j].X,
22                 frame.Point3Ds[j].Y,
23                 frame.Point3Ds[j].Z);
24         }
25     }
26 }

对此开创多个C3D文件,只须要使用C3DFile.Create()就足以创制二个空的C3D文件的,不带有别的的参数集合。而保存C3D文件则直接运用file.SaveTo("文件路线")就足以了。

对于增加参数会集能够选拔以下的代码:

1 //首先需要添加参数集合,ID为正数
2 file.Parameters.AddGroup(1, "POINT", "");
3 //然后往指定ID的参数集合中添加参数即可
4 file.Parameters[1].Add("USED", "").SetData<Int16>(5);

增添帧能够行使如下的代码:

1 file.AllFrames.Add(new C3DFrame(new C3DPoint3DData[] {
2     new C3DPoint3DData() { X = x, Y = y, Z = z, Residual = residual, CameraMask = cameraMask},
3     new C3DPoint3DData() { X = x, Y = y, Z = z, Residual = residual, CameraMask = cameraMask},
4     new C3DPoint3DData() { X = x, Y = y, Z = z, Residual = residual, CameraMask = cameraMask},
5     new C3DPoint3DData() { X = x, Y = y, Z = z, Residual = residual, CameraMask = cameraMask},
6     new C3DPoint3DData() { X = x, Y = y, Z = z, Residual = residual, CameraMask = cameraMask} }));

当然,也能够将C3DPoint3DData数组换到C3DAnalogSamples数组,恐怕两个同时增添也足以。

 

【相关链接】

  1. C3D.ORG:
  2. c3d4sharp - C3D File reading/writing tools written in C#:
  3. C3D.NET:

版权声明:本文由澳门萄京官网最大平台发布于澳门萄京官网,转载请注明出处:NET平台下对C3D文件的读写