openvino.js 40 KB

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