ncnn.js 33 KB

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