openvino.js 47 KB

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