{"id":19888297,"url":"https://github.com/wasmedge/mediapipe-rs","last_synced_at":"2025-04-04T17:04:30.006Z","repository":{"id":176935155,"uuid":"647666596","full_name":"WasmEdge/mediapipe-rs","owner":"WasmEdge","description":"The Google mediapipe AI library. Write AI inference applications for image recognition, text classification, audio / video processing and more, in Rust and run them in the secure WasmEdge sandbox. Zero Python dependency!","archived":false,"fork":false,"pushed_at":"2024-10-02T14:09:00.000Z","size":1396,"stargazers_count":175,"open_issues_count":3,"forks_count":20,"subscribers_count":12,"default_branch":"main","last_synced_at":"2025-03-28T16:04:39.636Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/WasmEdge.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-05-31T09:11:36.000Z","updated_at":"2025-03-25T14:48:44.000Z","dependencies_parsed_at":null,"dependency_job_id":"8165c623-3a61-4954-bf09-7210c39cf371","html_url":"https://github.com/WasmEdge/mediapipe-rs","commit_stats":null,"previous_names":["wasmedge/mediapipe-rs"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/WasmEdge%2Fmediapipe-rs","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/WasmEdge%2Fmediapipe-rs/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/WasmEdge%2Fmediapipe-rs/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/WasmEdge%2Fmediapipe-rs/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/WasmEdge","download_url":"https://codeload.github.com/WasmEdge/mediapipe-rs/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247217173,"owners_count":20903008,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-12T18:06:51.852Z","updated_at":"2025-04-04T17:04:29.990Z","avatar_url":"https://github.com/WasmEdge.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cdiv align=\"center\"\u003e\n  \u003ch1\u003e\u003ccode\u003eMediaPipe-rs\u003c/code\u003e\u003c/h1\u003e\n  \u003cp\u003e\n    \u003ca href=\"https://github.com/WasmEdge/mediapipe-rs/actions?query=workflow%3ACI\"\u003e\n      \u003cimg src=\"https://github.com/WasmEdge/mediapipe-rs/workflows/CI/badge.svg\" alt=\"CI status\" height=\"20\"/\u003e\n    \u003c/a\u003e\n    \u003ca href=\"https://crates.io/crates/mediapipe-rs\"\u003e\n      \u003cimg src=\"https://img.shields.io/crates/v/mediapipe-rs.svg\" alt=\"crates.io status\" height=\"20\"/\u003e\n    \u003c/a\u003e\n    \u003ca href=\"https://docs.rs/mediapipe-rs\"\u003e\n      \u003cimg src=\"https://img.shields.io/docsrs/mediapipe-rs\" alt=\"doc.rs status\" height=\"20\"/\u003e\n    \u003c/a\u003e\n  \u003c/p\u003e\n\u003c/div\u003e\n\n# A Rust library for MediaPipe tasks for WasmEdge WASI-NN\n\n## Introduction\n\n* **Easy to use**: low-code APIs such as mediapipe-python.\n* **Low overhead**: No unnecessary data copy, allocation, and free during the processing.\n* **Flexible**: Users can use custom media bytes as input.\n* For TfLite models, the library not only supports all models downloaded from [MediaPipe Solutions] but also supports\n  **[TF Hub]** models and **custom models** with essential information.\n\n### Status\n\n* [x] Object Detection\n* [x] Image Classification\n* [x] Image Segmentation\n* [ ] Interactive Image Segmentation\n* [x] Gesture Recognition\n* [x] Hand Landmark Detection\n* [x] Image Embedding\n* [x] Face Detection\n* [x] Face Landmark Detection\n* [ ] Pose Landmark Detection\n* [x] Audio Classification\n* [x] Text Classification\n* [x] Text Embedding\n* [ ] Language Detection\n\n## Task APIs\n\nEvery task has three types: ```XxxBuilder```, ```Xxx```, ```XxxSession```. (``Xxx`` is the task name)\n\n* ```XxxBuilder``` is used to create a task instance ```Xxx```, which has many options to set.\n\n  example: use ```ImageClassifierBuilder``` to build a ```ImageClassifier``` task.\n  ```\n  let classifier = ImageClassifierBuilder::new()\n        .max_results(3) // set max result\n        .category_deny_list(vec![\"denied label\".into()]) // set deny list\n        .gpu() // set running device\n        .build_from_file(model_path)?; // create a image classifier\n  ```\n* ```Xxx``` is a task instance, which contains task information and model information.\n\n  example: use ```ImageClassifier``` to create a new ```ImageClassifierSession```\n  ```\n  let classifier_session = classifier.new_session()?;\n  ```\n* ```XxxSession``` is a running session to perform pre-process, inference, and post-process, which has buffers to store\n  mid-results.\n\n  example: use ```ImageClassifierSession``` to run the image classification task and return classification results:\n  ```\n  let classification_result = classifier_session.classify(\u0026image::open(img_path)?)?;\n  ```\n  **Note**: the session can be reused to speed up, if the code just uses the session once, it can use the task's wrapper\n  function to simplify.\n  ```\n  // let classifier_session = classifier.new_session()?;\n  // let classification_result = classifier_session.classify(\u0026image::open(img_path)?)?;\n  // The above 2-line code is equal to: \n  let classification_result = classifier.classify(\u0026image::open(img_path)?)?;\n  ```\n\n### Available tasks\n\n* vision:\n    * gesture recognition: `GestureRecognizerBuilder` -\u003e `GestureRecognizer` -\u003e `GestureRecognizerSession`\n    * hand detection: `HandDetectorBuilder` -\u003e `HandDetector` -\u003e `HandDetectorSession`\n    * image classification: `ImageClassifierBuilder` -\u003e `ImageClassifier` -\u003e `ImageClassifierSession`\n    * image embedding: `ImageEmbedderBuilder` -\u003e `ImageEmbedder` -\u003e `ImageEmbedderSession`\n    * image segmentation: `ImageSegmenterBuilder` -\u003e `ImageSegmenter` -\u003e `ImageSegmenterSession`\n    * object detection: `ObjectDetectorBuilder` -\u003e `ObjectDetector` -\u003e `ObjectDetectorSession`\n    * face detection: `FaceDetectorBuilder` -\u003e `FaceDetector` -\u003e `FaceDetectorSession`\n    * face landmark detection: `FaceLandmarkerBuilder` -\u003e `FaceLandmarker` -\u003e `FaceLandmarkerSession`\n* audio:\n    * audio classification: `AudioClassifierBuilder` -\u003e `AudioClassifier` -\u003e `AudioClassifierSession`\n* text:\n    * text classification: `TextClassifierBuilder` -\u003e `TextClassifier` -\u003e `TextClassifierSession`\n\n## Examples\n\n### Image classification\n\n```rust\nuse mediapipe_rs::tasks::vision::ImageClassifierBuilder;\n\nfn main() -\u003e Result\u003c(), Box\u003cdyn std::error::Error\u003e\u003e {\n    let (model_path, img_path) = parse_args()?;\n\n    let classification_result = ImageClassifierBuilder::new()\n        .max_results(3) // set max result\n        .build_from_file(model_path)? // create a image classifier\n        .classify(\u0026image::open(img_path)?)?; // do inference and generate results\n\n    // show formatted result message\n    println!(\"{}\", classification_result);\n\n    Ok(())\n}\n```\n\nExample input: (The image is downloaded from https://storage.googleapis.com/mediapipe-assets/burger.jpg)\n\n\u003cimg height=\"30%\" src=\"https://storage.googleapis.com/mediapipe-assets/burger.jpg\" width=\"30%\" alt=\"burger.jpg\" /\u003e\n\nExample output in console:\n\n```console\n$ cargo run --release --example image_classification -- ./assets/models/image_classification/efficientnet_lite0_fp32.tflite ./assets/testdata/img/burger.jpg\n    Finished release [optimized] target(s) in 0.01s\n     Running `/mediapipe-rs/./scripts/wasmedge-runner.sh target/wasm32-wasi/release/examples/image_classification.wasm ./assets/models/image_classification/efficientnet_lite0_fp32.tflite ./assets/testdata/img/burger.jpg`\nClassificationResult:\n  Classification #0:\n    Category #0:\n      Category name: \"cheeseburger\"\n      Display name:  None\n      Score:         0.70625573\n      Index:         933\n```\n\n### Object Detection\n\n```rust\nuse mediapipe_rs::postprocess::utils::draw_detection;\nuse mediapipe_rs::tasks::vision::ObjectDetectorBuilder;\n\nfn main() -\u003e Result\u003c(), Box\u003cdyn std::error::Error\u003e\u003e {\n    let (model_path, img_path, output_path) = parse_args()?;\n\n    let mut input_img = image::open(img_path)?;\n    let detection_result = ObjectDetectorBuilder::new()\n        .max_results(2) // set max result\n        .build_from_file(model_path)? // create a object detector\n        .detect(\u0026input_img)?; // do inference and generate results\n\n    // show formatted result message\n    println!(\"{}\", detection_result);\n\n    if let Some(output_path) = output_path {\n        // draw detection result to image\n        draw_detection(\u0026mut input_img, \u0026detection_result);\n        // save output image\n        input_img.save(output_path)?;\n    }\n\n    Ok(())\n}\n```\n\nExample input: (The image is downloaded\nfrom https://storage.googleapis.com/mediapipe-tasks/object_detector/cat_and_dog.jpg)\n\n\u003cimg alt=\"cat_and_dog.jpg\" height=\"30%\" src=\"https://storage.googleapis.com/mediapipe-tasks/object_detector/cat_and_dog.jpg\" width=\"30%\"/\u003e\n\nExample output in console:\n\n```console\n$ cargo run --release --example object_detection -- ./assets/models/object_detection/efficientdet_lite0_fp32.tflite ./assets/testdata/img/cat_and_dog.jpg\n    Finished release [optimized] target(s) in 0.00s\n     Running `/mediapipe-rs/./scripts/wasmedge-runner.sh target/wasm32-wasi/release/examples/object_detection.wasm ./assets/models/object_detection/efficientdet_lite0_fp32.tflite ./assets/testdata/img/cat_and_dog.jpg`\nDetectionResult:\n  Detection #0:\n    Box: (left: 0.12283102, top: 0.38476586, right: 0.51069236, bottom: 0.851197)\n    Category #0:\n      Category name: \"cat\"\n      Display name:  None\n      Score:         0.8460574\n      Index:         16\n  Detection #1:\n    Box: (left: 0.47926134, top: 0.06873521, right: 0.8711677, bottom: 0.87927735)\n    Category #0:\n      Category name: \"dog\"\n      Display name:  None\n      Score:         0.8375256\n      Index:         17\n```\n\nExample output:\n\u003cimg height=\"30%\" src=\"./assets/doc/cat_and_dog_detection.jpg\" width=\"30%\"/\u003e\n\n### Text Classification\n\n```rust\nfn main() -\u003e Result\u003c(), Box\u003cdyn std::error::Error\u003e\u003e {\n    let model_path = parse_args()?;\n\n    let text_classifier = TextClassifierBuilder::new()\n        .max_results(1) // set max result\n        .build_from_file(model_path)?; // create a text classifier\n\n    let positive_str = \"I love coding so much!\";\n    let negative_str = \"I don't like raining.\";\n\n    // classify show formatted result message\n    let result = text_classifier.classify(\u0026positive_str)?;\n    println!(\"`{}` -- {}\", positive_str, result);\n\n    let result = text_classifier.classify(\u0026negative_str)?;\n    println!(\"`{}` -- {}\", negative_str, result);\n\n    Ok(())\n}\n```\n\nExample output in console (use the bert model):\n\n```console\n$ cargo run --release --example text_classification -- ./assets/models/text_classification/bert_text_classifier.tflite\n    Finished release [optimized] target(s) in 0.01s\n     Running `/mediapipe-rs/./scripts/wasmedge-runner.sh target/wasm32-wasi/release/examples/text_classification.wasm ./assets/models/text_classification/bert_text_classifier.tflite`\n`I love coding so much!` -- ClassificationResult:\n  Classification #0:\n    Category #0:\n      Category name: \"positive\"\n      Display name:  None\n      Score:         0.99990463\n      Index:         1\n\n`I don't like raining.` -- ClassificationResult:\n  Classification #0:\n    Category #0:\n      Category name: \"negative\"\n      Display name:  None\n      Score:         0.99541473\n      Index:         0\n\n```\n\n### Gesture Recognition\n\n```rust\nuse mediapipe_rs::tasks::vision::GestureRecognizerBuilder;\n\nfn main() -\u003e Result\u003c(), Box\u003cdyn std::error::Error\u003e\u003e {\n    let (model_path, img_path) = parse_args()?;\n\n    let gesture_recognition_results = GestureRecognizerBuilder::new()\n        .num_hands(1) // set only recognition one hand\n        .max_results(1) // set max result\n        .build_from_file(model_path)? // create a task instance\n        .recognize(\u0026image::open(img_path)?)?; // do inference and generate results\n\n    for g in gesture_recognition_results {\n        println!(\"{}\", g.gestures.classifications[0].categories[0]);\n    }\n\n    Ok(())\n}\n```\n\nExample input: (The image is download\nfrom https://storage.googleapis.com/mediapipe-tasks/gesture_recognizer/victory.jpg)\n\n\u003cimg alt=\"victory.jpg\" height=\"30%\" src=\"https://storage.googleapis.com/mediapipe-tasks/gesture_recognizer/victory.jpg\" width=\"30%\"/\u003e\n\nExample output in console:\n\n```console\n$ cargo run --release --example gesture_recognition -- ./assets/models/gesture_recognition/gesture_recognizer.task ./assets/testdata/img/gesture_recognition_google_samples/victory.jpg\n    Finished release [optimized] target(s) in 0.02s\n     Running `/mediapipe-rs/./scripts/wasmedge-runner.sh target/wasm32-wasi/release/examples/gesture_recognition.wasm ./assets/models/gesture_recognition/gesture_recognizer.task ./assets/testdata/img/gesture_recognition_google_samples/victory.jpg`\n      Category name: \"Victory\"\n      Display name:  None\n      Score:         0.9322255\n      Index:         6\n```\n\n### Face Landmarks Detection\n\n```rust\nuse mediapipe_rs::tasks::vision::FaceLandmarkerBuilder;\nuse mediapipe_rs::postprocess::utils::DrawLandmarksOptions;\nuse mediapipe_rs::tasks::vision::FaceLandmarkConnections;\n\nfn main() -\u003e Result\u003c(), Box\u003cdyn std::error::Error\u003e\u003e {\n    let (model_path, img_path, output_path) = parse_args()?;\n\n    let mut input_img = image::open(img_path)?;\n    let face_landmark_results = FaceLandmarkerBuilder::new()\n        .num_faces(1) // set max number of faces to detect\n        .min_face_detection_confidence(0.5)\n        .min_face_presence_confidence(0.5)\n        .min_tracking_confidence(0.5)\n        .output_face_blendshapes(true)\n        .build_from_file(model_path)? // create a face landmarker\n        .detect(\u0026input_img)?; // do inference and generate results\n\n    // show formatted result message\n    println!(\"{}\", face_landmark_results);\n\n    if let Some(output_path) = output_path {\n        // draw face landmarks result to image\n        let options = DrawLandmarksOptions::default()\n            .connections(FaceLandmarkConnections::get_connections(\n                \u0026FaceLandmarkConnections::FacemeshTesselation,\n            ))\n            .landmark_radius_percent(0.003);\n\n        for result in face_landmark_results.iter() {\n            result.draw_with_options(\u0026mut input_img, \u0026options);\n        }\n        // save output image\n        input_img.save(output_path)?;\n    }\n\n    Ok(())\n}\n```\n\nExample input: (The image is downloaded from https://storage.googleapis.com/mediapipe-assets/portrait.jpg)\n\n\u003cimg height=\"30%\" src=\"https://storage.googleapis.com/mediapipe-assets/portrait.jpg\" width=\"30%\" alt=\"face_detection_full_range_image.jpg\" /\u003e\n\nExample output in console:\n\n```console\n$ cargo run --release --example face_landmark -- ./assets/models/face_landmark/face_landmarker.task ./assets/testdata/img/face.jpg ./assets/doc/face_landmark_output.jpg\n\n    Finished release [optimized] target(s) in 4.50s\n     Running `./scripts/wasmedge-runner.sh target/wasm32-wasi/release/examples/face_landmark.wasm ./assets/models/face_landmark/face_landmarker.task ./assets/testdata/img/face.jpg ./assets/doc/face_landmark_output.jpg`\n\nFaceLandmarkResult #0\n  Landmarks:\n    Normalized Landmark #0:\n      x:       0.49687287\n      y:       0.24964334\n      z:       -0.029807145\n    Normalized Landmark #1:\n      x:       0.49801534\n      y:       0.22689381\n      z:       -0.05928771\n    Normalized Landmark #2:\n      x:       0.49707597\n      y:       0.23421054\n      z:       -0.03364953\n```\n\nExample output image:\n\u003cimg height=\"30%\" src=\"./assets/doc/face_landmark_output.jpg\" width=\"30%\"/\u003e\n\n### Audio Input\n\nEvery audio media which implements the trait ```AudioData``` can be used as audio tasks input.\nNow the library has builtin implementation to support ```symphonia```, ```ffmpeg```, and raw audio data as input.\n\nExamples for Audio Classification:\n\n```rust\nuse mediapipe_rs::tasks::audio::AudioClassifierBuilder;\n\n#[cfg(feature = \"ffmpeg\")]\nuse mediapipe_rs::preprocess::audio::FFMpegAudioData;\n#[cfg(not(feature = \"ffmpeg\"))]\nuse mediapipe_rs::preprocess::audio::SymphoniaAudioData;\n\n#[cfg(not(feature = \"ffmpeg\"))]\nfn read_audio_using_symphonia(audio_path: String) -\u003e SymphoniaAudioData {\n    let file = std::fs::File::open(audio_path).unwrap();\n    let probed = symphonia::default::get_probe()\n        .format(\n            \u0026Default::default(),\n            symphonia::core::io::MediaSourceStream::new(Box::new(file), Default::default()),\n            \u0026Default::default(),\n            \u0026Default::default(),\n        )\n        .unwrap();\n    let codec_params = \u0026probed.format.default_track().unwrap().codec_params;\n    let decoder = symphonia::default::get_codecs()\n        .make(codec_params, \u0026Default::default())\n        .unwrap();\n    SymphoniaAudioData::new(probed.format, decoder)\n}\n\n#[cfg(feature = \"ffmpeg\")]\nfn read_video_using_ffmpeg(audio_path: String) -\u003e FFMpegAudioData {\n    ffmpeg_next::init().unwrap();\n    FFMpegAudioData::new(ffmpeg_next::format::input(\u0026audio_path.as_str()).unwrap()).unwrap()\n}\n\nfn main() -\u003e Result\u003c(), Box\u003cdyn std::error::Error\u003e\u003e {\n    let (model_path, audio_path) = parse_args()?;\n\n    #[cfg(not(feature = \"ffmpeg\"))]\n        let audio = read_audio_using_symphonia(audio_path);\n    #[cfg(feature = \"ffmpeg\")]\n        let audio = read_video_using_ffmpeg(audio_path);\n\n    let classification_results = AudioClassifierBuilder::new()\n        .max_results(3) // set max result\n        .build_from_file(model_path)? // create a task instance\n        .classify(audio)?; // do inference and generate results\n\n    // show formatted result message\n    for c in classification_results {\n        println!(\"{}\", c);\n    }\n\n    Ok(())\n}\n```\n\n## Use the Session to speed up\n\nThe session includes inference sessions (such as TfLite interpreter), input and output buffers, etc.\nExplicitly using the session can reuse these resources to speed up.\n\n### Example: Text Classification\n\nOrigin :\n\n```rust\nuse mediapipe_rs::tasks::text::TextClassifier;\nuse mediapipe_rs::postprocess::ClassificationResult;\nuse mediapipe_rs::Error;\n\nfn inference(\n    text_classifier: \u0026TextClassifier,\n    inputs: \u0026Vec\u003cString\u003e\n) -\u003e Result\u003cVec\u003cClassificationResult\u003e, Error\u003e {\n    let mut res = Vec::with_capacity(inputs.len());\n    for input in inputs {\n        // text_classifier will create new session every time\n        res.push(text_classifier.classify(input.as_str())?);\n    }\n    Ok(res)\n}\n```\n\nUse the session to speed up:\n\n```rust\nuse mediapipe_rs::tasks::text::TextClassifier;\nuse mediapipe_rs::postprocess::ClassificationResult;\nuse mediapipe_rs::Error;\n\nfn inference(\n    text_classifier: \u0026TextClassifier,\n    inputs: \u0026Vec\u003cString\u003e\n) -\u003e Result\u003cVec\u003cClassificationResult\u003e, Error\u003e {\n    let mut res = Vec::with_capacity(inputs.len());\n    // only create one session and reuse the resources in session.\n    let mut session = text_classifier.new_session()?;\n    for input in inputs {\n        res.push(session.classify(input.as_str())?);\n    }\n    Ok(res)\n}\n```\n\n## Use the FFMPEG feature to process video and audio.\n\nWhen building the library with ```ffmpeg``` feature using cargo, users must set the following environment variables:\n\n* ```FFMPEG_DIR```: the pre-built FFmpeg library path. You can download it from\n  https://github.com/yanghaku/ffmpeg-wasm32-wasi/releases.\n* ```WASI_SDK``` or (```WASI_SYSROOT``` and ```CLANG_RT```), You can download it from\n  https://github.com/WebAssembly/wasi-sdk/releases\n* ```BINDGEN_EXTRA_CLANG_ARGS```: set **sysroot** and **target** and **function visibility** for libclang.\n  (The sysroot must be **absolute path**).\n\nExample:\n\n```shell\nexport FFMPEG_DIR=/path/to/ffmpeg/library\nexport WASI_SDK=/opt/wasi-sdk\nexport BINDGEN_EXTRA_CLANG_ARGS=\"--sysroot=/opt/wasi-sdk/share/wasi-sysroot --target=wasm32-wasi -fvisibility=default\"\n\n# Then run cargo\n```\n\n## GPU and TPU support\n\nThe default device is CPU, and user can use APIs to choose device to use:\n\n```rust\nuse mediapipe_rs::tasks::vision::ObjectDetectorBuilder;\n\nfn create_gpu(model_blob: Vec\u003cu8\u003e) {\n    let detector_gpu = ObjectDetectorBuilder::new()\n        .gpu()\n        .build_from_buffer(model_blob)\n        .unwrap();\n}\n\nfn create_tpu(model_blob: Vec\u003cu8\u003e) {\n    let detector_tpu = ObjectDetectorBuilder::new()\n        .tpu()\n        .build_from_buffer(model_blob)\n        .unwrap();\n}\n```\n\n## Notice\n\nThis work is made possible by **Google's work on [Mediapipe]**.\n\n## Related Links\n\n- [LFX Workspace: A Rust library crate for mediapipe models for WasmEdge NN](https://github.com/WasmEdge/WasmEdge/issues/2355)\n- [WasmEdge]\n- [MediaPipe]\n- [wasi-nn safe]\n- [wasi-nn specification]\n- [wasi-nn]\n\n[wasi-nn]: https://github.com/bytecodealliance/wasi-nn\n\n[wasi-nn specification]: https://github.com/WebAssembly/wasi-nn/\n\n[wasi-nn safe]: https://github.com/yanghaku/wasi-nn-safe\n\n[WasmEdge]: https://github.com/WasmEdge/WasmEdge\n\n[MediaPipe]: https://github.com/google/mediapipe\n\n[MediaPipe Solutions]: https://developers.google.com/mediapipe/solutions/\n\n[TF Hub]: https://tfhub.dev/\n\n## License\n\nThis project is licensed under the Apache 2.0 license. See [LICENSE] for more details.\n\n[LICENSE]: LICENSE\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwasmedge%2Fmediapipe-rs","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fwasmedge%2Fmediapipe-rs","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwasmedge%2Fmediapipe-rs/lists"}