openvino.js 48 KB

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