pose_format/src

index.ts

Description or Title for the TypeScript file
 1import {PoseBodyModel, PoseHeaderModel} from "./types";
 2import {Buffer} from "buffer";
 3import {parsePose} from "./parser";
 4import * as fs from "fs";
 5
 6export * from './types';
 7
 8export class Pose {
 9  constructor(public header: PoseHeaderModel, public body: PoseBodyModel) {
10  }
11
12  static from(buffer: Buffer) {
13    const pose = parsePose(buffer);
14    return new Pose(pose.header, pose.body);
15  }
16
17  static async fromLocal(path: string) {
18    const buffer = fs.readFileSync(path);
19    return Pose.from(buffer);
20  }
21
22  static async fromRemote(url: string, abortController?: AbortController) {
23    const init: RequestInit = {};
24    if (abortController) {
25      init.signal = abortController.signal;
26    }
27    const res = await fetch(url, init);
28    if (!res.ok) {
29      let message = res.statusText ?? String(res.status);
30      try {
31        const json = await res.json();
32        message = json.message;
33      } catch (e) {
34      }
35      throw new Error(message);
36    }
37    const buffer = Buffer.from(await res.arrayBuffer());
38    return Pose.from(buffer);
39  }
40}

parser.ts

Description or Title for the TypeScript file
  1import {Parser} from "binary-parser";
  2import {PoseBodyFrameModel, PoseBodyModel, PoseHeaderModel, PoseModel} from "./types";
  3
  4
  5function newParser() {
  6    return new Parser().endianess("little");
  7}
  8
  9function componentHeaderParser() {
 10    const limbParser = newParser()
 11        .uint16("from")
 12        .uint16("to");
 13    const colorParser = newParser()
 14        .uint16("R")
 15        .uint16("G")
 16        .uint16("B");
 17
 18    const strParser = newParser()
 19        .uint16("_chars")
 20        .string("text", {length: "_chars"});
 21
 22
 23    return newParser()
 24        .uint16("_name")
 25        .string("name", {length: "_name"})
 26        .uint16("_format",)
 27        .string("format", {length: "_format"})
 28        .uint16("_points")
 29        .uint16("_limbs")
 30        .uint16("_colors")
 31        .array("points", {
 32            type: strParser,
 33            formatter: (arr: any) => arr.map((item: any) => item.text),
 34            length: "_points"
 35        })
 36        .array("limbs", {
 37            type: limbParser,
 38            length: "_limbs"
 39        })
 40        .array("colors", {
 41            type: colorParser,
 42            length: "_colors"
 43        });
 44}
 45
 46function getHeaderParser() {
 47    const componentParser = componentHeaderParser();
 48
 49    return newParser()
 50        .floatle("version")
 51        .uint16("width")
 52        .uint16("height")
 53        .uint16("depth")
 54        .uint16("_components")
 55        .array("components", {
 56            type: componentParser,
 57            length: "_components"
 58        })
 59        // @ts-ignore
 60        .saveOffset('headerLength')
 61}
 62
 63
 64function getBodyParserV0_0(header: PoseHeaderModel) {
 65    let personParser: any = newParser()
 66        .int16("id");
 67    header.components.forEach(component => {
 68        let pointParser: any = newParser();
 69        Array.from(component.format).forEach(c => {
 70            pointParser = pointParser.floatle(c);
 71        });
 72
 73        personParser = personParser.array(component.name, {
 74            "type": pointParser,
 75            "length": component._points
 76        });
 77    });
 78
 79    const frameParser = newParser()
 80        .uint16("_people")
 81        .array("people", {
 82            type: personParser,
 83            length: "_people"
 84        });
 85
 86    return newParser()
 87        .seek(header.headerLength)
 88        .uint16("fps")
 89        .uint16("_frames")
 90        .array("frames", {
 91            type: frameParser,
 92            length: "_frames"
 93        })
 94}
 95
 96function parseBodyV0_0(header: PoseHeaderModel, buffer: Buffer): PoseBodyModel {
 97    return getBodyParserV0_0(header).parse(buffer) as unknown as PoseBodyModel
 98}
 99
100function parseBodyV0_1(header: PoseHeaderModel, buffer: Buffer): PoseBodyModel {
101    const _points = header.components.map(c => c.points.length).reduce((a, b) => a + b, 0);
102    const _dims = Math.max(...header.components.map(c => c.format.length)) - 1;
103
104    const infoParser = newParser()
105        .seek(header.headerLength)
106        .uint16("fps")
107        .uint16("_frames")
108        .uint16("_people");
109
110    const info = infoParser.parse(buffer);
111
112    // Issue https://github.com/keichi/binary-parser/issues/208
113    const parseFloat32Array = (length: number, offset: number) => {
114        const dataView = new DataView(buffer.buffer, buffer.byteOffset, buffer.length);
115        let currentOffset = offset;
116        const vars = {
117            data: new Float32Array(length),
118            offset: 0
119        };
120
121        for (let i = 0; i < vars.data.length; i++) {
122            let $tmp1 = dataView.getFloat32(currentOffset, true);
123            currentOffset += 4;
124            vars.data[i] = $tmp1
125        }
126        vars.offset = currentOffset;
127
128        return vars;
129    };
130
131    const data = parseFloat32Array(info._frames * info._people * _points * _dims, header.headerLength + 6)
132    const confidence = parseFloat32Array(info._frames * info._people * _points, data.offset)
133
134    function frameRepresentation(i: number) {
135        const people: any[] = new Array(info._people);
136        for (let j = 0; j < info._people; j++) {
137            const person: any = {};
138            people[j] = person;
139            let k = 0;
140            header.components.forEach(component => {
141                person[component.name] = [];
142
143                for (let l = 0; l < component.points.length; l++) {
144                    const offset = i * (info._people * _points) + j * _points;
145                    const place = offset + k + l;
146                    const point: any = {"C": confidence.data[place]};
147                    [...component.format].forEach((dim, dimIndex) => {
148                        if (dim !== "C") {
149                            point[dim] = data.data[place * _dims + dimIndex];
150                        }
151                    });
152                    person[component.name].push(point)
153                }
154                k += component.points.length;
155            });
156        }
157        return {people}
158    }
159
160    const frames = new Proxy({}, {
161        get: function (target: any, name: any) {
162            if (name === 'length') {
163                return info._frames
164            }
165            return frameRepresentation(name);
166        }
167    }) as PoseBodyFrameModel[];
168
169
170    return {
171        ...info,
172        frames
173    } as PoseBodyModel;
174
175}
176
177const headerParser = getHeaderParser();
178
179export function parsePose(buffer: Buffer): PoseModel {
180    const header = headerParser.parse(buffer) as unknown as PoseHeaderModel;
181
182    let body: PoseBodyModel;
183    const version = Math.round(header.version * 1000) / 1000;
184    switch (version) {
185        case 0:
186            body = parseBodyV0_0(header, buffer);
187            break;
188
189        case 0.1:
190            body = parseBodyV0_1(header, buffer);
191            break;
192
193        default:
194            throw new Error("Parsing this body version is not implemented - " + header.version);
195    }
196
197    return {header, body};
198}

types.ts