caffe2.js 28 KB

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