caffe2.js 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829
  1. /* jshint esversion: 6 */
  2. var caffe2 = caffe2 || {};
  3. var protobuf = protobuf || require('./protobuf');
  4. caffe2.ModelFactory = class {
  5. match(context) {
  6. const identifier = context.identifier.toLowerCase();
  7. const extension = identifier.split('.').pop().toLowerCase();
  8. switch (extension) {
  9. case 'pb': {
  10. const tags = context.tags('pb');
  11. if (tags.size > 0 &&
  12. Array.from(tags.keys()).every((tag) => tag <= 9) &&
  13. Array.from(tags.values()).every((type) => type <= 4)) {
  14. if (tags.size === 1 && tags.get(2) === 2 && identifier.endsWith('saved_model.pb')) {
  15. return false;
  16. }
  17. const schema = [[1,2],[2,2],[3,2],[4,0],[5,2],[6,2],[7,2],[8,2],[9,2]];
  18. if (schema.every((pair) => !tags.has(pair[0]) || tags.get(pair[0]) === pair[1])) {
  19. const stream = context.stream;
  20. if (stream.length > 3) {
  21. const buffer = stream.peek(Math.min(stream.length, 67));
  22. if (buffer[0] == 0x0A) {
  23. const size = buffer[1];
  24. if (size < 64 &&
  25. buffer.length > 2 + size + 1 &&
  26. buffer.slice(2, 2 + size).every((c) => c >= 32 && c <= 127) &&
  27. buffer[2 + size] == 0x12) {
  28. return true;
  29. }
  30. }
  31. if (buffer[0] == 0x12) {
  32. return true;
  33. }
  34. }
  35. }
  36. }
  37. break;
  38. }
  39. case 'pbtxt':
  40. case 'prototxt': {
  41. const tags = context.tags('pbtxt');
  42. if (tags.has('op') && !tags.has('op.attr') && !tags.has('op.graph_op_name') && !tags.has('op.endpoint')) {
  43. return true;
  44. }
  45. break;
  46. }
  47. }
  48. return false;
  49. }
  50. open(context) {
  51. return context.require('./caffe2-proto').then(() => {
  52. return caffe2.Metadata.open(context).then((metadata) => {
  53. const identifier = context.identifier;
  54. const parts = identifier.split('.');
  55. const extension = parts.pop().toLowerCase();
  56. const base = parts.join('.');
  57. if (extension == 'pbtxt' || extension == 'prototxt') {
  58. const openText = (predictBuffer, initBuffer, initTextFormat) => {
  59. let predict_net = null;
  60. let init_net = null;
  61. try {
  62. caffe2.proto = protobuf.get('caffe2').caffe2;
  63. const reader = protobuf.TextReader.open(predictBuffer);
  64. reader.field = function(tag, message) {
  65. if (message instanceof caffe2.proto.DeviceOption) {
  66. message[tag] = this.read();
  67. return;
  68. }
  69. throw new Error("Unknown field '" + tag + "'" + this.location());
  70. };
  71. predict_net = caffe2.proto.NetDef.decodeText(reader);
  72. }
  73. catch (error) {
  74. const message = error && error.message ? error.message : error.toString();
  75. throw new caffe2.Error('File text format is not caffe2.NetDef (' + message.replace(/\.$/, '') + ').');
  76. }
  77. try {
  78. caffe2.proto = protobuf.get('caffe2').caffe2;
  79. if (initBuffer) {
  80. if (initTextFormat) {
  81. const reader = protobuf.TextReader.open(initBuffer);
  82. init_net = caffe2.proto.NetDef.decodeText(reader);
  83. }
  84. else {
  85. const reader = protobuf.BinaryReader.open(initBuffer);
  86. init_net = caffe2.proto.NetDef.decode(reader);
  87. }
  88. }
  89. }
  90. catch (error) {
  91. // continue regardless of error
  92. }
  93. return new caffe2.Model(metadata, predict_net, init_net);
  94. };
  95. if (base.toLowerCase().endsWith('init_net') || base.toLowerCase().startsWith('init_net')) {
  96. return context.request(identifier.replace('init_net', 'predict_net'), null).then((stream) => {
  97. const buffer = stream.read();
  98. return openText(buffer, context.stream.peek(), true);
  99. }).catch(() => {
  100. return openText(context.stream.peek(), null, true);
  101. });
  102. }
  103. else if (base.toLowerCase().endsWith('predict_net') || base.toLowerCase().startsWith('predict_net')) {
  104. return context.request(identifier.replace('predict_net', 'init_net').replace(/\.pbtxt/, '.pb'), null).then((stream) => {
  105. const buffer = stream.read();
  106. return openText(context.stream.peek(), buffer, false);
  107. }).catch(() => {
  108. return context.request(identifier.replace('predict_net', 'init_net'), null).then((stream) => {
  109. const buffer = stream.read();
  110. return openText(context.stream.peek(), buffer, true);
  111. }).catch(() => {
  112. return openText(context.stream.peek(), null, true);
  113. });
  114. });
  115. }
  116. else {
  117. return context.request(base + '_init.pb', null).then((stream) => {
  118. const buffer = stream.read();
  119. return openText(context.stream.peek(), buffer, false);
  120. }).catch(() => {
  121. return openText(context.stream.peek(), null, false);
  122. });
  123. }
  124. }
  125. else {
  126. const openBinary = (predictBuffer, initBuffer) => {
  127. let predict_net = null;
  128. let init_net = null;
  129. try {
  130. caffe2.proto = protobuf.get('caffe2').caffe2;
  131. const reader = protobuf.BinaryReader.open(predictBuffer);
  132. predict_net = caffe2.proto.NetDef.decode(reader);
  133. }
  134. catch (error) {
  135. const message = error && error.message ? error.message : error.toString();
  136. throw new caffe2.Error('File format is not caffe2.NetDef (' + message.replace(/\.$/, '') + ').');
  137. }
  138. try {
  139. if (initBuffer) {
  140. caffe2.proto = protobuf.get('caffe2').caffe2;
  141. const reader = protobuf.BinaryReader.open(initBuffer);
  142. init_net = caffe2.proto.NetDef.decode(reader);
  143. }
  144. }
  145. catch (error) {
  146. // continue regardless of error
  147. }
  148. return new caffe2.Model(metadata, predict_net, init_net);
  149. };
  150. if (base.toLowerCase().endsWith('init_net')) {
  151. return context.request(base.replace(/init_net$/, '') + 'predict_net.' + extension, null).then((stream) => {
  152. const buffer = stream.read();
  153. return openBinary(buffer, context.stream.peek());
  154. }).catch(() => {
  155. return openBinary(context.stream.peek(), null);
  156. });
  157. }
  158. else if (base.toLowerCase().endsWith('_init')) {
  159. return context.request(base.replace(/_init$/, '') + '.' + extension, null).then((stream) => {
  160. const buffer = stream.read();
  161. return openBinary(buffer, context.stream.peek());
  162. }).catch(() => {
  163. return openBinary(context.stream.peek(), null);
  164. });
  165. }
  166. else if (base.toLowerCase().endsWith('predict_net') || base.toLowerCase().startsWith('predict_net')) {
  167. return context.request(identifier.replace('predict_net', 'init_net'), null).then((stream) => {
  168. const buffer = stream.read();
  169. return openBinary(context.stream.peek(), buffer);
  170. }).catch(() => {
  171. return openBinary(context.stream.peek(), null);
  172. });
  173. }
  174. else {
  175. return context.request(base + '_init.' + extension, null).then((stream) => {
  176. const buffer = stream.read();
  177. return openBinary(context.stream.peek(), buffer);
  178. }).catch(() => {
  179. return openBinary(context.stream.peek(), null);
  180. });
  181. }
  182. }
  183. });
  184. });
  185. }
  186. };
  187. caffe2.Model = class {
  188. constructor(metadata, predict_net, init_net) {
  189. this._domain = predict_net.domain || null;
  190. const graph = new caffe2.Graph(metadata, predict_net, init_net);
  191. this._graphs = [ graph ];
  192. }
  193. get format() {
  194. return 'Caffe2';
  195. }
  196. get domain() {
  197. return this._domain;
  198. }
  199. get graphs() {
  200. return this._graphs;
  201. }
  202. };
  203. caffe2.Graph = class {
  204. constructor(metadata, netDef, init) {
  205. this._name = netDef.name || '';
  206. this._type = netDef.type || '';
  207. this._nodes = [];
  208. const inputs = new Map();
  209. for (const input of netDef.external_input) {
  210. inputs.set(input, {});
  211. }
  212. if (init) {
  213. for (const op of init.op) {
  214. if (op.output && op.output.length == 1) {
  215. const name = op.output[0];
  216. if (!inputs.has(name)) {
  217. inputs.set(name, {});
  218. }
  219. const initializer = inputs.get(name);
  220. for (const arg of op.arg) {
  221. initializer[arg.name] = arg;
  222. }
  223. switch (op.type) {
  224. case 'GivenTensorFill':
  225. initializer.dataType = 'float32';
  226. break;
  227. case 'GivenTensorDoubleFill':
  228. initializer.dataType = 'float64';
  229. break;
  230. case 'GivenTensorBoolFill':
  231. initializer.dataType = 'boolean';
  232. break;
  233. case 'GivenTensorByteStringToUInt8Fill':
  234. initializer.dataType = 'uint8';
  235. break;
  236. case 'GivenTensorInt16Fill':
  237. case 'GivenTensorSInt16Fill':
  238. initializer.dataType = 'int16';
  239. break;
  240. case 'GivenTensorIntFill':
  241. initializer.dataType = 'int32';
  242. break;
  243. case 'GivenTensorInt64Fill':
  244. initializer.dataType = 'int64';
  245. break;
  246. case 'GivenTensorStringFill':
  247. initializer.dataType = 'string';
  248. break;
  249. case 'Int8GivenIntTensorFill':
  250. initializer.dataType = 'int32';
  251. break;
  252. case 'Int8GivenTensorFill':
  253. initializer.dataType = 'int8';
  254. break;
  255. case 'XavierFill':
  256. break;
  257. case 'ConstantFill':
  258. break;
  259. default:
  260. throw new caffe2.Error("Unknown init op '" + op.type + "'.");
  261. }
  262. if (initializer.values && initializer.values.floats && (initializer.values.floats.length !== 1 || initializer.values.floats[0] !== 0)) {
  263. initializer.input = false;
  264. }
  265. }
  266. }
  267. }
  268. const scope = {};
  269. let index = 0;
  270. for (const op of netDef.op) {
  271. op.input = op.input.map((input) => scope[input] ? scope[input] : input);
  272. op.output = op.output.map((output) => {
  273. if (scope[output]) {
  274. const next = output + '\n' + index.toString(); // custom argument id
  275. scope[output] = next;
  276. return next;
  277. }
  278. scope[output] = output;
  279. return output;
  280. });
  281. index++;
  282. }
  283. let lastNode = null;
  284. let lastOutput = null;
  285. for (const op of netDef.op) {
  286. const node = new caffe2.Node(metadata, op, inputs);
  287. if (op.input.length == 1 &&
  288. op.output.length >= 1 &&
  289. op.input[0].split('\n').shift() == op.output[0].split('\n').shift() &&
  290. lastNode &&
  291. lastOutput == op.input[0].split('\n').shift()) {
  292. lastNode.chain.push(node);
  293. }
  294. else {
  295. this._nodes.push(node);
  296. lastNode = null;
  297. lastOutput = null;
  298. if (op.output.length == 1) {
  299. lastNode = node;
  300. lastOutput = op.output[0].split('\n').shift();
  301. }
  302. }
  303. }
  304. this._inputs = [];
  305. for (const input of netDef.external_input) {
  306. if (netDef.external_input.length > 1) {
  307. const initializer = inputs.get(input);
  308. if (initializer && initializer.input === false) {
  309. continue;
  310. }
  311. }
  312. this._inputs.push(new caffe2.Parameter(input, [ new caffe2.Argument(input, null, null) ]));
  313. }
  314. this._outputs = [];
  315. for (const output of netDef.external_output) {
  316. this._outputs.push(new caffe2.Parameter(output, [ new caffe2.Argument(output, null, null) ]));
  317. }
  318. }
  319. get name() {
  320. return this._name;
  321. }
  322. get type() {
  323. return this._type;
  324. }
  325. get inputs() {
  326. return this._inputs;
  327. }
  328. get outputs() {
  329. return this._outputs;
  330. }
  331. get nodes() {
  332. return this._nodes;
  333. }
  334. toString() {
  335. return 'graph(' + this.name + ')';
  336. }
  337. };
  338. caffe2.Parameter = class {
  339. constructor(name, args) {
  340. this._name = name;
  341. this._arguments = args;
  342. }
  343. get name() {
  344. return this._name;
  345. }
  346. get visible() {
  347. return true;
  348. }
  349. get arguments() {
  350. return this._arguments;
  351. }
  352. };
  353. caffe2.Argument = class {
  354. constructor(name, type, initializer) {
  355. if (typeof name !== 'string') {
  356. throw new caffe2.Error("Invalid argument identifier '" + JSON.stringify(name) + "'.");
  357. }
  358. this._name = name;
  359. this._type = type || null;
  360. this._initializer = initializer || null;
  361. }
  362. get name() {
  363. return this._name;
  364. }
  365. get type() {
  366. if (this._initializer) {
  367. return this._initializer.type;
  368. }
  369. return this._type;
  370. }
  371. get quantization() {
  372. if (this._initializer) {
  373. return this._initializer.quantization;
  374. }
  375. return null;
  376. }
  377. get initializer() {
  378. return this._initializer;
  379. }
  380. };
  381. caffe2.Node = class {
  382. constructor(metadata, op, initializers) {
  383. this._name = op.name || '';
  384. this._device = op.engine || '';
  385. this._metadata = metadata;
  386. this._chain = [];
  387. this._attributes = [];
  388. this._type = metadata.type(op.type) || { name: op.type };
  389. for (const arg of op.arg) {
  390. this._attributes.push(new caffe2.Attribute(metadata, metadata.attribute(this._type, arg.name), arg));
  391. }
  392. const inputs = op.input;
  393. const outputs = op.output;
  394. const tensors = {};
  395. let index = 0;
  396. for (const input of inputs) {
  397. if (index > 0 && initializers.has(input)) {
  398. const initializer = initializers.get(input);
  399. tensors[input] = new caffe2.Tensor(input, initializer);
  400. initializer.input = false;
  401. }
  402. index++;
  403. }
  404. for (const output of outputs) {
  405. if (initializers.has(output)) {
  406. const initializer = initializers.get(output);
  407. initializer.input = false;
  408. }
  409. }
  410. this._inputs = [];
  411. let inputIndex = 0;
  412. if (this._type && this._type.inputs) {
  413. for (const inputDef of this._type.inputs) {
  414. if (inputIndex < inputs.length || inputDef.option != 'optional') {
  415. const inputCount = (inputDef.option == 'variadic') ? (inputs.length - inputIndex) : 1;
  416. const inputArguments = inputs.slice(inputIndex, inputIndex + inputCount).filter((id) => id != '' || inputDef.option != 'optional').map((id) => {
  417. return new caffe2.Argument(id, null, tensors[id]);
  418. });
  419. this._inputs.push(new caffe2.Parameter(inputDef.name, inputArguments));
  420. inputIndex += inputCount;
  421. }
  422. }
  423. }
  424. else {
  425. this._inputs.push(...inputs.slice(inputIndex).map((input, index) => {
  426. const inputName = ((inputIndex + index) == 0) ? 'input' : (inputIndex + index).toString();
  427. return new caffe2.Parameter(inputName, [
  428. new caffe2.Argument(input, null, tensors[input])
  429. ]);
  430. }));
  431. }
  432. this._outputs = [];
  433. let outputIndex = 0;
  434. if (this._type && this._type.outputs) {
  435. for (const outputDef of this._type.outputs) {
  436. if (outputIndex < outputs.length || outputDef.option != 'optional') {
  437. const outputCount = (outputDef.option == 'variadic') ? (outputs.length - outputIndex) : 1;
  438. const outputArguments = outputs.slice(outputIndex, outputIndex + outputCount).map((id) => new caffe2.Argument(id));
  439. this._outputs.push(new caffe2.Parameter(outputDef.name, outputArguments));
  440. outputIndex += outputCount;
  441. }
  442. }
  443. }
  444. else {
  445. this._outputs.push(...outputs.slice(outputIndex).map((output, index) => {
  446. const outputName = ((outputIndex + index) == 0) ? 'output' : (outputIndex + index).toString();
  447. return new caffe2.Parameter(outputName, [
  448. new caffe2.Argument(output, null, null)
  449. ]);
  450. }));
  451. }
  452. }
  453. get name() {
  454. return this._name || '';
  455. }
  456. get device() {
  457. return this._device || '';
  458. }
  459. get type() {
  460. return this._type;
  461. }
  462. get inputs() {
  463. return this._inputs;
  464. }
  465. get outputs() {
  466. return this._outputs;
  467. }
  468. get attributes() {
  469. return this._attributes;
  470. }
  471. get chain() {
  472. return this._chain;
  473. }
  474. };
  475. caffe2.Attribute = class {
  476. constructor(metadata, schema, arg) {
  477. this._name = arg.name;
  478. if (arg.floats && arg.floats.length > 0) {
  479. this._value = arg.floats;
  480. }
  481. else if (arg.ints && arg.ints.length > 0) {
  482. this._value = arg.ints;
  483. }
  484. else if (arg.nets && arg.nets.length > 0) {
  485. this._value = arg.nets.map((net) => new caffe2.Graph(metadata, net, null));
  486. this._type = 'graph[]';
  487. }
  488. else if (arg.n) {
  489. this._value = new caffe2.Graph(metadata, arg.n, null);
  490. this._type = 'graph';
  491. }
  492. else if (arg.i != 0) {
  493. this._value = arg.i;
  494. }
  495. else {
  496. this._value = arg.i;
  497. }
  498. if (schema) {
  499. if (Object.prototype.hasOwnProperty.call(schema, 'type')) {
  500. this._type = schema.type;
  501. if (this._type == 'boolean') {
  502. switch (this._value) {
  503. case 1: this._value = true; break;
  504. case 0: this._value = false; break;
  505. }
  506. }
  507. }
  508. }
  509. if (schema) {
  510. if (Object.prototype.hasOwnProperty.call(schema, 'visible') && !schema.visible) {
  511. this._visible = false;
  512. }
  513. else if (Object.prototype.hasOwnProperty.call(schema, 'default')) {
  514. if (this._value == schema.default || (this._value && this._value.toString() == schema.default.toString())) {
  515. this._visible = false;
  516. }
  517. }
  518. }
  519. }
  520. get name() {
  521. return this._name;
  522. }
  523. get type() {
  524. return this._type || null;
  525. }
  526. get value() {
  527. return this._value;
  528. }
  529. get visible() {
  530. return this._visible == false ? false : true;
  531. }
  532. };
  533. caffe2.Tensor = class {
  534. constructor(name, tensor) {
  535. this._name = name;
  536. const shape = tensor.shape && tensor.shape.ints ? tensor.shape.ints : null;
  537. this._type = new caffe2.TensorType(tensor.dataType, new caffe2.TensorShape(shape));
  538. this._values = tensor.values || null;
  539. this._scale = tensor.Y_scale ? tensor.Y_scale.f : 0;
  540. this._zeroPoint = tensor.Y_zero_point ? tensor.Y_zero_point.i : 0;
  541. }
  542. get name() {
  543. return this._name;
  544. }
  545. get type() {
  546. return this._type;
  547. }
  548. get kind() {
  549. return 'Initializer';
  550. }
  551. get quantization() {
  552. if (this._scale != 0 || this._zeroPoint != 0) {
  553. return this._scale.toString() + ' * ' + (this._zeroPoint == 0 ? 'q' : ('(q - ' + this._zeroPoint.toString() + ')'));
  554. }
  555. return null;
  556. }
  557. get state() {
  558. return this._context().state;
  559. }
  560. get value() {
  561. const context = this._context();
  562. if (context.state) {
  563. return null;
  564. }
  565. context.limit = Number.MAX_SAFE_INTEGER;
  566. return this._decode(context, 0);
  567. }
  568. toString() {
  569. const context = this._context();
  570. if (context.state) {
  571. return '';
  572. }
  573. context.limit = 10000;
  574. const value = this._decode(context, 0);
  575. return caffe2.Tensor._stringify(value, '', ' ');
  576. }
  577. _context() {
  578. const context = {};
  579. context.state = null;
  580. context.index = 0;
  581. context.count = 0;
  582. if (!this._values) {
  583. context.state = 'Tensor data is empty.';
  584. return context;
  585. }
  586. if (this._values.floats === undefined) {
  587. context.state = 'Tensor data is too large to load in Chrome.';
  588. return context;
  589. }
  590. switch (this._type.dataType) {
  591. case 'float32':
  592. context.data = this._values.floats;
  593. break;
  594. case 'boolean':
  595. context.data = this._values.ints;
  596. break;
  597. case 'int8':
  598. context.data = new Int8Array(this._values.s);
  599. break;
  600. case 'int32':
  601. context.data = this._values.ints;
  602. break;
  603. default:
  604. context.state = 'Unknown data type.';
  605. return context;
  606. }
  607. context.shape = this._type.shape.dimensions;
  608. context.dataType = this._type.dataType;
  609. return context;
  610. }
  611. _decode(context, dimension) {
  612. const results = [];
  613. const size = context.shape[dimension];
  614. if (dimension == context.shape.length - 1) {
  615. for (let i = 0; i < size; i++) {
  616. if (context.count > context.limit) {
  617. results.push('...');
  618. return results;
  619. }
  620. switch (context.dataType) {
  621. case 'float32':
  622. results.push(context.data[context.index]);
  623. break;
  624. case 'boolean':
  625. results.push(context.data[context.index] == 0 ? false : true);
  626. break;
  627. case 'int8':
  628. results.push(context.data[context.index]);
  629. break;
  630. case 'int32':
  631. results.push(context.data[context.index]);
  632. break;
  633. default:
  634. context.state = 'Unknown data type.';
  635. break;
  636. }
  637. context.index++;
  638. context.count++;
  639. }
  640. }
  641. else {
  642. for (let j = 0; j < size; j++) {
  643. if (context.count > context.limit) {
  644. results.push('...');
  645. return results;
  646. }
  647. results.push(this._decode(context, dimension + 1));
  648. }
  649. }
  650. return results;
  651. }
  652. static _stringify(value, indentation, indent) {
  653. if (Array.isArray(value)) {
  654. const result = [];
  655. result.push(indentation + '[');
  656. const items = value.map((item) => caffe2.Tensor._stringify(item, indentation + indent, indent));
  657. if (items.length > 0) {
  658. result.push(items.join(',\n'));
  659. }
  660. result.push(indentation + ']');
  661. return result.join('\n');
  662. }
  663. if (typeof value == 'string') {
  664. return indentation + value;
  665. }
  666. if (value == Infinity) {
  667. return indentation + 'Infinity';
  668. }
  669. if (value == -Infinity) {
  670. return indentation + '-Infinity';
  671. }
  672. if (isNaN(value)) {
  673. return indentation + 'NaN';
  674. }
  675. return indentation + value.toString();
  676. }
  677. };
  678. caffe2.TensorType = class {
  679. constructor(dataType, shape) {
  680. this._dataType = dataType;
  681. this._shape = shape;
  682. }
  683. get dataType() {
  684. return this._dataType || '?';
  685. }
  686. get shape() {
  687. return this._shape;
  688. }
  689. toString() {
  690. return this.dataType + this._shape.toString();
  691. }
  692. };
  693. caffe2.TensorShape = class {
  694. constructor(dimensions) {
  695. this._dimensions = dimensions;
  696. }
  697. get dimensions() {
  698. return this._dimensions;
  699. }
  700. toString() {
  701. return this._dimensions ? ('[' + this._dimensions.map((dimension) => dimension.toString()).join(',') + ']') : '';
  702. }
  703. };
  704. caffe2.Metadata = class {
  705. static open(context) {
  706. if (caffe2.Metadata._metadata) {
  707. return Promise.resolve(caffe2.Metadata._metadata);
  708. }
  709. return context.request('caffe2-metadata.json', 'utf-8', null).then((data) => {
  710. caffe2.Metadata._metadata = new caffe2.Metadata(data);
  711. return caffe2.Metadata._metadata;
  712. }).catch(() => {
  713. caffe2.Metadata._metadata = new caffe2.Metadata(null);
  714. return caffe2.Metadata._metadata;
  715. });
  716. }
  717. constructor(data) {
  718. this._map = new Map();
  719. this._attributeCache = {};
  720. if (data) {
  721. const metadata = JSON.parse(data);
  722. this._map = new Map(metadata.map((item) => [ item.name, item ]));
  723. }
  724. }
  725. type(name) {
  726. return this._map.get(name);
  727. }
  728. attribute(type, name) {
  729. let map = this._attributeCache[type];
  730. if (!map) {
  731. map = {};
  732. const schema = this.type(type);
  733. if (schema && schema.attributes && schema.attributes.length > 0) {
  734. for (const attribute of schema.attributes) {
  735. map[attribute.name] = attribute;
  736. }
  737. }
  738. this._attributeCache[type] = map;
  739. }
  740. return map[name] || null;
  741. }
  742. };
  743. caffe2.Error = class extends Error {
  744. constructor(message) {
  745. super(message);
  746. this.name = 'Error loading Caffe2 model.';
  747. }
  748. };
  749. if (typeof module !== 'undefined' && typeof module.exports === 'object') {
  750. module.exports.ModelFactory = caffe2.ModelFactory;
  751. }