openvino.js 44 KB

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