ncnn.js 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869
  1. /* jshint esversion: 6 */
  2. var ncnn = ncnn || {};
  3. // https://github.com/Tencent/ncnn/wiki/param-and-model-file-structure
  4. // https://github.com/Tencent/ncnn/wiki/operation-param-weight-table
  5. ncnn.ModelFactory = class {
  6. match(context) {
  7. const identifier = context.identifier.toLowerCase();
  8. if (identifier.endsWith('.param') || identifier.endsWith('.cfg.ncnn')) {
  9. let text = context.text;
  10. text = text.substring(0, Math.min(text.length, 32));
  11. const signature = text.split('\n').shift().trim();
  12. if (signature === '7767517') {
  13. return true;
  14. }
  15. }
  16. if (identifier.endsWith('.param.bin')) {
  17. const buffer = context.buffer;
  18. if (buffer.length > 4) {
  19. const signature = (buffer[0] | buffer[1] << 8 | buffer[2] << 16 | buffer [3] << 24) >>> 0;
  20. if (signature == 0x007685DD) {
  21. return true;
  22. }
  23. }
  24. }
  25. if (identifier.endsWith('.bin') || identifier.endsWith('.weights.ncnn')) {
  26. if (identifier == 'snapshot_blob.bin' || identifier === 'v8_context_snapshot.bin') {
  27. return false;
  28. }
  29. const buffer = context.buffer;
  30. if (buffer.length > 4) {
  31. const signature = (buffer[0] | buffer[1] << 8 | buffer[2] << 16 | buffer [3] << 24) >>> 0;
  32. if (signature === 0x00000000 || signature === 0x00000001 ||
  33. signature === 0x01306B47 || signature === 0x000D4B38 || signature === 0x0002C056) {
  34. return true;
  35. }
  36. }
  37. }
  38. return false;
  39. }
  40. open(context, host) {
  41. return ncnn.Metadata.open(host).then((metadata) => {
  42. const identifier = context.identifier.toLowerCase();
  43. const param = (param, bin) => {
  44. try {
  45. return new ncnn.Model(metadata, param, bin);
  46. }
  47. catch (error) {
  48. const message = error && error.message ? error.message : error.toString();
  49. throw new ncnn.Error(message.replace(/\.$/, '') + " in '" + identifier + "'.");
  50. }
  51. };
  52. let bin = null;
  53. if (identifier.endsWith('.param') || identifier.endsWith('.cfg.ncnn')) {
  54. if (identifier.endsWith('.param')) {
  55. bin = context.identifier.substring(0, context.identifier.length - 6) + '.bin';
  56. }
  57. else if (identifier.endsWith('.cfg.ncnn')) {
  58. bin = context.identifier.substring(0, context.identifier.length - 9) + '.weights.ncnn';
  59. }
  60. return context.request(bin, null).then((bin) => {
  61. return param(context.text, bin);
  62. }).catch(() => {
  63. return param(context.text, null);
  64. });
  65. }
  66. else if (identifier.endsWith('.param.bin')) {
  67. bin = context.identifier.substring(0, context.identifier.length - 10) + '.bin';
  68. return context.request(bin, null).then((bin) => {
  69. return param(context.buffer, bin);
  70. }).catch(() => {
  71. return param(context.buffer, null);
  72. });
  73. }
  74. else if (identifier.endsWith('.bin') || identifier.endsWith('.weights.ncnn')) {
  75. let text = null;
  76. if (identifier.endsWith('bin')) {
  77. text = context.identifier.substring(0, context.identifier.length - 4) + '.param';
  78. }
  79. else if (identifier.endsWith('.weights.ncnn')) {
  80. text = context.identifier.substring(0, context.identifier.length - 13) + '.cfg.ncnn';
  81. }
  82. return context.request(text, 'utf-8').then((text) => {
  83. return param(text, context.buffer);
  84. }).catch((error) => {
  85. const message = error && error.message ? error.message : error.toString();
  86. throw new ncnn.Error(message.replace(/\.$/, '') + " in '" + identifier + "'.");
  87. });
  88. }
  89. });
  90. }
  91. };
  92. ncnn.Model = class {
  93. constructor(metadata, param, bin) {
  94. this._graphs = [];
  95. this._graphs.push(new ncnn.Graph(metadata, param, bin));
  96. }
  97. get format() {
  98. return 'ncnn';
  99. }
  100. get graphs() {
  101. return this._graphs;
  102. }
  103. };
  104. ncnn.Graph = class {
  105. constructor(metadata, param, bin) {
  106. this._inputs = [];
  107. this._outputs = [];
  108. this._nodes = [];
  109. const blobReader = new ncnn.BlobReader(bin);
  110. const reader = (typeof param == 'string') ? new ncnn.TextParamReader(param) : new ncnn.BinaryParamReader(metadata, param);
  111. const layers = reader.layers;
  112. for (const layer of layers) {
  113. if (layer.type == 'Input') {
  114. const dimensions = layer.attributes.map((a) => !isNaN(parseInt(a.value, 10)) ? parseInt(a.value, 10) : a.value);
  115. const shape = new ncnn.TensorShape(dimensions);
  116. const type = new ncnn.TensorType('float32', shape);
  117. this._inputs.push(new ncnn.Parameter(layer.name, true, layer.outputs.map((output) => new ncnn.Argument(output, type, null))));
  118. }
  119. else {
  120. this._nodes.push(new ncnn.Node(metadata, blobReader, layer));
  121. }
  122. }
  123. }
  124. get inputs() {
  125. return this._inputs;
  126. }
  127. get outputs() {
  128. return this._outputs;
  129. }
  130. get nodes() {
  131. return this._nodes;
  132. }
  133. };
  134. ncnn.Parameter = class {
  135. constructor(name, visible, args) {
  136. this._name = name;
  137. this._visible = visible;
  138. this._arguments = args;
  139. }
  140. get name() {
  141. return this._name;
  142. }
  143. get visible() {
  144. return this._visible;
  145. }
  146. get arguments() {
  147. return this._arguments;
  148. }
  149. };
  150. ncnn.Argument = class {
  151. constructor(name, type, initializer) {
  152. if (typeof name !== 'string') {
  153. throw new ncnn.Error("Invalid argument identifier '" + JSON.stringify(name) + "'.");
  154. }
  155. this._name = name;
  156. this._type = type || null;
  157. this._initializer = initializer || null;
  158. }
  159. get name() {
  160. return this._name;
  161. }
  162. get type() {
  163. if (this._initializer) {
  164. return this._initializer.type;
  165. }
  166. return this._type;
  167. }
  168. get initializer() {
  169. return this._initializer;
  170. }
  171. };
  172. ncnn.Node = class {
  173. constructor(metadata, blobReader, layer) {
  174. this._metadata = metadata;
  175. this._inputs = [];
  176. this._outputs = [];
  177. this._attributes = [];
  178. this._type = layer.type;
  179. this._name = layer.name;
  180. const operator = metadata.operator(this._type);
  181. if (operator) {
  182. this._type = operator;
  183. }
  184. const schema = metadata.type(this._type);
  185. const attributeMetadata = schema && schema.attributes ? schema && schema.attributes : [];
  186. for (const attribute of layer.attributes) {
  187. const attributeSchema = attributeMetadata[attribute.key];
  188. this._attributes.push(new ncnn.Attribute(attributeSchema, attribute.key, attribute.value));
  189. }
  190. const inputs = layer.inputs;
  191. let inputIndex = 0;
  192. if (schema && schema.inputs) {
  193. for (const inputDef of schema.inputs) {
  194. if (inputIndex < inputs.length || inputDef.option != 'optional') {
  195. const inputCount = (inputDef.option == 'variadic') ? (inputs.length - inputIndex) : 1;
  196. const inputArguments = inputs.slice(inputIndex, inputIndex + inputCount).filter((id) => id != '' || inputDef.option != 'optional').map((id) => {
  197. return new ncnn.Argument(id, null, null);
  198. });
  199. this._inputs.push(new ncnn.Parameter(inputDef.name, true, inputArguments));
  200. inputIndex += inputCount;
  201. }
  202. }
  203. }
  204. this._inputs = this._inputs.concat(inputs.slice(inputIndex).map((input, index) => {
  205. const inputName = ((inputIndex + index) == 0) ? 'input' : (inputIndex + index).toString();
  206. return new ncnn.Parameter(inputName, true, [
  207. new ncnn.Argument(input, null, null)
  208. ]);
  209. }));
  210. const outputs = layer.outputs;
  211. let outputIndex = 0;
  212. if (schema && schema.outputs) {
  213. for (const outputDef of schema.outputs) {
  214. if (outputIndex < outputs.length || outputDef.option != 'optional') {
  215. const outputCount = (outputDef.option == 'variadic') ? (outputs.length - outputIndex) : 1;
  216. const outputArguments = outputs.slice(outputIndex, outputIndex + outputCount).map((id) => {
  217. return new ncnn.Argument(id, null, null);
  218. });
  219. this._outputs.push(new ncnn.Parameter(outputDef.name, true, outputArguments));
  220. outputIndex += outputCount;
  221. }
  222. }
  223. }
  224. this._outputs = this._outputs.concat(outputs.slice(outputIndex).map((output, index) => {
  225. const outputName = ((outputIndex + index) == 0) ? 'output' : (outputIndex + index).toString();
  226. return new ncnn.Parameter(outputName, true, [
  227. new ncnn.Argument(output, null, null)
  228. ]);
  229. }));
  230. switch (this._type) {
  231. case 'BatchNorm': {
  232. const channels = parseInt(layer.attr['0'] || 0, 10);
  233. this._weight(blobReader, 'slope', [ channels ], 'float32');
  234. this._weight(blobReader, 'mean', [ channels ], 'float32');
  235. this._weight(blobReader, 'variance', [ channels ], 'float32');
  236. this._weight(blobReader, 'bias', [ channels ], 'float32');
  237. break;
  238. }
  239. case 'InnerProduct': {
  240. const num_output = parseInt(layer.attr['0'] || 0, 10);
  241. const weight_data_size = parseInt(layer.attr['2'] || 0, 10);
  242. this._weight(blobReader, 'weight', [ num_output, weight_data_size / num_output ]);
  243. if (layer.attr['1'] == '1') {
  244. this._weight(blobReader, 'bias', [ num_output ], 'float32');
  245. }
  246. break;
  247. }
  248. case 'Bias': {
  249. const bias_data_size = parseInt(layer.attr['0'] || 0, 10);
  250. this._weight(blobReader, 'bias', [ bias_data_size ], 'float32');
  251. break;
  252. }
  253. case 'Embed': {
  254. const num_output = parseInt(layer.attr['0'] || 0, 10);
  255. const weight_data_size = parseInt(layer.attr['3'] || 0, 10);
  256. this._weight(blobReader, 'weight', [ weight_data_size ]);
  257. if (layer.attr['2'] == '1') {
  258. this._weight(blobReader, 'bias', [ num_output], 'float32');
  259. }
  260. break;
  261. }
  262. case 'Convolution':
  263. case 'ConvolutionDepthWise':
  264. case 'Deconvolution':
  265. case 'DeconvolutionDepthWise': {
  266. const num_output = parseInt(layer.attr['0'] || 0, 10);
  267. const kernel_w = parseInt(layer.attr['1'] || 0, 10);
  268. const kernel_h = parseInt(layer.attr['11'] || kernel_w, 10);
  269. const weight_data_size = parseInt(layer.attr['6'] || 0, 10);
  270. this._weight(blobReader, 'weight', [ num_output, weight_data_size / ( num_output * kernel_w * kernel_h), kernel_w, kernel_h ]);
  271. if (layer.attr['5'] == '1') {
  272. this._weight(blobReader, 'bias', [ num_output ], 'float32');
  273. }
  274. break;
  275. }
  276. case 'Dequantize': {
  277. if (layer.attr['1'] == '1') {
  278. const bias_data_size = parseInt(layer.attr['2'] || 0, 10);
  279. this._weight(blobReader, 'bias', [ bias_data_size ], 'float32');
  280. }
  281. break;
  282. }
  283. case 'Requantize': {
  284. if (layer.attr['2'] == '1') {
  285. const bias_data_size = parseInt(layer.attr['3'] || 0, 10);
  286. this._weight(blobReader, 'bias', [ bias_data_size ], 'float32');
  287. }
  288. break;
  289. }
  290. case 'InstanceNorm': {
  291. const channels = parseInt(layer.attr['0'] || 0, 10);
  292. this._weight(blobReader, 'gamma', [ channels ], 'float32');
  293. this._weight(blobReader, 'beta', [ channels ], 'float32');
  294. break;
  295. }
  296. case 'Scale': {
  297. const scale_data_size = parseInt(layer.attr['0'] || 0, 10);
  298. if (scale_data_size != -233) {
  299. this._weight(blobReader, 'scale', [ scale_data_size], 'float32');
  300. if (layer.attr['1'] == '1') {
  301. this._weight(blobReader, 'bias', [ scale_data_size ], 'float32');
  302. }
  303. }
  304. break;
  305. }
  306. case 'Normalize': {
  307. const scale_data_size = parseInt(layer.attr['3'] || 0, 10);
  308. this._weight(blobReader, 'scale', [ scale_data_size ], 'float32');
  309. break;
  310. }
  311. case 'PReLU': {
  312. const num_slope = parseInt(layer.attr['0'] || 0, 10);
  313. this._weight(blobReader, 'slope', [ num_slope ], 'float32');
  314. break;
  315. }
  316. }
  317. }
  318. get type() {
  319. return this._type;
  320. }
  321. get name() {
  322. return this._name;
  323. }
  324. get metadata() {
  325. return this._metadata.type(this._type);
  326. }
  327. get attributes() {
  328. return this._attributes;
  329. }
  330. get inputs() {
  331. return this._inputs;
  332. }
  333. get outputs() {
  334. return this._outputs;
  335. }
  336. _weight(blobReader, name, dimensions, dataType) {
  337. const blob = blobReader.read(dimensions, dataType);
  338. dataType = blob ? (blob.dataType || '?') : (dataType || '?');
  339. const data = blob ? blob.data : null;
  340. this._inputs.push(new ncnn.Parameter(name, true, [
  341. new ncnn.Argument('', null, new ncnn.Tensor(new ncnn.TensorType(dataType, new ncnn.TensorShape(dimensions)), data))
  342. ]));
  343. }
  344. };
  345. ncnn.Attribute = class {
  346. constructor(schema, key, value) {
  347. this._type = '';
  348. this._name = key;
  349. this._value = value;
  350. if (schema) {
  351. this._name = schema.name;
  352. if (schema.type) {
  353. this._type = schema.type;
  354. }
  355. switch (this._type) {
  356. case 'int32':
  357. this._value = parseInt(this._value, 10);
  358. break;
  359. case 'float32':
  360. this._value = parseFloat(this._value);
  361. break;
  362. case 'float32[]':
  363. this._value = this._value.map((v) => parseFloat(v));
  364. break;
  365. }
  366. if (Object.prototype.hasOwnProperty.call(schema, 'visible') && !schema.visible) {
  367. this._visible = false;
  368. }
  369. else if (Object.prototype.hasOwnProperty.call(schema, 'default')) {
  370. if (this._value == schema.default || (this._value && this._value.toString() == schema.default.toString())) {
  371. this._visible = false;
  372. }
  373. }
  374. }
  375. }
  376. get type() {
  377. return this._type;
  378. }
  379. get name() {
  380. return this._name;
  381. }
  382. get value() {
  383. return this._value;
  384. }
  385. get visible() {
  386. return this._visible == false ? false : true;
  387. }
  388. };
  389. ncnn.Tensor = class {
  390. constructor(type, data) {
  391. this._type = type;
  392. this._data = data;
  393. }
  394. get kind() {
  395. return 'Weight';
  396. }
  397. get type() {
  398. return this._type;
  399. }
  400. get state() {
  401. return this._context().state || null;
  402. }
  403. get value() {
  404. const context = this._context();
  405. if (context.state) {
  406. return null;
  407. }
  408. context.limit = Number.MAX_SAFE_INTEGER;
  409. return this._decode(context, 0);
  410. }
  411. toString() {
  412. const context = this._context();
  413. if (context.state) {
  414. return '';
  415. }
  416. context.limit = 10000;
  417. const value = this._decode(context, 0);
  418. return JSON.stringify(value, null, 4);
  419. }
  420. _context() {
  421. const context = {};
  422. context.index = 0;
  423. context.count = 0;
  424. context.state = null;
  425. if (this._type.dataType == '?') {
  426. context.state = 'Tensor has unknown data type.';
  427. return context;
  428. }
  429. if (!this._type.shape) {
  430. context.state = 'Tensor has no dimensions.';
  431. return context;
  432. }
  433. if (!this._data) {
  434. context.state = 'Tensor data is empty.';
  435. return context;
  436. }
  437. switch (this._type.dataType) {
  438. case 'float16':
  439. case 'float32':
  440. context.data = new DataView(this._data.buffer, this._data.byteOffset, this._data.byteLength);
  441. break;
  442. default:
  443. context.state = 'Tensor data type is not implemented.';
  444. break;
  445. }
  446. context.dataType = this._type.dataType;
  447. context.shape = this._type.shape.dimensions;
  448. return context;
  449. }
  450. _decode(context, dimension) {
  451. const shape = context.shape.length !== 0 ? context.shape : [ 1 ];
  452. const results = [];
  453. const size = shape[dimension];
  454. if (dimension == shape.length - 1) {
  455. for (let i = 0; i < size; i++) {
  456. if (context.count > context.limit) {
  457. results.push('...');
  458. return results;
  459. }
  460. switch (this._type.dataType) {
  461. case 'float32':
  462. results.push(context.data.getFloat32(context.index, true));
  463. context.index += 4;
  464. context.count++;
  465. break;
  466. case 'float16':
  467. results.push(context.data.getFloat16(context.index, true));
  468. context.index += 2;
  469. context.count++;
  470. break;
  471. }
  472. }
  473. }
  474. else {
  475. for (let j = 0; j < size; j++) {
  476. if (context.count > context.limit) {
  477. results.push('...');
  478. return results;
  479. }
  480. results.push(this._decode(context, dimension + 1));
  481. }
  482. }
  483. if (context.shape.length == 0) {
  484. return results[0];
  485. }
  486. return results;
  487. }
  488. };
  489. ncnn.TensorType = class {
  490. constructor(dataType, shape) {
  491. this._dataType = dataType || '?';
  492. this._shape = shape;
  493. }
  494. get dataType() {
  495. return this._dataType;
  496. }
  497. get shape() {
  498. return this._shape;
  499. }
  500. toString() {
  501. return this._dataType + this._shape.toString();
  502. }
  503. };
  504. ncnn.TensorShape = class {
  505. constructor(dimensions) {
  506. this._dimensions = dimensions;
  507. }
  508. get dimensions() {
  509. return this._dimensions;
  510. }
  511. toString() {
  512. return this._dimensions ? ('[' + this._dimensions.map((dimension) => dimension ? dimension.toString() : '?').join(',') + ']') : '';
  513. }
  514. };
  515. ncnn.Metadata = class {
  516. static open(host) {
  517. if (ncnn.Metadata._metadata) {
  518. return Promise.resolve(ncnn.Metadata._metadata);
  519. }
  520. return host.request(null, 'ncnn-metadata.json', 'utf-8').then((data) => {
  521. ncnn.Metadata._metadata = new ncnn.Metadata(data);
  522. return ncnn.Metadata._metadata;
  523. }).catch(() => {
  524. ncnn.Metadata._metadata = new ncnn.Metadata(null);
  525. return ncnn.Metadata._metadatas;
  526. });
  527. }
  528. constructor(data) {
  529. this._operatorMap = new Map();
  530. this._map = new Map();
  531. this._attributeCache = new Map();
  532. if (data) {
  533. const items = JSON.parse(data);
  534. if (items) {
  535. for (const item of items) {
  536. if (item.name && item.schema) {
  537. item.schema.name = item.name;
  538. this._map.set(item.name, item.schema);
  539. if (Object.prototype.hasOwnProperty.call(item.schema, 'operator')) {
  540. this._operatorMap.set(item.schema.operator, item.name);
  541. }
  542. }
  543. }
  544. }
  545. }
  546. }
  547. operator(code) {
  548. return this._operatorMap.get(code);
  549. }
  550. type(name) {
  551. return this._map.get(name);
  552. }
  553. attribute(type, name) {
  554. const key = type + ':' + name;
  555. if (!this._attributeCache.has(key)) {
  556. const schema = this.type(type);
  557. if (schema && schema.attributes && schema.attributes.length > 0) {
  558. for (const attribute of schema.attributes) {
  559. this._attributeCache.set(type + ':' + attribute.name, attribute);
  560. }
  561. }
  562. if (!this._attributeCache.has(key)) {
  563. this._attributeCache.set(key, null);
  564. }
  565. }
  566. return this._attributeCache.get(key);
  567. }
  568. };
  569. ncnn.TextParamReader = class {
  570. constructor(text) {
  571. const lines = text.split(/\r?\n/);
  572. const signature = lines.shift();
  573. if (signature !== '7767517') {
  574. throw new ncnn.Error('Invalid signature.');
  575. }
  576. const header = lines.shift().split(' ');
  577. if (header.length !== 2) {
  578. throw new ncnn.Error('Invalid header count.');
  579. }
  580. const layers = [];
  581. while (lines.length > 0) {
  582. const line = lines.shift().trim();
  583. if (line.length > 0) {
  584. const columns = line.split(' ').filter((s) => s.length != 0);
  585. const layer = {};
  586. layer.type = columns.shift();
  587. layer.name = columns.shift();
  588. const inputCount = parseInt(columns.shift(), 10);
  589. const outputCount = parseInt(columns.shift(), 10);
  590. layer.inputs = columns.splice(0, inputCount);
  591. layer.outputs = columns.splice(0, outputCount);
  592. layer.attr = {};
  593. layer.attributes = [];
  594. for (const column of columns) {
  595. const parts = column.split('=');
  596. if (parts.length === 2) {
  597. let key = parts[0].trim();
  598. let value = parts[1].trim();
  599. const keyInt = parseInt(key, 10);
  600. if (keyInt < 0) {
  601. value = value.split(',').map((v) => v.trim());
  602. value.shift();
  603. key = (-(keyInt + 23300)).toString();
  604. }
  605. layer.attr[key] = value;
  606. layer.attributes.push({ key: key, value: value });
  607. }
  608. }
  609. layers.push(layer);
  610. }
  611. }
  612. this._layers = layers;
  613. }
  614. get layers() {
  615. return this._layers;
  616. }
  617. };
  618. ncnn.BinaryParamReader = class {
  619. constructor(metadata, buffer) {
  620. const reader = new ncnn.BinaryReader(buffer);
  621. if (reader.int32() !== 0x007685DD) {
  622. throw new ncnn.Error('Invalid signature.');
  623. }
  624. const layerCount = reader.int32();
  625. /* const blobCount = */ reader.int32();
  626. const layers = [];
  627. for (let i = 0; i < layerCount; i++) {
  628. const typeIndex = reader.int32();
  629. const operator = metadata.operator(typeIndex);
  630. const layer = {
  631. type: operator || typeIndex.toString(),
  632. name: i.toString(),
  633. inputs: [],
  634. outputs: [],
  635. attr: {},
  636. attributes: []
  637. };
  638. const inputCount = reader.int32();
  639. const outputCount = reader.int32();
  640. for (let j = 0; j < inputCount; j++) {
  641. layer.inputs.push(reader.int32().toString());
  642. }
  643. for (let j = 0; j < outputCount; j++) {
  644. layer.outputs.push(reader.int32().toString());
  645. }
  646. let id = reader.int32();
  647. while (id != -233) {
  648. const isArray = id <= -23300;
  649. if (isArray) {
  650. id = -id - 23300;
  651. }
  652. if (isArray) {
  653. const len = reader.int32();
  654. const values = [];
  655. for (let i = 0; i < len; i++) {
  656. values.push(reader.int32());
  657. }
  658. layer.attributes.push({ key: id.toString(), value: values.toString() });
  659. layer.attr[id.toString()] = values;
  660. }
  661. else {
  662. const value = reader.int32();
  663. layer.attributes.push({ key: id.toString(), value: value.toString() });
  664. layer.attr[id.toString()] = value.toString();
  665. }
  666. id = reader.int32();
  667. }
  668. layers.push(layer);
  669. }
  670. this._layers = layers;
  671. }
  672. get layers() {
  673. return this._layers;
  674. }
  675. };
  676. ncnn.BlobReader = class {
  677. constructor(buffer) {
  678. this._buffer = buffer;
  679. this._position = 0;
  680. }
  681. read(shape, dataType) {
  682. if (this._buffer) {
  683. if (!dataType) {
  684. if (this._buffer && this._position + 4 < this._buffer.length) {
  685. const f0 = this._buffer[this._position++];
  686. const f1 = this._buffer[this._position++];
  687. const f2 = this._buffer[this._position++];
  688. const f3 = this._buffer[this._position++];
  689. const type = f0 | f1 << 8 | f2 << 16 | f3 << 24;
  690. switch (type) {
  691. case 0x00000000:
  692. dataType = 'float32';
  693. break;
  694. case 0x01306B47:
  695. dataType = 'float16';
  696. break;
  697. case 0x000D4B38:
  698. dataType = 'int8';
  699. break;
  700. case 0x00000001:
  701. dataType = 'qint8';
  702. break;
  703. case 0x0002C056: // size * sizeof(float) - raw data with extra scaling
  704. default:
  705. throw new ncnn.Error("Unknown weight type '" + type + "'.");
  706. }
  707. }
  708. else {
  709. this._buffer = null;
  710. }
  711. }
  712. let data = null;
  713. let size = 1;
  714. if (shape) {
  715. for (const dimension of shape) {
  716. size *= dimension;
  717. }
  718. }
  719. else {
  720. this._buffer = null;
  721. }
  722. if (this._buffer) {
  723. if (dataType) {
  724. const position = this._position;
  725. switch (dataType) {
  726. case 'float32':
  727. size *= 4;
  728. this._position += size;
  729. data = this._buffer.subarray(position, this._position);
  730. break;
  731. case 'float16':
  732. size *= 2;
  733. this._position += size;
  734. data = this._buffer.subarray(position, this._position);
  735. break;
  736. case 'int8':
  737. this._position += size;
  738. data = this._buffer.subarray(position, this._position);
  739. break;
  740. case 'qint8':
  741. this._position += size + 1024;
  742. data = null;
  743. break;
  744. default:
  745. throw new ncnn.Error("Unknown weight type '" + dataType + "'.");
  746. }
  747. }
  748. }
  749. return { dataType: dataType, data: data };
  750. }
  751. return null;
  752. }
  753. };
  754. ncnn.BinaryReader = class {
  755. constructor(buffer) {
  756. this._buffer = buffer;
  757. this._dataView = new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength);
  758. this._position = 0;
  759. }
  760. skip(size) {
  761. this._position += size;
  762. if (this._position > this._buffer.length) {
  763. throw new ncnn.Error('Expected ' + (this._position - this._buffer.length) + ' more bytes. The file might be corrupted. Unexpected end of file.');
  764. }
  765. }
  766. int32() {
  767. const position = this._position;
  768. this.skip(4);
  769. return this._dataView.getInt32(position, true);
  770. }
  771. };
  772. ncnn.Error = class extends Error {
  773. constructor(message) {
  774. super(message);
  775. this.name = 'Error loading ncnn model.';
  776. }
  777. };
  778. if (typeof module !== 'undefined' && typeof module.exports === 'object') {
  779. module.exports.ModelFactory = ncnn.ModelFactory;
  780. }