openvino.js 35 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800
  1. import * as xml from './xml.js';
  2. const openvino = {};
  3. openvino.ModelFactory = class {
  4. match(context) {
  5. const identifier = context.identifier;
  6. const extension = identifier.split('.').pop().toLowerCase();
  7. if (extension === 'bin') {
  8. const stream = context.stream;
  9. const signature = [ 0x21, 0xA8, 0xEF, 0xBE, 0xAD, 0xDE ];
  10. if (signature.length <= stream.length && stream.peek(signature.length).every((value, index) => value === signature[index])) {
  11. return;
  12. }
  13. if (stream.length > 4) {
  14. const buffer = stream.peek(Math.min(256, stream.length));
  15. const signature = (buffer[0] | buffer[1] << 8 | buffer[2] << 16 | buffer [3] << 24) >>> 0;
  16. if (signature === 0x00000000 || signature === 0x00000001 ||
  17. signature === 0x01306B47 || signature === 0x000D4B38 || signature === 0x0002C056) {
  18. return;
  19. }
  20. for (let i = 0; i < buffer.length - 4; i++) {
  21. const signature = (buffer[i] | buffer[i + 1] << 8 | buffer[i + 2] << 16 | buffer [i + 3] << 24) >>> 0;
  22. if (signature === 0xdeadbeef) {
  23. return;
  24. }
  25. }
  26. }
  27. if (/^.*pytorch_model.*\.bin$/.test(identifier) ||
  28. /^.*group.+-shard.+of.+\.bin$/.test(identifier)) {
  29. return;
  30. }
  31. const identifiers = new Set([ 'config.bin', 'model.bin', '__model__.bin', 'weights.bin', 'programs.bin', 'best.bin', 'ncnn.bin' ]);
  32. if (identifiers.has(identifier)) {
  33. return;
  34. }
  35. context.type = 'openvino.bin';
  36. return;
  37. }
  38. const tags = context.tags('xml');
  39. if (tags.has('net')) {
  40. context.type = 'openvino.xml';
  41. return;
  42. }
  43. }
  44. filter(context, type) {
  45. return context.type !== 'openvino.xml' || type !== 'openvino.bin';
  46. }
  47. async open(context) {
  48. const identifier = context.identifier;
  49. const base = identifier.substring(0, identifier.length - 4);
  50. let stream = null;
  51. let bin = null;
  52. switch (context.type) {
  53. case 'openvino.xml': {
  54. stream = context.stream;
  55. try {
  56. const file = `${base}.bin`;
  57. const content = await context.fetch(file);
  58. bin = content.stream.peek();
  59. } catch (error) {
  60. // continue regardless of error
  61. }
  62. break;
  63. }
  64. case 'openvino.bin': {
  65. const file = `${base}.xml`;
  66. const content = await context.fetch(file, null);
  67. stream = content.stream;
  68. bin = context.stream.peek();
  69. break;
  70. }
  71. default: {
  72. throw new openvino.Error(`Unsupported OpenVINO format '${context.type}'.`);
  73. }
  74. }
  75. const metadata = await context.metadata('openvino-metadata.json');
  76. let document = null;
  77. try {
  78. const reader = xml.TextReader.open(stream);
  79. document = reader.read();
  80. } catch (error) {
  81. const message = error && error.message ? error.message : error.toString();
  82. throw new openvino.Error(`File format is not OpenVINO XML (${message.replace(/\.$/, '')}).`);
  83. }
  84. if (!document.documentElement || document.documentElement.localName != 'net') {
  85. throw new openvino.Error('File format is not OpenVINO IR.');
  86. }
  87. const element = document.documentElement;
  88. const object = (element) => {
  89. const obj = {};
  90. for (const attribute of element.attributes) {
  91. obj[attribute.localName] = attribute.value;
  92. }
  93. return obj;
  94. };
  95. const child = (parent, name) => {
  96. const elements = parent.getElementsByTagName(name);
  97. if (elements.length > 1) {
  98. throw new openvino.Error(`Element '${parent.localName}' has multiple '${name}' elements.`);
  99. }
  100. return elements.length > 0 ? elements[0] : null;
  101. };
  102. const children = (parent, name, element) => {
  103. const list = child(parent, name);
  104. return list ? list.getElementsByTagName(element) : [];
  105. };
  106. const edges = (parent, name) => {
  107. const map = {};
  108. for (const element of children(parent, name || 'edges', 'edge')) {
  109. const fromLayer = element.getAttribute('from-layer');
  110. const fromPort = element.getAttribute('from-port');
  111. const toLayer = element.getAttribute('to-layer');
  112. const toPort = element.getAttribute('to-port');
  113. map[`${toLayer}:${toPort}`] = `${fromLayer}:${fromPort}`;
  114. }
  115. return map;
  116. };
  117. const layers = (parent) => {
  118. const ports = (parent, name) => {
  119. return children(parent, name, 'port').map((element) => {
  120. const port = object(element);
  121. port.dims = element.getElementsByTagName('dim').map((dim) => parseInt(dim.textContent.trim(), 10));
  122. return port;
  123. });
  124. };
  125. return children(parent, 'layers', 'layer').map((element) => {
  126. const layer = object(element);
  127. layer.input = ports(element, 'input');
  128. layer.output = ports(element, 'output');
  129. const data = child(element, 'data');
  130. const blobs = child(element, 'blobs');
  131. layer.data = !data ? {} : object(data);
  132. layer.blobs = !blobs ? [] : blobs.getElementsByTagName('*').map((blob) => {
  133. const obj = object(blob);
  134. obj.name = blob.localName;
  135. obj.offset = parseInt(obj.offset, 10);
  136. obj.size = parseInt(obj.size, 10);
  137. return obj;
  138. });
  139. if (layer.type === 'TensorIterator') {
  140. layer.back_edges = edges(element, 'back_edges');
  141. const body = child(element, 'body');
  142. if (body) {
  143. layer.body = {
  144. layers: layers(body),
  145. edges: edges(body)
  146. };
  147. }
  148. const port_map = child(element, 'port_map');
  149. if (port_map) {
  150. layer.port_map = { input: [], output: [] };
  151. for (const port of port_map.getElementsByTagName('*')) {
  152. const item = object(port);
  153. switch (port.localName) {
  154. case 'input': layer.port_map.input.push(item); break;
  155. case 'output': layer.port_map.output.push(item); break;
  156. default: throw new openvino.Error(`Unsupported port local name '${port.localName}'.`);
  157. }
  158. }
  159. }
  160. }
  161. return layer;
  162. });
  163. };
  164. const net = object(element);
  165. net.body = {
  166. layers: layers(element),
  167. edges: edges(element)
  168. };
  169. return new openvino.Model(metadata, net, bin);
  170. }
  171. };
  172. openvino.Model = class {
  173. constructor(metadata, net, bin) {
  174. this.name = net.name || '';
  175. this.graphs = [ new openvino.Graph(metadata, net, bin) ];
  176. this.format = 'OpenVINO IR';
  177. }
  178. };
  179. openvino.Graph = class {
  180. constructor(metadata, net, bin) {
  181. this.name = net.name || '';
  182. this.nodes = [];
  183. this.inputs = [];
  184. this.outputs = [];
  185. const tensors = new Map();
  186. const values = new Map();
  187. values.map = (layer, precision, port, map) => {
  188. const id = `${layer}:${port.id}`;
  189. const name = map && map[id] ? map[id] : id;
  190. if (name === '') {
  191. throw new openvino.Error('Empty value name.');
  192. }
  193. const shape = port.dims.length == 0 ? null : new openvino.TensorShape(port.dims);
  194. if (!precision && values.has(name)) {
  195. const value = values.get(name);
  196. if (value.type && value.type.shape && value.type.shape.equals(shape)) {
  197. return value;
  198. }
  199. }
  200. const type = new openvino.TensorType(precision, shape);
  201. let tensor = null;
  202. if (tensors.has(id)) {
  203. const blob = tensors.get(id);
  204. const offset = blob.offset;
  205. const size = blob.size;
  206. const shape = new openvino.TensorShape(blob.shape);
  207. const type = new openvino.TensorType(blob.precision || precision, shape);
  208. const data = (bin && (offset + size) <= bin.length) ? bin.slice(offset, offset + size) : null;
  209. tensor = new openvino.Tensor(type, data, 'Const');
  210. }
  211. if (!values.has(name)) {
  212. values.set(name, new openvino.Value(name, type, tensor));
  213. } else if (type && !type.equals(values.get(name).type)) {
  214. throw new openvino.Error(`Duplicate value '${name}'.`);
  215. }
  216. return values.get(name);
  217. };
  218. const nodes = new Map();
  219. const constant = (layers, edges, back_edges) => {
  220. back_edges = back_edges || {};
  221. for (const layer of layers) {
  222. if (layer.type === 'Const' &&
  223. layer.input.length === 0 && layer.output.length === 1 && layer.blobs.length === 0 &&
  224. layer.data && layer.data.element_type !== undefined && layer.data.offset !== undefined && layer.data.size !== undefined) {
  225. let precision = null;
  226. switch (layer.data.element_type) {
  227. case 'f16': precision = 'FP16'; break;
  228. case 'f32': precision = 'FP32'; break;
  229. case 'f64': precision = 'FP64'; break;
  230. default: precision = layer.data.element_type.toUpperCase();
  231. }
  232. const shape = layer.data.shape;
  233. layer.blobs.push({
  234. name: 'value',
  235. precision: precision,
  236. offset: parseInt(layer.data.offset, 10),
  237. size: parseInt(layer.data.size, 10),
  238. shape: shape ? shape.split(',').map((dim) => parseInt(dim.trim(), 10)) : null
  239. });
  240. layer.data = {};
  241. }
  242. if (layer.type === 'Const' && layer.blobs.length === 1 && !layer.blobs[0].shape &&
  243. layer.input.length === 0 && layer.output.length === 1 && layer.output[0].dims) {
  244. layer.blobs[0].shape = layer.output[0].dims;
  245. }
  246. }
  247. const constants = new Map();
  248. for (const layer of layers) {
  249. if (layer.type === 'Const' && layer.input.length === 0 && layer.output.length === 1) {
  250. const from = `${layer.id}:${layer.output[0].id}`;
  251. constants.set(from, { layer: layer, counter: 0 });
  252. }
  253. }
  254. for (const from of Object.values(edges)) {
  255. if (constants.has(from)) {
  256. constants.get(from).counter++;
  257. }
  258. }
  259. if (back_edges) {
  260. for (const from of Object.values(back_edges)) {
  261. if (constants.has(from)) {
  262. constants.get(from).counter++;
  263. }
  264. }
  265. }
  266. for (const [name, value] of constants) {
  267. if (value.counter !== 1) {
  268. constants.delete(name);
  269. }
  270. }
  271. for (const layer of layers) {
  272. if (layer.blobs.length === 0) {
  273. for (let i = layer.input.length - 1; i > 0; i--) {
  274. const input = layer.input[i];
  275. const to = `${layer.id}:${input.id}`;
  276. const from = edges[to] || back_edges[to];
  277. if (!constants.has(from)) {
  278. break;
  279. }
  280. const constLayer = constants.get(from).layer;
  281. if (constLayer && Array.isArray(constLayer.blobs) && constLayer.blobs.length > 0) {
  282. const [blob] = constLayer.blobs;
  283. if (blob) {
  284. blob.id = constLayer.name || constLayer.id;
  285. layer.input[i].blob = blob;
  286. constants.get(from).layer = null;
  287. constants.get(from).delete = true;
  288. }
  289. }
  290. }
  291. }
  292. }
  293. return layers.filter((layer) => {
  294. if (layer.type === 'Const' && layer.input.length === 0 && layer.output.length === 1) {
  295. const from = `${layer.id}:${layer.output[0].id}`;
  296. if (constants.has(from) && constants.get(from).delete) {
  297. return false;
  298. }
  299. }
  300. return true;
  301. });
  302. };
  303. const body = net.body;
  304. const layers = new Map(body.layers.map((entry) => [ entry.id, entry ]));
  305. const ports = new Map();
  306. if (Array.isArray(net.input)) {
  307. for (const input of net.input) {
  308. const value = values.map('', input.precision, input);
  309. const argument = new openvino.Argument(input.id, [ value ]);
  310. this.inputs.push(argument);
  311. ports.set(input.id, value);
  312. }
  313. }
  314. if (Array.isArray(net.output)) {
  315. for (const output of net.output) {
  316. const value = values.map('', output.precision, output);
  317. const argument = new openvino.Argument(output.id, [ value ]);
  318. this.outputs.push(argument);
  319. ports.set(output.id, value);
  320. }
  321. }
  322. for (const layer of body.layers) {
  323. for (const output of layer.output) {
  324. if (!output.precision) {
  325. output.precision = layer.precision;
  326. }
  327. }
  328. }
  329. if (net.port_map) {
  330. for (const input of net.port_map.input) {
  331. const external_port = net.input.find((v) => v.id === input.external_port_id);
  332. const layer = layers.get(input.internal_layer_id);
  333. if (input.internal_port_id === undefined) {
  334. input.internal_port_id = '';
  335. layer.input.push({
  336. id: input.internal_port_id,
  337. precision: layer.data.element_type,
  338. dims: layer.data.shape.split(',')
  339. });
  340. }
  341. const internal_port = layer.input.find((v) => v.id === input.internal_port_id);
  342. internal_port.precision = external_port.precision;
  343. }
  344. for (const output of net.port_map.output) {
  345. const external_port = net.output.find((v) => v.id === output.external_port_id);
  346. const layer = layers.get(output.internal_layer_id);
  347. if (output.internal_port_id === undefined) {
  348. output.internal_port_id = '';
  349. layer.output.push({
  350. id: output.internal_port_id,
  351. precision: external_port.precision,
  352. dims: external_port.dims
  353. });
  354. }
  355. }
  356. }
  357. const layer_list = constant(body.layers, body.edges);
  358. for (const layer of layer_list) {
  359. for (const input of layer.input) {
  360. if (input.blob) {
  361. tensors.set(`${layer.id}:${input.id}`, input.blob);
  362. }
  363. }
  364. }
  365. for (const layer of layer_list) {
  366. for (const output of layer.output) {
  367. values.map(layer.id, output.precision, output, null);
  368. }
  369. }
  370. for (const layer of layer_list) {
  371. const inputs = layer.input.map((input) => {
  372. const to = `${layer.id}:${input.id}`;
  373. if (body.edges[to]) {
  374. const output = body.edges[to] ? body.edges[to].split(':') : [];
  375. const [outputLayerId, outputId] = output;
  376. const outputLayer = layers.get(outputLayerId);
  377. if (outputLayer && outputId) {
  378. const output = outputLayer.output.find((output) => output.id === outputId);
  379. if (input && output) {
  380. input.precision = output.precision;
  381. }
  382. }
  383. }
  384. return values.map(layer.id, input.precision || layer.precision, input, body.edges);
  385. });
  386. const outputs = layer.output.map((output) => {
  387. const precision = output && output.precision ? output.precision : layer && layer.precision ? layer.precision : null;
  388. return values.map(layer.id, precision, output, null);
  389. });
  390. const subgraph = Array.isArray(net.input) || Array.isArray(net.output);
  391. if (!subgraph && (layer.type === 'Input' || layer.type === 'Parameter')) {
  392. const name = layer.name || '';
  393. // precision is a part of OpenVINO IR layers of IR v6 and earlier
  394. // in IR v7 and newer the port is no longer an attribute of the layer but of each output port
  395. // IR input is not just a placeholder, it is conceptually the legitimate layer
  396. // in order not to break compatibility with the overall approach
  397. // with openvino.Argument for inputs and openvino.Node for outputs
  398. // input openvino.Node would be stored as an optional attribute of openvino.Parameter
  399. this.inputs.push(new openvino.Argument(name, outputs));
  400. } else {
  401. const node = new openvino.Node(metadata, layer, inputs, outputs, bin);
  402. nodes.set(layer.id, node);
  403. }
  404. }
  405. this.nodes = Array.from(nodes.values());
  406. if (net.port_map) {
  407. const createMapLayer = (obj) => {
  408. const data = {};
  409. for (const [name, value] of Object.entries(obj)) {
  410. if (name === 'external_port_id' || name === 'internal_layer_id' || name === 'internal_port_id') {
  411. continue;
  412. }
  413. data[name] = value;
  414. }
  415. const layer = {};
  416. layer.type = '-';
  417. layer.data = data;
  418. return layer;
  419. };
  420. for (const input of net.port_map.input) {
  421. const internal_port = layers.get(input.internal_layer_id).input.find((v) => v.id === input.internal_port_id);
  422. const inputs = [ ports.get(input.external_port_id) ];
  423. const outputs = [ values.map(input.internal_layer_id, internal_port.precision, internal_port) ];
  424. const layer = createMapLayer(input);
  425. this.nodes.push(new openvino.Node(metadata, layer, inputs, outputs));
  426. }
  427. for (const output of net.port_map.output) {
  428. const internal_port = layers.get(output.internal_layer_id).output.find((v) => v.id === output.internal_port_id);
  429. const inputs = [ values.map(output.internal_layer_id, internal_port.precision, internal_port) ];
  430. const outputs = [ ports.get(output.external_port_id) ];
  431. const layer = createMapLayer(output);
  432. this.nodes.push(new openvino.Node(metadata, layer, inputs, outputs));
  433. }
  434. }
  435. }
  436. };
  437. openvino.Node = class {
  438. constructor(metadata, layer, inputs, outputs, bin) {
  439. this.name = layer.name || '';
  440. this.inputs = [];
  441. this.outputs = [];
  442. this.attributes = [];
  443. const type = layer.type;
  444. this.type = metadata.type(type) || { name: type };
  445. for (let i = 0; i < inputs.length;) {
  446. const input = this.type && Array.isArray(this.type.inputs) && i < this.type.inputs.length ? this.type.inputs[i] : inputs.length === 1 ? { name: 'input' } : { name: i.toString() };
  447. const count = input.type === 'Tensor[]' ? inputs.length - i : 1;
  448. const values = inputs.slice(i, i + count);
  449. const argument = new openvino.Argument(input.name, values);
  450. this.inputs.push(argument);
  451. i += count;
  452. }
  453. for (let i = 0; i < outputs.length;) {
  454. const output = this.type && Array.isArray(this.type.outputs) && i < this.type.outputs.length ? this.type.outputs[i] : outputs.length === 1 ? { name: 'output' } : { name: i.toString() };
  455. const count = output.type === 'Tensor[]' ? outputs.length - i : 1;
  456. const values = outputs.slice(i, i + count);
  457. const argument = new openvino.Argument(output.name, values);
  458. this.outputs.push(argument);
  459. i += count;
  460. }
  461. for (const [name, value] of Object.entries(layer.data)) {
  462. const attribute = new openvino.Attribute(metadata.attribute(type, name), name, value);
  463. this.attributes.push(attribute);
  464. }
  465. if (layer.type === 'TensorIterator') {
  466. const graph = new openvino.Graph(metadata, layer, null);
  467. const attribute = new openvino.Attribute({ type: 'graph' }, 'body', graph);
  468. this.attributes.push(attribute);
  469. }
  470. for (const blob of layer.blobs || []) {
  471. const name = blob.name;
  472. const offset = blob.offset;
  473. let data = (bin && (offset + blob.size) <= bin.length) ? bin.slice(offset, offset + blob.size) : null;
  474. let dimensions = blob.shape || null;
  475. const category = blob.kind || 'Blob';
  476. const id = blob.id || '';
  477. const precision = blob.precision || layer.precision;
  478. let itemSize = undefined;
  479. switch (precision) {
  480. case 'BOOL': case 'BOOLEAN': itemSize = 1; break;
  481. case 'I1': case 'U1': itemSize = 0.125; break;
  482. case 'I4': case 'U4': itemSize = 0.5; break;
  483. case 'I8': case 'U8': itemSize = 1; break;
  484. case 'I16': case 'U16': case 'FP16': itemSize = 2; break;
  485. case 'I32': case 'U32': case 'FP32': itemSize = 4; break;
  486. case 'I64': case 'U64': case 'FP64': itemSize = 8; break;
  487. default: throw new openvino.Error(`Unsupported data type size '${precision}'.`);
  488. }
  489. const weight = (name, precision, dimensions, data) => {
  490. const shape = dimensions ? new openvino.TensorShape(dimensions) : null;
  491. const type = new openvino.TensorType(precision, shape);
  492. const tensor = new openvino.Tensor(type, data, category);
  493. const value = new openvino.Value(id, null, tensor);
  494. this.inputs.push(new openvino.Argument(name, [ value ]));
  495. const size = Math.ceil(dimensions.reduce((a, b) => a * b, 1) * itemSize);
  496. if (data && data.length !== size) {
  497. return data.slice(size, data.length);
  498. }
  499. return null;
  500. };
  501. if (itemSize) {
  502. switch (`${type}:${name}`) {
  503. case 'FullyConnected:weights': {
  504. const outSize = parseInt(layer.data['out-size'], 10);
  505. dimensions = [ layer.input[0].dims[1], outSize ];
  506. break;
  507. }
  508. case 'FullyConnected:biases': {
  509. dimensions = [ parseInt(layer.data['out-size'], 10) ];
  510. break;
  511. }
  512. case 'Convolution:weights':
  513. case 'Deconvolution:weights': {
  514. /* eslint-disable prefer-destructuring */
  515. const c = this.inputs[0].value[0].type.shape.dimensions[1];
  516. /* eslint-enable prefer-destructuring */
  517. const group = parseInt(layer.data.group || '1', 10);
  518. const kernel = layer.data['kernel-x'] !== undefined && layer.data['kernel-y'] !== undefined ?
  519. [ parseInt(layer.data['kernel-x'], 10), parseInt(layer.data['kernel-y'], 10) ] :
  520. layer.data.kernel.split(',').map((v) => parseInt(v.trim(), 10));
  521. const n = parseInt(layer.data.output, 10);
  522. dimensions = [ Math.floor(c / group), n ].concat(kernel);
  523. break;
  524. }
  525. case 'LSTMCell:weights': {
  526. /* eslint-disable prefer-destructuring */
  527. const input_size = inputs[0].type.shape.dimensions[1];
  528. /* eslint-enable prefer-destructuring */
  529. const hidden_size = parseInt(layer.data.hidden_size, 10);
  530. data = weight('W', precision, [ 4 * hidden_size, input_size ], data);
  531. data = weight('R', precision, [ 4 * hidden_size, hidden_size ], data);
  532. break;
  533. }
  534. case 'LSTMCell:biases': {
  535. const hidden_size = parseInt(layer.data.hidden_size, 10);
  536. data = weight('B', precision, [ 4 * hidden_size ], data);
  537. break;
  538. }
  539. case 'GRUCell:weights': {
  540. /* eslint-disable prefer-destructuring */
  541. const input_size = inputs[0].type.shape.dimensions[1];
  542. /* eslint-enable prefer-destructuring */
  543. const hidden_size = parseInt(layer.data.hidden_size, 10);
  544. data = weight('W', precision, [ 3 * hidden_size, input_size ], data);
  545. data = weight('R', precision, [ 3 * hidden_size, hidden_size ], data);
  546. break;
  547. }
  548. case 'GRUCell:biases': {
  549. const linear_before_reset = parseInt(layer.data.linear_before_reset, 10);
  550. const hidden_size = parseInt(layer.data.hidden_size, 10);
  551. dimensions = linear_before_reset ? [ 4 * hidden_size ] : [ 3 * hidden_size ];
  552. data = weight('B', precision, dimensions, data);
  553. break;
  554. }
  555. case 'Convolution:biases': {
  556. dimensions = [ parseInt(layer.data.output, 10) ];
  557. break;
  558. }
  559. case 'ScaleShift:weights':
  560. case 'ScaleShift:biases':
  561. case 'Normalize:weights': {
  562. dimensions = [ layer.input[0].dims[1] ];
  563. break;
  564. }
  565. case 'PReLU:weights': {
  566. dimensions = layer.data.channel_shared === '1' ? [ 1 ] : [ layer.input[0].dims[1] ];
  567. break;
  568. }
  569. case 'Const:custom': {
  570. if (this.outputs.length > 0 &&
  571. this.outputs[0].value.length > 0 &&
  572. this.outputs[0].value[0].type &&
  573. this.outputs[0].value[0].type.shape &&
  574. this.outputs[0].value[0].type.shape.dimensions) {
  575. dimensions = this.outputs[0].value[0].type.shape.dimensions;
  576. }
  577. break;
  578. }
  579. default: {
  580. break;
  581. }
  582. }
  583. }
  584. if (data) {
  585. weight(name, precision, dimensions, data);
  586. }
  587. }
  588. }
  589. };
  590. openvino.Argument = class {
  591. constructor(name, value) {
  592. this.name = name;
  593. this.value = value;
  594. }
  595. };
  596. openvino.Value = class {
  597. constructor(name, type, initializer) {
  598. if (typeof name !== 'string') {
  599. throw new openvino.Error(`Invalid value identifier '${JSON.stringify(name)}'.`);
  600. }
  601. this.name = name;
  602. this.type = initializer ? initializer.type : type;
  603. this.initializer = initializer || null;
  604. }
  605. };
  606. openvino.Attribute = class {
  607. constructor(metadata, name, value) {
  608. this.name = name;
  609. this.value = value;
  610. if (metadata && metadata.type !== undefined) {
  611. this.type = metadata.type;
  612. switch (metadata.type) {
  613. case '':
  614. case 'graph':
  615. case 'string':
  616. break;
  617. case 'boolean':
  618. if (value === '1' || value === 'true' || value === 'True') {
  619. this.value = true;
  620. } else if (value === '0' || value === 'false' || value === 'False') {
  621. this.value = false;
  622. } else {
  623. throw new openvino.Error(`Unsupported attribute boolean value '${value}'.`);
  624. }
  625. break;
  626. case 'int32':
  627. case 'int64': {
  628. const intValue = Number.parseInt(this.value, 10);
  629. this.value = Number.isNaN(this.value - intValue) ? value : intValue;
  630. break;
  631. }
  632. case 'float32':
  633. case 'float64': {
  634. const floatValue = Number.parseFloat(this.value);
  635. this.value = Number.isNaN(this.value - floatValue) ? value : floatValue;
  636. break;
  637. }
  638. case 'int32[]':
  639. if (this.value.length > 2) {
  640. let ints = [];
  641. for (const entry of this.value.split(',')) {
  642. const item = entry.trim();
  643. const intValue = Number.parseInt(item, 10);
  644. if (Number.isNaN(item - intValue)) {
  645. ints = null;
  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. } else if (floats != null) {
  664. floats.push(floatValue);
  665. }
  666. }
  667. if (floats != null) {
  668. this.value = floats;
  669. }
  670. }
  671. break;
  672. default:
  673. throw new openvino.Error(`Unsupported attribute type '${metadata.type}'.`);
  674. }
  675. }
  676. if (metadata && metadata.visible == false) {
  677. this.visible = false;
  678. } else if (metadata && metadata.default !== undefined) {
  679. let defaultValue = metadata.default;
  680. if (this.value == defaultValue) {
  681. this.visible = false;
  682. } else if (Array.isArray(this.value) && Array.isArray(defaultValue)) {
  683. defaultValue = defaultValue.slice(0, defaultValue.length);
  684. if (defaultValue.length > 1 && defaultValue[defaultValue.length - 1] == null) {
  685. defaultValue.pop();
  686. while (defaultValue.length < this.value.length) {
  687. defaultValue.push(defaultValue[defaultValue.length - 1]);
  688. }
  689. }
  690. if (this.value.every((item, index) => item == defaultValue[index])) {
  691. this.visible = false;
  692. }
  693. }
  694. }
  695. }
  696. };
  697. openvino.Tensor = class {
  698. constructor(type, data, category) {
  699. this.type = type;
  700. this.values = data;
  701. this.category = category;
  702. }
  703. };
  704. openvino.TensorType = class {
  705. constructor(precision, shape) {
  706. precision = precision ? precision.toLowerCase() : precision;
  707. switch (precision) {
  708. case 'f16': this.dataType = 'float16'; break;
  709. case 'f32': this.dataType = 'float32'; break;
  710. case 'f64': this.dataType = 'float64'; break;
  711. case 'fp16': this.dataType = 'float16'; break;
  712. case 'fp32': this.dataType = 'float32'; break;
  713. case 'fp64': this.dataType = 'float64'; break;
  714. case 'bf16': this.dataType = 'bfloat16'; break;
  715. case 'i4': this.dataType = 'int4'; break;
  716. case 'i8': this.dataType = 'int8'; break;
  717. case 'i16': this.dataType = 'int16'; break;
  718. case 'i32': this.dataType = 'int32'; break;
  719. case 'i64': this.dataType = 'int64'; break;
  720. case 'u1': this.dataType = 'boolean'; break;
  721. case 'u4': this.dataType = 'uint4'; break;
  722. case 'u8': this.dataType = 'uint8'; break;
  723. case 'u16': this.dataType = 'uint16'; break;
  724. case 'u32': this.dataType = 'uint32'; break;
  725. case 'u64': this.dataType = 'uint64'; break;
  726. case 'bool': this.dataType = 'boolean'; break;
  727. case 'boolean': this.dataType = 'boolean'; break;
  728. case 'bin': this.dataType = 'bit'; break;
  729. case '': this.dataType = '?'; break;
  730. case null: this.dataType = '?'; break;
  731. default: throw new openvino.Error(`Unsupported precision '${JSON.stringify(precision)}'.`);
  732. }
  733. this.shape = shape;
  734. }
  735. equals(obj) {
  736. return obj && this.dataType === obj.dataType &&
  737. ((this.shape === null && obj.shape === null) || this.shape && this.shape.equals(obj.shape));
  738. }
  739. toString() {
  740. if (this.shape == null) {
  741. return `${this.dataType}[?]`;
  742. }
  743. return this.dataType + this.shape.toString();
  744. }
  745. };
  746. openvino.TensorShape = class {
  747. constructor(dimensions) {
  748. this.dimensions = dimensions;
  749. }
  750. equals(obj) {
  751. return obj && Array.isArray(obj.dimensions) &&
  752. Array.isArray(this.dimensions) && this.dimensions.length === obj.dimensions.length
  753. && obj.dimensions.every((value, index) => this.dimensions[index] === value);
  754. }
  755. toString() {
  756. if (!this.dimensions || this.dimensions.length == 0) {
  757. return '';
  758. }
  759. return `[${this.dimensions.join(',')}]`;
  760. }
  761. };
  762. openvino.Error = class extends Error {
  763. constructor(message) {
  764. super(message);
  765. this.name = 'Error loading OpenVINO model.';
  766. }
  767. };
  768. export const ModelFactory = openvino.ModelFactory;