openvino.js 48 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206
  1. /* jshint esversion: 6 */
  2. var openvino = openvino || {};
  3. var text = text || require('./text');
  4. openvino.ModelFactory = class {
  5. match(context) {
  6. const identifier = context.identifier;
  7. const extension = identifier.split('.').pop().toLowerCase();
  8. if (extension === 'xml') {
  9. try {
  10. const reader = text.Reader.open(context.stream.peek(), 2048);
  11. for (;;) {
  12. const line = reader.read();
  13. if (line === undefined) {
  14. break;
  15. }
  16. if (line.trim().startsWith('<net ')) {
  17. return 'openvino.xml';
  18. }
  19. }
  20. }
  21. catch (err) {
  22. // continue regardless of error
  23. }
  24. }
  25. if (extension === 'bin') {
  26. switch (identifier) {
  27. case 'natives_blob.bin':
  28. case 'snapshot_blob.bin':
  29. case 'v8_context_snapshot.bin':
  30. return undefined;
  31. }
  32. const stream = context.stream;
  33. const signature = [ 0x21, 0xA8, 0xEF, 0xBE, 0xAD, 0xDE ];
  34. if (signature.length <= stream.length && stream.peek(signature.length).every((value, index) => value === signature[index])) {
  35. return undefined;
  36. }
  37. if (stream.length > 4) {
  38. const buffer = stream.peek(4);
  39. const signature = (buffer[0] | buffer[1] << 8 | buffer[2] << 16 | buffer [3] << 24) >>> 0;
  40. if (signature === 0x00000000 || signature === 0x00000001 ||
  41. signature === 0x01306B47 || signature === 0x000D4B38 || signature === 0x0002C056) {
  42. return undefined;
  43. }
  44. }
  45. if (stream.length > 4) {
  46. const buffer = stream.peek(Math.min(256, stream.length));
  47. for (let i = 0; i < buffer.length - 4; i++) {
  48. const signature = (buffer[i] | buffer[i + 1] << 8 | buffer[i + 2] << 16 | buffer [i + 3] << 24) >>> 0;
  49. if (signature === 0xdeadbeef) {
  50. return undefined;
  51. }
  52. }
  53. }
  54. return 'openvino.bin';
  55. }
  56. return undefined;
  57. }
  58. open(context, match) {
  59. const open = (xml, bin) => {
  60. return openvino.Metadata.open(context).then((metadata) => {
  61. let errors = false;
  62. let xmlDoc = null;
  63. try {
  64. const parser = new DOMParser({ errorHandler: () => { errors = true; } });
  65. xmlDoc = parser.parseFromString(xml, 'text/xml');
  66. }
  67. catch (error) {
  68. const message = error && error.message ? error.message : error.toString();
  69. throw new openvino.Error('File format is not OpenVINO XML (' + message.replace(/\.$/, '') + ').');
  70. }
  71. if (errors || xmlDoc.documentElement == null || xmlDoc.getElementsByTagName('parsererror').length > 0) {
  72. throw new openvino.Error('File format is not OpenVINO.');
  73. }
  74. if (!xmlDoc.documentElement || xmlDoc.documentElement.nodeName != 'net') {
  75. throw new openvino.Error('File format is not OpenVINO IR.');
  76. }
  77. const net = openvino.XmlReader.read(xmlDoc.documentElement);
  78. return new openvino.Model(metadata, net, bin);
  79. });
  80. };
  81. const identifier = context.identifier;
  82. switch (match) {
  83. case 'openvino.xml':
  84. return context.request(identifier.substring(0, identifier.length - 4) + '.bin', null).then((stream) => {
  85. const buffer = stream.read();
  86. const decoder = new TextDecoder('utf-8');
  87. const xml = decoder.decode(context.stream.peek());
  88. return open(xml, buffer);
  89. }).catch(() => {
  90. const stream = context.stream;
  91. const buffer = stream.peek();
  92. const decoder = new TextDecoder('utf-8');
  93. const xml = decoder.decode(buffer);
  94. return open(xml, null);
  95. });
  96. case 'openvino.bin':
  97. return context.request(identifier.substring(0, identifier.length - 4) + '.xml', 'utf-8').then((xml) => {
  98. return open(xml, context.stream.peek());
  99. });
  100. }
  101. }
  102. };
  103. openvino.Model = class {
  104. constructor(metadata, net, bin) {
  105. this._name = net.name || '';
  106. this._graphs = [ new openvino.Graph(metadata, net, bin) ];
  107. }
  108. get name() {
  109. return this._name;
  110. }
  111. get format() {
  112. return 'OpenVINO IR';
  113. }
  114. get graphs() {
  115. return this._graphs;
  116. }
  117. };
  118. openvino.Graph = class {
  119. constructor(metadata, net, bin) {
  120. this._name = net.name || '';
  121. this._nodes = [];
  122. this._inputs = [];
  123. this._outputs = [];
  124. this._arguments = {};
  125. const layers = new Map(net.layers.map((entry) => [ entry.id, entry ]));
  126. for (const layer of this._const(net.layers, net.edges)) {
  127. const inputs = layer.inputs.map((input) => {
  128. const to = layer.id + ':' + input.id;
  129. if (net.edges[to]) {
  130. const output = net.edges[to] ? net.edges[to].split(':') : [];
  131. const outputLayerId = output[0];
  132. const outputId = output[1];
  133. const outputLayer = layers.get(outputLayerId);
  134. if (outputLayer && outputId) {
  135. const output = outputLayer.outputs.find((output) => output.id === outputId);
  136. if (input && output) {
  137. input.precision = output.precision;
  138. }
  139. }
  140. }
  141. return this._argument(layer.id, input.precision || layer.precision, input, net.edges);
  142. });
  143. const outputs = layer.outputs.map((output) => this._argument(layer.id, output && output.precision ? output.precision : layer && layer.precision ? layer.precision : null, output, null));
  144. switch (layer.type) {
  145. case 'Input': {
  146. const name = layer.name || '';
  147. // precision is a part of OpenVINO IR layers of IR v6 and earlier
  148. // in IR v7 and newer the port is no longer an attribute of the layer but of each output port
  149. // IR input is not just a placeholder, it is conceptually the legitimate layer
  150. // in order not to break compatibility with the overall approach
  151. // with openvino.Parameter for inputs and openvino.Node for outputs
  152. // input openvino.Node would be stored as an optional attribute of openvino.Parameter
  153. this._inputs.push(new openvino.Parameter(name, outputs));
  154. break;
  155. }
  156. case 'Parameter': {
  157. const name = layer.name || '';
  158. this._inputs.push(new openvino.Parameter(name, outputs));
  159. break;
  160. }
  161. default: {
  162. this._nodes.push(new openvino.Node(this, metadata, bin, layer, inputs, outputs));
  163. break;
  164. }
  165. }
  166. }
  167. this._replaceTensorIteratorWithSubgraph(metadata, bin, net.layers, net.edges);
  168. delete this._arguments;
  169. // Validation
  170. // all graph elements are split between inputs and nodes
  171. // by definition IR is a graph can have inputs of two types: "Input" and "Const"
  172. // "Input" layers are already moved to inputs when we parse a graph
  173. // if there are any layers that do not have input arguments and they are no Const ones
  174. // this means that this graph was not properly processed by the graph building logic
  175. const outputSet = new Set();
  176. for (const node of this._nodes) {
  177. for (const output of node.outputs) {
  178. for (const argument of output.arguments) {
  179. outputSet.add(argument.name);
  180. }
  181. }
  182. }
  183. for (const input of this.inputs) {
  184. for (const argument of input.arguments) {
  185. outputSet.add(argument.name);
  186. }
  187. }
  188. const nodesWithNonExistentInputs = new Set();
  189. for (const node of this._nodes) {
  190. for (const input of node.inputs) {
  191. for (const argument of input.arguments) {
  192. if (!argument.initializer && !outputSet.has(argument.name)) {
  193. nodesWithNonExistentInputs.add(node);
  194. }
  195. }
  196. }
  197. }
  198. if (nodesWithNonExistentInputs.size !== 0){
  199. net.disconnectedLayers = Array.from(nodesWithNonExistentInputs).map((node) => node.name);
  200. }
  201. }
  202. get name() {
  203. return this._name;
  204. }
  205. get inputs() {
  206. return this._inputs;
  207. }
  208. get outputs() {
  209. return this._outputs;
  210. }
  211. get nodes() {
  212. return this._nodes;
  213. }
  214. _argument(layer, precision, port, map) {
  215. let id = layer + ':' + port.id;
  216. if (map) {
  217. id = map[id];
  218. }
  219. let argument = this._arguments[id];
  220. if (!argument) {
  221. const shape = port.dims.length == 0 ? null : new openvino.TensorShape(port.dims);
  222. argument = new openvino.Argument(id, new openvino.TensorType(precision, shape), null);
  223. }
  224. return argument;
  225. }
  226. _replaceTensorIteratorWithSubgraph(metadata, bin, layers, edges) {
  227. const tensorIteratorLayers = layers.filter((node) => node.type === 'TensorIterator');
  228. for (const tensorIteratorLayer of tensorIteratorLayers) {
  229. const singleTensorIteratorNodeId = tensorIteratorLayer.id;
  230. const tiNode = this._nodes.find((n) => n._id === singleTensorIteratorNodeId);
  231. const iteratorLayers = tensorIteratorLayer.body.layers;
  232. const iteratorEdgeMap = tensorIteratorLayer.body.edges;
  233. const iteratorBackEdgesMap = tensorIteratorLayer.back_edges;
  234. const iteratorAllEdges = Object.assign({}, iteratorEdgeMap, iteratorBackEdgesMap);
  235. const mappingForNestedIR = tensorIteratorLayer.port_map;
  236. for (const nestedLayer of this._const(iteratorLayers, iteratorAllEdges, iteratorBackEdgesMap)) {
  237. const inputs = nestedLayer.inputs.map((input) => this._argument(nestedLayer.id, nestedLayer.precision, input, iteratorAllEdges));
  238. const outputs = nestedLayer.outputs.map((output) => this._argument(nestedLayer.id, nestedLayer.precision || output.precision, output, null));
  239. const nestedNode = new openvino.Node(this, metadata, bin, nestedLayer, inputs, outputs);
  240. nestedNode._id = singleTensorIteratorNodeId + '_' + nestedLayer.id;
  241. for (const input of nestedNode._inputs) {
  242. for (const input_argument of input.arguments) {
  243. // we had a argument with id: 0:1 - meaning from layer "0" and its port "1"
  244. // now as we rename all internal nodes to have an id of the TI included
  245. // e.g. internal layer with id "0" and TI with id "14" results in internal layer to get id "14_0"
  246. if (input_argument.name){
  247. input_argument._name = singleTensorIteratorNodeId + '_' + input_argument.name;
  248. }
  249. }
  250. }
  251. for (const output of nestedNode._outputs) {
  252. for (const output_argument of output.arguments) {
  253. // we had a argument with id: 1:1 - meaning from me with id "1" and my port "1"
  254. // now as we rename all internal nodes to have an id of the TI included
  255. // e.g. my layer with id "1" and TI with id "14" results in internal layer to get id "14_1"
  256. if (output_argument.name){
  257. output_argument._name = singleTensorIteratorNodeId + '_' + output_argument.name;
  258. }
  259. }
  260. }
  261. this._nodes.push(nestedNode);
  262. }
  263. // We know for sure that edges that appeared in the nested IR are not aware of the external context
  264. for (const nestedInput of mappingForNestedIR.input) {
  265. const nestedNode = this._nodes.find((n) => n._id === singleTensorIteratorNodeId + '_' + nestedInput.internal_layer_id);
  266. const candidate_edge = edges[singleTensorIteratorNodeId + ':' + nestedInput.external_port_id];
  267. if (candidate_edge) {
  268. const parts = candidate_edge.split(':');
  269. const parentLayerID = parts[0];
  270. const parentPortID = parts[1];
  271. const parentNode = this._nodes.find((n) => n._id === parentLayerID);
  272. if (!parentNode) {
  273. // its parent is a TensorIterator that was removed on the previous cycle
  274. // information is still present in the inputs of the current TensorIterator node
  275. const potentialParentInput = tiNode._inputs.find((tiInput) => tiInput._name === 'input');
  276. if (!potentialParentInput) {
  277. return;
  278. }
  279. const inputWithoutId = nestedNode._inputs.find((input) => {
  280. return Boolean(input.arguments.find((argument) => !argument.name));
  281. });
  282. if (inputWithoutId) {
  283. const argumentWithoutId = inputWithoutId.arguments.find((argument) => !argument.name);
  284. if (argumentWithoutId){
  285. argumentWithoutId._name = potentialParentInput.arguments[0].name;
  286. }
  287. }
  288. }
  289. else {
  290. if (!nestedNode._inputs){
  291. throw new openvino.Error("Tensor Iterator node with name '" + nestedNode._id + "' does not have inputs.");
  292. }
  293. const newId = parentLayerID + ':' + parentPortID;
  294. const inputWithoutId = nestedNode._inputs.find((input) => {
  295. return Boolean(input.arguments.find((argument) => !argument.name));
  296. });
  297. if (inputWithoutId) {
  298. const argumentWithoutId = inputWithoutId._arguments.find((argument) => !argument._name);
  299. if (argumentWithoutId){
  300. argumentWithoutId._name = newId;
  301. }
  302. }
  303. else {
  304. // TODO: no tensor information in the new argument - passed as null for now
  305. nestedNode._inputs.push(new openvino.Parameter((nestedNode._inputs.length + 1).toString(), [
  306. new openvino.Argument(newId, null, null)
  307. ]));
  308. }
  309. }
  310. }
  311. }
  312. for (const nestedOutput of mappingForNestedIR.output) {
  313. const nestedNode = this._nodes.find((n) => n._id === `${singleTensorIteratorNodeId}_${nestedOutput.internal_layer_id}`);
  314. const toEdge = singleTensorIteratorNodeId + ':' + nestedOutput.external_port_id;
  315. const candidate_edges = Object.keys(edges).filter((key) => edges[key] === toEdge);
  316. for (const candidate_edge of candidate_edges) {
  317. const childLayerID = candidate_edge.split(':')[0];
  318. const child = this._nodes.find((layer) => layer._id === childLayerID);
  319. if (!child._inputs || (child._inputs && child._inputs.length === 0)){
  320. continue;
  321. }
  322. for (const child_input of child._inputs) {
  323. for (const argument of child_input._arguments) {
  324. if (!argument.name || (argument.name && argument.name.split(':')[0] !== singleTensorIteratorNodeId)) {
  325. continue;
  326. }
  327. if (nestedNode._outputs && nestedNode._outputs.length === 0) {
  328. // it turns out that IRs of version 10 with TensorIterators (TI) can omit
  329. // output section in the output layers of TI body. It seems to happen only
  330. // for cases when TI has a single output. Therefore, there is a workaround that
  331. // creates fake output section for the TI output layer in order to connect it
  332. // with a layer from the main IR.
  333. const myPort = 0;
  334. const newId = nestedNode.id + ':' + myPort;
  335. nestedNode._outputs.push(new openvino.Parameter('output', [
  336. new openvino.Argument(newId, null, null)
  337. ]));
  338. }
  339. const myPort = nestedNode.outputs[0].arguments[0].name.split(':')[1];
  340. argument._name = nestedNode.id + ':' + myPort;
  341. }
  342. }
  343. }
  344. }
  345. this._nodes = this._nodes.filter((node) => node.id !== tensorIteratorLayer.id);
  346. }
  347. }
  348. _const(layers, edges, back_edges, omitConstLayers) {
  349. const results = [];
  350. back_edges = back_edges || {};
  351. layers = layers.slice();
  352. for (const layer of layers) {
  353. if (layer.type === 'Const' && layer.inputs.length === 0 && layer.outputs.length === 1 &&
  354. layer.blobs.length === 0 && layer.data && layer.data.length > 3) {
  355. const data = {};
  356. for (const attribute of layer.data) {
  357. data[attribute.name] = attribute.value;
  358. }
  359. if (data['element_type'] && data['offset'] && data['size']) {
  360. const element_type = data['element_type'];
  361. let precision = null;
  362. switch (element_type) {
  363. case 'f16': precision = 'FP16'; break;
  364. case 'f32': precision = 'FP32'; break;
  365. default: precision = element_type.toUpperCase();
  366. }
  367. const shape = data['shape'] ? data['shape'].split(',').map((dim) => parseInt(dim.trim(), 10)) : null;
  368. layer.data = [];
  369. layer.blobs.push({ name: 'custom', precision: precision, offset: parseInt(data['offset'], 10), size: parseInt(data['size'], 10), shape: shape });
  370. }
  371. }
  372. if (layer.type === 'Const' && layer.blobs.length === 1 && !layer.blobs[0].shape &&
  373. layer.inputs.length === 0 && layer.outputs.length === 1 && layer.outputs[0].dims) {
  374. layer.blobs[0].shape = layer.outputs[0].dims;
  375. }
  376. }
  377. const constMap = new Map();
  378. for (const layer of layers) {
  379. if (layer.type === 'Const' && layer.inputs.length === 0 && layer.outputs.length === 1) {
  380. const from = layer.id + ':' + layer.outputs[0].id;
  381. constMap.set(from, { layer: layer, counter: 0 });
  382. }
  383. }
  384. for (const to of Object.keys(edges)) {
  385. const from = edges[to];
  386. if (constMap.has(from)) {
  387. constMap.get(from).counter++;
  388. }
  389. }
  390. if (back_edges) {
  391. for (const to of Object.keys(back_edges)) {
  392. const from = back_edges[to];
  393. if (constMap.has(from)) {
  394. constMap.get(from).counter++;
  395. }
  396. }
  397. }
  398. for (const pair of constMap) {
  399. if (pair[1].counter !== 1) {
  400. constMap.delete(pair[0]);
  401. }
  402. }
  403. for (const layer of layers) {
  404. if (layer.blobs.length === 0) {
  405. for (let i = layer.inputs.length - 1; i > 0; i--) {
  406. const input = layer.inputs[i];
  407. const to = layer.id + ':' + input.id;
  408. const from = edges[to] || back_edges[to];
  409. if (!constMap.has(from)) {
  410. break;
  411. }
  412. const constLayer = constMap.get(from).layer;
  413. if (constLayer && Array.isArray(constLayer.blobs)) {
  414. const blob = constLayer.blobs[0];
  415. if (blob) {
  416. blob.id = constLayer.name || constLayer.id;
  417. blob.kind = 'Const';
  418. layer.blobs.push(blob);
  419. layer.inputs.splice(i, 1);
  420. constMap.get(from).layer = null;
  421. constMap.get(from).delete = true;
  422. }
  423. }
  424. }
  425. }
  426. }
  427. if (omitConstLayers) {
  428. for (const layer of layers) {
  429. if (layer.blobs.length === 0) {
  430. for (let i = layer.inputs.length - 1; i > 0; i--) {
  431. const input = layer.inputs[i];
  432. const to = layer.id + ':' + input.id;
  433. const from = edges[to] || back_edges[to];
  434. if (!constMap.has(from)) {
  435. break;
  436. }
  437. const constLayer = constMap.get(from).layer;
  438. const blob = constLayer.blobs[0];
  439. if (blob) {
  440. blob.id = constLayer.name || constLayer.id;
  441. blob.kind = 'Const';
  442. layer.blobs.push(blob);
  443. layer.inputs.splice(i, 1);
  444. constMap.get(from).layer = null;
  445. constMap.get(from).delete = true;
  446. }
  447. }
  448. }
  449. }
  450. }
  451. while (layers.length > 0) {
  452. const layer = layers.shift();
  453. if (layer.type === 'Const' && layer.inputs.length === 0 && layer.outputs.length === 1) {
  454. const from = layer.id + ':' + layer.outputs[0].id;
  455. if (constMap.has(from) && constMap.get(from).delete) {
  456. continue;
  457. }
  458. }
  459. results.push(layer);
  460. }
  461. return results;
  462. }
  463. };
  464. openvino.Node = class {
  465. constructor(graph, metadata, bin, layer, inputs, outputs) {
  466. this._name = layer.name || '';
  467. this._id = layer.id;
  468. this._inputs = [];
  469. this._outputs = [];
  470. this._attributes = [];
  471. const type = layer.type;
  472. this._type = metadata.type(type) || { name: type };
  473. const precision = layer.precision;
  474. for (let i = 0; i < inputs.length; ) {
  475. const input = this._type && this._type.inputs && i < this._type.inputs.length ? this._type.inputs[i] : inputs.length === 1 ? { name: 'input' } : { name: i.toString() };
  476. const count = input.list ? inputs.length - i : 1;
  477. const list = inputs.slice(i, i + count);
  478. this._inputs.push(new openvino.Parameter(input.name, list));
  479. i += count;
  480. }
  481. for (let i = 0; i < outputs.length; ) {
  482. const output = this._type && this._type.outputs && i < this._type.outputs.length ? this._type.outputs[i] : outputs.length === 1 ? { name: 'output' } : { name: i.toString() };
  483. const count = output.list ? outputs.length - i : 1;
  484. const list = outputs.slice(i, i + count);
  485. this._outputs.push(new openvino.Parameter(output.name, list));
  486. i += count;
  487. }
  488. const attributes = {};
  489. for (const attribute of layer.data) {
  490. attributes[attribute.name] = attribute.value;
  491. const attributeSchema = metadata.attribute(type, attribute.name);
  492. this._attributes.push(new openvino.Attribute(attributeSchema, attribute.name, attribute.value));
  493. }
  494. for (const blob of layer.blobs) {
  495. const name = blob.name;
  496. const offset = blob.offset;
  497. const size = blob.size;
  498. const data = (bin && (offset + size) <= bin.length) ? bin.slice(offset, offset + size) : null;
  499. let dimensions = blob.shape || null;
  500. const kind = blob.kind || 'Blob';
  501. const id = blob.id || '';
  502. const dataType = blob.precision || precision;
  503. const precisionMap = {
  504. 'FP16': 2, 'FP32': 4,
  505. 'I8': 1, 'I16': 2, 'I32': 4, 'I64': 8,
  506. 'U8': 1, 'U16': 2, 'U32': 4, 'U64': 8
  507. };
  508. const itemSize = precisionMap[dataType];
  509. if (itemSize) {
  510. switch (type + ':' + name) {
  511. case 'FullyConnected:weights': {
  512. const outSize = parseInt(attributes['out-size'], 10);
  513. dimensions = [ size / (outSize * itemSize), outSize ];
  514. break;
  515. }
  516. case 'FullyConnected:biases': {
  517. dimensions = [ parseInt(attributes['out-size'], 10) ];
  518. break;
  519. }
  520. case 'Convolution:weights':
  521. case 'Deconvolution:weights': {
  522. const c = this.inputs[0].arguments[0].type.shape.dimensions[1];
  523. const group = parseInt(attributes['group'] || '1', 10);
  524. const kernel = attributes['kernel-x'] && attributes['kernel-y'] ?
  525. [ parseInt(attributes['kernel-x'], 10), parseInt(attributes['kernel-y'], 10) ] :
  526. attributes['kernel'].split(',').map((v) => parseInt(v.trim(), 10));
  527. const n = parseInt(attributes['output'], 10);
  528. dimensions = [ Math.floor(c / group), n ].concat(kernel);
  529. break;
  530. }
  531. case 'LSTMCell:weights': {
  532. const hidden_size = parseInt(attributes['hidden_size'], 10);
  533. dimensions = [ Math.floor(size / (itemSize * hidden_size)) , hidden_size ];
  534. break;
  535. }
  536. case 'ScaleShift:weights':
  537. case 'ScaleShift:biases':
  538. case 'Convolution:biases':
  539. case 'Normalize:weights':
  540. case 'LSTMCell:biases':
  541. case 'PReLU:weights': {
  542. dimensions = [ Math.floor(size / itemSize) ];
  543. break;
  544. }
  545. case 'Const:custom': {
  546. if (this._outputs.length > 0 &&
  547. this._outputs[0].arguments.length > 0 &&
  548. this._outputs[0].arguments[0].type &&
  549. this._outputs[0].arguments[0].type.shape &&
  550. this._outputs[0].arguments[0].type.shape.dimensions) {
  551. dimensions = this._outputs[0].arguments[0].type.shape.dimensions;
  552. }
  553. break;
  554. }
  555. }
  556. }
  557. const shape = dimensions ? new openvino.TensorShape(dimensions) : null;
  558. this._inputs.push(new openvino.Parameter(name, [
  559. new openvino.Argument(id, null, new openvino.Tensor(dataType, shape, data, kind))
  560. ]));
  561. }
  562. }
  563. get id() {
  564. return this._id;
  565. }
  566. get name() {
  567. return this._name;
  568. }
  569. get device() {
  570. return this._device || '';
  571. }
  572. get type() {
  573. return this._type;
  574. }
  575. get attributes() {
  576. return this._attributes;
  577. }
  578. get inputs() {
  579. return this._inputs;
  580. }
  581. get outputs() {
  582. return this._outputs;
  583. }
  584. };
  585. openvino.Parameter = class {
  586. constructor(name, args) {
  587. this._name = name;
  588. this._arguments = args;
  589. }
  590. get name() {
  591. return this._name;
  592. }
  593. get visible() {
  594. return true;
  595. }
  596. get arguments() {
  597. return this._arguments;
  598. }
  599. };
  600. openvino.Argument = class {
  601. constructor(name, type, initializer) {
  602. // if (typeof name !== 'string') {
  603. // throw new openvino.Error("Invalid argument identifier '" + JSON.stringify(name) + "'.");
  604. // }
  605. this._name = name;
  606. this._type = type || null;
  607. this._initializer = initializer || null;
  608. }
  609. get name() {
  610. return this._name;
  611. }
  612. get type() {
  613. if (this._initializer) {
  614. return this._initializer.type;
  615. }
  616. return this._type;
  617. }
  618. get initializer() {
  619. return this._initializer;
  620. }
  621. };
  622. openvino.Attribute = class {
  623. constructor(schema, name, value) {
  624. this._name = name;
  625. this._value = value;
  626. if (schema) {
  627. if (Object.prototype.hasOwnProperty.call(schema, 'type')) {
  628. this._type = schema.type;
  629. switch (schema.type) {
  630. case 'boolean':
  631. switch (value) {
  632. case '1':
  633. case 'true':
  634. case 'True':
  635. this._value = true;
  636. break;
  637. case '0':
  638. case 'false':
  639. case 'False':
  640. this._value = false;
  641. break;
  642. }
  643. break;
  644. case 'int32': {
  645. const intValue = Number.parseInt(this._value, 10);
  646. this._value = Number.isNaN(this._value - intValue) ? value : intValue;
  647. break;
  648. }
  649. case 'float32':
  650. case 'float64': {
  651. const floatValue = Number.parseFloat(this._value);
  652. this._value = Number.isNaN(this._value - floatValue) ? value : floatValue;
  653. break;
  654. }
  655. case 'int32[]':
  656. if (this._value.length > 2) {
  657. let ints = [];
  658. this._value.split(',').map((item) => {
  659. item = item.trim();
  660. const intValue = Number.parseInt(item, 10);
  661. if (Number.isNaN(item - intValue)) {
  662. ints = null;
  663. }
  664. else if (ints != null) {
  665. ints.push(intValue);
  666. }
  667. });
  668. if (ints != null) {
  669. this._value = ints;
  670. }
  671. }
  672. break;
  673. case 'float32[]':
  674. if (this._value.length > 2) {
  675. let floats = [];
  676. this._value.split(',').map((item) => {
  677. item = item.trim();
  678. const floatValue = Number.parseFloat(item);
  679. if (Number.isNaN(item - floatValue)) {
  680. floats = null;
  681. }
  682. else if (floats != null) {
  683. floats.push(floatValue);
  684. }
  685. });
  686. if (floats != null) {
  687. this._value = floats;
  688. }
  689. }
  690. break;
  691. }
  692. }
  693. if (Object.prototype.hasOwnProperty.call(schema, 'visible') && schema.visible == false) {
  694. this._visible = false;
  695. }
  696. else if (Object.prototype.hasOwnProperty.call(schema, 'default')) {
  697. let defaultValue = schema.default;
  698. if (this._value == defaultValue) {
  699. this._visible = false;
  700. }
  701. else if (Array.isArray(this._value) && Array.isArray(defaultValue)) {
  702. defaultValue = defaultValue.slice(0, defaultValue.length);
  703. if (defaultValue.length > 1 && defaultValue[defaultValue.length - 1] == null) {
  704. defaultValue.pop();
  705. while (defaultValue.length < this._value.length) {
  706. defaultValue.push(defaultValue[defaultValue.length - 1]);
  707. }
  708. }
  709. if (this._value.every((item, index) => { return item == defaultValue[index]; })) {
  710. this._visible = false;
  711. }
  712. }
  713. }
  714. }
  715. }
  716. get name() {
  717. return this._name;
  718. }
  719. get value() {
  720. return this._value;
  721. }
  722. get type() {
  723. return this._type;
  724. }
  725. get visible() {
  726. return this._visible == false ? false : true;
  727. }
  728. };
  729. openvino.Tensor = class {
  730. constructor(precision, shape, data, kind) {
  731. this._data = data;
  732. this._type = new openvino.TensorType(precision, shape);
  733. this._kind = kind;
  734. }
  735. get kind() {
  736. return this._kind;
  737. }
  738. get type() {
  739. return this._type;
  740. }
  741. get state() {
  742. return this._context().state;
  743. }
  744. get value() {
  745. const context = this._context();
  746. if (context.state) {
  747. return null;
  748. }
  749. context.limit = Number.MAX_SAFE_INTEGER;
  750. return this._decode(context, 0);
  751. }
  752. toString() {
  753. const context = this._context();
  754. if (context.state) {
  755. return '';
  756. }
  757. context.limit = 10000;
  758. const value = this._decode(context, 0);
  759. return openvino.Tensor._stringify(value, '', ' ');
  760. }
  761. _context() {
  762. const context = {};
  763. context.state = null;
  764. if (!this._data) {
  765. context.state = 'Tensor data is empty.';
  766. return context;
  767. }
  768. if (!this._type.shape) {
  769. context.state = 'Tensor shape is not defined.';
  770. return context;
  771. }
  772. switch(this._type.dataType) {
  773. case 'float16':
  774. case 'float32':
  775. case 'int8':
  776. case 'int16':
  777. case 'int32':
  778. case 'int64':
  779. case 'uint8':
  780. case 'uint16':
  781. case 'uint32':
  782. case 'uint64':
  783. context.index = 0;
  784. context.count = 0;
  785. context.data = new DataView(this._data.buffer, this._data.byteOffset, this._data.byteLength);
  786. break;
  787. default:
  788. context.state = 'Tensor data type is not implemented.';
  789. break;
  790. }
  791. context.dataType = this._type.dataType;
  792. context.shape = this._type.shape.dimensions;
  793. return context;
  794. }
  795. _decode(context, dimension) {
  796. const shape = context.shape.length == 0 ? [ 1 ] : context.shape;
  797. const results = [];
  798. const size = shape[dimension];
  799. if (dimension == shape.length - 1) {
  800. for (let i = 0; i < size; i++) {
  801. if (context.count > context.limit) {
  802. results.push('...');
  803. return results;
  804. }
  805. switch (this._type.dataType) {
  806. case 'float32':
  807. results.push(context.data.getFloat32(context.index, true));
  808. context.index += 4;
  809. context.count++;
  810. break;
  811. case 'float16':
  812. results.push(context.data.getFloat16(context.index, true));
  813. context.index += 2;
  814. context.count++;
  815. break;
  816. case 'int8':
  817. results.push(context.data.getInt8(context.index));
  818. context.index += 1;
  819. context.count++;
  820. break;
  821. case 'int16':
  822. results.push(context.data.getInt16(context.index, true));
  823. context.index += 2;
  824. context.count++;
  825. break;
  826. case 'int32':
  827. results.push(context.data.getInt32(context.index, true));
  828. context.index += 4;
  829. context.count++;
  830. break;
  831. case 'int64':
  832. results.push(context.data.getInt64(context.index, true));
  833. context.index += 8;
  834. context.count++;
  835. break;
  836. case 'uint8':
  837. results.push(context.data.getUint8(context.index));
  838. context.index += 1;
  839. context.count++;
  840. break;
  841. case 'uint16':
  842. results.push(context.data.getUint16(context.index, true));
  843. context.index += 2;
  844. context.count++;
  845. break;
  846. case 'uint32':
  847. results.push(context.data.getUint32(context.index, true));
  848. context.index += 4;
  849. context.count++;
  850. break;
  851. case 'uint64':
  852. results.push(context.data.getUint64(context.index, true));
  853. context.index += 8;
  854. context.count++;
  855. break;
  856. }
  857. }
  858. }
  859. else {
  860. for (let j = 0; j < size; j++) {
  861. if (context.count > context.limit) {
  862. results.push('...');
  863. return results;
  864. }
  865. results.push(this._decode(context, dimension + 1));
  866. }
  867. }
  868. if (context.shape.length == 0) {
  869. return results[0];
  870. }
  871. return results;
  872. }
  873. static _stringify(value, indentation, indent) {
  874. if (Array.isArray(value)) {
  875. const result = [];
  876. result.push(indentation + '[');
  877. const items = value.map((item) => openvino.Tensor._stringify(item, indentation + indent, indent));
  878. if (items.length > 0) {
  879. result.push(items.join(',\n'));
  880. }
  881. result.push(indentation + ']');
  882. return result.join('\n');
  883. }
  884. if (typeof value == 'string') {
  885. return indentation + value;
  886. }
  887. if (value == Infinity) {
  888. return indentation + 'Infinity';
  889. }
  890. if (value == -Infinity) {
  891. return indentation + '-Infinity';
  892. }
  893. if (isNaN(value)) {
  894. return indentation + 'NaN';
  895. }
  896. return indentation + value.toString();
  897. }
  898. };
  899. openvino.TensorType = class {
  900. constructor(precision, shape) {
  901. precision = precision ? precision.toLowerCase() : precision;
  902. switch (precision) {
  903. case 'f16': this._dataType = 'float16'; break;
  904. case 'fp16': this._dataType = 'float16'; break;
  905. case 'f32': this._dataType = 'float32'; break;
  906. case 'fp32': this._dataType = 'float32'; break;
  907. case 'bf16': this._dataType = 'bfloat16'; break;
  908. case 'i4': this._dataType = 'int4'; break;
  909. case 'i8': this._dataType = 'int8'; break;
  910. case 'i16': this._dataType = 'int16'; break;
  911. case 'i32': this._dataType = 'int32'; break;
  912. case 'i64': this._dataType = 'int64'; break;
  913. case 'u1': this._dataType = 'boolean'; break;
  914. case 'u4': this._dataType = 'uint4'; break;
  915. case 'u8': this._dataType = 'uint8'; break;
  916. case 'u16': this._dataType = 'uint16'; break;
  917. case 'u32': this._dataType = 'uint32'; break;
  918. case 'u64': this._dataType = 'uint64'; break;
  919. case 'bool': this._dataType = 'boolean'; break;
  920. case 'boolean': this._dataType = 'boolean'; break;
  921. case 'bin': this._dataType = 'bit'; break;
  922. case '': this._dataType = '?'; break;
  923. case null: this._dataType = '?'; break;
  924. default: throw new openvino.Error("Unknown precision '" + JSON.stringify(precision) + "'.");
  925. }
  926. this._shape = shape;
  927. }
  928. get dataType() {
  929. return this._dataType;
  930. }
  931. get shape() {
  932. return this._shape;
  933. }
  934. toString() {
  935. if (this._shape == null) {
  936. return this.dataType + '[?]';
  937. }
  938. return this.dataType + this._shape.toString();
  939. }
  940. };
  941. openvino.TensorShape = class {
  942. constructor(dimensions) {
  943. this._dimensions = dimensions;
  944. }
  945. get dimensions() {
  946. return this._dimensions;
  947. }
  948. toString() {
  949. if (!this._dimensions || this._dimensions.length == 0) {
  950. return '';
  951. }
  952. return '[' + this._dimensions.join(',') + ']';
  953. }
  954. };
  955. openvino.Metadata = class {
  956. static open(context) {
  957. if (openvino.Metadata._metadata) {
  958. return Promise.resolve(openvino.Metadata._metadata);
  959. }
  960. return context.request('openvino-metadata.json', 'utf-8', null).then((data) => {
  961. openvino.Metadata._metadata = new openvino.Metadata(data);
  962. return openvino.Metadata._metadata;
  963. }).catch(() => {
  964. openvino.Metadata._metadata = new openvino.Metadata(null);
  965. return openvino.Metadata._metadata;
  966. });
  967. }
  968. constructor(data) {
  969. this._map = new Map();
  970. this._attributeMap = new Map();
  971. if (data) {
  972. const metadata = JSON.parse(data);
  973. this._map = new Map(metadata.map((item) => [ item.name, item ]));
  974. }
  975. }
  976. type(name) {
  977. return this._map.get(name);
  978. }
  979. attribute(type, name) {
  980. const key = type + ':' + name;
  981. if (!this._attributeMap.has(key)) {
  982. this._attributeMap.set(key, null);
  983. const schema = this.type(type);
  984. if (schema && schema.attributes) {
  985. for (const attribute of schema.attributes) {
  986. this._attributeMap.set(type + ':' + attribute.name, attribute);
  987. }
  988. }
  989. }
  990. return this._attributeMap.get(key);
  991. }
  992. };
  993. openvino.XmlReader = class {
  994. static read(element) {
  995. const children = (parent, name) => {
  996. const children = [];
  997. let child = parent.firstChild;
  998. while (child != null) {
  999. if (child.nodeType == 1 && child.nodeName == name) {
  1000. children.push(child);
  1001. }
  1002. child = child.nextSibling;
  1003. }
  1004. return children;
  1005. };
  1006. const child = (parent, name) => {
  1007. const elements = children(parent, name);
  1008. if (elements.length > 1) {
  1009. throw new openvino.Error("Element '" + parent.nodeName + "' has multiple '" + name + "' elements.");
  1010. }
  1011. return elements.length > 0 ? elements[0] : null;
  1012. };
  1013. const ports = (parent, name) => {
  1014. const elements = child(parent, name);
  1015. if (elements) {
  1016. return children(elements, 'port').map((element) => {
  1017. return {
  1018. id: element.getAttribute('id'),
  1019. precision: element.getAttribute('precision'),
  1020. dims: Array.prototype.slice.call(element.getElementsByTagName('dim')).map((dim) => parseInt(dim.textContent.trim(), 10))
  1021. };
  1022. });
  1023. }
  1024. return [];
  1025. };
  1026. const layers = (parent) => {
  1027. const elements = child(parent, 'layers');
  1028. if (elements) {
  1029. return children(elements, 'layer').map((element) => {
  1030. const data = child(element, 'data');
  1031. const blobs = child(element, 'blobs');
  1032. const layer = {
  1033. id: element.getAttribute('id'),
  1034. name: element.getAttribute('name'),
  1035. type: element.getAttribute('type'),
  1036. precision: element.getAttribute('precision'),
  1037. data: !data ? [] : Array.from(data.attributes).map((attribute) => {
  1038. return { name: attribute.name, value: attribute.value};
  1039. }),
  1040. blobs: !blobs ? [] : Array.from(blobs.childNodes).filter((node) => node.nodeType === 1).map((blob) => {
  1041. return {
  1042. name: blob.nodeName,
  1043. precision: blob.getAttribute('precision'),
  1044. offset: parseInt(blob.getAttribute('offset'), 10),
  1045. size: parseInt(blob.getAttribute('size'), 10)
  1046. };
  1047. }),
  1048. inputs: ports(element, 'input'),
  1049. outputs: ports(element, 'output'),
  1050. };
  1051. if (layer.type === 'TensorIterator') {
  1052. layer.back_edges = edges(element, 'back_edges');
  1053. const body = child(element, 'body');
  1054. if (body) {
  1055. layer.body = {
  1056. layers: layers(body),
  1057. edges: edges(body)
  1058. };
  1059. }
  1060. const port_map = child(element, 'port_map');
  1061. if (port_map) {
  1062. layer.port_map = { input: [], output: [] };
  1063. for (const port of Array.from(port_map.childNodes).filter((element) => element.nodeType === 1)) {
  1064. const item = {
  1065. axis: port.getAttribute("axis"),
  1066. external_port_id: port.getAttribute("external_port_id"),
  1067. internal_layer_id: port.getAttribute("internal_layer_id"),
  1068. internal_port_id: port.getAttribute("internal_port_id")
  1069. };
  1070. switch (port.nodeName) {
  1071. case 'input': layer.port_map.input.push(item); break;
  1072. case 'output': layer.port_map.output.push(item); break;
  1073. }
  1074. }
  1075. }
  1076. }
  1077. return layer;
  1078. });
  1079. }
  1080. return [];
  1081. };
  1082. const edges = (parent, name) => {
  1083. const map = {};
  1084. const elements = child(parent, name || 'edges');
  1085. if (elements) {
  1086. for (const element of children(elements, 'edge')) {
  1087. const fromLayer = element.getAttribute('from-layer');
  1088. const fromPort = element.getAttribute('from-port');
  1089. const toLayer = element.getAttribute('to-layer');
  1090. const toPort = element.getAttribute('to-port');
  1091. map[toLayer + ':' + toPort] = fromLayer + ':' + fromPort;
  1092. }
  1093. }
  1094. return map;
  1095. };
  1096. return {
  1097. name: element.getAttribute('name'),
  1098. batch: element.getAttribute('batch'),
  1099. version: element.getAttribute('version'),
  1100. layers: layers(element),
  1101. edges: edges(element)
  1102. };
  1103. }
  1104. };
  1105. openvino.Error = class extends Error {
  1106. constructor(message) {
  1107. super(message);
  1108. this.name = 'Error loading OpenVINO model.';
  1109. }
  1110. };
  1111. if (typeof module !== 'undefined' && typeof module.exports === 'object') {
  1112. module.exports.ModelFactory = openvino.ModelFactory;
  1113. }