mediapipe.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400
  1. var mediapipe = mediapipe || {};
  2. var protobuf = protobuf || require('./protobuf');
  3. mediapipe.ModelFactory = class {
  4. match(context) {
  5. const tags = context.tags('pbtxt');
  6. if (tags.has('node') && ['input_stream', 'output_stream', 'input_side_packet', 'output_side_packet'].some((key) => tags.has(key) || tags.has('node.' + key))) {
  7. return 'mediapipe.pbtxt';
  8. }
  9. return undefined;
  10. }
  11. open(context) {
  12. return Promise.resolve().then(() => {
  13. // return context.require('./mediapipe-proto').then(() => {
  14. mediapipe.proto = protobuf.get('mediapipe');
  15. let config = null;
  16. try {
  17. const stream = context.stream;
  18. const reader = protobuf.TextReader.open(stream);
  19. // const config = mediapipe.proto.mediapipe.CalculatorGraphConfig.decodeText(reader);
  20. config = new mediapipe.Object(reader);
  21. }
  22. catch (error) {
  23. const message = error && error.message ? error.message : error.toString();
  24. throw new mediapipe.Error('File text format is not mediapipe.CalculatorGraphConfig (' + message.replace(/\.$/, '') + ').');
  25. }
  26. return new mediapipe.Model(config);
  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. const type = node.calculator || '?';
  112. this._type = { name: 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 inputs() {
  208. return this._inputs;
  209. }
  210. get outputs() {
  211. return this._outputs;
  212. }
  213. get attributes() {
  214. return this._attributes;
  215. }
  216. };
  217. mediapipe.Attribute = class {
  218. constructor(name, value) {
  219. this._name = name;
  220. this._value = value;
  221. }
  222. get name() {
  223. return this._name;
  224. }
  225. get value() {
  226. return this._value;
  227. }
  228. get visible() {
  229. return true;
  230. }
  231. };
  232. mediapipe.Parameter = class {
  233. constructor(name, args) {
  234. this._name = name;
  235. this._arguments = args;
  236. }
  237. get name() {
  238. return this._name;
  239. }
  240. get visible() {
  241. return true;
  242. }
  243. get arguments() {
  244. return this._arguments;
  245. }
  246. };
  247. mediapipe.Argument = class {
  248. constructor(name, type, initializer) {
  249. if (typeof name !== 'string') {
  250. throw new mediapipe.Error("Invalid argument identifier '" + JSON.stringify(name) + "'.");
  251. }
  252. this._name = name;
  253. this._type = type || null;
  254. this._initializer = initializer || null;
  255. }
  256. get name() {
  257. return this._name;
  258. }
  259. get type() {
  260. if (this._type) {
  261. return this._type;
  262. }
  263. if (this._initializer) {
  264. return this._initializer.type;
  265. }
  266. return null;
  267. }
  268. get initializer() {
  269. return this._initializer;
  270. }
  271. };
  272. mediapipe.Object = class {
  273. constructor(reader, block) {
  274. if (!block) {
  275. reader.start();
  276. }
  277. const type = reader.token();
  278. if (type.startsWith('[') && type.endsWith(']')) {
  279. this.__type__ = type.substring(1, type.length - 1);
  280. reader.next();
  281. reader.match(':');
  282. reader.start();
  283. }
  284. const arrayTags = new Set();
  285. while (!reader.end()) {
  286. const tag = reader.tag();
  287. const next = reader.token();
  288. let obj = null;
  289. if (next === '{') {
  290. reader.start();
  291. obj = new mediapipe.Object(reader, true);
  292. if (obj.__type__) {
  293. while (!reader.end()) {
  294. if (!Array.isArray(obj)) {
  295. obj = [ obj ];
  296. }
  297. const token = reader.token();
  298. if (token.startsWith('[') && token.endsWith(']')) {
  299. obj.push(new mediapipe.Object(reader, true));
  300. continue;
  301. }
  302. break;
  303. }
  304. }
  305. }
  306. else if (next.startsWith('"') && next.endsWith('"')) {
  307. obj = next.substring(1, next.length - 1);
  308. reader.next();
  309. }
  310. else if (next === 'true' || next === 'false') {
  311. obj = next;
  312. reader.next();
  313. }
  314. else if (reader.first()) {
  315. obj = [];
  316. while (!reader.last()) {
  317. const data = reader.token();
  318. reader.next();
  319. if (!isNaN(data)) {
  320. obj.push(parseFloat(data));
  321. }
  322. }
  323. }
  324. else if (!isNaN(next)) {
  325. obj = parseFloat(next);
  326. reader.next();
  327. }
  328. else {
  329. obj = next;
  330. reader.next();
  331. }
  332. if (this[tag] && (!Array.isArray(this[tag]) || arrayTags.has(tag))) {
  333. this[tag] = [ this[tag] ];
  334. arrayTags.delete(tag);
  335. }
  336. if (this[tag]) {
  337. this[tag].push(obj);
  338. }
  339. else {
  340. if (Array.isArray(obj)) {
  341. arrayTags.add(tag);
  342. }
  343. this[tag] = obj;
  344. }
  345. reader.match(',');
  346. }
  347. }
  348. };
  349. mediapipe.Error = class extends Error {
  350. constructor(message) {
  351. super(message);
  352. this.name = 'Error loading MediaPipe model.';
  353. }
  354. };
  355. if (typeof module !== 'undefined' && typeof module.exports === 'object') {
  356. module.exports.ModelFactory = mediapipe.ModelFactory;
  357. }