mediapipe.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404
  1. /* jshint esversion: 6 */
  2. var mediapipe = mediapipe || {};
  3. var protobuf = protobuf || require('./protobuf');
  4. mediapipe.ModelFactory = class {
  5. match(context) {
  6. const tags = context.tags('pbtxt');
  7. if (tags.has('node') && ['input_stream', 'output_stream', 'input_side_packet', 'output_side_packet'].some((key) => tags.has(key) || tags.has('node.' + key))) {
  8. return true;
  9. }
  10. return false;
  11. }
  12. open(context) {
  13. return Promise.resolve().then(() => {
  14. // return context.require('./mediapipe-proto').then(() => {
  15. mediapipe.proto = protobuf.get('mediapipe');
  16. try {
  17. const stream = context.stream;
  18. const reader = protobuf.TextReader.open(stream);
  19. // const config = mediapipe.proto.mediapipe.CalculatorGraphConfig.decodeText(reader);
  20. const config = new mediapipe.Object(reader);
  21. return new mediapipe.Model(config);
  22. }
  23. catch (error) {
  24. const message = error && error.message ? error.message : error.toString();
  25. throw new mediapipe.Error('File text format is not mediapipe.CalculatorGraphConfig (' + message.replace(/\.$/, '') + ').');
  26. }
  27. });
  28. }
  29. };
  30. mediapipe.Model = class {
  31. constructor(root) {
  32. this._graphs = [ new mediapipe.Graph(root) ];
  33. }
  34. get format() {
  35. return 'MediaPipe';
  36. }
  37. get graphs() {
  38. return this._graphs;
  39. }
  40. };
  41. mediapipe.Graph = class {
  42. constructor(root) {
  43. this._inputs = [];
  44. this._outputs = [];
  45. this._nodes = [];
  46. if (root) {
  47. if (root.input_stream) {
  48. const inputs = Array.isArray(root.input_stream) ? root.input_stream : [ root.input_stream ];
  49. for (const input of inputs) {
  50. const parts = input.split(':');
  51. const type = (parts.length > 1) ? parts.shift() : '';
  52. const name = parts.shift();
  53. this._inputs.push(new mediapipe.Parameter(name, [
  54. new mediapipe.Argument(name, type, null)
  55. ]));
  56. }
  57. }
  58. if (root.output_stream) {
  59. const outputs = Array.isArray(root.output_stream) ? root.output_stream : [ root.output_stream ];
  60. for (const output of outputs) {
  61. const parts = output.split(':');
  62. const type = (parts.length > 1) ? parts.shift() : '';
  63. const name = parts.shift();
  64. this._outputs.push(new mediapipe.Parameter(name, [
  65. new mediapipe.Argument(name, type, null)
  66. ]));
  67. }
  68. }
  69. if (root.input_side_packet) {
  70. const inputs = Array.isArray(root.input_side_packet) ? root.input_side_packet : [ root.input_side_packet ];
  71. for (const input of inputs) {
  72. const parts = input.split(':');
  73. const type = (parts.length > 1) ? parts.shift() : '';
  74. const name = parts.shift();
  75. this._inputs.push(new mediapipe.Parameter(name, [
  76. new mediapipe.Argument(name, type, null)
  77. ]));
  78. }
  79. }
  80. if (root.output_side_packet) {
  81. const outputs = Array.isArray(root.output_side_packet) ? root.output_side_packet : [ root.output_side_packet ];
  82. for (const output of outputs) {
  83. const parts = output.split(':');
  84. const type = (parts.length > 1) ? parts.shift() : '';
  85. const name = parts.shift();
  86. this._outputs.push(new mediapipe.Parameter(output, [
  87. new mediapipe.Argument(name, type, null)
  88. ]));
  89. }
  90. }
  91. if (root.node) {
  92. const nodes = Array.isArray(root.node) ? root.node : [ root.node ];
  93. for (const node of nodes) {
  94. this._nodes.push(new mediapipe.Node(node));
  95. }
  96. }
  97. }
  98. }
  99. get inputs() {
  100. return this._inputs;
  101. }
  102. get outputs() {
  103. return this._outputs;
  104. }
  105. get nodes() {
  106. return this._nodes;
  107. }
  108. };
  109. mediapipe.Node = class {
  110. constructor(node) {
  111. this._type = node.calculator || '?';
  112. this._type = this._type.replace(/Calculator$/, '');
  113. this._inputs = [];
  114. this._outputs = [];
  115. this._attributes = [];
  116. if (node.input_stream) {
  117. const args = [];
  118. const inputs = Array.isArray(node.input_stream) ? node.input_stream : [ node.input_stream ];
  119. for (const input of inputs) {
  120. const parts = input.split(':');
  121. const type = (parts.length > 1) ? parts.shift() : '';
  122. const name = parts.shift();
  123. args.push(new mediapipe.Argument(name, type, null));
  124. }
  125. this._inputs.push(new mediapipe.Parameter('input_stream', args));
  126. }
  127. if (node.output_stream) {
  128. const args = [];
  129. const outputs = Array.isArray(node.output_stream) ? node.output_stream : [ node.output_stream ];
  130. for (const output of outputs) {
  131. const parts = output.split(':');
  132. const type = (parts.length > 1) ? parts.shift() : '';
  133. const name = parts.shift();
  134. args.push(new mediapipe.Argument(name, type, null));
  135. }
  136. this._outputs.push(new mediapipe.Parameter('output_stream', args));
  137. }
  138. if (node.input_side_packet) {
  139. const args = [];
  140. const inputs = Array.isArray(node.input_side_packet) ? node.input_side_packet : [ node.input_side_packet ];
  141. for (const input of inputs) {
  142. const parts = input.split(':');
  143. const type = (parts.length > 1) ? parts.shift() : '';
  144. const name = parts.shift();
  145. args.push(new mediapipe.Argument(name, type, null));
  146. }
  147. this._inputs.push(new mediapipe.Parameter('input_side_packet', args));
  148. }
  149. if (node.output_side_packet) {
  150. const args = [];
  151. const outputs = Array.isArray(node.output_side_packet) ? node.output_side_packet : [ node.output_side_packet ];
  152. for (const output of outputs) {
  153. const parts = output.split(':');
  154. const type = (parts.length > 1) ? parts.shift() : '';
  155. const name = parts.shift();
  156. args.push(new mediapipe.Argument(name, type, null));
  157. }
  158. this._outputs.push(new mediapipe.Parameter('output_side_packet', args));
  159. }
  160. const options = new Map();
  161. if (node.options) {
  162. for (const key of Object.keys(node.options)) {
  163. options.set(key, node.options[key]);
  164. }
  165. }
  166. const node_options = node.node_options ? Array.isArray(node.node_options) ? node.node_options : [ node.node_options ] : [];
  167. if (mediapipe.proto.google && node_options.every((options) => options instanceof mediapipe.proto.google.protobuf.Any)) {
  168. for (const entry of node_options) {
  169. const value = new RegExp(/^\{(.*)\}\s*$/, 's').exec(entry.value);
  170. const buffer = new TextEncoder('utf-8').encode(value[1]);
  171. const reader = protobuf.TextReader.open(buffer);
  172. if (entry.type_url.startsWith('type.googleapis.com/mediapipe.')) {
  173. const type = entry.type_url.split('.').pop();
  174. if (mediapipe.proto && mediapipe.proto.mediapipe && mediapipe.proto.mediapipe[type]) {
  175. const message = mediapipe.proto.mediapipe[type].decodeText(reader);
  176. for (const key of Object.keys(message)) {
  177. options.set(key, message[key]);
  178. }
  179. continue;
  180. }
  181. }
  182. const message = new mediapipe.Object(reader);
  183. for (const key of Object.keys(message)) {
  184. options.set(key, message[key]);
  185. }
  186. }
  187. }
  188. else {
  189. for (const entry of node_options) {
  190. for (const key of Object.keys(entry)) {
  191. if (key !== '__type__') {
  192. options.set(key, entry[key]);
  193. }
  194. }
  195. }
  196. }
  197. for (const pair of options) {
  198. this._attributes.push(new mediapipe.Attribute(pair[0], pair[1]));
  199. }
  200. }
  201. get name() {
  202. return '';
  203. }
  204. get type() {
  205. return this._type;
  206. }
  207. get metadata() {
  208. return null;
  209. }
  210. get inputs() {
  211. return this._inputs;
  212. }
  213. get outputs() {
  214. return this._outputs;
  215. }
  216. get attributes() {
  217. return this._attributes;
  218. }
  219. };
  220. mediapipe.Attribute = class {
  221. constructor(name, value) {
  222. this._name = name;
  223. this._value = value;
  224. }
  225. get name() {
  226. return this._name;
  227. }
  228. get value() {
  229. return this._value;
  230. }
  231. get visible() {
  232. return true;
  233. }
  234. };
  235. mediapipe.Parameter = class {
  236. constructor(name, args) {
  237. this._name = name;
  238. this._arguments = args;
  239. }
  240. get name() {
  241. return this._name;
  242. }
  243. get visible() {
  244. return true;
  245. }
  246. get arguments() {
  247. return this._arguments;
  248. }
  249. };
  250. mediapipe.Argument = class {
  251. constructor(name, type, initializer) {
  252. if (typeof name !== 'string') {
  253. throw new mediapipe.Error("Invalid argument identifier '" + JSON.stringify(name) + "'.");
  254. }
  255. this._name = name;
  256. this._type = type || null;
  257. this._initializer = initializer || null;
  258. }
  259. get name() {
  260. return this._name;
  261. }
  262. get type() {
  263. if (this._type) {
  264. return this._type;
  265. }
  266. if (this._initializer) {
  267. return this._initializer.type;
  268. }
  269. return null;
  270. }
  271. get initializer() {
  272. return this._initializer;
  273. }
  274. };
  275. mediapipe.Object = class {
  276. constructor(reader, block) {
  277. if (!block) {
  278. reader.start();
  279. }
  280. const type = reader.token();
  281. if (type.startsWith('[') && type.endsWith(']')) {
  282. this.__type__ = type.substring(1, type.length - 1);
  283. reader.next();
  284. reader.match(':');
  285. reader.start();
  286. }
  287. const arrayTags = new Set();
  288. while (!reader.end()) {
  289. const tag = reader.tag();
  290. const next = reader.token();
  291. let obj = null;
  292. if (next === '{') {
  293. reader.start();
  294. obj = new mediapipe.Object(reader, true);
  295. if (obj.__type__) {
  296. while (!reader.end()) {
  297. if (!Array.isArray(obj)) {
  298. obj = [ obj ];
  299. }
  300. const token = reader.token();
  301. if (token.startsWith('[') && token.endsWith(']')) {
  302. obj.push(new mediapipe.Object(reader, true));
  303. continue;
  304. }
  305. break;
  306. }
  307. }
  308. }
  309. else if (next.startsWith('"') && next.endsWith('"')) {
  310. obj = next.substring(1, next.length - 1);
  311. reader.next();
  312. }
  313. else if (next === 'true' || next === 'false') {
  314. obj = next;
  315. reader.next();
  316. }
  317. else if (reader.first()) {
  318. obj = [];
  319. while (!reader.last()) {
  320. const data = reader.token();
  321. reader.next();
  322. if (!isNaN(data)) {
  323. obj.push(parseFloat(data));
  324. }
  325. }
  326. }
  327. else if (!isNaN(next)) {
  328. obj = parseFloat(next);
  329. reader.next();
  330. }
  331. else {
  332. obj = next;
  333. reader.next();
  334. }
  335. if (this[tag] && (!Array.isArray(this[tag]) || arrayTags.has(tag))) {
  336. this[tag] = [ this[tag] ];
  337. arrayTags.delete(tag);
  338. }
  339. if (this[tag]) {
  340. this[tag].push(obj);
  341. }
  342. else {
  343. if (Array.isArray(obj)) {
  344. arrayTags.add(tag);
  345. }
  346. this[tag] = obj;
  347. }
  348. reader.match(',');
  349. }
  350. }
  351. };
  352. mediapipe.Error = class extends Error {
  353. constructor(message) {
  354. super(message);
  355. this.name = 'Error loading MediaPipe model.';
  356. }
  357. };
  358. if (typeof module !== 'undefined' && typeof module.exports === 'object') {
  359. module.exports.ModelFactory = mediapipe.ModelFactory;
  360. }