├── .github ├── ISSUE_TEMPLATE │ ├── bug.md │ ├── build.md │ └── others.md └── workflows │ ├── macos-x86-cpu.yml │ ├── pylint-doc-check.yml │ └── ubuntu-x86-cpu.yml ├── .gitignore ├── .gitmodules ├── ACKNOWLEDGEMENTS ├── Cargo.lock ├── Cargo.toml ├── Dockerfile.github-dev ├── Dockerfile.github-release ├── LICENSE ├── README.md ├── README_zh.md ├── ci ├── build_doc.sh ├── cargo-config.github ├── doc_link_checker.py ├── run_check.sh └── run_github_docker_build.sh ├── docs ├── 01-how-to-build │ ├── appendix-A-build-options.md │ ├── build-from-source.zh.md │ ├── build-on-aarch64.zh.md │ ├── build-on-win10.zh.md │ └── build-with-docker.zh.md ├── 02-how-to-run │ ├── generate-rtsp.zh.md │ ├── run-in-15-minutes.en.md │ ├── run-in-15-minutes.zh.md │ └── workflow.png ├── 03-how-to-add-my-service │ ├── 01-quickstart.zh.md │ ├── 02-det-attr.zh.md │ ├── 03-batching-and-pipeline-test.zh.md │ ├── 04-web-visualization.zh.md │ ├── appendix-A-graph-definition.zh.md │ ├── appendix-B-python-plugin.zh.md │ └── appendix-C-dump-model.zh.md ├── FAQ.zh.md ├── Makefile ├── conf.py ├── download-models.zh.md ├── how-to-contribute.zh.md ├── how-to-debug.zh.md ├── how-to-pack-python-whl.zh.md ├── images │ ├── catfinder_image.png │ ├── catfinder_video.png │ ├── structure.png │ ├── visualize_deployment.jpg │ └── visualize_result.jpg ├── index.rst ├── make.bat └── requirements.txt ├── flow-debugger ├── Cargo.lock ├── Cargo.toml ├── debugger-ui │ ├── .gitignore │ ├── .prettierrc │ ├── package-lock.json │ ├── package.json │ ├── public │ │ ├── index.html │ │ └── robots.txt │ ├── src │ │ ├── App.css │ │ ├── App.js │ │ ├── AppHeader.js │ │ ├── AppHeader.module.css │ │ ├── app │ │ │ ├── actor.js │ │ │ ├── index.js │ │ │ └── store.js │ │ ├── component │ │ │ ├── button │ │ │ │ ├── Button.js │ │ │ │ └── Button.module.css │ │ │ ├── input │ │ │ │ ├── Input.js │ │ │ │ └── Input.module.css │ │ │ ├── line_with_descp │ │ │ │ ├── LineWithDescp.js │ │ │ │ └── LineWithDescp.module.css │ │ │ ├── resizable_area │ │ │ │ ├── ResizableArea.js │ │ │ │ └── ResizableArea.module.css │ │ │ └── tab │ │ │ │ ├── Tab.js │ │ │ │ └── Tab.module.css │ │ ├── event_listener.js │ │ ├── index.css │ │ ├── index.js │ │ ├── page │ │ │ ├── chart │ │ │ │ ├── Chart.js │ │ │ │ ├── Chart.module.css │ │ │ │ └── ChartSlice.js │ │ │ ├── editor │ │ │ │ ├── Editor.js │ │ │ │ └── EditorSlice.js │ │ │ ├── empty │ │ │ │ ├── Empty.js │ │ │ │ └── Empty.module.css │ │ │ ├── index.js │ │ │ ├── text │ │ │ │ ├── Text.js │ │ │ │ ├── Text.module.css │ │ │ │ └── TextSlice.js │ │ │ └── topology │ │ │ │ ├── Topology.js │ │ │ │ ├── TopologySlice.js │ │ │ │ └── VisGraph.js │ │ ├── parser.js │ │ ├── serviceWorker.js │ │ ├── types.js │ │ └── websocket.js │ └── yarn.lock └── src │ └── main.rs ├── flow-derive ├── Cargo.toml ├── README.md ├── examples │ └── node_derive.rs └── src │ ├── actor.rs │ ├── internal.rs │ ├── lib.rs │ ├── lit.rs │ ├── node.rs │ ├── ports.rs │ ├── resource.rs │ ├── type_name.rs │ └── utils.rs ├── flow-message ├── Cargo.lock ├── Cargo.toml └── src │ ├── c.rs │ ├── cross │ ├── cow_list.rs │ ├── cow_map.rs │ ├── data │ │ ├── list.rs │ │ ├── mod.rs │ │ └── value.rs │ ├── list.rs │ ├── map.rs │ ├── mod.rs │ └── python │ │ ├── cow_list.rs │ │ ├── cow_map.rs │ │ ├── iterator.rs │ │ ├── list.rs │ │ ├── map.rs │ │ └── mod.rs │ └── lib.rs ├── flow-plugins ├── Cargo.toml ├── examples │ ├── bytes_server.toml │ ├── image_input.toml │ ├── image_server.toml │ ├── video_input.toml │ └── video_server.toml └── src │ ├── bytes_server.rs │ ├── image_input.rs │ ├── image_server.rs │ ├── lib.rs │ ├── utils │ ├── args_parser.rs │ ├── bare_json.rs │ ├── bytes.rs │ ├── codec.rs │ ├── either.rs │ ├── error.rs │ ├── find_lib.rs │ ├── frame.rs │ ├── image.rs │ ├── mod.rs │ └── multipart.rs │ ├── video_input.rs │ └── video_server.rs ├── flow-python ├── Cargo.toml ├── examples │ ├── application │ │ ├── cat_finder │ │ │ ├── README.md │ │ │ ├── __init__.py │ │ │ ├── det.py │ │ │ ├── image_cpu.toml │ │ │ ├── image_gpu.toml │ │ │ ├── images │ │ │ │ ├── cat_finder_image_result.jpg │ │ │ │ ├── cat_finder_image_select.jpg │ │ │ │ └── cat_finder_video_select.jpg │ │ │ ├── redis_proxy.py │ │ │ ├── reid_image.py │ │ │ ├── reid_video.py │ │ │ ├── shaper.py │ │ │ ├── track.py │ │ │ ├── video_cpu.toml │ │ │ ├── video_gpu.toml │ │ │ ├── video_visualize.toml │ │ │ └── visualize │ │ │ │ ├── __init__.py │ │ │ │ ├── shaper_visualize.py │ │ │ │ └── visualize.py │ │ ├── electric_bicycle │ │ │ ├── README.md │ │ │ ├── __init__.py │ │ │ ├── det.py │ │ │ ├── electric_bicycle.toml │ │ │ ├── redis_proxy.py │ │ │ ├── shaper.py │ │ │ └── track.py │ │ ├── misc │ │ │ ├── bytes_client.py │ │ │ ├── dump_resnet.py │ │ │ ├── image_client.py │ │ │ ├── video_client.py │ │ │ └── visualize_client │ │ │ │ ├── demo.css │ │ │ │ ├── index.html │ │ │ │ └── push_video.py │ │ ├── requires.txt │ │ ├── simple_classification │ │ │ ├── __init__.py │ │ │ ├── classify.py │ │ │ ├── image_cpu.toml │ │ │ ├── image_test.toml │ │ │ ├── lite.py │ │ │ └── synset_words.txt │ │ ├── simple_det_classify │ │ │ ├── __init__.py │ │ │ ├── classify.py │ │ │ ├── det.py │ │ │ ├── lite.py │ │ │ ├── video_cpu.toml │ │ │ └── video_test.toml │ │ ├── utils.py │ │ ├── video_super_resolution │ │ │ ├── README.md │ │ │ ├── __init__.py │ │ │ ├── config.toml │ │ │ ├── lite.py │ │ │ ├── model.py │ │ │ ├── requires.sh │ │ │ └── requires.txt │ │ └── warehouse │ │ │ ├── detection_memd │ │ │ ├── README.md │ │ │ ├── __init__.py │ │ │ ├── main.py │ │ │ ├── onnx_model.py │ │ │ └── requirements.txt │ │ │ ├── detection_yolox │ │ │ ├── README.md │ │ │ ├── __init__.py │ │ │ ├── coco_classes.py │ │ │ ├── lite.py │ │ │ ├── process.py │ │ │ └── visualize.py │ │ │ ├── quality_naive │ │ │ ├── __init__.py │ │ │ └── quality.py │ │ │ ├── reid_alignedreid │ │ │ ├── README.md │ │ │ ├── __init__.py │ │ │ ├── dump.py │ │ │ ├── image │ │ │ │ ├── badcase_tiger.jpg │ │ │ │ ├── negative.jpg │ │ │ │ ├── pinggai_1.jpg │ │ │ │ ├── positive1.jpg │ │ │ │ ├── positive2.jpg │ │ │ │ └── positive_pinggai.jpg │ │ │ ├── lite.py │ │ │ ├── main.py │ │ │ ├── model.py │ │ │ ├── process.py │ │ │ └── resnet.py │ │ │ └── track_iou │ │ │ ├── __init__.py │ │ │ └── track_iou.py │ ├── basis_function │ │ ├── batch │ │ │ ├── __init__.py │ │ │ ├── batch.toml │ │ │ └── op.py │ │ ├── demux │ │ │ ├── demux.toml │ │ │ └── op.py │ │ ├── future │ │ │ ├── future.toml │ │ │ └── op.py │ │ ├── interactive │ │ │ └── main.py │ │ ├── map_reduce │ │ │ ├── map_reduce.toml │ │ │ └── op.py │ │ ├── nest │ │ │ ├── nest.toml │ │ │ └── op.py │ │ ├── profile │ │ │ ├── __init__.py │ │ │ ├── profile.png │ │ │ ├── profile.toml │ │ │ ├── sink.py │ │ │ ├── source.py │ │ │ └── transport.py │ │ └── unused │ │ │ ├── op.py │ │ │ └── unused.toml │ └── logical_test │ │ ├── __init__.py │ │ ├── buffer.py │ │ ├── logical_test.png │ │ ├── logical_test.toml │ │ ├── printer.py │ │ ├── process.py │ │ └── source.py ├── megflow │ ├── __init__.py │ ├── command_line.py │ ├── func_op.py │ └── registry.py ├── setup.py └── src │ └── lib.rs ├── flow-quickstart ├── Cargo.toml └── src │ ├── git.rs │ ├── lib.rs │ ├── log.rs │ └── main.rs ├── flow-rs ├── Cargo.toml ├── examples │ └── graph.rs ├── src │ ├── broker.rs │ ├── channel │ │ ├── error.rs │ │ ├── inner.rs │ │ ├── mod.rs │ │ ├── receiver.rs │ │ ├── sender.rs │ │ └── storage.rs │ ├── config │ │ ├── graphviz.rs │ │ ├── insert.rs │ │ ├── interlayer.rs │ │ ├── mod.rs │ │ ├── parser.rs │ │ ├── presentation.rs │ │ └── table.rs │ ├── debug │ │ ├── feature.rs │ │ ├── mod.rs │ │ ├── protocol.rs │ │ └── server.rs │ ├── envelope │ │ ├── any_envelope.rs │ │ ├── envelope.rs │ │ └── mod.rs │ ├── future.rs │ ├── graph │ │ ├── channel.rs │ │ ├── context.rs │ │ ├── debug.rs │ │ ├── mod.rs │ │ ├── node.rs │ │ └── subgraph.rs │ ├── helper │ │ ├── mod.rs │ │ └── python.rs │ ├── lib.rs │ ├── loader │ │ ├── mod.rs │ │ └── python │ │ │ ├── channel.rs │ │ │ ├── context.rs │ │ │ ├── envelope.rs │ │ │ ├── mod.rs │ │ │ ├── node.rs │ │ │ ├── port.rs │ │ │ ├── unlimited.rs │ │ │ └── utils.rs │ ├── node │ │ ├── bcast.rs │ │ ├── demux.rs │ │ ├── mod.rs │ │ ├── noop.rs │ │ ├── port.rs │ │ ├── reorder.rs │ │ ├── shared.rs │ │ └── transform.rs │ ├── registry.rs │ ├── resource │ │ ├── any_resource.rs │ │ ├── collection.rs │ │ ├── lazy.rs │ │ ├── mod.rs │ │ └── storage.rs │ └── sandbox.rs └── tests │ ├── 01-subgraph.rs │ ├── 02-dyn-subgraph.rs │ ├── 03-share-subgraph.rs │ ├── 04-isolated.rs │ └── nodes_ext.rs ├── logo.png └── pylintrc /.github/ISSUE_TEMPLATE/bug.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: "bug issue (╯°□°)╯︵ ┻━┻ " 3 | about: submit a bug report 4 | --- 5 | 6 | ## error log | 日志或报错信息 7 | 8 | ## context | 编译/运行环境 9 | 10 | ## how to reproduce | 复现步骤 11 | 1. 12 | 2. 13 | 3. 14 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/build.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: "compatibility issue ┬─┬ノ( ゜-゜ノ)" 3 | about: build error 4 | --- 5 | 6 | ## platform | 软硬件环境 7 | 8 | ## error log | 日志或报错信息 9 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/others.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: "others (Ծ‸ Ծ)" 3 | about: discussion, suggestion and question 4 | --- 5 | 6 | ## detail | 详细描述 7 | -------------------------------------------------------------------------------- /.github/workflows/macos-x86-cpu.yml: -------------------------------------------------------------------------------- 1 | name: macos-x86-cpu 2 | on: 3 | push: 4 | branches: [master] 5 | pull_request: 6 | branches: [master] 7 | jobs: 8 | build: 9 | name: macos-x86-cpu 10 | runs-on: macos-latest 11 | steps: 12 | - name: cancel-previous-runs 13 | uses: styfle/cancel-workflow-action@0.9.1 14 | with: 15 | access_token: ${{ secrets.GITHUB_TOKEN }} 16 | - uses: actions/checkout@v2 17 | - uses: actions-rs/toolchain@v1 18 | with: 19 | toolchain: stable 20 | - run: brew install yasm 21 | - run: brew install openssl@1.1 22 | - run: brew install pkg-config 23 | - uses: actions/setup-python@v2 24 | with: 25 | python-version: '3.8' 26 | architecture: 'x64' 27 | - run: cd $HOME && git clone https://github.com/tpoisonooo/rust-ffmpeg && cd rust-ffmpeg && cargo build --release 28 | - run: cat /tmp/megflow_ffmpeg_dynamic_link.sh 29 | - run: ls -alh `cat /tmp/megflow_ffmpeg_dynamic_link.sh | head -n 1`/lib/ 30 | - run: echo 'export FFMPEG_DIR=`cat ${HOME}/megflow_ffmpeg_dynamic_link.sh | head -n 1`' >> $HOME/myenv 31 | - run: echo 'export CARGO_FEATURE_PREBUILD="PREBUILD"' >> $HOME/myenv 32 | - run: echo 'export CARGO_FEATURE_DYNAMIC="DYNAMIC"' >> $HOME/myenv 33 | - run: echo 'export LD_LIBRARY_PATH=${FFMPEG_DIR}/lib:${LD_LIBRARY_PATH}' >> $HOME/myenv 34 | - run: echo 'export PKG_CONFIG_PATH=${FFMPEG_DIR}/lib/pkgconfig:${PKG_CONFIG_PATH}' >> $HOME/myenv 35 | - run: chmod +x $HOME/myenv 36 | - run: cat $HOME/myenv 37 | - run: . $HOME/myenv && cargo test --release 38 | - run: . $HOME/myenv && cd flow-python && python3.8 setup.py install --user && cd examples && megflow_run -p logical_test 39 | -------------------------------------------------------------------------------- /.github/workflows/pylint-doc-check.yml: -------------------------------------------------------------------------------- 1 | name: pylint-doc-check 2 | on: 3 | push: 4 | branches: [master] 5 | pull_request: 6 | branches: [master] 7 | jobs: 8 | build: 9 | name: ubuntu-x86-cpu 10 | runs-on: ubuntu-latest 11 | steps: 12 | - name: cancel-previous-runs 13 | uses: styfle/cancel-workflow-action@0.9.1 14 | with: 15 | access_token: ${{ secrets.GITHUB_TOKEN }} 16 | - uses: actions/checkout@v2 17 | - uses: actions/setup-python@v2 18 | with: 19 | python-version: '3.8' 20 | - run: ./ci/run_check.sh 21 | -------------------------------------------------------------------------------- /.github/workflows/ubuntu-x86-cpu.yml: -------------------------------------------------------------------------------- 1 | name: ubuntu-x86-cpu 2 | on: 3 | push: 4 | branches: [master] 5 | pull_request: 6 | branches: [master] 7 | jobs: 8 | build: 9 | name: ubuntu-x86-cpu 10 | runs-on: ubuntu-latest 11 | steps: 12 | - name: cancel-previous-runs 13 | uses: styfle/cancel-workflow-action@0.9.1 14 | with: 15 | access_token: ${{ secrets.GITHUB_TOKEN }} 16 | - uses: actions/checkout@v2 17 | - uses: actions-rs/toolchain@v1 18 | with: 19 | toolchain: stable 20 | - run: sudo apt update && sudo apt install yasm 21 | - run: sudo apt install -y libssl-dev 22 | - run: sudo apt update && sudo apt-get install -y pkg-config --fix-missing 23 | - run: sudo apt install -y libv4l-dev liblzma-dev 24 | - run: cd $HOME && git clone https://github.com/tpoisonooo/rust-ffmpeg && cd rust-ffmpeg && cargo build --release 25 | - run: echo 'export FFMPEG_DIR=`cat /tmp/megflow_ffmpeg_dynamic_link.sh | head -n 1`' >> $HOME/myenv 26 | - run: echo 'export CARGO_FEATURE_PREBUILD="PREBUILD"' >> $HOME/myenv 27 | - run: echo 'export CARGO_FEATURE_DYNAMIC="DYNAMIC"' >> $HOME/myenv 28 | - run: echo 'export LD_LIBRARY_PATH=${FFMPEG_DIR}/lib:${LD_LIBRARY_PATH}' >> $HOME/myenv 29 | - run: echo 'export PKG_CONFIG_PATH=${FFMPEG_DIR}/lib/pkgconfig:${PKG_CONFIG_PATH}' >> $HOME/myenv 30 | - run: chmod +x $HOME/myenv 31 | - run: sudo apt install python3.8-dev 32 | - run: . $HOME/myenv && cargo test --release 33 | - run: . $HOME/myenv && cd flow-python && python3 setup.py install --user && cd examples && megflow_run -p logical_test 34 | 35 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /gh-pages 2 | __pycache__ 3 | *.out 4 | *.pyc 5 | *.pid 6 | *.sock 7 | *~ 8 | target/ 9 | *.pyo 10 | *.so 11 | .eggs 12 | .tox 13 | dist 14 | build 15 | .pytest_cache 16 | *.egg-info 17 | setuptools_rust/version.py 18 | pyo3 19 | docs/_build 20 | *.dot 21 | .clippy.toml 22 | flow-python/examples/application/models 23 | flow-python/megflow/megflow_quickstart_inner 24 | flow-python/megflow/megflow_run_inner 25 | .vim 26 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MegEngine/MegFlow/41cb9f85828aa080f6004674904fe430d8268052/.gitmodules -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = [ 3 | "flow-rs", 4 | "flow-derive", 5 | "flow-plugins", 6 | "flow-debugger", 7 | "flow-message", 8 | "flow-quickstart", 9 | "flow-python", 10 | ] 11 | 12 | [profile.dev] 13 | incremental = false 14 | panic = 'abort' 15 | 16 | [profile.release] 17 | panic = 'abort' 18 | -------------------------------------------------------------------------------- /Dockerfile.github-dev: -------------------------------------------------------------------------------- 1 | FROM ubuntu:18.04 2 | 3 | WORKDIR /megflow 4 | 5 | # install requirements 6 | RUN apt-get update 7 | RUN apt-get install -y wget yasm clang git build-essential 8 | RUN apt install -y libssl-dev 9 | RUN apt update && apt-get install -y pkg-config --fix-missing 10 | RUN apt-get install -y curl 11 | RUN apt install -y libv4l-dev liblzma-dev 12 | 13 | # prepare cargo env 14 | ENV CARGO_HOME /cargo 15 | ENV RUSTUP_HOME /rustup 16 | RUN curl --proto '=https' --tlsv1.2 https://sh.rustup.rs -sSf -o run.sh \ 17 | && chmod a+x run.sh \ 18 | && ./run.sh -y && rm run.sh 19 | ENV PATH $PATH:/cargo/bin 20 | RUN chmod -R 777 /cargo /rustup 21 | COPY ci/cargo-config.github /cargo/config 22 | 23 | # copy workspace 24 | RUN mkdir -p $HOME/megflow-runspace 25 | WORKDIR $HOME/megflow-runspace 26 | 27 | # build conda env and activate 28 | RUN wget https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh 29 | RUN bash Miniconda3-latest-Linux-x86_64.sh -b -p /conda && rm Miniconda3-latest-Linux-x86_64.sh 30 | ENV PATH $PATH:/conda/bin 31 | RUN echo "PATH=${PATH}" >> ~/.bashrc 32 | RUN conda init 33 | 34 | COPY . $HOME/megflow-runspace/ 35 | 36 | # python install 37 | RUN cd flow-python && python3 setup.py install --user 38 | 39 | RUN export PATH=/root/.local/bin:${PATH} && cd flow-python/examples \ 40 | && megflow_run -p logical_test 41 | -------------------------------------------------------------------------------- /Dockerfile.github-release: -------------------------------------------------------------------------------- 1 | FROM megflow:latest as stage 2 | 3 | # build .whl 4 | RUN cd flow-python \ 5 | && python3 setup.py bdist_wheel -p linux-x86_64 -d ../dist 6 | 7 | # copy back to host 8 | FROM scratch AS export-stage 9 | COPY --from=stage /megflow-runspace/dist/ . 10 | -------------------------------------------------------------------------------- /ci/build_doc.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -ex 2 | 3 | rm -rf $1 4 | 5 | : ${TARGET_DIR:=/target/${CI_COMMIT_SHA}} 6 | 7 | cargo doc --no-deps 8 | 9 | echo '' > ${TARGET_DIR}/doc/index.html 10 | 11 | mv ${TARGET_DIR}/doc $1 12 | -------------------------------------------------------------------------------- /ci/cargo-config.github: -------------------------------------------------------------------------------- 1 | [source.crates-io] 2 | replace-with = 'tuna' 3 | 4 | [source.tuna] 5 | registry = "https://mirrors.tuna.tsinghua.edu.cn/git/crates.io-index.git" 6 | -------------------------------------------------------------------------------- /ci/run_check.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | 3 | python -m pip install pylint==2.5.2 requests 4 | 5 | CHECK_DIR="flow-python/examples/application/simple_classification" 6 | CHECK_DIR+=" flow-python/examples/application/simple_det_classify" 7 | CHECK_DIR+=" flow-python/examples/application/cat_finder" 8 | CHECK_DIR+=" flow-python/examples/application/electric_bicycle" 9 | CHECK_DIR+=" flow-python/examples/application/misc" 10 | 11 | pylint $CHECK_DIR 12 | 13 | python ci/doc_link_checker.py 14 | -------------------------------------------------------------------------------- /ci/run_github_docker_build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -ex 2 | 3 | python3 ci/convert_project_to_github.py 4 | 5 | docker build --rm -t megflow -f Dockerfile.github-dev . -------------------------------------------------------------------------------- /docs/01-how-to-build/appendix-A-build-options.md: -------------------------------------------------------------------------------- 1 | # Build options 2 | 3 | | cargo features | function | 4 | | --------- | ----------- | 5 | | open-camera | open camera via v4l2 on VideoServer | 6 | | no-default-features | build without rweb/ffmpeg/decoder | 7 | 8 | | environment | function | 9 | | --------- | ----------- | 10 | | CARGO_FEATURE_PREBUILD | use prebuild ffmpeg | 11 | | CARGO_FEATURE_STATIC | build static ffmpeg lib | 12 | | FFMPEG_DIR | specify prebuild ffmpeg dir | 13 | -------------------------------------------------------------------------------- /docs/01-how-to-build/build-from-source.zh.md: -------------------------------------------------------------------------------- 1 | # Building from Source 2 | 3 | ## 一、安装依赖 4 | 5 | ### 安装 Rust 6 | ```bash 7 | $ sudo apt install curl 8 | $ curl --proto '=https' --tlsv1.2 https://sh.rustup.rs -sSf | sh 9 | ``` 10 | 11 | 成功后,`cargo` 应该可以正常执行 12 | ```bash 13 | $ cargo --version 14 | cargo 1.56.0 (4ed5d137b 2021-10-04) 15 | ``` 16 | 17 | 如果不成功,提示`Command 'cargo' not found`,可以按照提示加载一下环境变量(重新连接或打开终端也可以): 18 | ``` 19 | source $HOME/.cargo/env 20 | ``` 21 | 22 | > `cargo` 是 Rust 的包管理器兼编译辅助工具。类似 Java maven/go pkg/C++ CMake 的角色,更易使用。 23 | 24 | ### 安装 python3.x (推荐 conda) 25 | 26 | 打开 [miniconda 官网](https://docs.conda.io/en/latest/miniconda.html) 下载 miniconda 安装包,修改权限并安装。 27 | 28 | ```bash 29 | $ wget https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh 30 | $ chmod a+x Miniconda3-latest-Linux-x86_64.sh 31 | $ ./Miniconda3-latest-Linux-x86_64.sh 32 | ``` 33 | 34 | 安装时接受 conda 修改默认 .bashrc 环境变量(zsh 用户还需自行修改 .zshrc 中的 conda initialize 配置)。成功后 `conda` 可正常运行 35 | ```bash 36 | $ conda --version 37 | conda 4.10.3 38 | ``` 39 | 40 | 创建一个 Python3.x(这里以 3.8 为例) 的环境,激活。 41 | ```bash 42 | $ conda create --name py38 python=3.8 43 | $ conda activate py38 44 | ``` 45 | 46 | ### 安装基础依赖库 47 | 48 | megflow的构建需要一些基础依赖库, 下面以ubuntu系统为例: 49 | ```bash 50 | $ sudo apt update 51 | $ sudo apt install -y wget yasm clang git build-essential 52 | $ sudo apt install -y libssl-dev 53 | $ sudo apt install -y pkg-config --fix-missing 54 | ``` 55 | 56 | 57 | ## 二、编译 58 | 59 | 编译并安装Python包到当前用户下 60 | 61 | ```bash 62 | $ git clone --recursive https://github.com/MegEngine/MegFlow --depth=1 63 | $ cd MegFlow/flow-python 64 | $ python3 setup.py install --user 65 | ``` 66 | 编译成功后,在 Python `import megflow` 正常。 67 | 68 | ## 三、Python“开机自检” 69 | ```bash 70 | $ cd examples 71 | $ megflow_run -p logical_test 72 | ``` 73 | `logical_test` 是 examples 下最基础的计算图测试用例,运行能正常结束表示 MegFlow 编译成功、基本语义无问题。 74 | 75 | `megflow_run` 是计算图的实现。编译完成之后不再需要 `cargo` 和 `Rust`,使用者只需要 76 | 77 | * `import megflow` 成功 78 | * `megflow_run -h` 正常 79 | 80 | ## 四、[编译选项](appendix-A-build-options.md) 81 | -------------------------------------------------------------------------------- /docs/01-how-to-build/build-on-aarch64.zh.md: -------------------------------------------------------------------------------- 1 | # aarch64 源码编译 2 | 3 | aarch64 源码编译方式和 [源码编译](build-from-source.zh.md) 相同,此处只说明差异。 4 | 5 | ## 环境差异 6 | 7 | 如果是华为鲲鹏 ARM 服务器 CentOS 系统, gcc 需要 >= 7.5 版本,系统默认的 `aarch64-redhat-linux-gcc 4.8.5` 缺 `__ARM_NEON` 会导致大量异常。 8 | ```bash 9 | $ yum install -y centos-release-scl 10 | $ yum install -y devtoolset-8-gcc devtoolset-8-gcc-c++ 11 | $ source /opt/rh/devtoolset-8/enable 12 | $ gcc --version 13 | gcc (GCC) 8.3.1 20190311 (Red Hat 8.3.1-3) 14 | ... 15 | ``` 16 | ## 软件差异 17 | 18 | conda 建议使用 `archiconda`,目前(2021.12.06) miniconda aarch64 官网版本在 KhadasVIM3/JetsonNao 上均会崩溃。`archiconda` 安装: 19 | ```bash 20 | $ wget https://github.com/Archiconda/build-tools/releases/download/0.2.3/Archiconda3-0.2.3-Linux-aarch64.sh 21 | $ chmod +x Archiconda3-0.2.3-Linux-aarch64.sh && ./Archiconda3-0.2.3-Linux-aarch64.sh 22 | ``` 23 | -------------------------------------------------------------------------------- /docs/01-how-to-build/build-on-win10.zh.md: -------------------------------------------------------------------------------- 1 | # Build on win10 2 | 3 | ## 下载模型包 4 | docker 运行方式,建议把模型包下好,解压备用。[下载地址](../download-models.zh.md) 5 | 6 | ## 安装 wsl2 7 | 8 | [安装文档](https://docs.microsoft.com/zh-cn/windows/wsl/install-win10) 已经非常详细,核心是安装 Linux 内核更新包。完成后第 6 步中的 Linux 分发应该可以正常运行。 9 | 10 | ## 安装 docker 11 | 下载 [windows docker 客户端](https://www.docker.com/products/docker-desktop) 并安装。docker 依赖 wsl2,Docker Desktop 启动正常没有报 fail 即可。 12 | 13 | ## 安装 git 14 | 15 | 下载安装 [git 客户端](https://git-scm.com/downloads) 并运行 Git Bash。 16 | 17 | ```bash 18 | $ pwd 19 | /c/Users/username 20 | $ cd /d # 切换到合适的盘符 21 | $ git clone https://github.com/MegEngine/MegFlow 22 | ... 23 | $ cd MegFlow 24 | $ docker build -t megflow . 25 | ... # 等待镜像完成,取决于网络和 CPU 26 | ``` 27 | > 注意:**不要移动 Dockerfile 文件的位置**。受 [EAR](https://www.federalregister.gov/documents/2019/10/09/2019-22210/addition-of-certain-entities-to-the-entity-list) 约束,MegFlow 无法提供现成的 docker 镜像,需要自己 build 出来,这个过程用了相对路径。 28 | 29 | ## 运行 30 | 31 | ```bash 32 | $ docker images 33 | REPOSITORY TAG IMAGE ID CREATED SIZE 34 | megflow latest c65e37e1df6c 18 hours ago 5.05GB 35 | ``` 36 | 直接用 ${IMAGE ID} 进入开始跑应用,挂载上之前下载好的模型包 37 | ```bash 38 | $ docker run -p 18081:8081 -p 18082:8082 -v ${DOWNLOAD_MODEL_PATH}:/megflow-runspace/flow-python/examples/models -i -t c65e37e1df6c /bin/bash 39 | ``` 40 | -------------------------------------------------------------------------------- /docs/01-how-to-build/build-with-docker.zh.md: -------------------------------------------------------------------------------- 1 | # Building with docker 2 | 3 | docker build 方式能够“可复现地”生成运行环境、减少依赖缺失的痛苦 4 | 5 | ## 下载模型包 6 | docker 运行方式,建议把模型包下好,解压备用。[下载地址](../download-models.zh.md) 7 | 8 | ## 编译 Docker 镜像 9 | 10 | ```bash 11 | $ cd MegFlow 12 | $ docker build -t megflow -f Dockerfile.github-dev . 13 | ``` 14 | 稍等一段时间(取决于网络和 CPU)镜像构建完成并做了基础自测 15 | > 注意:**不要移动 Dockerfile 文件的位置**。受 [EAR](https://www.federalregister.gov/documents/2019/10/09/2019-22210/addition-of-certain-entities-to-the-entity-list) 约束,MegFlow 无法提供现成的 docker 镜像,需要自己 build 出来,这个过程用了相对路径。 16 | ```bash 17 | $ docker images 18 | REPOSITORY TAG IMAGE ID CREATED SIZE 19 | megflow latest c65e37e1df6c 18 hours ago 5.05GB 20 | ``` 21 | 直接用 ${IMAGE ID} 进入开始跑应用,挂载上之前下载好的模型包 22 | ```bash 23 | $ docker run -p 18081:8081 -p 18082:8082 -v ${DOWNLOAD_MODEL_PATH}:/megflow-runspace/flow-python/examples/models -i -t c65e37e1df6c /bin/bash 24 | ``` 25 | 26 | ## Python Built-in Applications 27 | 28 | 接下来开始运行好玩的 Python 应用 29 | 30 | * [猫猫围栏运行手册](https://github.com/MegEngine/MegFlow/tree/master/flow-python/examples/application/cat_finder) 31 | * 图片注册猫猫 32 | * 部署视频围栏,注册的猫离开围栏时会发通知 33 | * 未注册的不会提示 34 | * [电梯电瓶车告警](https://github.com/MegEngine/MegFlow/tree/master/flow-python/examples/application/electric_bicycle) 35 | * 电梯里看到电瓶车立即报警 36 | * [quickstart](../03-how-to-add-my-service/01-quickstart.zh.md) 37 | * 问答式创建自己的应用 38 | * [视频实时超分](https://github.com/MegEngine/MegFlow/tree/master/flow-python/examples/application/video_super_resolution) 39 | -------------------------------------------------------------------------------- /docs/02-how-to-run/workflow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MegEngine/MegFlow/41cb9f85828aa080f6004674904fe430d8268052/docs/02-how-to-run/workflow.png -------------------------------------------------------------------------------- /docs/03-how-to-add-my-service/02-det-attr.zh.md: -------------------------------------------------------------------------------- 1 | # 串联检测和分类 2 | 3 | 本文将在 [tutorial01](01-quickstart.zh.md) modelserving 的基础上扩展计算图:先检测、再扣图分类。对外提供视频解析服务。完整的代码在 flow-python/examples/simple_det_classify 目录。 4 | 5 | ## 移除分类预处理 6 | 7 | 见 [生成带预处理的模型](appendix-C-dump-model.zh.md) 8 | 9 | ## 准备检测模型 10 | 这里直接用现成的 YOLOX mge 模型。复用 [cat_finder 的检测](https://github.com/MegEngine/MegFlow/blob/master/flow-python/examples/application/cat_finder/det.py) 或者从 [YOLOX 官网](https://github.com/Megvii-BaseDetection/YOLOX/tree/main/demo/MegEngine/python) 下载最新版。 11 | 12 | ## 配置计算图 13 | `flow-python/examples` 增加 `simple_det_classify/video_cpu.toml` 14 | 15 | ```bash 16 | $ cat flow-python/examples/simple_det_classify/video_cpu.toml 17 | 18 | main = "tutorial_02" 19 | 20 | # 重资源结点要先声明 21 | [[nodes]] 22 | name = "det" 23 | ty = "Detect" 24 | model = "yolox-s" 25 | conf = 0.25 26 | nms = 0.45 27 | tsize = 640 28 | path = "models/simple_det_classify_models/yolox_s.mge" 29 | interval = 5 30 | visualize = 1 31 | device = "cpu" 32 | device_id = 0 33 | 34 | [[nodes]] 35 | name = "classify" 36 | ty = "Classify" 37 | path = "models/simple_det_classify_models/resnet18_preproc_inside.mge" 38 | device = "cpu" 39 | device_id = 0 40 | 41 | [[graphs]] 42 | name = "subgraph" 43 | inputs = [{ name = "inp", cap = 16, ports = ["det:inp"] }] 44 | outputs = [{ name = "out", cap = 16, ports = ["classify:out"] }] 45 | # 描述连接关系 46 | connections = [ 47 | { cap = 16, ports = ["det:out", "classify:inp"] }, 48 | ] 49 | 50 | ... 51 | # ty 改成 VdieoServer 52 | [[graphs.nodes]] 53 | name = "source" 54 | ty = "VideoServer" 55 | port = 8085 56 | 57 | ... 58 | ``` 59 | 想对上一期的配置,需要关注 3 点: 60 | * 视频流中的重资源结点,需要声明在 `[[graphs]]` 之外,因为多路视频需要复用这个结点。如果每一路都要启一个 det 结点,资源会爆掉 61 | * `connections` 不再是空白,因为两个结点要描述连接关系 62 | * Server 类型改成 `VideoServer`,告诉 UI 是要处理视频的 63 | 64 | ## 运行测试 65 | 66 | 运行服务 67 | ```bash 68 | $ cd flow-python/examples 69 | $ megflow_run -c simple_det_classify/video_cpu.toml -p simple_det_classify 70 | ``` 71 | ## 资源 72 | 73 | [此文档描述 graph toml 定义](appendix-A-graph-definition.zh.md) 74 | 75 | 76 | [此文档描述 Python node 接口定义](appendix-B-python-plugin.zh.md) -------------------------------------------------------------------------------- /docs/03-how-to-add-my-service/appendix-B-python-plugin.zh.md: -------------------------------------------------------------------------------- 1 | # Python Plugins 2 | 3 | 从一个最简单的例子开始 4 | ``` 5 | import megflow 6 | @megflow.register(name="alias", inputs=["inp"], outputs=["out"]) 7 | class Node: 8 | def __init__(self, name, args): 9 | pass 10 | def exec(self): 11 | envelope = self.inp.recv() 12 | msg = dowith(envelope.msg) 13 | self.out.send(envelope.repack(msg)) 14 | ``` 15 | 16 | 这其中有三部分内容: register装饰器,Node的构造函数,Node的执行函数 17 | 18 | 1. register装饰器 19 | - name: 若指定,则register所修饰插件重命名为name,默认为register所修饰类的类名 20 | - inputs: Node的输入列表,每个输入`input`都可以在`exec`方法中,通过`self.input`访问, 21 | - outputs: Node的输出列表,每个输出`output`都可以在`exec`方法中,通过`self.output`访问 22 | - exclusive: 默认为False, 调度模型是一个thread local的协程调度器, 若为True, 则将该任务安排到线程池中 23 | 24 | 2. Node的构造函数 25 | - name: 即参数文件中Node的name字段 26 | - args: 即参数文件中Node的剩余参数字段 27 | 28 | 3. Node的执行函数 29 | - 一个python插件的执行方法必须是命名为`exec`的零参成员方法 30 | - 对于在参数文件中该插件引用的资源`resource`, 可以在`exec`方法中,通过`self.resource`访问 31 | - 通过输入的`recv`方法取得输入消息,输入消息是`Envelope`对象,其`msg`成员即开发者真正需要读写的消息实例 32 | - `Envelope`语义为在图中流转的消息的相关信息,由于这些信息需要在图中被传递,所以开发者应该保持消息与`Envelope`的对应关系 33 | - 若一个`Envelope`携带的消息被拆分为多个消息,或者转换为另一个消息,应该通过`Envelope`的`repack`方法,将`Envelope`与消息关联起来 34 | - 通过输出的`send`方法发送输出消息,输出消息是`Envelope`对象 35 | 36 | MegFlow也提供了一系列异步工具 37 | 1. `yield_now()`, 让出当前任务的执行权 38 | 2. `sleep(dur)`, 使当前任务沉睡`dur`毫秒 39 | 3. `join(tasks)`, `tasks`参数是一个函数列表,`join`堵塞直到`tasks`中的函数都执行完毕 40 | 4. `create_future(callback)`, `callback`参数是一个函数, 默认值为None,`create_future`返回一个`(Future, Waker)`对象 41 | - `Future::wait`, 堵塞直到`Waker::wake`被调用,返回`Waker::wake(result)`传入的`result`参数 42 | -------------------------------------------------------------------------------- /docs/FAQ.zh.md: -------------------------------------------------------------------------------- 1 | # FAQ 2 | 3 | Q:`megflow_run -p logical_test ` 无法运行怎么办? 4 | 5 | A1:如果报错 `message: "No such file or directory" }'`,确认是否`cd flow-python/examples` 6 | 7 | A2:确认安装了 python 组件,即 `cd flow-python` 再 `python3 setup.py install --user` 8 | 9 | 还不行就提 issue。 10 | 11 | ___ 12 | Q:视频流跑一路没事。跑多个,内存爆了/显存爆了/模型加载多次是因为啥? 13 | 14 | A:参照 `cat_finder/video.toml`,把涉模型的 nodes 移到 `[[graphs]]`上面,让多个子图来共享。 15 | 16 | 每启动一个视频流就会启一个子图,如果`nodes`放到`[[graphs]]`里,20 路视频就会创建 20 套 nodes。 17 | ___ 18 | Q:如何修改服务端口号,8080 已被占用? 19 | 20 | A:以`cat_finder` 为例,端口配置在 `image.toml` 的 `port` 中。 21 | ___ 22 | Q:如何让 ImageServer 返回 json,而不是渲染后的图? 23 | 24 | A:ImageServer 默认返回 `envelope.msg["data"]`图像。如果要返回 json 需要改两处: 25 | * `image.toml` 的配置里增加 `response = "json"` 26 | * 最终结果用`self.out.send(envelope.repack(json.dumps(results)))`发送出去 27 | 28 | 框架既不知道哪些字段可序列化,也不知道要序列化那几个字段,因此需要调用方序列化成 `str`。代码可参照 `examaples/cat_finder/redis_proxy.py`。 29 | ___ 30 | Q:视频流为什么无法停止,调用了 stop 接口还是在处理? 31 | 32 | A:调用 stop 之后队列的 `push/put` 接口已经被关闭了,不能追加新的,但之前解好的帧还在队列里。需要把遗留的处理完、依次停止子图节点才完全结束。流不会调用 stop 即刻停止,实际上有延迟。 33 | ___ 34 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line, and also 5 | # from the environment for the first two. 6 | SPHINXOPTS ?= 7 | SPHINXBUILD ?= sphinx-build 8 | SOURCEDIR = . 9 | BUILDDIR = _build 10 | 11 | # Put it first so that "make" without argument is like "make help". 12 | help: 13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 14 | 15 | .PHONY: help Makefile 16 | 17 | # Catch-all target: route all unknown targets to Sphinx using the new 18 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 19 | %: Makefile 20 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 21 | 22 | -------------------------------------------------------------------------------- /docs/download-models.zh.md: -------------------------------------------------------------------------------- 1 | # 模型和测试数据下载 2 | 3 | | 应用名称 | 云盘 | google drive | 4 | | - | - | - | 5 | | 猫猫围栏、电动车检测 | 链接: https://pan.baidu.com/s/1ZLVBR0igJ1hL6PoYQDtByA 提取码: 8ajf | [google](https://drive.google.com/file/d/1EwMJFjNp2kuNglutoleZOVsqccSOW2Z4/view?usp=sharing) | 6 | | 视频超分 | 链接: https://pan.baidu.com/s/131Ul2A9DNxTXbatO1SGKFg?pwd=viy5 提取码: viy5 | [google](https://drive.google.com/file/d/1oyrVL20MODJOSf7BJ9T5OioE-ZaARDBC/view?usp=sharing) | 7 | 8 | 取最新的 models_xxx.zip,解压、软链为 examples/models 9 | 10 | ```bash 11 | $ wget ${URL}/modes.zip 12 | $ cd flow-python/examples/application 13 | $ ln -s ${DOWNLOAD_DIR}/models models 14 | ``` 15 | 16 | 如果有 MegFlow-models repo,可以直接 17 | 18 | ```bash 19 | $ cd MegFlow-models 20 | $ git-lfs update 21 | $ git lfs pull 22 | ``` 23 | -------------------------------------------------------------------------------- /docs/how-to-contribute.zh.md: -------------------------------------------------------------------------------- 1 | # 如何提交代码 2 | 3 | ## 一、fork 分支 4 | 在浏览器中打开 [MegFlow](https://github.com/MegEngine/MegFlow), `fork` 到自己的 repositories,例如 5 | ``` 6 | https://github.com/user/MegFlow 7 | ``` 8 | 9 | clone 项目到本地,添加官方 remote 并 fetch: 10 | ``` 11 | $ git clone https://github.com/user/MegFlow && cd MegFlow 12 | $ git remote add megvii https://github.com/MegEngine/MegFlow 13 | $ git fetch megvii 14 | ``` 15 | 对于 `git clone` 下来的项目,它现在有两个 remote,分别是 origin 和 megvii 16 | 17 | ``` 18 | $ git remote -v 19 | origin https://github.com/user/MegFlow (fetch) 20 | origin https://github.com/user/MegFlow (push) 21 | megvii https://github.com/MegEngine/MegFlow (fetch) 22 | megvii https://github.com/MegEngine/MegFlow (push) 23 | ``` 24 | origin 指向你 fork 的仓库地址;remote 即官方 repo。可以基于不同的 remote 创建和提交分支。 25 | 26 | 例如切换到官方 master 分支,并基于此创建自己的分支(命名尽量言简意赅。一个分支只做一件事,方便 review 和 revert) 27 | ``` 28 | $ git checkout MegEngine/master 29 | $ git checkout -b my-awesome-branch 30 | ``` 31 | 32 | 或创建分支时指定基于官方 master 分支: 33 | ``` 34 | $ git checkout -b fix-typo-in-document MegFlow/master 35 | ``` 36 | 37 | > `git fetch` 是从远程获取最新代码到本地。如果是第二次 pr MegFlow `git fetch megvii` 开始即可,不需要 `git remote add megvii`,也不需要修改 `github.com/user/MegFlow`。 38 | 39 | ## 二、代码习惯 40 | 为了增加沟通效率,reviewer 一般要求 contributor 遵从以下规则 41 | 42 | * 不能随意增删空行 43 | * tab 替换为 4 个空格 44 | * 若是新增功能或平台,需有对应测试用例 45 | * 文档放到`docs`目录下,中文用`.zh.md`做后缀;英文直接用`.md`后缀 46 | 47 | 开发完成后提交到自己的 repository 48 | ``` 49 | $ git commit -a 50 | $ git push origin my-awesome-branch 51 | ``` 52 | 推荐使用 [`commitizen`](https://pypi.org/project/commitizen/) 或 [`gitlint`](https://jorisroovers.com/gitlint/) 等工具格式化 commit message,方便事后检索海量提交记录 53 | 54 | ## 三、代码提交 55 | 浏览器中打开 [MegFlow pulls](https://github.com/MegEngine/MegFlow/pulls) ,此时应有此分支 pr 提示,点击 `Compare & pull request` 56 | 57 | * 标题**必须**是英文。未完成的分支应以 `WIP:` 开头,例如 `WIP: fix-typo` 58 | * 正文宜包含以下内容,中英不限 59 | * 内容概述和实现方式 60 | * 功能或性能测试 61 | * 测试结果 62 | 63 | 回到浏览器签署 CLA,所有 CI 测试通过后通知 reviewer merge 此分支。 -------------------------------------------------------------------------------- /docs/how-to-debug.zh.md: -------------------------------------------------------------------------------- 1 | # 如何 Debug 常见问题 2 | 3 | 一、`megflow_run` 无法启动服务,直接 core dump 报错退出 4 | 5 | 如果“Python 开机自检”的 `megflow_run -p logical_test` 能够正常结束,排查方向应该是 Python import error。调试方法举例 6 | ```bash 7 | $ gdb --args ./megflow_run -c electric_bicycle/electric_bicycle_cpu.toml -p electric_bicycle 8 | ... 9 | illegal instruction 10 | ... 11 | ``` 12 | 可以看到 crash 发生在哪个 import -------------------------------------------------------------------------------- /docs/how-to-pack-python-whl.zh.md: -------------------------------------------------------------------------------- 1 | # 打包成 Python .whl 2 | 3 | ## 作用 4 | 打成 whl 包,使用方直接安装即可,不再需要编译。 5 | 6 | ## 执行 7 | 8 | 现在使用 Dockerfile 生成各 python 版本 .whl 9 | 10 | ```bash 11 | $ cd ${MegFlow_dir} 12 | $ # 构造开发环境,安装依赖。已执行过 docker 编译可以跳过此步骤 13 | $ docker build -t megflow -f Dockerfile.github-dev . 14 | $ # 创建结果目录 15 | $ mkdir dist 16 | $ # docker 打包 whl 17 | $ # https://stackoverflow.com/questions/33377022/how-to-copy-files-from-dockerfile-to-host 18 | $ DOCKER_BUILDKIT=1 docker build -f Dockerfile.github-release --output dist . 19 | ``` 20 | 21 | **注意** COPY to host 需要: 22 | * Docker 19.03 以上版本 23 | * 需要 DOCKER_BUILDKIT 环境变量 24 | * 需要 --output 参数 -------------------------------------------------------------------------------- /docs/images/catfinder_image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MegEngine/MegFlow/41cb9f85828aa080f6004674904fe430d8268052/docs/images/catfinder_image.png -------------------------------------------------------------------------------- /docs/images/catfinder_video.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MegEngine/MegFlow/41cb9f85828aa080f6004674904fe430d8268052/docs/images/catfinder_video.png -------------------------------------------------------------------------------- /docs/images/structure.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MegEngine/MegFlow/41cb9f85828aa080f6004674904fe430d8268052/docs/images/structure.png -------------------------------------------------------------------------------- /docs/images/visualize_deployment.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MegEngine/MegFlow/41cb9f85828aa080f6004674904fe430d8268052/docs/images/visualize_deployment.jpg -------------------------------------------------------------------------------- /docs/images/visualize_result.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MegEngine/MegFlow/41cb9f85828aa080f6004674904fe430d8268052/docs/images/visualize_result.jpg -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | .. MegFlow documentation master file, created by 2 | sphinx-quickstart on Wed Mar 24 22:40:10 2021. 3 | You can adapt this file completely to your liking, but it should at least 4 | contain the root `toctree` directive. 5 | Welcome to MegFlow's documentation! 6 | =================================== 7 | 8 | 9 | *Please select a specific version of the document in the lower left corner of the page.* 10 | 11 | .. toctree:: 12 | :maxdepth: 1 13 | :caption: how to build and run 14 | :name: sec-introduction 15 | 16 | 01-how-to-build/build-with-docker.zh 17 | 01-how-to-build/build-from-source.zh 18 | 01-how-to-build/build-on-aarch64.zh 19 | 01-how-to-build/build-on-win10.zh 20 | 01-how-to-build/appendix-A-build-options.zh 21 | 02-how-to-run/generate-rtsp.zh 22 | 02-how-to-run/run-in-15-minutes.zh 23 | download-models.zh 24 | 25 | .. toctree:: 26 | :maxdepth: 1 27 | :caption: built-in applications 28 | :name: sec-introduction 29 | 30 | .. toctree:: 31 | :maxdepth: 1 32 | :caption: how to use 33 | :name: sec-how-to-use 34 | 35 | 03-how-to-add-my-service/01-quickstart.zh 36 | 03-how-to-add-my-service/02-det-attr.zh 37 | 03-how-to-add-my-service/03-batching-and-pipeline-test.zh 38 | 03-how-to-add-my-service/04-web-visualization.zh 39 | 03-how-to-add-my-service/appendix-A-graph-definition.zh 40 | 03-how-to-add-my-service/appendix-B-python-plugin.zh 41 | 03-how-to-add-my-service/appendix-C-dump-model.zh 42 | 43 | .. toctree:: 44 | :maxdepth: 1 45 | :caption: home 46 | 47 | FAQ.zh 48 | how-to-debug.zh 49 | how-to-contribute.zh 50 | how-to-pack-python-whl.zh 51 | 52 | -------------------------------------------------------------------------------- /docs/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | pushd %~dp0 4 | 5 | REM Command file for Sphinx documentation 6 | 7 | if "%SPHINXBUILD%" == "" ( 8 | set SPHINXBUILD=sphinx-build 9 | ) 10 | set SOURCEDIR=. 11 | set BUILDDIR=_build 12 | 13 | if "%1" == "" goto help 14 | 15 | %SPHINXBUILD% >NUL 2>NUL 16 | if errorlevel 9009 ( 17 | echo. 18 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx 19 | echo.installed, then set the SPHINXBUILD environment variable to point 20 | echo.to the full path of the 'sphinx-build' executable. Alternatively you 21 | echo.may add the Sphinx directory to PATH. 22 | echo. 23 | echo.If you don't have Sphinx installed, grab it from 24 | echo.http://sphinx-doc.org/ 25 | exit /b 1 26 | ) 27 | 28 | %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% 29 | goto end 30 | 31 | :help 32 | %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% 33 | 34 | :end 35 | popd 36 | -------------------------------------------------------------------------------- /docs/requirements.txt: -------------------------------------------------------------------------------- 1 | sphinx 2 | recommonmark 3 | sphinx_markdown_tables 4 | sphinx_rtd_theme 5 | -------------------------------------------------------------------------------- /flow-debugger/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "flow-debugger" 3 | version = "0.3.5" 4 | edition = "2018" 5 | authors = ["megvii"] 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | tokio = { version="1.10.0", features=["macros", "rt-multi-thread"] } 11 | warp = "0.3.1" 12 | -------------------------------------------------------------------------------- /flow-debugger/debugger-ui/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /flow-debugger/debugger-ui/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "tabWidth": 4 3 | } 4 | -------------------------------------------------------------------------------- /flow-debugger/debugger-ui/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "debugger-ui", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@codemirror/basic-setup": "^0.19.0", 7 | "@codemirror/legacy-modes": "^0.19.0", 8 | "@codemirror/state": "^0.19.1", 9 | "@codemirror/stream-parser": "^0.19.1", 10 | "@codemirror/theme-one-dark": "^0.19.0", 11 | "@codemirror/view": "^0.19.1", 12 | "@popperjs/core": "^2.9.3", 13 | "@reduxjs/toolkit": "^1.5.1", 14 | "@testing-library/jest-dom": "^4.2.4", 15 | "@testing-library/react": "^9.3.2", 16 | "@testing-library/user-event": "^7.1.2", 17 | "lodash": "^4.17.21", 18 | "react": "^17.0.2", 19 | "react-charts": "^3.0.0-beta.32", 20 | "react-dom": "^17.0.2", 21 | "react-popper": "^2.2.5", 22 | "react-redux": "^7.2.3", 23 | "react-scripts": "^2.0.0", 24 | "react-wait": "^0.3.0", 25 | "reactjs-popup": "^2.0.5", 26 | "rodemirror": "^1.6.3", 27 | "split-grid": "^1.0.11", 28 | "toml": "^3.0.0", 29 | "vis-data": "^7.1.2", 30 | "vis-network": "^9.0.5" 31 | }, 32 | "scripts": { 33 | "start": "react-scripts start", 34 | "build": "react-scripts build", 35 | "test": "react-scripts test", 36 | "eject": "react-scripts eject", 37 | "fix": "npx prettier --write ./src" 38 | }, 39 | "eslintConfig": { 40 | "extends": "react-app" 41 | }, 42 | "browserslist": { 43 | "production": [ 44 | ">0.2%", 45 | "not dead", 46 | "not op_mini all" 47 | ], 48 | "development": [ 49 | "last 1 chrome version", 50 | "last 1 firefox version", 51 | "last 1 safari version" 52 | ] 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /flow-debugger/debugger-ui/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 11 | Debugger UI 12 | 13 | 14 | 15 |
16 | 17 | 18 | -------------------------------------------------------------------------------- /flow-debugger/debugger-ui/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | -------------------------------------------------------------------------------- /flow-debugger/debugger-ui/src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | display: flex; 3 | height: 100vh; 4 | flex-direction: column; 5 | padding-bottom: 1em; 6 | align-items: center; 7 | justify-content: center; 8 | } 9 | 10 | .Area { 11 | width: 100%; 12 | min-width: 0; 13 | height: 100%; 14 | min-height: 0; 15 | } 16 | 17 | .BorderArea { 18 | display: flex; 19 | flex-direction: column; 20 | width: 100%; 21 | min-width: 0; 22 | height: 100%; 23 | min-height: 0; 24 | border: 4px solid #dedede; 25 | border-radius: 4px; 26 | } 27 | 28 | .tabClose { 29 | border: 1px solid #dedede; 30 | border-right: none; 31 | background-color: #d4dfe6; 32 | cursor: pointer; 33 | line-height: 1.5em; 34 | } 35 | 36 | .body { 37 | flex-grow: 1; 38 | overflow-y: auto; 39 | } 40 | -------------------------------------------------------------------------------- /flow-debugger/debugger-ui/src/AppHeader.module.css: -------------------------------------------------------------------------------- 1 | .AppHeader { 2 | display: flex; 3 | justify-content: flex-start; 4 | padding: 1.25em 0; 5 | font-size: 12px; 6 | width: 100%; 7 | } 8 | 9 | .AppHeader button:enabled { 10 | cursor: pointer; 11 | } 12 | 13 | .ButtonSet { 14 | margin-right: 0.5em; 15 | } 16 | 17 | AppHeaderSet:last-child { 18 | margin-right: 0; 19 | } 20 | 21 | .PopupContent { 22 | display: flex; 23 | flex-flow: column; 24 | justify-content: center; 25 | align-items: center; 26 | padding: 1.25em 0; 27 | font-size: 8px; 28 | width: auto; 29 | } 30 | 31 | .spinner { 32 | display: block; 33 | height: 1em; 34 | width: 1em; 35 | } 36 | -------------------------------------------------------------------------------- /flow-debugger/debugger-ui/src/app/actor.js: -------------------------------------------------------------------------------- 1 | import { createAction } from "@reduxjs/toolkit"; 2 | 3 | export const BrowserWidthChanged = "BROWSER_WIDTH_CHANGED"; 4 | export const browserWidthChanged = createAction( 5 | BrowserWidthChanged, 6 | (isSmall) => { 7 | return { 8 | payload: { 9 | isSmall, 10 | }, 11 | }; 12 | } 13 | ); 14 | 15 | export const StoreDebuggerPage = "STORE_DEBUGGER_PAGE"; 16 | export const storeDebuggerPage = createAction(StoreDebuggerPage, () => { 17 | return { 18 | payload: {}, 19 | }; 20 | }); 21 | 22 | export const RestoreDebuggerPage = "RESTORE_DEBUGGER_PAGE"; 23 | export const restoreDebuggerPage = createAction( 24 | RestoreDebuggerPage, 25 | (focus) => { 26 | return { 27 | payload: { 28 | focus, 29 | }, 30 | }; 31 | } 32 | ); 33 | 34 | export const ChangeMainPage = "CHANGE_MAIN_PAGE"; 35 | export const changeMainPage = createAction(ChangeMainPage, (focus) => { 36 | return { 37 | payload: { 38 | focus, 39 | }, 40 | }; 41 | }); 42 | 43 | export const ChangeStage = "CHANGE_STAGE"; 44 | export const changeStage = createAction(ChangeStage, (stage) => { 45 | return { 46 | payload: { 47 | stage, 48 | }, 49 | }; 50 | }); 51 | -------------------------------------------------------------------------------- /flow-debugger/debugger-ui/src/app/index.js: -------------------------------------------------------------------------------- 1 | import { store } from "./store"; 2 | import { browserWidthChanged } from "./actor"; 3 | 4 | const z = (evt) => store.dispatch(browserWidthChanged(evt.matches)); 5 | const maxWidthMediaQuery = window.matchMedia("(max-width: 1600px)"); 6 | z(maxWidthMediaQuery); 7 | maxWidthMediaQuery.addListener(z); 8 | 9 | export * from "./actor"; 10 | export * from "./store"; 11 | -------------------------------------------------------------------------------- /flow-debugger/debugger-ui/src/app/store.js: -------------------------------------------------------------------------------- 1 | import { configureStore } from "@reduxjs/toolkit"; 2 | import * as actor from "./actor"; 3 | import editor from "../page/editor/EditorSlice"; 4 | import topology from "../page/topology/TopologySlice"; 5 | import text from "../page/text/TextSlice"; 6 | import chart from "../page/chart/ChartSlice"; 7 | import { MainTabs, DebuggerTabs } from "../types"; 8 | 9 | export const store = configureStore({ 10 | reducer: { 11 | editor, 12 | topology, 13 | text, 14 | chart, 15 | isSmall: (state = false, action) => { 16 | switch (action.type) { 17 | case actor.BrowserWidthChanged: 18 | return action.payload.isSmall; 19 | default: 20 | return state; 21 | } 22 | }, 23 | main: (state = { focus: 0, page: "" }, action) => { 24 | switch (action.type) { 25 | case actor.ChangeMainPage: 26 | return { 27 | focus: action.payload.focus, 28 | page: MainTabs[action.payload.focus], 29 | }; 30 | default: 31 | return state; 32 | } 33 | }, 34 | dbg: (state = { focus: -1, page: "" }, action) => { 35 | switch (action.type) { 36 | case actor.StoreDebuggerPage: 37 | return { focus: -1, page: state.page }; 38 | case actor.RestoreDebuggerPage: 39 | return { 40 | focus: 0, 41 | page: 42 | action.payload.focus >= 0 43 | ? DebuggerTabs[action.payload.focus] 44 | : state.page, 45 | }; 46 | default: 47 | return state; 48 | } 49 | }, 50 | stage: (state = "", action) => { 51 | switch (action.type) { 52 | case actor.ChangeStage: 53 | return action.payload.stage; 54 | default: 55 | return state; 56 | } 57 | }, 58 | }, 59 | }); 60 | -------------------------------------------------------------------------------- /flow-debugger/debugger-ui/src/component/button/Button.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import styles from "./Button.module.css"; 3 | 4 | export const Button = ({ red, bold, icon, rightIcon, children, ...props }) => { 5 | const c = [styles.container]; 6 | 7 | if (bold) { 8 | c.push(styles.bold); 9 | } 10 | if (icon) { 11 | c.push(styles.hasLeftIcon); 12 | } 13 | if (rightIcon) { 14 | c.push(styles.hasRightIcon); 15 | } 16 | if (red) { 17 | c.push(styles.red); 18 | } 19 | 20 | return ( 21 | 30 | ); 31 | }; 32 | -------------------------------------------------------------------------------- /flow-debugger/debugger-ui/src/component/input/Input.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import styles from "./Input.module.css"; 3 | 4 | export const Input = ({ label, ...props }) => { 5 | return ( 6 |
7 | 8 | 14 |
15 | ); 16 | }; 17 | -------------------------------------------------------------------------------- /flow-debugger/debugger-ui/src/component/input/Input.module.css: -------------------------------------------------------------------------------- 1 | .inner { 2 | display: table-cell; 3 | margin: 10px; 4 | font-weight: bold; 5 | } 6 | 7 | .container { 8 | margin: 5px; 9 | display: table-row; 10 | } 11 | -------------------------------------------------------------------------------- /flow-debugger/debugger-ui/src/component/line_with_descp/LineWithDescp.js: -------------------------------------------------------------------------------- 1 | import React, { useMemo } from "react"; 2 | import styles from "./LineWithDescp.module.css"; 3 | import { Chart } from "react-charts"; 4 | 5 | export const LineWithDescp = ({ data, children, ...props }) => { 6 | const primaryAxis = useMemo( 7 | () => ({ 8 | getValue: (datum) => datum.primary, 9 | }), 10 | [] 11 | ); 12 | 13 | const secondaryAxes = useMemo( 14 | () => [ 15 | { 16 | getValue: (datum) => datum.secondary, 17 | }, 18 | ], 19 | [] 20 | ); 21 | let convert_data = []; 22 | for (const k in data) { 23 | convert_data.push({ 24 | label: k, 25 | data: data[k].map((v, i)=>{ 26 | return { 27 | primary: i, 28 | secondary: v, 29 | } 30 | }), 31 | }) 32 | } 33 | 34 | return ( 35 |
36 |
{children}
37 |
38 | 39 |
40 |
41 | ); 42 | }; 43 | -------------------------------------------------------------------------------- /flow-debugger/debugger-ui/src/component/line_with_descp/LineWithDescp.module.css: -------------------------------------------------------------------------------- 1 | .container { 2 | width: 100%; 3 | height: 300px; 4 | display: flex; 5 | justify-content: center; 6 | align-items: center; 7 | flex-direction: column; 8 | } 9 | 10 | .descp { 11 | white-space: nowrap; 12 | text-decoration: none; 13 | padding: 0 1.25em; 14 | height: 3em; 15 | font-weight: 700; 16 | color: #444; 17 | display: flex; 18 | width: 50%; 19 | } 20 | 21 | .line { 22 | flex-grow: 1; 23 | width: 50%; 24 | } 25 | -------------------------------------------------------------------------------- /flow-debugger/debugger-ui/src/component/resizable_area/ResizableArea.module.css: -------------------------------------------------------------------------------- 1 | .resizeable { 2 | width: 100%; 3 | min-width: 0; 4 | height: 100%; 5 | min-height: 0; 6 | } 7 | 8 | .container { 9 | display: flex; 10 | height: 100vh; 11 | flex-direction: column; 12 | padding-bottom: 1em; 13 | } 14 | 15 | .resizeableAreaRow { 16 | composes: resizeable; 17 | display: grid; 18 | grid-template-rows: 1fr 12px 1fr; 19 | } 20 | 21 | .resizeableAreaColumn { 22 | composes: resizeable; 23 | display: grid; 24 | grid-template-columns: 1fr 12px 1fr; 25 | } 26 | 27 | .resizeableHiddenAreaRow { 28 | composes: resizeable; 29 | display: grid; 30 | grid-template-rows: 1fr auto; 31 | } 32 | 33 | .resizeableHiddenAreaColumn { 34 | composes: resizeable; 35 | display: grid; 36 | grid-template-columns: 1fr auto; 37 | } 38 | 39 | .splitRowsGutter { 40 | display: flex; 41 | align-items: center; 42 | justify-content: center; 43 | cursor: row-resize; 44 | color: #fff; 45 | } 46 | 47 | .splitRowsGutterHandle { 48 | pointer-events: none; 49 | transform: rotate(90deg); 50 | } 51 | 52 | .splitColumnsGutter { 53 | display: flex; 54 | align-items: center; 55 | justify-content: center; 56 | cursor: col-resize; 57 | color: #fff; 58 | } 59 | -------------------------------------------------------------------------------- /flow-debugger/debugger-ui/src/component/tab/Tab.js: -------------------------------------------------------------------------------- 1 | import styles from "./Tab.module.css"; 2 | import React from "react"; 3 | 4 | const Tab = ({ focus, label, onClick, ...props }) => { 5 | return ( 6 | 13 | ); 14 | }; 15 | 16 | export const TabList = ({ focus, tabs, children = null }) => { 17 | let tabsLabel = tabs.map((tab, i) => ( 18 | 19 | )); 20 | return ( 21 |
22 | {tabsLabel} 23 | {children} 24 |
25 | ); 26 | }; 27 | -------------------------------------------------------------------------------- /flow-debugger/debugger-ui/src/component/tab/Tab.module.css: -------------------------------------------------------------------------------- 1 | .container { 2 | display: flex; 3 | width: 100%; 4 | height: 100%; 5 | flex-direction: column; 6 | } 7 | 8 | .tabs { 9 | display: flex; 10 | } 11 | 12 | .tab { 13 | flex: 1 1 auto; 14 | border: 1px solid #dedede; 15 | border-right: none; 16 | background-color: #dedede; 17 | cursor: pointer; 18 | line-height: 1.5em; 19 | } 20 | 21 | .tab:last-of-type { 22 | border-right: 1px solid #dedede; 23 | } 24 | 25 | .tabSelected { 26 | composes: tab; 27 | border-bottom: none; 28 | background-color: #f9ffff; 29 | cursor: default; 30 | } 31 | 32 | .tabSelected:focus { 33 | outline: none; 34 | background-color: #fff; 35 | } 36 | -------------------------------------------------------------------------------- /flow-debugger/debugger-ui/src/event_listener.js: -------------------------------------------------------------------------------- 1 | import { useState } from "react"; 2 | import { pin, unpin } from "./page/editor/EditorSlice"; 3 | import { update } from "./page/topology/TopologySlice"; 4 | import { update as update_err } from "./page/text/TextSlice"; 5 | import { changeMainPage, restoreDebuggerPage, changeStage } from "./app/actor"; 6 | import { ERROR, TOPOLOGY, EDITOR } from "./types"; 7 | import { parse } from "./parser"; 8 | import toml from "toml"; 9 | 10 | export default (dispatch, notify) => { 11 | const [parsed, setParsed] = useState({}); 12 | return [parsed, { 13 | _onerror: (e) => { 14 | dispatch(update_err(`WEBSOCKET ERROR: ${e}`)); 15 | dispatch(restoreDebuggerPage(ERROR)); 16 | notify('connect') 17 | notify('disconnect') 18 | }, 19 | _onclose: (e) => { 20 | dispatch(changeStage("")); 21 | dispatch(unpin()); 22 | dispatch(changeMainPage(EDITOR)); 23 | notify('connect') 24 | notify('disconnect') 25 | }, 26 | initialized: (msg) => { 27 | try { 28 | dispatch(pin(msg.graph)); 29 | const [for_viz, tmp] = parse(toml.parse(msg.graph)); 30 | setParsed(tmp); 31 | dispatch(update(for_viz)); 32 | dispatch(changeMainPage(TOPOLOGY)); 33 | } catch (e) { 34 | if (e.line) { 35 | dispatch( 36 | update_err( 37 | `ERROR: ${e.message} at lint ${e.line}, column ${e.column}` 38 | ) 39 | ); 40 | } else { 41 | dispatch(update_err(`ERROR: ${e.message}`)); 42 | } 43 | dispatch(restoreDebuggerPage(ERROR)); 44 | } 45 | notify('connect') 46 | }, 47 | terminated: (msg) => { 48 | notify('disconnect') 49 | }, 50 | }]; 51 | }; 52 | -------------------------------------------------------------------------------- /flow-debugger/debugger-ui/src/index.css: -------------------------------------------------------------------------------- 1 | textarea { 2 | font-family: "Open Sans", sans-serif; 3 | } 4 | 5 | html { 6 | box-sizing: border-box; 7 | } 8 | 9 | *, 10 | *:before, 11 | *:after { 12 | box-sizing: inherit; 13 | } 14 | 15 | body { 16 | overflow-y: hidden; 17 | background-color: #282c37; 18 | padding: 0 1em; 19 | font-family: "Open Sans", sans-serif; 20 | -webkit-font-smoothing: antialiased; 21 | -moz-osx-font-smoothing: grayscale; 22 | } 23 | 24 | code { 25 | font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", 26 | monospace; 27 | font-size: 0.9em; 28 | } 29 | -------------------------------------------------------------------------------- /flow-debugger/debugger-ui/src/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ReactDOM from "react-dom"; 3 | import "./index.css"; 4 | import { store } from "./app"; 5 | import App from "./App"; 6 | import { Provider } from "react-redux"; 7 | import * as serviceWorker from "./serviceWorker"; 8 | import { Waiter } from "react-wait"; 9 | 10 | ReactDOM.render( 11 | 12 | 13 | 14 | 15 | 16 | 17 | , 18 | document.getElementById("root") 19 | ); 20 | 21 | serviceWorker.register(); 22 | -------------------------------------------------------------------------------- /flow-debugger/debugger-ui/src/page/chart/Chart.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import styles from "./Chart.module.css"; 3 | import { useSelector } from "react-redux"; 4 | import { LineWithDescp } from "../../component/line_with_descp/LineWithDescp"; 5 | import { selectChartLines, selectChartFocus } from "./ChartSlice"; 6 | 7 | export const Chart = () => { 8 | const ids = useSelector(selectChartFocus); 9 | const lines = useSelector(selectChartLines(ids)); 10 | const labels = Object.values(lines).map((line, i)=> { 11 | return ( {line.descp} ) 12 | }); 13 | return ( 14 |
15 |
16 | { labels } 17 |
18 |
19 | ); 20 | }; 21 | -------------------------------------------------------------------------------- /flow-debugger/debugger-ui/src/page/chart/Chart.module.css: -------------------------------------------------------------------------------- 1 | .container { 2 | height: 100%; 3 | width: 100%; 4 | background-color: #f9ffff; 5 | } 6 | 7 | .list { 8 | border: 1px solid #dedede; 9 | border-top: none; 10 | border-bottom: none; 11 | padding: 0.5em; 12 | background-color: #f9ffff; 13 | display: flex; 14 | height: auto; 15 | width: 100%; 16 | justify-content: flex-start; 17 | flex-direction: column; 18 | } 19 | -------------------------------------------------------------------------------- /flow-debugger/debugger-ui/src/page/chart/ChartSlice.js: -------------------------------------------------------------------------------- 1 | import { createSlice } from "@reduxjs/toolkit"; 2 | 3 | export const chartSlice = createSlice({ 4 | name: "chart", 5 | initialState: { 6 | lines: {}, 7 | focus: [], 8 | }, 9 | reducers: { 10 | append: (state, elem) => { 11 | if (!(elem.payload.id in state.lines)) { 12 | state.lines[elem.payload.id] = { descp: elem.payload.descp, start: Date.now(), data: {} }; 13 | } 14 | let dst = state.lines[elem.payload.id].data; 15 | let start = state.lines[elem.payload.id].start; 16 | let src = elem.payload.data; 17 | 18 | let now = Date.now(); 19 | let dur = now - start; 20 | 21 | for (const k in src) { 22 | if (!(k in dst)) { 23 | dst[k] = []; 24 | } 25 | const len = dst[k].length; 26 | if (dur > 1000 || len === 0) { 27 | dst[k].push(src[k]); 28 | } else { 29 | dst[k][len-1] = Math.max(dst[k][len-1], src[k]); 30 | } 31 | if (len >= 60) { // TODO: remove the hardcode 32 | dst[k].shift(); 33 | } 34 | } 35 | if (dur > 1000) { // TODO: remove the hardcode 36 | state.lines[elem.payload.id].start = Date.now(); 37 | } 38 | }, 39 | reset: (state) => { 40 | state.lines = {}; 41 | state.focus = []; 42 | }, 43 | focus: (state, ids) => { 44 | state.focus = ids.payload; 45 | } 46 | }, 47 | }); 48 | 49 | export const { append, reset, focus } = chartSlice.actions; 50 | 51 | export const selectChartLines = (ids) => (state) => { 52 | let lines = {}; 53 | for (const id of ids) { 54 | if (id in state.chart.lines) { 55 | lines[id] = state.chart.lines[id]; 56 | } 57 | } 58 | return lines; 59 | }; 60 | 61 | export const selectChartFocus = (state) => state.chart.focus; 62 | 63 | export default chartSlice.reducer; 64 | -------------------------------------------------------------------------------- /flow-debugger/debugger-ui/src/page/editor/Editor.js: -------------------------------------------------------------------------------- 1 | import React, { useMemo } from "react"; 2 | import { useDispatch, useSelector } from "react-redux"; 3 | import CodeMirror from "rodemirror"; 4 | import { basicSetup } from "@codemirror/basic-setup"; 5 | import { oneDark } from "@codemirror/theme-one-dark"; 6 | import { StreamLanguage } from "@codemirror/stream-parser"; 7 | import { toml } from "@codemirror/legacy-modes/mode/toml"; 8 | import { update, selectEditorInit, selectEdit } from "./EditorSlice"; 9 | 10 | export const Editor = () => { 11 | const dispatch = useDispatch(); 12 | const init = useSelector(selectEditorInit); 13 | const edit = useSelector(selectEdit); 14 | const extensions = useMemo( 15 | () => [basicSetup, oneDark, StreamLanguage.define(toml)], 16 | [] 17 | ); 18 | return ( 19 | { 23 | if (v.docChanged && edit) { 24 | dispatch(update(v.state.doc.toString())); 25 | } 26 | }} 27 | /> 28 | ); 29 | }; 30 | -------------------------------------------------------------------------------- /flow-debugger/debugger-ui/src/page/editor/EditorSlice.js: -------------------------------------------------------------------------------- 1 | import { createSlice } from "@reduxjs/toolkit"; 2 | 3 | export const editorSlice = createSlice({ 4 | name: "editor", 5 | initialState: { 6 | init: "", 7 | changed: "", 8 | edit: true, 9 | }, 10 | reducers: { 11 | save: (state) => { 12 | state.init = state.changed; 13 | }, 14 | update: (state, changed) => { 15 | state.changed = changed.payload; 16 | }, 17 | pin: (state, overwrite) => { 18 | state.init = overwrite.payload; 19 | state.changed = overwrite.payload; 20 | state.edit = false; 21 | }, 22 | unpin: (state) => { 23 | state.edit = true; 24 | }, 25 | }, 26 | }); 27 | 28 | export const { save, update, pin, unpin } = editorSlice.actions; 29 | 30 | export const selectEditorInit = (state) => state.editor.init; 31 | export const selectEditorChanged = (state) => state.editor.changed; 32 | export const selectEdit = (state) => state.editor.edit; 33 | 34 | export default editorSlice.reducer; 35 | -------------------------------------------------------------------------------- /flow-debugger/debugger-ui/src/page/empty/Empty.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import styles from "./Empty.module.css"; 3 | 4 | export const Empty = (props) => { 5 | return
{props.children}
; 6 | }; 7 | -------------------------------------------------------------------------------- /flow-debugger/debugger-ui/src/page/empty/Empty.module.css: -------------------------------------------------------------------------------- 1 | .empty { 2 | height: 100%; 3 | width: 100%; 4 | padding: 0.5em; 5 | border: 1px solid #dedede; 6 | border-top: none; 7 | background-color: #f9ffff; 8 | } 9 | -------------------------------------------------------------------------------- /flow-debugger/debugger-ui/src/page/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { useSelector } from "react-redux"; 3 | import { Empty } from "./empty/Empty"; 4 | import { Text } from "./text/Text"; 5 | import { Topology } from "./topology/Topology"; 6 | import { Editor } from "./editor/Editor"; 7 | import { Chart } from "./chart/Chart"; 8 | 9 | export const MainSwitcher = () => { 10 | const page = useSelector((state) => state.main.page); 11 | switch (page) { 12 | case "topology": 13 | return ; 14 | case "editor": 15 | return ; 16 | default: 17 | return ; 18 | } 19 | }; 20 | 21 | export const DebuggerSwitcher = () => { 22 | const page = useSelector((state) => state.dbg.page); 23 | 24 | switch (page) { 25 | case "error": 26 | return ; 27 | case "chart": 28 | return ; 29 | default: 30 | return ; 31 | } 32 | }; 33 | -------------------------------------------------------------------------------- /flow-debugger/debugger-ui/src/page/text/Text.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import styles from "./Text.module.css"; 3 | import { useSelector } from "react-redux"; 4 | import { selectTextMessage } from "./TextSlice"; 5 | 6 | export const Text = () => { 7 | const message = useSelector(selectTextMessage); 8 | return ( 9 |
10 |

{message}

11 |
12 | ); 13 | }; 14 | -------------------------------------------------------------------------------- /flow-debugger/debugger-ui/src/page/text/Text.module.css: -------------------------------------------------------------------------------- 1 | .text { 2 | height: 100%; 3 | width: 100%; 4 | padding: 0.5em; 5 | border: 1px solid #dedede; 6 | border-top: none; 7 | background-color: #f9ffff; 8 | } 9 | 10 | .text h1 { 11 | margin: 1em; 12 | color: rgb(214, 122, 127); 13 | } 14 | -------------------------------------------------------------------------------- /flow-debugger/debugger-ui/src/page/text/TextSlice.js: -------------------------------------------------------------------------------- 1 | import { createSlice } from "@reduxjs/toolkit"; 2 | 3 | export const textSlice = createSlice({ 4 | name: "text", 5 | initialState: { 6 | message: "", 7 | }, 8 | reducers: { 9 | update: (state, changed) => { 10 | state.message = changed.payload; 11 | }, 12 | }, 13 | }); 14 | 15 | export const { update } = textSlice.actions; 16 | 17 | export const selectTextMessage = (state) => state.text.message; 18 | 19 | export default textSlice.reducer; 20 | -------------------------------------------------------------------------------- /flow-debugger/debugger-ui/src/page/topology/Topology.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { useSelector, useDispatch } from "react-redux"; 3 | import { selectDescp, selectListen } from "./TopologySlice"; 4 | import Graph from "./VisGraph"; 5 | import { restoreDebuggerPage, storeDebuggerPage } from "../../app/actor"; 6 | import { CHART } from "../../types"; 7 | import { focus } from "../chart/ChartSlice"; 8 | 9 | const options = { 10 | autoResize: true, 11 | width: "100%", 12 | height: "100%", 13 | layout: { 14 | randomSeed: 0, 15 | hierarchical: false, 16 | }, 17 | edges: { 18 | color: "#ffffff", 19 | }, 20 | physics: { 21 | enabled: false, 22 | }, 23 | }; 24 | 25 | export const Topology = () => { 26 | let dispatch = useDispatch(); 27 | let descp = useSelector(selectDescp); 28 | let listen = useSelector(selectListen); 29 | let edges = descp.edges.map((edge) => { 30 | return { ...edge }; 31 | }); // workaround for graph-viz 32 | let events = listen ? { 33 | click: (e) => { 34 | if (e.nodes.length === 1) { 35 | let id = e.nodes[0]; 36 | let ports = descp.nodes.find(node=>node.id === id).ports; 37 | let ids = ports.map(port=>`${id}#${port}`); 38 | dispatch(focus(ids)); 39 | dispatch(restoreDebuggerPage(CHART)); 40 | } 41 | }, 42 | deselectNode: (e) => { 43 | if (e.nodes.length === 0) { 44 | dispatch(storeDebuggerPage()) 45 | } 46 | }, 47 | } : {}; 48 | return ( 49 | 55 | ); 56 | }; 57 | -------------------------------------------------------------------------------- /flow-debugger/debugger-ui/src/page/topology/TopologySlice.js: -------------------------------------------------------------------------------- 1 | import { createSlice } from "@reduxjs/toolkit"; 2 | 3 | export const topologySlice = createSlice({ 4 | name: "topology", 5 | initialState: { 6 | descp: { 7 | nodes: [], 8 | edges: [], 9 | }, 10 | listen: false, 11 | }, 12 | reducers: { 13 | update: (state, descp) => { 14 | state.descp = { 15 | nodes: descp.payload.nodes || [], 16 | edges: descp.payload.edges || [], 17 | }; 18 | }, 19 | setBlock: (state, ids) => { 20 | for (let node of state.descp.nodes) { 21 | if (ids.payload.includes(node.id)) { 22 | node.color = '#ff0000'; 23 | node.font = { color: "#fff" }; 24 | } else if (node.title) { 25 | node.color = '#fff'; 26 | node.font = { color: "#000" }; 27 | } 28 | } 29 | }, 30 | listen: (state, listen) => { 31 | state.listen = listen.payload; 32 | } 33 | }, 34 | }); 35 | 36 | export const { update, setBlock, listen } = topologySlice.actions; 37 | 38 | export const selectDescp = (state) => state.topology.descp; 39 | export const selectListen = (state) => state.topology.listen; 40 | 41 | export default topologySlice.reducer; 42 | -------------------------------------------------------------------------------- /flow-debugger/debugger-ui/src/types.js: -------------------------------------------------------------------------------- 1 | export const Orientation = { 2 | Horizontal: "horizontal", 3 | Vertical: "vertical", 4 | }; 5 | 6 | export const MainTabs = ["editor", "topology"]; 7 | export const EDITOR = 0; 8 | export const TOPOLOGY = 1; 9 | 10 | export const DebuggerTabs = ["empty", "error", "chart"]; 11 | export const EMPTY = 0; 12 | export const ERROR = 1; 13 | export const CHART = 2; 14 | -------------------------------------------------------------------------------- /flow-debugger/src/main.rs: -------------------------------------------------------------------------------- 1 | #[tokio::main] 2 | async fn main() { 3 | warp::serve(warp::fs::dir("debugger-ui/build")) 4 | .run(([0, 0, 0, 0], 3000)) 5 | .await; 6 | } 7 | -------------------------------------------------------------------------------- /flow-derive/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "flow-derive" 3 | version = "0.3.5" 4 | authors = ["megvii"] 5 | edition = "2018" 6 | 7 | [lib] 8 | proc-macro = true 9 | 10 | [dependencies] 11 | quote = "1" 12 | syn = "1" 13 | proc-macro2 = {version ="1", features= ["span-locations"]} 14 | anyhow = "1.0.38" 15 | 16 | [dev-dependencies] 17 | flow-rs = { path = "../flow-rs" } 18 | toml = "0.5.8" 19 | -------------------------------------------------------------------------------- /flow-derive/README.md: -------------------------------------------------------------------------------- 1 | # flow-derive 2 | 3 | ### Q & A 4 | 1. Proc macro attributes are not yet supported in rust analyzer, and here is a workaround way. 5 | ```json 6 | "rust-analyzer.diagnostics.disabled": [ 7 | "no-such-field" 8 | ] 9 | ``` 10 | -------------------------------------------------------------------------------- /flow-derive/examples/node_derive.rs: -------------------------------------------------------------------------------- 1 | /** 2 | * \file flow-derive/examples/node_derive.rs 3 | * MegFlow is Licensed under the Apache License, Version 2.0 (the "License") 4 | * 5 | * Copyright (c) 2019-2021 Megvii Inc. All rights reserved. 6 | * 7 | * Unless required by applicable law or agreed to in writing, 8 | * software distributed under the License is distributed on an 9 | * "AS IS" BASIS, WITHOUT ARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | */ 11 | use anyhow::Result; 12 | use flow_rs::prelude::*; 13 | 14 | #[allow(dead_code)] 15 | #[inputs(first, second:dyn, third:[], fourth:{})] 16 | #[outputs(front, last:dyn)] 17 | #[derive(Node, Actor, Default)] 18 | struct SubNode {} 19 | 20 | impl SubNode { 21 | fn new(_: String, _args: &toml::value::Table) -> Self { 22 | SubNode { 23 | ..Default::default() 24 | } 25 | } 26 | 27 | async fn initialize(&mut self, _: ResourceCollection) {} 28 | async fn finalize(&mut self) {} 29 | 30 | async fn exec(&mut self, _: &Context) -> Result<()> { 31 | let _: Envelope = self.first.recv::().await.unwrap(); 32 | Ok(()) 33 | } 34 | } 35 | 36 | node_register!("sub", SubNode); 37 | 38 | fn main() {} 39 | -------------------------------------------------------------------------------- /flow-derive/src/lit.rs: -------------------------------------------------------------------------------- 1 | /** 2 | * \file flow-derive/src/lit.rs 3 | * MegFlow is Licensed under the Apache License, Version 2.0 (the "License") 4 | * 5 | * Copyright (c) 2019-2021 Megvii Inc. All rights reserved. 6 | * 7 | * Unless required by applicable law or agreed to in writing, 8 | * software distributed under the License is distributed on an 9 | * "AS IS" BASIS, WITHOUT ARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | */ 11 | use proc_macro2::Span; 12 | use std::fmt::Display; 13 | 14 | use syn::{Ident, LitStr}; 15 | 16 | pub fn string(lit: T) -> LitStr { 17 | LitStr::new(lit.to_string().as_str(), Span::call_site()) 18 | } 19 | 20 | pub fn ident(lit: impl AsRef) -> Ident { 21 | Ident::new(lit.as_ref(), Span::call_site()) 22 | } 23 | -------------------------------------------------------------------------------- /flow-derive/src/resource.rs: -------------------------------------------------------------------------------- 1 | use proc_macro2::TokenStream; 2 | use quote::quote; 3 | use syn::parse::{Parse, ParseStream, Result}; 4 | use syn::Token; 5 | 6 | pub struct ResourceDefine { 7 | name: syn::Expr, 8 | ty: syn::Type, 9 | } 10 | impl Parse for ResourceDefine { 11 | fn parse(input: ParseStream) -> Result { 12 | let name: syn::Expr = input.parse()?; 13 | input.parse::()?; 14 | let ty: syn::Type = input.parse()?; 15 | Ok(ResourceDefine { name, ty }) 16 | } 17 | } 18 | 19 | pub fn registry_expand(input: ResourceDefine) -> TokenStream { 20 | let name = &input.name; 21 | let ty = &input.ty; 22 | quote! { 23 | flow_rs::submit!(#name.to_owned(), 24 | flow_rs::node::ResourceSlice{ 25 | cons: Box::new(|name: String, args: &toml::value::Table| Box::new(<#ty>::new(name, args))), 26 | } 27 | ); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /flow-derive/src/type_name.rs: -------------------------------------------------------------------------------- 1 | /** 2 | * \file flow-derive/src/type_name.rs 3 | * MegFlow is Licensed under the Apache License, Version 2.0 (the "License") 4 | * 5 | * Copyright (c) 2019-2021 Megvii Inc. All rights reserved. 6 | * 7 | * Unless required by applicable law or agreed to in writing, 8 | * software distributed under the License is distributed on an 9 | * "AS IS" BASIS, WITHOUT ARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | */ 11 | 12 | pub const IN_T: &str = "Receiver"; 13 | pub const OUT_T: &str = "Sender"; 14 | -------------------------------------------------------------------------------- /flow-message/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "flow-message" 3 | version = "0.3.5" 4 | edition = "2018" 5 | 6 | [features] 7 | cross = ["byteorder", "dashmap", "enum-as-inner", "im", "paste", "pyo3"] 8 | c = ["libc"] 9 | 10 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 11 | 12 | [dependencies] 13 | byteorder = {version="1.4", optional=true} 14 | dashmap = {version="4.0", optional=true} 15 | enum-as-inner = {version="0.3", optional=true} 16 | im = {version="15.0", optional=true} 17 | paste = {version="1.0",optional=true} 18 | pyo3 = {version="0.15", features=["abi3"], optional=true} 19 | libc = {version="0.2",optional=true} 20 | -------------------------------------------------------------------------------- /flow-message/src/c.rs: -------------------------------------------------------------------------------- 1 | /** 2 | * \file flow-message/c.rs 3 | * MegFlow is Licensed under the Apache License, Version 2.0 (the "License") 4 | * 5 | * Copyright (c) 2019-2021 Megvii Inc. All rights reserved. 6 | * 7 | * Unless required by applicable law or agreed to in writing, 8 | * software distributed under the License is distributed on an 9 | * "AS IS" BASIS, WITHOUT ARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | */ 11 | 12 | #[repr(C)] 13 | pub struct CMessage { 14 | pub ptr: *mut libc::c_void, 15 | pub clone_func: extern "C" fn(*mut libc::c_void) -> *mut libc::c_void, 16 | pub release_func: extern "C" fn(*mut libc::c_void), 17 | } 18 | 19 | pub struct Message(Option); 20 | 21 | unsafe impl Send for Message {} 22 | unsafe impl Sync for Message {} 23 | 24 | impl Message { 25 | pub fn as_ptr(&self) -> *const T { 26 | self.0.as_ref().unwrap().ptr as *const _ 27 | } 28 | 29 | pub fn as_mut_ptr(&mut self) -> *mut T { 30 | self.0.as_mut().unwrap().ptr as *mut T 31 | } 32 | } 33 | 34 | impl Drop for Message { 35 | fn drop(&mut self) { 36 | if let Some(inner) = self.0.as_ref() { 37 | (inner.release_func)(inner.ptr) 38 | } 39 | } 40 | } 41 | 42 | impl Clone for Message { 43 | fn clone(&self) -> Self { 44 | let inner = self.0.as_ref().unwrap(); 45 | Message(Some(CMessage { 46 | ptr: (inner.clone_func)(inner.ptr), 47 | clone_func: inner.clone_func, 48 | release_func: inner.release_func, 49 | })) 50 | } 51 | } 52 | 53 | impl From for Message { 54 | fn from(msg: CMessage) -> Self { 55 | Message(Some(msg)) 56 | } 57 | } 58 | 59 | impl From for CMessage { 60 | fn from(mut msg: Message) -> Self { 61 | msg.0.take().unwrap() 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /flow-message/src/cross/cow_list.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | impl private::IndexGetDataRefForGetRef for CowList { 4 | fn get_ref(&self, key: &usize) -> Option<&data::Data> { 5 | self.get(*key) 6 | } 7 | } 8 | 9 | impl private::IndexGetDataRefForGet for CowList { 10 | fn get_ref(&self, key: &usize) -> Option<&data::Data> { 11 | self.get(*key) 12 | } 13 | } 14 | 15 | impl private::IndexSetData for CowList { 16 | fn set(&mut self, key: usize, data: data::Data) { 17 | self.set(key, data); 18 | } 19 | } 20 | 21 | impl private::IndexGetDataMut for CowList { 22 | fn get_mut(&mut self, key: &usize) -> Option<&mut data::Data> { 23 | self.get_mut(*key) 24 | } 25 | } 26 | 27 | impl VectorOpr for CowList 28 | where 29 | T: From, 30 | data::Data: From, 31 | { 32 | fn xpop_front(&mut self) -> Option { 33 | self.pop_front().map(T::from) 34 | } 35 | 36 | fn xpop_back(&mut self) -> Option { 37 | self.pop_back().map(T::from) 38 | } 39 | 40 | fn xpush_back(&mut self, value: T) { 41 | self.push_back(data::Data::from(value)) 42 | } 43 | 44 | fn xpush_front(&mut self, value: T) { 45 | self.push_front(data::Data::from(value)) 46 | } 47 | } 48 | 49 | #[cfg(test)] 50 | mod test { 51 | use super::*; 52 | 53 | #[test] 54 | fn test_basis() { 55 | let mut list = CowList::new(); 56 | list.xpush_back(1f64); 57 | list.xpush_back(1i64); 58 | list.xpush_back(false); 59 | assert_eq!(list.len(), 3); 60 | assert_eq!(list.xget(&1), Some(1i64)); 61 | assert_eq!(list.xpop_back(), Some(false)); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /flow-message/src/cross/cow_map.rs: -------------------------------------------------------------------------------- 1 | /** 2 | * \file flow-message/cross/cow_map.rs 3 | * MegFlow is Licensed under the Apache License, Version 2.0 (the "License") 4 | * 5 | * Copyright (c) 2019-2021 Megvii Inc. All rights reserved. 6 | * 7 | * Unless required by applicable law or agreed to in writing, 8 | * software distributed under the License is distributed on an 9 | * "AS IS" BASIS, WITHOUT ARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | */ 11 | use super::*; 12 | 13 | impl private::IndexSetData for CowMap { 14 | fn set(&mut self, key: String, data: data::Data) { 15 | self.insert(key, data); 16 | } 17 | } 18 | 19 | impl private::IndexGetDataRefForGet for CowMap { 20 | fn get_ref(&self, key: &str) -> Option<&data::Data> { 21 | self.get(key) 22 | } 23 | } 24 | 25 | impl private::IndexGetDataRefForGetRef for CowMap { 26 | fn get_ref(&self, key: &str) -> Option<&data::Data> { 27 | self.get(key) 28 | } 29 | } 30 | 31 | impl private::IndexGetDataMut for CowMap { 32 | fn get_mut(&mut self, key: &str) -> Option<&mut data::Data> { 33 | self.get_mut(key) 34 | } 35 | } 36 | 37 | #[cfg(test)] 38 | mod test { 39 | use super::*; 40 | 41 | #[test] 42 | fn test_basis() { 43 | let mut map = CowMap::new(); 44 | map.xset("a".to_owned(), 1i64); 45 | map.xset("b".to_owned(), 1f64); 46 | let mut sub = CowMap::new(); 47 | sub.xset("d".to_owned(), false); 48 | map.xset("c".to_owned(), sub); 49 | 50 | assert_eq!(map.xget("b"), Some(1f64)); 51 | let has_c: Option<&mut CowMap> = map.xget_mut("c"); 52 | assert!(has_c.is_some()); 53 | let sub = has_c.unwrap(); 54 | assert_eq!(sub.xget("d"), Some(false)); 55 | sub.xset("e".to_owned(), true); 56 | 57 | let has_c: Option<&CowMap> = map.xget_ref("c"); 58 | assert!(has_c.is_some()); 59 | let sub = has_c.unwrap(); 60 | assert_eq!(sub.xget("e"), Some(true)); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /flow-message/src/cross/list.rs: -------------------------------------------------------------------------------- 1 | /** 2 | * \file flow-message/cross/list.rs 3 | * MegFlow is Licensed under the Apache License, Version 2.0 (the "License") 4 | * 5 | * Copyright (c) 2019-2021 Megvii Inc. All rights reserved. 6 | * 7 | * Unless required by applicable law or agreed to in writing, 8 | * software distributed under the License is distributed on an 9 | * "AS IS" BASIS, WITHOUT ARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | */ 11 | use super::*; 12 | 13 | impl IndexGet for List 14 | where 15 | Output: Copy, 16 | data::Data: data::IndexGet, 17 | { 18 | fn xget(&self, i: &usize) -> Option { 19 | data::IndexGet::get(self, *i) 20 | } 21 | } 22 | 23 | impl IndexSet for List 24 | where 25 | Value: Copy, 26 | data::Data: data::IndexSet, 27 | { 28 | fn xset(&mut self, i: usize, data: Value) { 29 | data::IndexSet::set(self, i, data) 30 | } 31 | } 32 | 33 | #[cfg(test)] 34 | mod test { 35 | use super::*; 36 | 37 | #[test] 38 | fn test_basis() { 39 | let list = List::from(vec![0i64, 1, 2, 3, 4]); 40 | assert_eq!(list.len(), 5); 41 | assert_eq!(list.xget(&3), Some(3i64)); 42 | let list: Vec = list.into(); 43 | assert_eq!(list, vec![0i64, 1, 2, 3, 4]); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /flow-message/src/cross/python/cow_map.rs: -------------------------------------------------------------------------------- 1 | /** 2 | * \file flow-message/cross/python/cow_map.rs 3 | * MegFlow is Licensed under the Apache License, Version 2.0 (the "License") 4 | * 5 | * Copyright (c) 2019-2021 Megvii Inc. All rights reserved. 6 | * 7 | * Unless required by applicable law or agreed to in writing, 8 | * software distributed under the License is distributed on an 9 | * "AS IS" BASIS, WITHOUT ARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | */ 11 | use super::*; 12 | use pyo3::{PyIterProtocol, PyMappingProtocol}; 13 | 14 | #[pyproto] 15 | impl PyMappingProtocol for PyCowMap { 16 | fn __len__(&self) -> usize { 17 | self.as_ref().len() 18 | } 19 | fn __setitem__(&mut self, attr: String, value: PyObject) -> PyResult<()> { 20 | let gil = Python::acquire_gil(); 21 | let py = gil.python(); 22 | let data = to_data(py, value)?; 23 | self.as_mut().insert(attr, data); 24 | Ok(()) 25 | } 26 | 27 | fn __getitem__(&self, attr: &str) -> PyResult { 28 | let gil = Python::acquire_gil(); 29 | let py = gil.python(); 30 | if let Some(data) = self.as_ref().get(attr) { 31 | from_data(py, data) 32 | } else { 33 | Err(pyo3::exceptions::PyTypeError::new_err("Key Error")) 34 | } 35 | } 36 | 37 | fn __delitem__(&mut self, attr: &str) -> PyResult<()> { 38 | self.as_mut().remove(attr); 39 | Ok(()) 40 | } 41 | } 42 | 43 | #[pyproto] 44 | impl PyIterProtocol for PyCowMap { 45 | fn __iter__(this: PyRefMut) -> PyResult { 46 | let gil = Python::acquire_gil(); 47 | let py = gil.python(); 48 | 49 | let mut elements = std::vec::Vec::new(); 50 | for (k, v) in this.as_ref().iter() { 51 | elements.push((k.to_object(py), from_data(py, &v.clone())?)); 52 | } 53 | 54 | Ok(super::iterator::PyObjectPairIterator::new( 55 | elements.into_iter(), 56 | )) 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /flow-message/src/cross/python/iterator.rs: -------------------------------------------------------------------------------- 1 | /** 2 | * \file flow-message/cross/python/iterator.rs 3 | * MegFlow is Licensed under the Apache License, Version 2.0 (the "License") 4 | * 5 | * Copyright (c) 2019-2021 Megvii Inc. All rights reserved. 6 | * 7 | * Unless required by applicable law or agreed to in writing, 8 | * software distributed under the License is distributed on an 9 | * "AS IS" BASIS, WITHOUT ARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | */ 11 | use pyo3::prelude::{pyclass, pyproto}; 12 | use pyo3::{Py, PyIterProtocol, PyObject, PyRefMut, PyResult}; 13 | 14 | #[pyclass] 15 | pub struct PyObjectIterator { 16 | iterator: std::vec::IntoIter, 17 | } 18 | 19 | impl PyObjectIterator { 20 | #[must_use] 21 | pub fn new(iterator: std::vec::IntoIter) -> Self { 22 | PyObjectIterator { iterator } 23 | } 24 | } 25 | 26 | #[pyproto] 27 | impl PyIterProtocol for PyObjectIterator { 28 | fn __iter__(slf: PyRefMut) -> PyResult> { 29 | Ok(slf.into()) 30 | } 31 | fn __next__(mut slf: PyRefMut) -> PyResult> { 32 | Ok(slf.iterator.next()) 33 | } 34 | } 35 | 36 | #[pyclass] 37 | pub struct PyObjectPairIterator { 38 | iterator: std::vec::IntoIter<(PyObject, PyObject)>, 39 | } 40 | 41 | impl PyObjectPairIterator { 42 | #[must_use] 43 | pub fn new(iterator: std::vec::IntoIter<(PyObject, PyObject)>) -> Self { 44 | PyObjectPairIterator { iterator } 45 | } 46 | } 47 | 48 | #[pyproto] 49 | impl PyIterProtocol for PyObjectPairIterator { 50 | fn __iter__(slf: PyRefMut) -> PyResult> { 51 | Ok(slf.into()) 52 | } 53 | fn __next__(mut slf: PyRefMut) -> PyResult> { 54 | Ok(slf.iterator.next()) 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /flow-message/src/cross/python/map.rs: -------------------------------------------------------------------------------- 1 | /** 2 | * \file flow-message/cross/python/map.rs 3 | * MegFlow is Licensed under the Apache License, Version 2.0 (the "License") 4 | * 5 | * Copyright (c) 2019-2021 Megvii Inc. All rights reserved. 6 | * 7 | * Unless required by applicable law or agreed to in writing, 8 | * software distributed under the License is distributed on an 9 | * "AS IS" BASIS, WITHOUT ARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | */ 11 | use super::*; 12 | use pyo3::{PyIterProtocol, PyMappingProtocol}; 13 | 14 | #[pyproto] 15 | impl PyMappingProtocol for PyMap { 16 | fn __len__(&self) -> usize { 17 | self.as_ref().len() 18 | } 19 | fn __setitem__(&mut self, attr: String, value: PyObject) -> PyResult<()> { 20 | let gil = Python::acquire_gil(); 21 | let py = gil.python(); 22 | let data = to_data(py, value)?; 23 | self.as_mut().insert(attr, data); 24 | Ok(()) 25 | } 26 | 27 | fn __getitem__(&self, attr: &str) -> PyResult { 28 | let gil = Python::acquire_gil(); 29 | let py = gil.python(); 30 | if let Some(data) = self.as_ref().get(attr) { 31 | from_data(py, data) 32 | } else { 33 | Err(pyo3::exceptions::PyTypeError::new_err("Key Error")) 34 | } 35 | } 36 | 37 | fn __delitem__(&mut self, attr: &str) -> PyResult<()> { 38 | self.as_mut().remove(attr); 39 | Ok(()) 40 | } 41 | } 42 | 43 | #[pyproto] 44 | impl PyIterProtocol for PyMap { 45 | fn __iter__(this: PyRefMut) -> PyResult { 46 | let gil = Python::acquire_gil(); 47 | let py = gil.python(); 48 | 49 | let mut elements = std::vec::Vec::new(); 50 | for pair in this.as_ref().iter() { 51 | elements.push(( 52 | pair.key().to_object(py), 53 | from_data(py, &pair.value().clone())?, 54 | )); 55 | } 56 | 57 | Ok(super::iterator::PyObjectPairIterator::new( 58 | elements.into_iter(), 59 | )) 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /flow-message/src/lib.rs: -------------------------------------------------------------------------------- 1 | /** 2 | * \file flow-message/lib.rs 3 | * MegFlow is Licensed under the Apache License, Version 2.0 (the "License") 4 | * 5 | * Copyright (c) 2019-2021 Megvii Inc. All rights reserved. 6 | * 7 | * Unless required by applicable law or agreed to in writing, 8 | * software distributed under the License is distributed on an 9 | * "AS IS" BASIS, WITHOUT ARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | */ 11 | 12 | #[cfg(feature = "c")] 13 | pub mod c; 14 | #[cfg(feature = "cross")] 15 | pub mod cross; 16 | -------------------------------------------------------------------------------- /flow-plugins/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "flow-plugins" 3 | version = "0.3.5" 4 | authors = ["megvii"] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [features] 10 | default = ["external"] 11 | external = ["ffmpeg-next", "rweb", "mime", "indexmap", "hyper", "headers", "serde_json", "urlencoding", "image"] 12 | python = [ "pyo3", "numpy", "flow-rs/python" ] 13 | open-camera = ["ffmpeg-next/open-camera", "default"] 14 | 15 | [dependencies.ffmpeg-next] 16 | git = "https://github.com/tpoisonooo/rust-ffmpeg" 17 | rev = "f80c21fd" 18 | optional = true 19 | 20 | [dependencies] 21 | bytes = "1.1.0" 22 | flow-rs = { path = "../flow-rs", default-features = false } 23 | toml = "0.5" 24 | futures-util = "0.3" 25 | pyo3 = { version = "0.15", features=["abi3"], optional=true } 26 | numpy = { version = "0.15", optional=true } 27 | anyhow = "1.0" 28 | oneshot = "0.1" 29 | log = "0.4" 30 | pretty_env_logger = "0.3" 31 | 32 | image = { version = "0.23", optional=true } 33 | rweb = { version="0.12.5", features=["openapi"], optional=true } 34 | mime = { version="0.3", optional=true } 35 | indexmap = { version="1", optional=true } 36 | hyper = { version="0.14", optional=true } 37 | headers = { version="0.3", optional=true } 38 | serde_json = { version="1.0", optional=true } 39 | urlencoding = { version="2.1", optional=true } 40 | 41 | serde = "1.0" 42 | rand = "0.8" 43 | samotop-async-trait = "0.2" 44 | num = "0.4" 45 | -------------------------------------------------------------------------------- /flow-plugins/examples/bytes_server.toml: -------------------------------------------------------------------------------- 1 | main = "bytes" 2 | 3 | [[graphs]] 4 | name = "bytes" 5 | connections = [{ cap=16, ports=["server:inp", "server:out"] }] 6 | 7 | # BytesServer: 8 | # @inp: input port, message format: dict { 'data': } (pyobject) 9 | # @out: output port, message format: dict { 'data': } (pyobject) 10 | [[graphs.nodes]] 11 | name = "server" 12 | ty = "BytesServer" 13 | port = 3030 # int, the port on which server will listen 14 | -------------------------------------------------------------------------------- /flow-plugins/examples/image_input.toml: -------------------------------------------------------------------------------- 1 | main = "input_image" 2 | 3 | [[graphs]] 4 | name = "input_image" 5 | connections = [{ cap=16, ports=["server:inp", "server:out"] }] 6 | 7 | # ImageInput: 8 | # @inp: input port, message format: Any 9 | # @out: output port, message format: dict { 'data': , 'extra_data': } (pyobject) 10 | [[graphs.nodes]] 11 | name = "server" 12 | ty = "ImageInput" 13 | urls = ["", ""] # image folder path 14 | -------------------------------------------------------------------------------- /flow-plugins/examples/image_server.toml: -------------------------------------------------------------------------------- 1 | main = "image" 2 | 3 | [[graphs]] 4 | name = "image" 5 | connections = [{ cap=16, ports=["server:inp", "server:out"] }] 6 | 7 | # ImageServer: 8 | # @inp: input port, message format: JSON string (python) if response format is json 9 | # dict { 'data': } (pyobject) if response is image 10 | # @out: output port, message format: dict { 'data': , 'extra_data': } (pyobject) 11 | [[graphs.nodes]] 12 | name = "server" 13 | ty = "ImageServer" 14 | port = 3030 # int, the port on which server will listen 15 | response = "image" # json | image, which means response format 16 | -------------------------------------------------------------------------------- /flow-plugins/examples/video_input.toml: -------------------------------------------------------------------------------- 1 | main = "video" 2 | 3 | [[graphs]] 4 | name = "subgraph" 5 | inputs = [{name="inp", cap=16, ports=["trans:inp"]}] 6 | outputs = [{name="out", cap=16, ports=["trans:out"]}] 7 | 8 | [[graphs.nodes]] 9 | name = "trans" 10 | ty = "Transform" 11 | 12 | [[graphs]] 13 | name = "video" 14 | connections = [ 15 | { cap=16, ports=["server:inp", "trans:out"] }, 16 | { cap=16, ports=["server:out", "trans:inp"] } 17 | ] 18 | 19 | # VideoInput: 20 | # @inp: input port, message format: Any 21 | # @out: output port, message format: dict { 'data': } (pyobject) 22 | [[graphs.nodes]] 23 | name = "server" 24 | ty = "VideoInput" 25 | urls = ["1.ts", "2.ts"] # video urls 26 | repeat = 10 # repeat times 27 | # just an example, which can not run because of invalid message format 28 | [[graphs.nodes]] 29 | name = "trans" 30 | ty = "subgraph" 31 | 32 | -------------------------------------------------------------------------------- /flow-plugins/examples/video_server.toml: -------------------------------------------------------------------------------- 1 | main = "video" 2 | 3 | [[graphs]] 4 | name = "subgraph" 5 | inputs = [{name="inp", cap=16, ports=["trans:inp"]}] 6 | outputs = [{name="out", cap=16, ports=["trans:out"]}] 7 | 8 | [[graphs.nodes]] 9 | name = "trans" 10 | ty = "Transform" 11 | 12 | [[graphs]] 13 | name = "video" 14 | connections = [ 15 | { cap=16, ports=["server:inp", "trans:out"] }, 16 | { cap=16, ports=["server:out", "trans:inp"] } 17 | ] 18 | 19 | # VideoServer: 20 | # @inp: input port, message format: JSON string (pyobject) 21 | # @out: output port, message format: dict { 'data': } (pyobject) 22 | [[graphs.nodes]] 23 | name = "server" 24 | ty = "VideoServer" 25 | port = 8080 # int, the port on which server will listen 26 | # just an example, which can not run because of invalid message format 27 | [[graphs.nodes]] 28 | name = "trans" 29 | ty = "subgraph" 30 | 31 | 32 | -------------------------------------------------------------------------------- /flow-plugins/src/lib.rs: -------------------------------------------------------------------------------- 1 | /** 2 | * \file flow-plugins/src/lib.rs 3 | * MegFlow is Licensed under the Apache License, Version 2.0 (the "License") 4 | * 5 | * Copyright (c) 2019-2021 Megvii Inc. All rights reserved. 6 | * 7 | * Unless required by applicable law or agreed to in writing, 8 | * software distributed under the License is distributed on an 9 | * "AS IS" BASIS, WITHOUT ARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | */ 11 | mod utils; 12 | #[cfg(feature = "python")] 13 | #[path = "./"] 14 | mod python { 15 | #[cfg(feature = "default")] 16 | mod bytes_server; 17 | #[cfg(feature = "default")] 18 | mod image_input; 19 | #[cfg(feature = "default")] 20 | mod image_server; 21 | #[cfg(feature = "default")] 22 | mod video_input; 23 | #[cfg(feature = "default")] 24 | mod video_server; 25 | 26 | pub(super) fn export() { 27 | pyo3::prepare_freethreaded_python(); 28 | } 29 | } 30 | 31 | #[doc(hidden)] 32 | pub fn export() { 33 | pretty_env_logger::init(); 34 | #[cfg(feature = "python")] 35 | python::export(); 36 | } 37 | -------------------------------------------------------------------------------- /flow-plugins/src/utils/args_parser.rs: -------------------------------------------------------------------------------- 1 | use num::traits::AsPrimitive; 2 | use toml::value::*; 3 | 4 | pub fn as_integer(map: &Table, key: &str, default: T) -> T 5 | where 6 | T: 'static + Copy, 7 | i64: AsPrimitive, 8 | { 9 | map.get(key) 10 | .map(|x| { 11 | x.as_integer() 12 | .unwrap_or_else(|| panic!("Invalid arguments in field {}", key)) 13 | .as_() 14 | }) 15 | .unwrap_or(default) 16 | } 17 | 18 | pub fn as_bool(map: &Table, key: &str, default: bool) -> bool { 19 | map.get(key) 20 | .map(|x| { 21 | x.as_bool() 22 | .unwrap_or_else(|| panic!("Invalid arguments in field {}", key)) 23 | }) 24 | .unwrap_or(default) 25 | } 26 | 27 | pub fn as_str<'a>(map: &'a Table, key: &str, default: &'a str) -> &'a str { 28 | map.get(key) 29 | .map(|x| { 30 | x.as_str() 31 | .unwrap_or_else(|| panic!("Invalid arguments in field {}", key)) 32 | }) 33 | .unwrap_or(default) 34 | } 35 | 36 | pub fn as_list<'a>(map: &'a Table, key: &str) -> Vec<&'a Value> { 37 | map.get(key) 38 | .map(|x| { 39 | x.as_array() 40 | .unwrap_or_else(|| panic!("Invalid arguments in field {}", key)) 41 | .iter() 42 | .collect() 43 | }) 44 | .unwrap_or_default() 45 | } 46 | -------------------------------------------------------------------------------- /flow-plugins/src/utils/bare_json.rs: -------------------------------------------------------------------------------- 1 | /** 2 | * \file flow-plugins/src/utils/bare_json.rs 3 | * MegFlow is Licensed under the Apache License, Version 2.0 (the "License") 4 | * 5 | * Copyright (c) 2019-2021 Megvii Inc. All rights reserved. 6 | * 7 | * Unless required by applicable law or agreed to in writing, 8 | * software distributed under the License is distributed on an 9 | * "AS IS" BASIS, WITHOUT ARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | */ 11 | use headers::{ContentType, HeaderMapExt}; 12 | use indexmap::IndexMap; 13 | use mime::Mime; 14 | use rweb::openapi::*; 15 | use rweb::*; 16 | use std::borrow::Cow; 17 | use std::str::FromStr; 18 | 19 | #[derive(Schema)] 20 | pub(crate) struct BareJson { 21 | inner: String, 22 | } 23 | 24 | impl BareJson { 25 | pub fn new(inner: String) -> BareJson { 26 | BareJson { inner } 27 | } 28 | } 29 | 30 | impl ResponseEntity for BareJson { 31 | fn describe_responses() -> Responses { 32 | let mut resps = IndexMap::new(); 33 | 34 | let mut content = IndexMap::new(); 35 | content.insert( 36 | Cow::Borrowed("application/json"), 37 | MediaType { 38 | schema: Some(ObjectOrReference::Object(Self::describe())), 39 | examples: None, 40 | encoding: Default::default(), 41 | }, 42 | ); 43 | 44 | resps.insert( 45 | Cow::Borrowed("200"), 46 | Response { 47 | content, 48 | ..Default::default() 49 | }, 50 | ); 51 | resps 52 | } 53 | } 54 | 55 | impl Reply for BareJson { 56 | #[inline] 57 | fn into_response(self) -> reply::Response { 58 | let mut resp = reply::Response::new(self.inner.into()); 59 | resp.headers_mut().typed_insert(ContentType::from( 60 | Mime::from_str("application/json").unwrap(), 61 | )); 62 | resp 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /flow-plugins/src/utils/bytes.rs: -------------------------------------------------------------------------------- 1 | /** 2 | * \file flow-plugins/src/utils/bytes.rs 3 | * MegFlow is Licensed under the Apache License, Version 2.0 (the "License") 4 | * 5 | * Copyright (c) 2019-2021 Megvii Inc. All rights reserved. 6 | * 7 | * Unless required by applicable law or agreed to in writing, 8 | * software distributed under the License is distributed on an 9 | * "AS IS" BASIS, WITHOUT ARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | */ 11 | use headers::{ContentType, HeaderMapExt}; 12 | use indexmap::IndexMap; 13 | use mime::Mime; 14 | use rweb::openapi::*; 15 | use rweb::*; 16 | use std::borrow::Cow; 17 | use std::str::FromStr; 18 | 19 | #[derive(Schema)] 20 | pub(crate) struct Bytes { 21 | inner: Vec, 22 | } 23 | 24 | impl Bytes { 25 | pub fn new(_inner: &[u8]) -> Bytes { 26 | Bytes { 27 | inner: _inner.to_owned(), 28 | } 29 | } 30 | } 31 | 32 | impl ResponseEntity for Bytes { 33 | fn describe_responses() -> Responses { 34 | let mut resps = IndexMap::new(); 35 | 36 | let mut content = IndexMap::new(); 37 | content.insert( 38 | Cow::Borrowed("application/octet-stream"), 39 | MediaType { 40 | schema: Some(ObjectOrReference::Object(Self::describe())), 41 | examples: None, 42 | encoding: Default::default(), 43 | }, 44 | ); 45 | 46 | resps.insert( 47 | Cow::Borrowed("200"), 48 | Response { 49 | content, 50 | ..Default::default() 51 | }, 52 | ); 53 | resps 54 | } 55 | } 56 | 57 | impl Reply for Bytes { 58 | #[inline] 59 | fn into_response(self) -> reply::Response { 60 | let mut resp = reply::Response::new(self.inner.into()); 61 | resp.headers_mut().typed_insert(ContentType::from( 62 | Mime::from_str("application/octet-stream").unwrap(), 63 | )); 64 | resp 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /flow-plugins/src/utils/either.rs: -------------------------------------------------------------------------------- 1 | /** 2 | * \file flow-plugins/src/utils/either.rs 3 | * MegFlow is Licensed under the Apache License, Version 2.0 (the "License") 4 | * 5 | * Copyright (c) 2019-2021 Megvii Inc. All rights reserved. 6 | * 7 | * Unless required by applicable law or agreed to in writing, 8 | * software distributed under the License is distributed on an 9 | * "AS IS" BASIS, WITHOUT ARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | */ 11 | use rweb::openapi::{ResponseEntity, Responses}; 12 | use rweb::*; 13 | 14 | #[derive(Schema)] 15 | pub(crate) enum Either { 16 | Left(L), 17 | Right(R), 18 | } 19 | 20 | impl Reply for Either 21 | where 22 | L: Reply, 23 | R: Reply, 24 | { 25 | #[inline] 26 | fn into_response(self) -> reply::Response { 27 | match self { 28 | Either::Left(l) => l.into_response(), 29 | Either::Right(r) => r.into_response(), 30 | } 31 | } 32 | } 33 | 34 | impl ResponseEntity for Either 35 | where 36 | L: ResponseEntity, 37 | R: ResponseEntity, 38 | { 39 | fn describe_responses() -> Responses { 40 | let mut lresps = L::describe_responses(); 41 | let rresps = R::describe_responses(); 42 | 43 | for (k, rresp) in &rresps { 44 | if lresps.contains_key(k) { 45 | let lresp = lresps.get_mut(k).unwrap(); 46 | for (k, v) in &rresp.content { 47 | if !lresp.content.contains_key(k) { 48 | lresp.content.insert(k.clone(), v.clone()); 49 | } 50 | } 51 | } else { 52 | lresps.insert(k.clone(), rresp.clone()); 53 | } 54 | } 55 | 56 | lresps 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /flow-plugins/src/utils/error.rs: -------------------------------------------------------------------------------- 1 | /** 2 | * \file flow-plugins/src/utils/error.rs 3 | * MegFlow is Licensed under the Apache License, Version 2.0 (the "License") 4 | * 5 | * Copyright (c) 2019-2021 Megvii Inc. All rights reserved. 6 | * 7 | * Unless required by applicable law or agreed to in writing, 8 | * software distributed under the License is distributed on an 9 | * "AS IS" BASIS, WITHOUT ARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | */ 11 | use std::fmt::Debug; 12 | 13 | use rweb::Rejection; 14 | 15 | #[derive(Debug)] 16 | struct RejectCause 17 | where 18 | T: 'static + Debug + Send + Sync, 19 | { 20 | err: T, 21 | } 22 | 23 | impl rweb::reject::Reject for RejectCause where T: 'static + Debug + Send + Sync {} 24 | 25 | impl From for RejectCause 26 | where 27 | T: 'static + Debug + Send + Sync, 28 | { 29 | fn from(err: T) -> Self { 30 | RejectCause { err } 31 | } 32 | } 33 | 34 | pub fn reject_cause(err: T) -> Rejection 35 | where 36 | T: 'static + Debug + Send + Sync, 37 | { 38 | rweb::reject::custom(RejectCause::from(err)) 39 | } 40 | -------------------------------------------------------------------------------- /flow-plugins/src/utils/find_lib.rs: -------------------------------------------------------------------------------- 1 | #[cfg(feature = "python")] 2 | pub fn find_lib_from_python(module_name: &str, libs: &[&str]) -> Option> { 3 | use pyo3::prelude::*; 4 | if let Ok(path) = Python::with_gil(|py| -> PyResult { 5 | let module = py.import(module_name)?; 6 | module.getattr("__file__")?.extract() 7 | }) { 8 | let mut path = std::path::PathBuf::from(path); 9 | path.pop(); 10 | Some( 11 | libs.iter() 12 | .map(|x| { 13 | let mut path = path.clone(); 14 | path.push(x); 15 | path 16 | }) 17 | .collect(), 18 | ) 19 | } else { 20 | None 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /flow-plugins/src/utils/mod.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code)] 2 | /** 3 | * \file flow-plugins/src/utils/mod.rs 4 | * MegFlow is Licensed under the Apache License, Version 2.0 (the "License") 5 | * 6 | * Copyright (c) 2019-2021 Megvii Inc. All rights reserved. 7 | * 8 | * Unless required by applicable law or agreed to in writing, 9 | * software distributed under the License is distributed on an 10 | * "AS IS" BASIS, WITHOUT ARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | */ 12 | pub mod args_parser; 13 | #[cfg(feature = "external")] 14 | pub mod bare_json; 15 | #[cfg(feature = "external")] 16 | pub mod bytes; 17 | #[cfg(feature = "external")] 18 | pub mod codec; 19 | #[cfg(feature = "external")] 20 | pub mod either; 21 | #[cfg(feature = "external")] 22 | pub mod error; 23 | pub mod find_lib; 24 | #[cfg(feature = "internal")] 25 | pub mod frame; 26 | #[cfg(feature = "external")] 27 | pub mod image; 28 | #[cfg(feature = "external")] 29 | pub mod multipart; 30 | -------------------------------------------------------------------------------- /flow-python/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "flow-python" 3 | version = "0.3.5" 4 | authors = ["megvii"] 5 | edition = "2018" 6 | 7 | [lib] 8 | crate-type = ["cdylib"] 9 | 10 | [features] 11 | debug = ["flow-rs/debug"] 12 | extension-module = ["pyo3/extension-module"] 13 | 14 | [dependencies] 15 | flow-rs = { path="../flow-rs", features=["python"] } 16 | pyo3 = { version = "0.15", features = ["abi3"] } 17 | rand = "0.8.4" 18 | libc = "0.2" 19 | tempfile = "3.2.0" 20 | ctrlc = "3.1.9" 21 | log = "0.4" 22 | anyhow = "1.0" 23 | 24 | [dependencies.flow-plugins] 25 | path = "../flow-plugins" 26 | features = [ 27 | "python", 28 | ] 29 | 30 | [dev-dependencies] 31 | clap = "3.0.0-beta.2" 32 | 33 | -------------------------------------------------------------------------------- /flow-python/examples/application/cat_finder/__init__.py: -------------------------------------------------------------------------------- 1 | # MegFlow is Licensed under the Apache License, Version 2.0 (the "License") 2 | # 3 | # Copyright (c) 2019-2021 Megvii Inc. All rights reserved. 4 | # 5 | # Unless required by applicable law or agreed to in writing, 6 | # software distributed under the License is distributed on an 7 | # "AS IS" BASIS, WITHOUT ARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 8 | 9 | #!/usr/bin/env python 10 | # coding=utf-8 11 | -------------------------------------------------------------------------------- /flow-python/examples/application/cat_finder/image_cpu.toml: -------------------------------------------------------------------------------- 1 | main = "cat_finder_image" 2 | 3 | [[graphs]] 4 | name = "subgraph" 5 | inputs = [{ name = "inp", cap = 16, ports = ["det:inp"] }] 6 | outputs = [{ name = "out", cap = 16, ports = ["redis_proxy:out"] }] 7 | connections = [ 8 | { cap = 16, ports = ["det:out", "reid_image:inp"] }, 9 | { cap = 16, ports = ["reid_image:out", "redis_proxy:inp"] }, 10 | ] 11 | 12 | [[graphs.nodes]] 13 | name = "det" 14 | ty = "Detect" 15 | model = "yolox-s" 16 | conf = 0.25 17 | nms = 0.45 18 | tsize = 640 19 | path = "models/cat_finder_models/yolox_s.mge" 20 | interval = 1 21 | visualize = 1 22 | device = "cpu" 23 | device_id = 0 24 | 25 | [[graphs.nodes]] 26 | name = "reid_image" 27 | ty = "ReIDImage" 28 | path = "models/cat_finder_models/resnet50.mge" 29 | device = "cpu" 30 | device_id = 1 31 | 32 | [[graphs.nodes]] 33 | name = "redis_proxy" 34 | ty = "RedisProxy" 35 | ip = "127.0.0.1" 36 | port = "6379" 37 | mode = "save" 38 | prefix = "feature." 39 | 40 | [[graphs]] 41 | name = "cat_finder_image" 42 | connections = [ 43 | { cap = 16, ports = ["source:out", "destination:inp"] }, 44 | { cap = 16, ports = ["source:inp", "destination:out"] } 45 | ] 46 | 47 | [[graphs.nodes]] 48 | name = "source" 49 | ty = "ImageServer" 50 | port = 8081 51 | 52 | [[graphs.nodes]] 53 | name = "destination" 54 | ty = "subgraph" 55 | -------------------------------------------------------------------------------- /flow-python/examples/application/cat_finder/image_gpu.toml: -------------------------------------------------------------------------------- 1 | main = "cat_finder_image" 2 | 3 | [[graphs]] 4 | name = "subgraph" 5 | inputs = [{ name = "inp", cap = 16, ports = ["det:inp"] }] 6 | outputs = [{ name = "out", cap = 16, ports = ["redis_proxy:out"] }] 7 | connections = [ 8 | { cap = 16, ports = ["det:out", "reid_image:inp"] }, 9 | { cap = 16, ports = ["reid_image:out", "redis_proxy:inp"] }, 10 | ] 11 | 12 | [[graphs.nodes]] 13 | name = "det" 14 | ty = "Detect" 15 | model = "yolox-s" 16 | conf = 0.25 17 | nms = 0.45 18 | tsize = 640 19 | path = "models/cat_finder_models/yolox_s.mge" 20 | interval = 1 21 | visualize = 1 22 | device = "gpu" 23 | device_id = 0 24 | 25 | [[graphs.nodes]] 26 | name = "reid_image" 27 | ty = "ReIDImage" 28 | path = "models/cat_finder_models/resnet50.mge" 29 | device = "gpu" 30 | device_id = 0 31 | 32 | [[graphs.nodes]] 33 | name = "redis_proxy" 34 | ty = "RedisProxy" 35 | ip = "127.0.0.1" 36 | port = "6379" 37 | mode = "save" 38 | prefix = "feature." 39 | 40 | [[graphs]] 41 | name = "cat_finder_image" 42 | connections = [ 43 | { cap = 16, ports = ["source:out", "destination:inp"] }, 44 | { cap = 16, ports = ["source:inp", "destination:out"] } 45 | ] 46 | 47 | [[graphs.nodes]] 48 | name = "source" 49 | ty = "ImageServer" 50 | port = 8081 51 | 52 | [[graphs.nodes]] 53 | name = "destination" 54 | ty = "subgraph" 55 | -------------------------------------------------------------------------------- /flow-python/examples/application/cat_finder/images/cat_finder_image_result.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MegEngine/MegFlow/41cb9f85828aa080f6004674904fe430d8268052/flow-python/examples/application/cat_finder/images/cat_finder_image_result.jpg -------------------------------------------------------------------------------- /flow-python/examples/application/cat_finder/images/cat_finder_image_select.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MegEngine/MegFlow/41cb9f85828aa080f6004674904fe430d8268052/flow-python/examples/application/cat_finder/images/cat_finder_image_select.jpg -------------------------------------------------------------------------------- /flow-python/examples/application/cat_finder/images/cat_finder_video_select.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MegEngine/MegFlow/41cb9f85828aa080f6004674904fe430d8268052/flow-python/examples/application/cat_finder/images/cat_finder_video_select.jpg -------------------------------------------------------------------------------- /flow-python/examples/application/cat_finder/reid_image.py: -------------------------------------------------------------------------------- 1 | # MegFlow is Licensed under the Apache License, Version 2.0 (the "License") 2 | # 3 | # Copyright (c) 2019-2021 Megvii Inc. All rights reserved. 4 | # 5 | # Unless required by applicable law or agreed to in writing, 6 | # software distributed under the License is distributed on an 7 | # "AS IS" BASIS, WITHOUT ARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 8 | 9 | #!/usr/bin/env python 10 | # coding=utf-8 11 | 12 | import numpy as np 13 | from loguru import logger 14 | from megflow import register 15 | 16 | from warehouse.reid_alignedreid import PredictorLite 17 | 18 | 19 | @register(inputs=['inp'], outputs=['out']) 20 | class ReIDImage: 21 | def __init__(self, name, args): 22 | logger.info("loading Image Reid...") 23 | self.name = name 24 | 25 | # load ReID model and warmup 26 | self._model = PredictorLite(path=args['path'], 27 | device=args['device'], 28 | device_id=args['device_id']) 29 | warmup_data = np.zeros((224, 224, 3), dtype=np.uint8) 30 | self._model.inference(warmup_data) 31 | logger.info("Image Reid loaded.") 32 | 33 | def exec(self): 34 | envelope = self.inp.recv() 35 | if envelope is None: 36 | return 37 | image = envelope.msg 38 | 39 | data = image['data'] 40 | items = image['items'] 41 | assert isinstance(items, list) 42 | 43 | for item in items: 44 | bbox = item['bbox'] 45 | crop = data[round(bbox[1]):round(bbox[3]), 46 | round(bbox[0]):round(bbox[2])] 47 | 48 | # cv2.imwrite(f'reid_image_{envelope.partial_id}.jpg', crop) 49 | feature = self._model.inference(crop) 50 | item['feature'] = feature 51 | logger.info(f'feature {feature}') 52 | self.out.send(envelope) 53 | -------------------------------------------------------------------------------- /flow-python/examples/application/cat_finder/reid_video.py: -------------------------------------------------------------------------------- 1 | # MegFlow is Licensed under the Apache License, Version 2.0 (the "License") 2 | # 3 | # Copyright (c) 2019-2021 Megvii Inc. All rights reserved. 4 | # 5 | # Unless required by applicable law or agreed to in writing, 6 | # software distributed under the License is distributed on an 7 | # "AS IS" BASIS, WITHOUT ARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 8 | 9 | #!/usr/bin/env python 10 | # coding=utf-8 11 | 12 | import numpy as np 13 | from loguru import logger 14 | from megflow import register 15 | 16 | from warehouse.reid_alignedreid import PredictorLite 17 | 18 | 19 | @register(inputs=['inp'], outputs=['out']) 20 | class ReIDVideo: 21 | def __init__(self, name, args): 22 | logger.info("loading Video Reid...") 23 | self.name = name 24 | 25 | # load ReID model and warmup 26 | self._model = PredictorLite(path=args['path'], 27 | device=args['device'], 28 | device_id=args['device_id']) 29 | warmup_data = np.zeros((224, 224, 3), dtype=np.uint8) 30 | self._model.inference(warmup_data) 31 | logger.info(" Video Reid loaded.") 32 | 33 | def exec(self): 34 | envelope = self.inp.recv() 35 | if envelope is None: 36 | return 37 | msg = envelope.msg 38 | 39 | # for crop in image['shaper']: 40 | # cv2.imwrite(f'reid_video_{envelope.partial_id}.jpg', crop) 41 | # logger.info(f'envelope id {envelope.partial_id}') 42 | 43 | msg['features'] = [] 44 | if 'crop' in msg: 45 | msg['feature'] = self._model.inference(msg['crop']) 46 | # logger.info(image['features']) 47 | self.out.send(envelope) 48 | -------------------------------------------------------------------------------- /flow-python/examples/application/cat_finder/track.py: -------------------------------------------------------------------------------- 1 | # MegFlow is Licensed under the Apache License, Version 2.0 (the "License") 2 | # 3 | # Copyright (c) 2019-2021 Megvii Inc. All rights reserved. 4 | # 5 | # Unless required by applicable law or agreed to in writing, 6 | # software distributed under the License is distributed on an 7 | # "AS IS" BASIS, WITHOUT ARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 8 | 9 | #!/usr/bin/env python 10 | # coding=utf-8 11 | 12 | from loguru import logger 13 | from megflow import register 14 | 15 | from warehouse.track_iou import Tracker 16 | 17 | 18 | @register(inputs=['inp'], outputs=['out']) 19 | class Track: 20 | def __init__(self, name, _): 21 | self.name = name 22 | self._tracker = Tracker() 23 | 24 | def exec(self): 25 | envelope = self.inp.recv() 26 | if envelope is None: 27 | logger.info('stream tracker finish') 28 | return 29 | 30 | msg = envelope.msg 31 | if msg['process']: 32 | items = msg['items'] 33 | 34 | tracks, failed_ids = self._tracker.track(items) 35 | msg['tracks'] = tracks 36 | msg['failed_ids'] = failed_ids 37 | # logger.debug(f'track output: {tracks}, failed_ids: {failed_ids}') 38 | self.out.send(envelope) 39 | -------------------------------------------------------------------------------- /flow-python/examples/application/cat_finder/video_cpu.toml: -------------------------------------------------------------------------------- 1 | main = "cat_finder_video" 2 | 3 | [[nodes]] 4 | name = "det" 5 | ty = "Detect" 6 | model = "yolox-s" 7 | conf = 0.25 8 | nms = 0.45 9 | tsize = 640 10 | path = "models/cat_finder_models/yolox_s.mge" 11 | interval = 5 12 | visualize = 0 13 | device = "cpu" 14 | device_id = 0 15 | 16 | [[nodes]] 17 | name = "reid_video" 18 | ty = "ReIDVideo" 19 | path = "models/cat_finder_models/resnet50.mge" 20 | device = "cpu" 21 | device_id = 1 22 | 23 | [[nodes]] 24 | name = "redis_proxy" 25 | ty = "RedisProxy" 26 | ip = "127.0.0.1" 27 | port = "6379" 28 | mode = "search" 29 | prefix = "feature." 30 | 31 | [[graphs]] 32 | name = "subgraph" 33 | inputs = [{ name = "inp", cap = 16, ports = ["det:inp"] }] 34 | outputs = [{ name = "out", cap = 16, ports = ["redis_proxy:out"] }] 35 | connections = [ 36 | { cap = 16, ports = ["det:out", "track:inp"] }, 37 | { cap = 16, ports = ["track:out", "shaper:inp"] }, 38 | { cap = 16, ports = ["shaper:out", "reid_video:inp"] }, 39 | { cap = 16, ports = ["reid_video:out", "redis_proxy:inp"] }, 40 | ] 41 | 42 | [[graphs.nodes]] 43 | name = "track" 44 | ty = "Track" 45 | 46 | [[graphs.nodes]] 47 | name = "shaper" 48 | ty = "Shaper" 49 | mode = "BEST" 50 | 51 | 52 | [[graphs]] 53 | name = "cat_finder_video" 54 | connections = [ 55 | { cap = 16, ports = ["source:out", "destination:inp"] }, 56 | { cap = 16, ports = ["source:inp", "destination:out"] } 57 | ] 58 | 59 | [[graphs.nodes]] 60 | name = "source" 61 | ty = "VideoServer" 62 | port = 8082 63 | 64 | [[graphs.nodes]] 65 | name = "destination" 66 | ty = "subgraph" 67 | -------------------------------------------------------------------------------- /flow-python/examples/application/cat_finder/video_gpu.toml: -------------------------------------------------------------------------------- 1 | main = "cat_finder_video" 2 | 3 | [[nodes]] 4 | name = "det" 5 | ty = "Detect" 6 | model = "yolox-s" 7 | conf = 0.25 8 | nms = 0.45 9 | tsize = 640 10 | path = "models/cat_finder_models/yolox_s.mge" 11 | interval = 5 12 | visualize = 0 13 | device = "gpu" 14 | device_id = 0 15 | 16 | [[nodes]] 17 | name = "reid_video" 18 | ty = "ReIDVideo" 19 | path = "models/cat_finder_models/resnet50.mge" 20 | device = "gpu" 21 | device_id = 0 22 | 23 | [[nodes]] 24 | name = "redis_proxy" 25 | ty = "RedisProxy" 26 | ip = "127.0.0.1" 27 | port = "6379" 28 | mode = "search" 29 | prefix = "feature." 30 | 31 | [[graphs]] 32 | name = "subgraph" 33 | inputs = [{ name = "inp", cap = 16, ports = ["det:inp"] }] 34 | outputs = [{ name = "out", cap = 16, ports = ["redis_proxy:out"] }] 35 | connections = [ 36 | { cap = 16, ports = ["det:out", "track:inp"] }, 37 | { cap = 16, ports = ["track:out", "shaper:inp"] }, 38 | { cap = 16, ports = ["shaper:out", "reid_video:inp"] }, 39 | { cap = 16, ports = ["reid_video:out", "redis_proxy:inp"] }, 40 | ] 41 | 42 | [[graphs.nodes]] 43 | name = "track" 44 | ty = "Track" 45 | 46 | [[graphs.nodes]] 47 | name = "shaper" 48 | ty = "Shaper" 49 | mode = "BEST" 50 | 51 | 52 | [[graphs]] 53 | name = "cat_finder_video" 54 | connections = [ 55 | { cap = 16, ports = ["source:out", "destination:inp"] }, 56 | { cap = 16, ports = ["source:inp", "destination:out"] } 57 | ] 58 | 59 | [[graphs.nodes]] 60 | name = "source" 61 | ty = "VideoServer" 62 | port = 8082 63 | 64 | [[graphs.nodes]] 65 | name = "destination" 66 | ty = "subgraph" 67 | -------------------------------------------------------------------------------- /flow-python/examples/application/cat_finder/video_visualize.toml: -------------------------------------------------------------------------------- 1 | main = "cat_finder_video" 2 | 3 | [[nodes]] 4 | name = "det" 5 | ty = "Detect" 6 | model = "yolox-s" 7 | conf = 0.25 8 | nms = 0.45 9 | tsize = 640 10 | path = "models/cat_finder_models/yolox_s.mge" 11 | interval = 5 12 | visualize = 0 13 | device = "gpu" 14 | device_id = 0 15 | 16 | [[nodes]] 17 | name = "reid_video" 18 | ty = "ReIDVideo" 19 | path = "models/cat_finder_models/resnet50.mge" 20 | device = "gpu" 21 | device_id = 0 22 | 23 | [[nodes]] 24 | name = "redis_proxy" 25 | ty = "RedisProxy" 26 | ip = "127.0.0.1" 27 | port = "6379" 28 | mode = "search" 29 | prefix = "feature." 30 | 31 | [[graphs]] 32 | name = "subgraph" 33 | inputs = [{ name = "inp", cap = 16, ports = ["det:inp"] }] 34 | outputs = [{ name = "out_source", cap = 16, ports = ["redis_proxy:out"] }, { name = "out_hang", cap = 16, ports = ["visualize:out"] }] 35 | connections = [ 36 | { cap = 16, ports = ["det:out", "track:inp"] }, 37 | { cap = 16, ports = ["track:out", "shaper:inp"] }, 38 | { cap = 16, ports = ["shaper:out", "reid_video:inp"] }, 39 | { cap = 16, ports = ["shaper:visualize", "visualize:inp"]}, 40 | { cap = 16, ports = ["reid_video:out", "redis_proxy:inp"] }, 41 | ] 42 | 43 | [[graphs.nodes]] 44 | name = "track" 45 | ty = "Track" 46 | 47 | [[graphs.nodes]] 48 | name = "shaper" 49 | ty = "ShaperVisualize" 50 | mode = "BEST" 51 | 52 | [[graphs.nodes]] 53 | name = "visualize" 54 | ty = "Visualize" 55 | livego_manger_url="http://localhost:8090/control/get?room=megflow-test" 56 | livego_upload_url_template="rtmp://localhost:1935/live/{}" 57 | 58 | 59 | [[graphs]] 60 | name = "cat_finder_video" 61 | connections = [ 62 | { cap = 16, ports = ["source:out", "destination:inp"] }, 63 | { cap = 16, ports = ["source:inp", "destination:out_source"] } 64 | ] 65 | 66 | [[graphs.nodes]] 67 | name = "source" 68 | ty = "VideoServer" 69 | port = 8082 70 | 71 | [[graphs.nodes]] 72 | name = "destination" 73 | ty = "subgraph" 74 | -------------------------------------------------------------------------------- /flow-python/examples/application/cat_finder/visualize/__init__.py: -------------------------------------------------------------------------------- 1 | # MegFlow is Licensed under the Apache License, Version 2.0 (the "License") 2 | # 3 | # Copyright (c) 2019-2021 Megvii Inc. All rights reserved. 4 | # 5 | # Unless required by applicable law or agreed to in writing, 6 | # software distributed under the License is distributed on an 7 | # "AS IS" BASIS, WITHOUT ARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 8 | 9 | #!/usr/bin/env python 10 | # coding=utf-8 11 | 12 | from .visualize import * 13 | from .shaper_visualize import * 14 | -------------------------------------------------------------------------------- /flow-python/examples/application/electric_bicycle/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | slug: electric-bicycle 3 | title: Electric Bicycle 4 | --- 5 | 6 | # 电梯电瓶车报警 7 | 8 | ## 功能概述 9 | 镜头前出现电瓶车立即报警,不会对同一辆车重复报警。本服务和[猫猫围栏](../cat_finder/README.md)在推送时机上有区别,猫是离开时才通知,电瓶车是进入就提示。一些安装命令和常见问题处理方式在[猫猫围栏](../cat_finder/README.md)已经说明,强烈建议先看上一个教程。 10 | 11 | ## 软硬件环境 12 | 13 | *nix 系统(Linux/Mac),x86 芯片。支持 onnx runtime 即可。 14 | 15 | ## [模型下载](../../../../docs/download-models.zh.md) 16 | 17 | ## 启动服务 18 | 19 | 安装运行依赖 20 | ```bash 21 | $ apt install redis-server 22 | $ redis-server & 23 | ... 24 | $ conda activate py38 25 | $ pip3 install onnxruntime --user 26 | ``` 27 | 28 | 准备一个 rtsp 视频流地址或者视频文件绝对路径做测试输入。相关教程已整合在 [如何生成自己的 rtsp 流地址](../../../../docs/02-how-to-run/generate-rtsp.zh.md) 。这里仅仅需要 29 | ```bash 30 | $ wget https://github.com/aler9/rtsp-simple-server/releases/download/v0.17.2/rtsp-simple-server_v0.17.2_linux_amd64.tar.gz 31 | $ 32 | $ tar xvf rtsp-simple-server_v0.17.2_linux_amd64.tar.gz && ./rtsp-simple-server 33 | $ ffmpeg -re -stream_loop -1 -i ${models}/cat_finder_testdata/test1.ts -c copy -f rtsp rtsp://127.0.0.1:8554/test1.ts 34 | ``` 35 | 36 | 启动服务 37 | ```bash 38 | $ cd ${path/to/MegFlow}/flow-python/examples/application 39 | $ megflow_run -c electric_bicycle/electric_bicycle.toml -p electric_bicycle 40 | ``` 41 | 服务配置文件在`electric_bicycle/electric_bicycle.toml`,解释参考 [graph definition](../../../../docs/03-how-to-add-my-service/appendix-A-graph-definition.zh.md) 。这里只需要打开 8083 端口服务,操作和[猫猫围栏](../cat_finder/README.md) 近似。 42 | 43 | ```bash 44 | $ google-chrome-stable http://127.0.0.1:8083/docs 45 | ``` 46 | -------------------------------------------------------------------------------- /flow-python/examples/application/electric_bicycle/__init__.py: -------------------------------------------------------------------------------- 1 | # MegFlow is Licensed under the Apache License, Version 2.0 (the "License") 2 | # 3 | # Copyright (c) 2019-2021 Megvii Inc. All rights reserved. 4 | # 5 | # Unless required by applicable law or agreed to in writing, 6 | # software distributed under the License is distributed on an 7 | # "AS IS" BASIS, WITHOUT ARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 8 | 9 | #!/usr/bin/env python 10 | # coding=utf-8 11 | -------------------------------------------------------------------------------- /flow-python/examples/application/electric_bicycle/electric_bicycle.toml: -------------------------------------------------------------------------------- 1 | main = "electric_bicycle" 2 | 3 | [[nodes]] 4 | name = "det" 5 | ty = "Detect" 6 | path = "models/memd_models/model.onnx" 7 | interval = 5 8 | visualize = 0 9 | score_thres = 0.5 10 | nms_thres = 0.5 11 | 12 | [[nodes]] 13 | name = "redis_proxy" 14 | ty = "RedisProxy" 15 | ip = "127.0.0.1" 16 | port = "6379" 17 | key = "notification.electric_bicycle" 18 | 19 | [[graphs]] 20 | name = "subgraph" 21 | inputs = [{ name = "inp", cap = 16, ports = ["det:inp"] }] 22 | outputs = [{ name = "out", cap = 16, ports = ["redis_proxy:out"] }] 23 | connections = [ 24 | { cap = 16, ports = ["det:out", "track:inp"] }, 25 | { cap = 16, ports = ["track:out", "shaper:inp"] }, 26 | { cap = 16, ports = ["shaper:out", "redis_proxy:inp"] }, 27 | ] 28 | 29 | [[graphs.nodes]] 30 | name = "track" 31 | ty = "Track" 32 | 33 | [[graphs.nodes]] 34 | name = "shaper" 35 | ty = "Shaper" 36 | mode = "FAST" 37 | 38 | 39 | [[graphs]] 40 | name = "electric_bicycle" 41 | connections = [ 42 | { cap = 16, ports = ["source:out", "destination:inp"] }, 43 | { cap = 16, ports = ["source:inp", "destination:out"] } 44 | ] 45 | 46 | [[graphs.nodes]] 47 | name = "source" 48 | ty = "VideoServer" 49 | port = 8083 50 | 51 | [[graphs.nodes]] 52 | name = "destination" 53 | ty = "subgraph" 54 | -------------------------------------------------------------------------------- /flow-python/examples/application/electric_bicycle/redis_proxy.py: -------------------------------------------------------------------------------- 1 | # MegFlow is Licensed under the Apache License, Version 2.0 (the "License") 2 | # 3 | # Copyright (c) 2019-2021 Megvii Inc. All rights reserved. 4 | # 5 | # Unless required by applicable law or agreed to in writing, 6 | # software distributed under the License is distributed on an 7 | # "AS IS" BASIS, WITHOUT ARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 8 | 9 | #!/usr/bin/env python 10 | # coding=utf-8 11 | 12 | import redis 13 | from loguru import logger 14 | from megflow import register 15 | 16 | 17 | @register(inputs=['inp'], outputs=['out']) 18 | class RedisProxy: 19 | def __init__(self, name, args): 20 | logger.info("init redis pool...") 21 | self.name = name 22 | self._key = args['key'] 23 | self._db = dict() 24 | 25 | ip = args['ip'] 26 | port = args['port'] 27 | 28 | self._pool = redis.ConnectionPool(host=ip, 29 | port=port, 30 | decode_responses=False) 31 | logger.info('redis pool initialized.') 32 | 33 | 34 | def exec(self): 35 | envelope = self.inp.recv() 36 | if envelope is None: 37 | return 38 | msg = envelope.msg 39 | crops = msg['shaper'] 40 | assert isinstance(crops, list) 41 | 42 | if len(crops) > 0: 43 | try: 44 | r = redis.Redis(connection_pool=self._pool) 45 | alert = f'{len(crops)} electric bicycle occur !' 46 | logger.info('alert {}'.format(alert)) 47 | r.lpush(self._key, alert) 48 | self.out.send(envelope.repack(alert)) 49 | except redis.exceptions.ConnectionError as e: 50 | logger.error(str(e)) 51 | -------------------------------------------------------------------------------- /flow-python/examples/application/electric_bicycle/shaper.py: -------------------------------------------------------------------------------- 1 | # MegFlow is Licensed under the Apache License, Version 2.0 (the "License") 2 | # 3 | # Copyright (c) 2019-2021 Megvii Inc. All rights reserved. 4 | # 5 | # Unless required by applicable law or agreed to in writing, 6 | # software distributed under the License is distributed on an 7 | # "AS IS" BASIS, WITHOUT ARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 8 | 9 | #!/usr/bin/env python 10 | # coding=utf-8 11 | 12 | from megflow import register 13 | 14 | 15 | @register(inputs=['inp'], outputs=['out']) 16 | class Shaper: 17 | def __init__(self, name, args): 18 | self.name = name 19 | self._mode = args['mode'] 20 | self._map = dict() 21 | 22 | self.idx = 0 23 | 24 | def expand(self, box, max_w, max_h, ratio): 25 | l = box[0] 26 | r = box[2] 27 | t = box[1] 28 | b = box[3] 29 | center_x = (l + r) / 2 30 | center_y = (t + b) / 2 31 | w_side = (r - l) * ratio / 2 32 | h_side = (b - t) * ratio / 2 33 | 34 | l = center_x - w_side 35 | r = center_x + w_side 36 | t = center_y - h_side 37 | b = center_y + h_side 38 | l = max(0, l) 39 | t = max(0, t) 40 | r = min(max_w, r) 41 | b = min(max_h, b) 42 | return int(l), int(t), int(r), int(b) 43 | 44 | def exec(self): 45 | envelope = self.inp.recv() 46 | if envelope is None: 47 | self._map.clear() 48 | return 49 | 50 | msg = envelope.msg 51 | 52 | # push the first 53 | msg['shaper'] = [] 54 | for track in msg['tracks']: 55 | tid = track['tid'] 56 | box = track['bbox'] 57 | if tid not in self._map: 58 | self._map[tid] = dict() 59 | 60 | data = msg['data'] 61 | l, t, r, b = self.expand(box, data.shape[1], data.shape[0], 62 | 1.1) 63 | crop = data[t:b, l:r] 64 | msg['shaper'].append(crop) 65 | # cv2.imwrite(f'shaper_{envelope.partial_id}.jpg', crop) 66 | 67 | self.out.send(envelope) 68 | -------------------------------------------------------------------------------- /flow-python/examples/application/electric_bicycle/track.py: -------------------------------------------------------------------------------- 1 | # MegFlow is Licensed under the Apache License, Version 2.0 (the "License") 2 | # 3 | # Copyright (c) 2019-2021 Megvii Inc. All rights reserved. 4 | # 5 | # Unless required by applicable law or agreed to in writing, 6 | # software distributed under the License is distributed on an 7 | # "AS IS" BASIS, WITHOUT ARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 8 | 9 | #!/usr/bin/env python 10 | # coding=utf-8 11 | 12 | from megflow import register 13 | 14 | from warehouse.track_iou import Tracker 15 | 16 | 17 | @register(inputs=['inp'], outputs=['out']) 18 | class Track: 19 | def __init__(self, name, _): 20 | self.name = name 21 | self._tracker = Tracker() 22 | 23 | def exec(self): 24 | envelope = self.inp.recv() 25 | if envelope is None: 26 | return 27 | 28 | items = envelope.msg['items'] 29 | # logger.debug(f'track input: {items}') 30 | 31 | tracks, failed_ids = self._tracker.track(items) 32 | envelope.msg['tracks'] = tracks 33 | envelope.msg['failed_ids'] = failed_ids 34 | # logger.debug(f'track output: {tracks}, failed_ids: {failed_ids}') 35 | self.out.send(envelope) 36 | -------------------------------------------------------------------------------- /flow-python/examples/application/misc/bytes_client.py: -------------------------------------------------------------------------------- 1 | # MegFlow is Licensed under the Apache License, Version 2.0 (the "License") 2 | # 3 | # Copyright (c) 2019-2021 Megvii Inc. All rights reserved. 4 | # 5 | # Unless required by applicable law or agreed to in writing, 6 | # software distributed under the License is distributed on an 7 | # "AS IS" BASIS, WITHOUT ARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 8 | 9 | #!/usr/bin/env python 10 | # coding=utf-8 11 | import requests 12 | 13 | def test(): 14 | ip = 'localhost' 15 | port = '8080' 16 | url = f'http://{ip}:{port}/analyze/bytes' 17 | data = b'abcde' 18 | 19 | headers = {'Content-Length': f'{len(data)}', 'Content-Type': '*/*'} 20 | res = requests.post(url, data=data, headers=headers) 21 | print(res.content) 22 | 23 | 24 | if __name__ == "__main__": 25 | test() 26 | -------------------------------------------------------------------------------- /flow-python/examples/application/misc/image_client.py: -------------------------------------------------------------------------------- 1 | # MegFlow is Licensed under the Apache License, Version 2.0 (the "License") 2 | # 3 | # Copyright (c) 2019-2021 Megvii Inc. All rights reserved. 4 | # 5 | # Unless required by applicable law or agreed to in writing, 6 | # software distributed under the License is distributed on an 7 | # "AS IS" BASIS, WITHOUT ARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 8 | 9 | #!/usr/bin/env python 10 | # coding=utf-8 11 | import requests 12 | import cv2 13 | 14 | def test(): 15 | ip = 'localhost' 16 | port = '8084' 17 | user_define_string = 'content' 18 | url = f'http://{ip}:{port}/analyze/{user_define_string}' 19 | img = cv2.imread("./test.jpg") 20 | _, data = cv2.imencode(".jpg", img) 21 | data = data.tobytes() 22 | 23 | headers = {'Content-Length': f'{len(data)}', 'Content-Type': 'image/*'} 24 | res = requests.post(url, data=data, headers=headers) 25 | print(res.content) 26 | 27 | 28 | if __name__ == "__main__": 29 | test() 30 | -------------------------------------------------------------------------------- /flow-python/examples/application/misc/video_client.py: -------------------------------------------------------------------------------- 1 | # MegFlow is Licensed under the Apache License, Version 2.0 (the "License") 2 | # 3 | # Copyright (c) 2019-2021 Megvii Inc. All rights reserved. 4 | # 5 | # Unless required by applicable law or agreed to in writing, 6 | # software distributed under the License is distributed on an 7 | # "AS IS" BASIS, WITHOUT ARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 8 | 9 | #!/usr/bin/env python 10 | # coding=utf-8 11 | import urllib 12 | import requests 13 | 14 | 15 | def test(): 16 | ip = 'localhost' 17 | port = '8085' 18 | video_path = 'rtsp://127.0.0.1:8554/vehicle.ts' 19 | video_path = urllib.parse.quote(video_path, safe='') 20 | url = 'http://{}:{}/start/{}'.format(ip, port, video_path) 21 | 22 | res = requests.post(url) 23 | ret = res.content 24 | print(ret) 25 | 26 | 27 | if __name__ == "__main__": 28 | test() 29 | -------------------------------------------------------------------------------- /flow-python/examples/application/misc/visualize_client/demo.css: -------------------------------------------------------------------------------- 1 | .mainContainer { 2 | display: block; 3 | width: 100%; 4 | margin-left: auto; 5 | margin-right: auto; 6 | } 7 | @media screen and (min-width: 1152px) { 8 | .mainContainer { 9 | display: block; 10 | width: 1152px; 11 | margin-left: auto; 12 | margin-right: auto; 13 | } 14 | } 15 | 16 | .video-container { 17 | position: relative; 18 | margin-top: 8px; 19 | } 20 | 21 | .video-container:before { 22 | display: block; 23 | content: ""; 24 | width: 100%; 25 | padding-bottom: 56.25%; 26 | } 27 | 28 | .video-container > div { 29 | position: absolute; 30 | top: 0; 31 | left: 0; 32 | right: 0; 33 | bottom: 0; 34 | } 35 | 36 | .video-container video { 37 | width: 100%; 38 | height: 100%; 39 | } 40 | 41 | .urlInput { 42 | display: block; 43 | width: 100%; 44 | margin-left: auto; 45 | margin-right: auto; 46 | margin-top: 8px; 47 | margin-bottom: 8px; 48 | } 49 | 50 | .centeredVideo { 51 | display: block; 52 | width: 100%; 53 | height: 100%; 54 | margin-left: auto; 55 | margin-right: auto; 56 | margin-bottom: auto; 57 | } 58 | 59 | .controls { 60 | display: block; 61 | width: 100%; 62 | text-align: left; 63 | margin-left: auto; 64 | margin-right: auto; 65 | margin-top: 8px; 66 | margin-bottom: 10px; 67 | } 68 | 69 | .logcatBox { 70 | border-color: #CCCCCC; 71 | font-size: 11px; 72 | font-family: Menlo, Consolas, monospace; 73 | display: block; 74 | width: 100%; 75 | text-align: left; 76 | margin-left: auto; 77 | margin-right: auto; 78 | } 79 | 80 | .url-input , .options { 81 | font-size: 13px; 82 | } 83 | 84 | .url-input { 85 | display: flex; 86 | } 87 | 88 | .url-input label { 89 | flex: initial; 90 | } 91 | 92 | .url-input input { 93 | flex: auto; 94 | margin-left: 8px; 95 | } 96 | 97 | .url-input button { 98 | flex: initial; 99 | margin-left: 8px; 100 | } 101 | 102 | .options { 103 | margin-top: 5px; 104 | } 105 | 106 | .hidden { 107 | display: none; 108 | } 109 | -------------------------------------------------------------------------------- /flow-python/examples/application/misc/visualize_client/push_video.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import os 3 | import requests 4 | 5 | def main(ip, filename): 6 | url_template = "http://{}:8090/control/get?room=megflow-test" 7 | url = url_template.format(ip) 8 | print(url) 9 | result = requests.get(url) 10 | if not result.ok: 11 | raise Exception('req channel failed') 12 | channel = result.json()['data'] 13 | upload_cmd = f'ffmpeg -re -i {filename} -c copy -f flv rtmp://{ip}:1935/live/{channel}' 14 | # upload flv 15 | os.system(upload_cmd) 16 | 17 | 18 | if __name__ == "__main__": 19 | videoname = 'demo.flv' 20 | if len(sys.argv) < 2: 21 | print(f'usage python3 {sys.argv[0]} videoname, use default demo.flv') 22 | else: 23 | videoname = sys.argv[1] 24 | main('localhost', videoname) 25 | -------------------------------------------------------------------------------- /flow-python/examples/application/requires.txt: -------------------------------------------------------------------------------- 1 | numpy 2 | scipy 3 | opencv-python 4 | loguru 5 | redis 6 | -------------------------------------------------------------------------------- /flow-python/examples/application/simple_classification/__init__.py: -------------------------------------------------------------------------------- 1 | # MegFlow is Licensed under the Apache License, Version 2.0 (the "License") 2 | # 3 | # Copyright (c) 2019-2021 Megvii Inc. All rights reserved. 4 | # 5 | # Unless required by applicable law or agreed to in writing, 6 | # software distributed under the License is distributed on an 7 | # "AS IS" BASIS, WITHOUT ARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 8 | 9 | #!/usr/bin/env python 10 | # coding=utf-8 11 | -------------------------------------------------------------------------------- /flow-python/examples/application/simple_classification/classify.py: -------------------------------------------------------------------------------- 1 | # MegFlow is Licensed under the Apache License, Version 2.0 (the "License") 2 | # 3 | # Copyright (c) 2019-2021 Megvii Inc. All rights reserved. 4 | # 5 | # Unless required by applicable law or agreed to in writing, 6 | # software distributed under the License is distributed on an 7 | # "AS IS" BASIS, WITHOUT ARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 8 | 9 | #!/usr/bin/env python 10 | # coding=utf-8 11 | 12 | import json 13 | import numpy as np 14 | from loguru import logger 15 | from megflow import register 16 | 17 | from .lite import PredictorLite 18 | 19 | 20 | @register(inputs=['inp'], outputs=['out']) 21 | class Classify: 22 | def __init__(self, name, arg): 23 | logger.info("loading Resnet18 Classification...") 24 | self.name = name 25 | 26 | # load ReID model and warmup 27 | self._model = PredictorLite(path=arg['path'], 28 | device=arg['device'], 29 | device_id=arg['device_id']) 30 | warmup_data = np.zeros((224, 224, 3), dtype=np.uint8) 31 | self._model.inference(warmup_data) 32 | logger.info("Resnet18 loaded.") 33 | 34 | def exec(self): 35 | envelope = self.inp.recv() 36 | if envelope is None: 37 | return 38 | 39 | data = envelope.msg['data'] 40 | result = self._model.inference(data) 41 | self.out.send(envelope.repack(json.dumps(str(result)))) 42 | -------------------------------------------------------------------------------- /flow-python/examples/application/simple_classification/image_cpu.toml: -------------------------------------------------------------------------------- 1 | main = "tutorial_01_image" 2 | 3 | [[graphs]] 4 | name = "subgraph" 5 | inputs = [{ name = "inp", cap = 16, ports = ["classify:inp"] }] 6 | outputs = [{ name = "out", cap = 16, ports = ["classify:out"] }] 7 | connections = [ 8 | ] 9 | 10 | [[graphs.nodes]] 11 | name = "classify" 12 | ty = "Classify" 13 | path = "models/simple_classification_models/resnet18.mge" 14 | device = "cpu" 15 | device_id = 0 16 | 17 | [[graphs]] 18 | name = "tutorial_01_image" 19 | connections = [ 20 | { cap = 16, ports = ["source:out", "destination:inp"] }, 21 | { cap = 16, ports = ["source:inp", "destination:out"] } 22 | ] 23 | 24 | [[graphs.nodes]] 25 | name = "source" 26 | ty = "ImageServer" 27 | port = 8084 28 | response = "json" 29 | 30 | [[graphs.nodes]] 31 | name = "destination" 32 | ty = "subgraph" 33 | -------------------------------------------------------------------------------- /flow-python/examples/application/simple_classification/image_test.toml: -------------------------------------------------------------------------------- 1 | main = "tutorial_01_image" 2 | 3 | [[graphs]] 4 | name = "subgraph" 5 | inputs = [{ name = "inp", cap = 16, ports = ["classify:inp"] }] 6 | outputs = [{ name = "out", cap = 16, ports = ["classify:out"] }] 7 | connections = [ 8 | ] 9 | 10 | [[graphs.nodes]] 11 | name = "classify" 12 | ty = "Classify" 13 | path = "models/simple_classification_models/resnet18.mge" 14 | device = "cpu" 15 | device_id = 0 16 | 17 | [[graphs]] 18 | name = "tutorial_01_image" 19 | connections = [ 20 | { cap = 16, ports = ["source:out", "destination:inp"] }, 21 | { cap = 16, ports = ["source:inp", "destination:out"] } 22 | ] 23 | 24 | [[graphs.nodes]] 25 | name = "source" 26 | ty = "ImageInput" 27 | urls = ["/path/to/your/images/"] 28 | 29 | [[graphs.nodes]] 30 | name = "destination" 31 | ty = "subgraph" 32 | -------------------------------------------------------------------------------- /flow-python/examples/application/simple_det_classify/__init__.py: -------------------------------------------------------------------------------- 1 | # MegFlow is Licensed under the Apache License, Version 2.0 (the "License") 2 | # 3 | # Copyright (c) 2019-2021 Megvii Inc. All rights reserved. 4 | # 5 | # Unless required by applicable law or agreed to in writing, 6 | # software distributed under the License is distributed on an 7 | # "AS IS" BASIS, WITHOUT ARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 8 | 9 | #!/usr/bin/env python 10 | # coding=utf-8 11 | -------------------------------------------------------------------------------- /flow-python/examples/application/simple_det_classify/video_cpu.toml: -------------------------------------------------------------------------------- 1 | main = "tutorial_02" 2 | 3 | [[nodes]] 4 | name = "det" 5 | ty = "Detect" 6 | model = "yolox-s" 7 | conf = 0.25 8 | nms = 0.45 9 | tsize = 640 10 | path = "models/simple_det_classify_models/yolox_s.mge" 11 | interval = 5 12 | visualize = 1 13 | device = "cpu" 14 | device_id = 0 15 | 16 | [[nodes]] 17 | name = "classify" 18 | ty = "Classify" 19 | path = "models/simple_det_classify_models/resnet18_preproc_inside.mge" 20 | device = "cpu" 21 | device_id = 0 22 | max_batch = 4 23 | wait_time = 100 24 | 25 | [[graphs]] 26 | name = "subgraph" 27 | inputs = [{ name = "inp", cap = 16, ports = ["det:inp"] }] 28 | outputs = [{ name = "out", cap = 16, ports = ["classify:out"] }] 29 | connections = [ 30 | { cap = 16, ports = ["det:out", "classify:inp"] }, 31 | ] 32 | 33 | [[graphs]] 34 | name = "tutorial_02" 35 | connections = [ 36 | { cap = 16, ports = ["source:out", "destination:inp"] }, 37 | { cap = 16, ports = ["source:inp", "destination:out"] } 38 | ] 39 | 40 | [[graphs.nodes]] 41 | name = "source" 42 | ty = "VideoServer" 43 | port = 8085 44 | 45 | [[graphs.nodes]] 46 | name = "destination" 47 | ty = "subgraph" 48 | -------------------------------------------------------------------------------- /flow-python/examples/application/simple_det_classify/video_test.toml: -------------------------------------------------------------------------------- 1 | main = "tutorial_02" 2 | 3 | [[nodes]] 4 | name = "det" 5 | ty = "Detect" 6 | model = "yolox-s" 7 | conf = 0.25 8 | nms = 0.45 9 | tsize = 640 10 | path = "models/simple_det_classify_models/yolox_s.mge" 11 | interval = 1 12 | visualize = 1 13 | device = "gpu" 14 | device_id = 0 15 | 16 | [[nodes]] 17 | name = "classify" 18 | ty = "Classify" 19 | path = "models/simple_det_classify_models/resnet18_preproc_inside.mge" 20 | device = "gpu" 21 | device_id = 0 22 | max_batch = 4 23 | wait_time = 100 24 | 25 | [[graphs]] 26 | name = "subgraph" 27 | inputs = [{ name = "inp", cap = 16, ports = ["det:inp"] }] 28 | outputs = [{ name = "out", cap = 16, ports = ["classify:out"] }] 29 | connections = [ 30 | { cap = 16, ports = ["det:out", "classify:inp"] }, 31 | ] 32 | 33 | [[graphs]] 34 | name = "tutorial_02" 35 | connections = [ 36 | { cap = 16, ports = ["source:out", "destination:inp"] }, 37 | { cap = 16, ports = ["source:inp", "destination:out"] } 38 | ] 39 | 40 | [[graphs.nodes]] 41 | name = "source" 42 | ty = "VideoInput" 43 | repeat = 1 44 | urls = ["/path/to/your/video.ts"] 45 | 46 | [[graphs.nodes]] 47 | name = "destination" 48 | ty = "subgraph" 49 | -------------------------------------------------------------------------------- /flow-python/examples/application/video_super_resolution/README.md: -------------------------------------------------------------------------------- 1 | # 视频实时超分 2 | 3 | ## 一、[下载模型和测试数据](../../../../docs/download-models.zh.md) 4 | 5 | ## 二、运行 6 | 7 | 模型软链后,使用`megflow_run`运行 8 | ```bash 9 | $ cd ${path/to/MegFlow}/flow-python/examples/application # 这行必须 10 | $ megflow_run -c video_super_resolution/config.toml -p video_super_resolution 11 | ``` 12 | 13 | 浏览器打开 [8087 端口](http://10.122.101.175:8087/docs#/default/post_start__url_),try it out。 14 | 15 | 1)上传测试数据中的 `a.mp4`,获取 stream_id 16 | 17 | 2)用 stream_id 查询结果存到了哪个文件,例如 kj2WAS.flv 18 | 19 | ## 三、如何使用自己的模型 20 | 21 | 超分模型使用 [basicVSR_mge](https://github.com/Feynman1999/basicVSR_mge) 做训练,炼丹结束后请 cherry-pick [这个 PR](https://github.com/Feynman1999/basicVSR_mge/pull/6),然后把模型 jit.trace 成纯推理格式。 22 | 23 | ```bash 24 | $ cd ${path/to/basicVSR_mge} 25 | $ python3 tools/dump.py configs/restorers/BasicVSR/basicVSR_test_valid.py 26 | ``` 27 | 28 | ## 四、其他 29 | 构造测试输入需**注意视频质量**,例如 30 | ```bash 31 | $ ffmpeg -i images/%08d.png -q:v 2 -vcodec mpeg4 -r 20 a.mp4 32 | ``` 33 | -------------------------------------------------------------------------------- /flow-python/examples/application/video_super_resolution/__init__.py: -------------------------------------------------------------------------------- 1 | # MegFlow is Licensed under the Apache License, Version 2.0 (the "License") 2 | # 3 | # Copyright (c) 2019-2021 Megvii Inc. All rights reserved. 4 | # 5 | # Unless required by applicable law or agreed to in writing, 6 | # software distributed under the License is distributed on an 7 | # "AS IS" BASIS, WITHOUT ARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 8 | 9 | #!/usr/bin/env python 10 | # coding=utf-8 11 | -------------------------------------------------------------------------------- /flow-python/examples/application/video_super_resolution/config.toml: -------------------------------------------------------------------------------- 1 | main = "vsr" 2 | 3 | [[graphs]] 4 | name = "subgraph" 5 | inputs = [{ name = "inp", cap = 128, ports = ["model:inp"] }] 6 | outputs = [{ name = "out", cap = 128, ports = ["save:out"] }] 7 | connections = [ 8 | { cap = 128, ports = ["model:out", "save:inp"] } 9 | ] 10 | 11 | [[graphs.nodes]] 12 | name = "model" 13 | ty = "Model" 14 | dir = "models/vsr_models" 15 | device = "gpu" 16 | device_id = 0 17 | 18 | [[graphs.nodes]] 19 | name = "save" 20 | ty = "Save" 21 | path = "./" 22 | 23 | [[graphs]] 24 | name = "vsr" 25 | connections = [ 26 | { cap = 16, ports = ["source:out", "destination:inp"] }, 27 | { cap = 16, ports = ["source:inp", "destination:out"] } 28 | ] 29 | 30 | [[graphs.nodes]] 31 | name = "source" 32 | ty = "VideoServer" 33 | port = 8087 34 | response = "json" 35 | 36 | [[graphs.nodes]] 37 | name = "destination" 38 | ty = "subgraph" 39 | -------------------------------------------------------------------------------- /flow-python/examples/application/video_super_resolution/requires.sh: -------------------------------------------------------------------------------- 1 | # upgrade pip 2 | python3 -m pip install --upgrade pip 3 | # install partial requirements with tuna mirror 4 | python3 -m pip install -i https://pypi.tuna.tsinghua.edu.cn/simple -r requires.txt 5 | # install megenginelite for inference 6 | python3 -m pip install megengine -f https://megengine.org.cn/whl/mge.html 7 | 8 | -------------------------------------------------------------------------------- /flow-python/examples/application/video_super_resolution/requires.txt: -------------------------------------------------------------------------------- 1 | numpy 2 | scipy 3 | opencv-python 4 | loguru 5 | -------------------------------------------------------------------------------- /flow-python/examples/application/warehouse/detection_memd/README.md: -------------------------------------------------------------------------------- 1 | # Megvii Electric Moped Detector 2 | 包含电动车检测 ONNX 模型及推理代码 3 | 4 | gif1 gif2 gif3 5 | 6 | 完整视频: [demo_1](https://v.qq.com/x/page/k3257ewacxa.html), [demo_2](https://v.qq.com/x/page/e3257kxhhut.html), [demo_3](https://v.qq.com/x/page/y32572ztgnt.html) 7 | 8 | ## 准备工作 9 | 安装 cuda 10.1 以及 cudnn 7.6.3 10 | 11 | ## 安装 12 | ``` 13 | git clone https://github.com/megvii-research/MEMD.git 14 | cd MEMD 15 | pip3 install -r requirements.txt 16 | ``` 17 | 18 | ## 推理 19 | ``` 20 | python3 scripts/inference.py --detector ./models/model.onnx --input-img ./demo/input.jpg --model-json ./models/config.json --output-path ./demo/output.jpg 21 | 22 | --detector :onnx模型 23 | --input-img :输入图片地址 24 | --model-json :模型的配置文件地址 25 | --output-path :检测结果文件地址 26 | ``` 27 | -------------------------------------------------------------------------------- /flow-python/examples/application/warehouse/detection_memd/__init__.py: -------------------------------------------------------------------------------- 1 | # MegFlow is Licensed under the Apache License, Version 2.0 (the "License") 2 | # 3 | # Copyright (c) 2019-2021 Megvii Inc. All rights reserved. 4 | # 5 | # Unless required by applicable law or agreed to in writing, 6 | # software distributed under the License is distributed on an 7 | # "AS IS" BASIS, WITHOUT ARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 8 | 9 | #!/usr/bin/env python 10 | # coding=utf-8 11 | 12 | from .onnx_model import load_onnx_model, run 13 | -------------------------------------------------------------------------------- /flow-python/examples/application/warehouse/detection_memd/requirements.txt: -------------------------------------------------------------------------------- 1 | onnxruntime-gpu==1.2.0 2 | opencv-python 3 | numpy 4 | loguru 5 | Cython -------------------------------------------------------------------------------- /flow-python/examples/application/warehouse/detection_yolox/README.md: -------------------------------------------------------------------------------- 1 | # YOLOX-Python-MegEngine 2 | 3 | Python version of YOLOX object detection base on [MegEngine](https://github.com/MegEngine/MegEngine). 4 | 5 | ## Tutorial 6 | 7 | ### Step1: install requirements 8 | 9 | ``` 10 | python3 -m pip install megengine -f https://megengine.org.cn/whl/mge.html 11 | ``` 12 | 13 | ### Step2: convert checkpoint weights from torch's path file 14 | 15 | ``` 16 | python3 convert_weights.py -w yolox_s.pth -o yolox_s_mge.pkl 17 | ``` 18 | 19 | ### Step3: run demo 20 | 21 | This part is the same as torch's python demo, but no need to specify device. 22 | 23 | ``` 24 | python3 lite.py image -n yolox-s -c yolox_s_mge.pkl --path ../../../assets/dog.jpg --conf 0.25 --nms 0.45 --tsize 640 --save_result 25 | ``` 26 | 27 | ### [Optional]Step4: dump model for cpp inference 28 | 29 | > **Note**: result model is dumped with `optimize_for_inference` and `enable_fuse_conv_bias_nonlinearity`. 30 | 31 | ``` 32 | python3 tools/dump.py -n yolox-s -c yolox_s_mge.pkl --dump_path yolox_s.mge 33 | ``` 34 | -------------------------------------------------------------------------------- /flow-python/examples/application/warehouse/detection_yolox/__init__.py: -------------------------------------------------------------------------------- 1 | # MegFlow is Licensed under the Apache License, Version 2.0 (the "License") 2 | # 3 | # Copyright (c) 2019-2021 Megvii Inc. All rights reserved. 4 | # 5 | # Unless required by applicable law or agreed to in writing, 6 | # software distributed under the License is distributed on an 7 | # "AS IS" BASIS, WITHOUT ARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 8 | 9 | #!/usr/bin/env python 10 | # coding=utf-8 11 | 12 | from .lite import PredictorLite 13 | -------------------------------------------------------------------------------- /flow-python/examples/application/warehouse/detection_yolox/coco_classes.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding:utf-8 -*- 3 | # Copyright (c) Megvii, Inc. and its affiliates. 4 | 5 | COCO_CLASSES = ( 6 | "person", 7 | "bicycle", 8 | "car", 9 | "motorcycle", 10 | "airplane", 11 | "bus", 12 | "train", 13 | "truck", 14 | "boat", 15 | "traffic light", 16 | "fire hydrant", 17 | "stop sign", 18 | "parking meter", 19 | "bench", 20 | "bird", 21 | "cat", 22 | "dog", 23 | "horse", 24 | "sheep", 25 | "cow", 26 | "elephant", 27 | "bear", 28 | "zebra", 29 | "giraffe", 30 | "backpack", 31 | "umbrella", 32 | "handbag", 33 | "tie", 34 | "suitcase", 35 | "frisbee", 36 | "skis", 37 | "snowboard", 38 | "sports ball", 39 | "kite", 40 | "baseball bat", 41 | "baseball glove", 42 | "skateboard", 43 | "surfboard", 44 | "tennis racket", 45 | "bottle", 46 | "wine glass", 47 | "cup", 48 | "fork", 49 | "knife", 50 | "spoon", 51 | "bowl", 52 | "banana", 53 | "apple", 54 | "sandwich", 55 | "orange", 56 | "broccoli", 57 | "carrot", 58 | "hot dog", 59 | "pizza", 60 | "donut", 61 | "cake", 62 | "chair", 63 | "couch", 64 | "potted plant", 65 | "bed", 66 | "dining table", 67 | "toilet", 68 | "tv", 69 | "laptop", 70 | "mouse", 71 | "remote", 72 | "keyboard", 73 | "cell phone", 74 | "microwave", 75 | "oven", 76 | "toaster", 77 | "sink", 78 | "refrigerator", 79 | "book", 80 | "clock", 81 | "vase", 82 | "scissors", 83 | "teddy bear", 84 | "hair drier", 85 | "toothbrush", 86 | ) 87 | -------------------------------------------------------------------------------- /flow-python/examples/application/warehouse/quality_naive/__init__.py: -------------------------------------------------------------------------------- 1 | # MegFlow is Licensed under the Apache License, Version 2.0 (the "License") 2 | # 3 | # Copyright (c) 2019-2021 Megvii Inc. All rights reserved. 4 | # 5 | # Unless required by applicable law or agreed to in writing, 6 | # software distributed under the License is distributed on an 7 | # "AS IS" BASIS, WITHOUT ARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 8 | 9 | #!/usr/bin/env python 10 | # coding=utf-8 11 | 12 | from .quality import Quality 13 | -------------------------------------------------------------------------------- /flow-python/examples/application/warehouse/quality_naive/quality.py: -------------------------------------------------------------------------------- 1 | # MegFlow is Licensed under the Apache License, Version 2.0 (the "License") 2 | # 3 | # Copyright (c) 2019-2021 Megvii Inc. All rights reserved. 4 | # 5 | # Unless required by applicable law or agreed to in writing, 6 | # software distributed under the License is distributed on an 7 | # "AS IS" BASIS, WITHOUT ARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 8 | 9 | #!/usr/bin/env python 10 | # coding=utf-8 11 | 12 | import cv2 13 | 14 | 15 | class Quality: 16 | @staticmethod 17 | def clearness(mat): 18 | return cv2.Laplacian(mat, cv2.CV_64F).var() 19 | 20 | @staticmethod 21 | def area(mat): 22 | return mat.shape[0] * mat.shape[1] 23 | 24 | 25 | if __name__ == "__main__": 26 | import sys 27 | mat = cv2.imread(sys.argv[1]) 28 | if mat is None: 29 | print(f'load {sys.argv[1]} failed') 30 | sys.exit(-1) 31 | print(f'brightness: {Quality.clearness(mat)}') 32 | -------------------------------------------------------------------------------- /flow-python/examples/application/warehouse/reid_alignedreid/README.md: -------------------------------------------------------------------------------- 1 | # How to test AlignedReid Model 2 | 3 | Download models and soft link 4 | ```bash 5 | $ ln -s ${DOWNLOAD_DIR} flow-python/examples/models 6 | ``` 7 | 8 | Run 9 | 10 | ```bash 11 | $ cd flow-python/examples/warehouse 12 | $ python3 -m reid_alignedreid.main ../models/aligned_reid.pkl reid_alignedreid/image/positive1.jpg reid_alignedreid/image/positive2.jpg reid_alignedreid/image/negative.jpg 13 | 14 | 10 19:07:09 WRN Unused params in `strict=False` mode, unused={'local_bn.bias', 'fc.weight', 'local_bn.running_mean', 'local_bn.running_var', 'local_conv.weight', 'local_conv.bias', 'fc.bias', 'local_bn.weight'} 15 | distance_positive: 0.0987139493227005 16 | distance_negtive: 0.2990190088748932 17 | ``` 18 | -------------------------------------------------------------------------------- /flow-python/examples/application/warehouse/reid_alignedreid/__init__.py: -------------------------------------------------------------------------------- 1 | # MegFlow is Licensed under the Apache License, Version 2.0 (the "License") 2 | # 3 | # Copyright (c) 2019-2021 Megvii Inc. All rights reserved. 4 | # 5 | # Unless required by applicable law or agreed to in writing, 6 | # software distributed under the License is distributed on an 7 | # "AS IS" BASIS, WITHOUT ARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 8 | 9 | #!/usr/bin/env python 10 | # coding=utf-8 11 | 12 | # from .model import Model 13 | from .lite import PredictorLite 14 | -------------------------------------------------------------------------------- /flow-python/examples/application/warehouse/reid_alignedreid/dump.py: -------------------------------------------------------------------------------- 1 | # MegFlow is Licensed under the Apache License, Version 2.0 (the "License") 2 | # 3 | # Copyright (c) 2019-2021 Megvii Inc. All rights reserved. 4 | # 5 | # Unless required by applicable law or agreed to in writing, 6 | # software distributed under the License is distributed on an 7 | # "AS IS" BASIS, WITHOUT ARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 8 | 9 | #!/usr/bin/env python 10 | # coding=utf-8 11 | 12 | import argparse 13 | import megengine as mge 14 | import numpy as np 15 | from megengine import jit 16 | from .model import Model 17 | 18 | 19 | def make_parser(): 20 | parser = argparse.ArgumentParser("Resnet50 Dump") 21 | parser.add_argument("-c", 22 | "--ckpt", 23 | default=None, 24 | type=str, 25 | help="ckpt for eval") 26 | parser.add_argument("--dump_path", 27 | default="model.mge", 28 | help="path to save the dumped model") 29 | return parser 30 | 31 | 32 | def dump_static_graph(model, graph_name="model.mge"): 33 | model.eval() 34 | 35 | data = mge.Tensor(np.random.random((1, 3, 224, 224))) 36 | 37 | @jit.trace(capture_as_const=True) 38 | def pred_func(data): 39 | outputs = model(data) 40 | return outputs 41 | 42 | pred_func(data) 43 | pred_func.dump( 44 | graph_name, 45 | arg_names=["data"], 46 | optimize_for_inference=True, 47 | enable_fuse_conv_bias_nonlinearity=True, 48 | ) 49 | 50 | 51 | def main(args): 52 | model = Model() 53 | sd = mge.load(args.ckpt) 54 | model.load_state_dict(sd, strict=False) 55 | model.eval() 56 | 57 | dump_static_graph(model, args.dump_path) 58 | 59 | 60 | if __name__ == "__main__": 61 | args = make_parser().parse_args() 62 | main(args) 63 | -------------------------------------------------------------------------------- /flow-python/examples/application/warehouse/reid_alignedreid/image/badcase_tiger.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MegEngine/MegFlow/41cb9f85828aa080f6004674904fe430d8268052/flow-python/examples/application/warehouse/reid_alignedreid/image/badcase_tiger.jpg -------------------------------------------------------------------------------- /flow-python/examples/application/warehouse/reid_alignedreid/image/negative.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MegEngine/MegFlow/41cb9f85828aa080f6004674904fe430d8268052/flow-python/examples/application/warehouse/reid_alignedreid/image/negative.jpg -------------------------------------------------------------------------------- /flow-python/examples/application/warehouse/reid_alignedreid/image/pinggai_1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MegEngine/MegFlow/41cb9f85828aa080f6004674904fe430d8268052/flow-python/examples/application/warehouse/reid_alignedreid/image/pinggai_1.jpg -------------------------------------------------------------------------------- /flow-python/examples/application/warehouse/reid_alignedreid/image/positive1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MegEngine/MegFlow/41cb9f85828aa080f6004674904fe430d8268052/flow-python/examples/application/warehouse/reid_alignedreid/image/positive1.jpg -------------------------------------------------------------------------------- /flow-python/examples/application/warehouse/reid_alignedreid/image/positive2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MegEngine/MegFlow/41cb9f85828aa080f6004674904fe430d8268052/flow-python/examples/application/warehouse/reid_alignedreid/image/positive2.jpg -------------------------------------------------------------------------------- /flow-python/examples/application/warehouse/reid_alignedreid/image/positive_pinggai.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MegEngine/MegFlow/41cb9f85828aa080f6004674904fe430d8268052/flow-python/examples/application/warehouse/reid_alignedreid/image/positive_pinggai.jpg -------------------------------------------------------------------------------- /flow-python/examples/application/warehouse/reid_alignedreid/main.py: -------------------------------------------------------------------------------- 1 | # MegFlow is Licensed under the Apache License, Version 2.0 (the "License") 2 | # 3 | # Copyright (c) 2019-2021 Megvii Inc. All rights reserved. 4 | # 5 | # Unless required by applicable law or agreed to in writing, 6 | # software distributed under the License is distributed on an 7 | # "AS IS" BASIS, WITHOUT ARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 8 | 9 | #!/usr/bin/env python 10 | # coding=utf-8 11 | 12 | from math import log 13 | from loguru import logger 14 | import megengine as mge 15 | import cv2 16 | import megengine.functional as F 17 | import numpy as np 18 | 19 | from .model import Model 20 | 21 | if __name__ == "__main__": 22 | import sys 23 | if len(sys.argv) < 5: 24 | print( 25 | "usage: python3 -m reid_alignedreid/demo reid.pkl positive1.png positive2.png negtive.jpg" 26 | ) 27 | sys.exit(0) 28 | model = Model() 29 | sd = mge.load(sys.argv[1]) 30 | model.load_state_dict(sd, strict=False) 31 | model.eval() 32 | feat1 = model.inference(cv2.imread(sys.argv[2])) 33 | logger.info(f'{feat1}') 34 | feat2 = model.inference(cv2.imread(sys.argv[3])) 35 | logger.info(f'{feat2}') 36 | 37 | feat3 = model.inference(cv2.imread(sys.argv[4])) 38 | logger.info(f'{feat3}') 39 | 40 | positive = np.linalg.norm(feat1 - feat2) 41 | print(f'distance_positive: {positive}') 42 | 43 | negtive = np.linalg.norm(feat3 - feat2) 44 | print(f'distance_negtive: {negtive}') 45 | -------------------------------------------------------------------------------- /flow-python/examples/application/warehouse/reid_alignedreid/model.py: -------------------------------------------------------------------------------- 1 | # MegFlow is Licensed under the Apache License, Version 2.0 (the "License") 2 | # 3 | # Copyright (c) 2019-2021 Megvii Inc. All rights reserved. 4 | # 5 | # Unless required by applicable law or agreed to in writing, 6 | # software distributed under the License is distributed on an 7 | # "AS IS" BASIS, WITHOUT ARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 8 | 9 | #!/usr/bin/env python 10 | # coding=utf-8 11 | 12 | import megengine as mge 13 | import megengine.module as nn 14 | import megengine.module.init as init 15 | import megengine.functional as F 16 | import time 17 | from loguru import logger 18 | 19 | from .resnet import resnet50, resnet18 20 | from .process import preprocess 21 | 22 | 23 | class Model(nn.Module): 24 | def __init__(self): 25 | super(Model, self).__init__() 26 | self.base = resnet50() 27 | # planes = 2048 28 | 29 | def forward(self, x): 30 | """ 31 | Returns: 32 | global_feat: shape [N, C] 33 | local_feat: shape [N, H, c] 34 | """ 35 | # shape [N, C, H, W] 36 | feat = self.base(x) 37 | feat = F.avg_pool2d(feat, (feat.shape[2], feat.shape[3])) 38 | # shape [N, C] 39 | feat = F.flatten(feat, 1) 40 | feat = F.normalize(feat, axis=1) 41 | return feat 42 | 43 | def inference(self, mat): 44 | t0 = time.time() 45 | img = preprocess(mat, 46 | input_size=(224, 224), 47 | scale_im=True, 48 | mean=[0.486, 0.459, 0.408], 49 | std=[0.229, 0.224, 0.225]) 50 | img = F.expand_dims(mge.tensor(img), 0) 51 | feat = self.forward(img) 52 | logger.info("AlignedReID infer time: {:.4f}s".format(time.time() - t0)) 53 | 54 | return feat[0].numpy() 55 | -------------------------------------------------------------------------------- /flow-python/examples/application/warehouse/reid_alignedreid/process.py: -------------------------------------------------------------------------------- 1 | # MegFlow is Licensed under the Apache License, Version 2.0 (the "License") 2 | # 3 | # Copyright (c) 2019-2021 Megvii Inc. All rights reserved. 4 | # 5 | # Unless required by applicable law or agreed to in writing, 6 | # software distributed under the License is distributed on an 7 | # "AS IS" BASIS, WITHOUT ARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 8 | 9 | #!/usr/bin/env python3 10 | # -*- coding:utf-8 -*- 11 | 12 | import cv2 13 | import megengine.functional as F 14 | import numpy as np 15 | from loguru import logger 16 | 17 | __all__ = ["preprocess"] 18 | 19 | 20 | def preprocess(image, input_size, scale_im, mean, std, swap=(2, 0, 1)): 21 | if image is None: 22 | logger.error("image is None") 23 | return image 24 | image = cv2.resize(image, input_size) 25 | image = image.astype(np.float32) 26 | image = image[:, :, ::-1] 27 | if scale_im: 28 | image /= 255.0 29 | if mean is not None: 30 | image -= mean 31 | if std is not None: 32 | image /= std 33 | image = image.transpose(swap) 34 | image = np.ascontiguousarray(image, dtype=np.float32) 35 | return image 36 | -------------------------------------------------------------------------------- /flow-python/examples/application/warehouse/track_iou/__init__.py: -------------------------------------------------------------------------------- 1 | # MegFlow is Licensed under the Apache License, Version 2.0 (the "License") 2 | # 3 | # Copyright (c) 2019-2021 Megvii Inc. All rights reserved. 4 | # 5 | # Unless required by applicable law or agreed to in writing, 6 | # software distributed under the License is distributed on an 7 | # "AS IS" BASIS, WITHOUT ARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 8 | 9 | #!/usr/bin/env python 10 | # coding=utf-8 11 | 12 | from .track_iou import Tracker 13 | -------------------------------------------------------------------------------- /flow-python/examples/basis_function/batch/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding=utf-8 3 | -------------------------------------------------------------------------------- /flow-python/examples/basis_function/batch/batch.toml: -------------------------------------------------------------------------------- 1 | main = "batch" 2 | 3 | [[graphs]] 4 | name = "batch" 5 | 6 | connections = [ 7 | {cap=4, ports=["gen:out", "group:inp"]}, 8 | {cap=4, ports=["printer:inp", "group:out"]} 9 | ] 10 | 11 | [[graphs.nodes]] 12 | name = "printer" 13 | ty = "Printer" 14 | 15 | [[graphs.nodes]] 16 | name = "gen" 17 | ty = "Gen" 18 | n = 23 19 | 20 | [[graphs.nodes]] 21 | name = "group" 22 | ty = "Group" 23 | id = 0 24 | batch_size = 5 25 | timeout = 2000 26 | -------------------------------------------------------------------------------- /flow-python/examples/basis_function/batch/op.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding=utf-8 3 | 4 | from megflow.func_op import * 5 | 6 | @batch_def() 7 | def group(inp, context=None): 8 | for x in inp: 9 | x['group_id'] = context.id 10 | context.id += 1 11 | 12 | @source_def() 13 | def gen(context=None): 14 | for i in range(context.n): 15 | yield { 'id': i } 16 | 17 | @sink_def() 18 | def printer(inp): 19 | print(inp) 20 | -------------------------------------------------------------------------------- /flow-python/examples/basis_function/demux/demux.toml: -------------------------------------------------------------------------------- 1 | main = "demux" 2 | 3 | [[graphs]] 4 | name = "demux" 5 | connections = [ 6 | { cap = 16, ports = ["source:out", "demux:inp"] }, 7 | { cap = 16, ports = ["demux:out:0", "even:inp"] }, 8 | { cap = 16, ports = ["demux:out:1", "odd:inp"] }, 9 | ] 10 | 11 | [[graphs.nodes]] 12 | name = "demux" 13 | ty = "Demux" 14 | 15 | [[graphs.nodes]] 16 | name = "odd" 17 | ty = "Sink" 18 | 19 | [[graphs.nodes]] 20 | name = "even" 21 | ty = "Sink" 22 | 23 | [[graphs.nodes]] 24 | name = "source" 25 | ty = "Source" 26 | n = 100 27 | 28 | -------------------------------------------------------------------------------- /flow-python/examples/basis_function/demux/op.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding=utf-8 3 | 4 | from megflow.func_op import * 5 | from megflow import register, Envelope 6 | 7 | @register(outputs=['out']) 8 | class Source: 9 | def __init__(self, name, args): 10 | self.n = args['n'] 11 | 12 | def exec(self): 13 | for i in range(self.n): 14 | envelope = Envelope.pack(i) 15 | envelope.to_addr = i % 2 16 | self.out.send(envelope) 17 | 18 | @sink_def() 19 | def sink(inp, context=None): 20 | print(context.name, 'get', inp) 21 | 22 | -------------------------------------------------------------------------------- /flow-python/examples/basis_function/future/future.toml: -------------------------------------------------------------------------------- 1 | main = "future" 2 | [[graphs]] 3 | name = "future" 4 | 5 | [[graphs.connections]] 6 | cap = 64 7 | ports = ["src:out", "dst:inp"] 8 | 9 | [[graphs.nodes]] 10 | name = "src" 11 | ty = "Printer" 12 | [[graphs.nodes]] 13 | name = "dst" 14 | ty = "Rnd" 15 | 16 | -------------------------------------------------------------------------------- /flow-python/examples/basis_function/future/op.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding=utf-8 3 | 4 | from megflow.func_op import * 5 | from megflow import create_future, join 6 | import random 7 | 8 | 9 | @sink_def() 10 | def rnd(inp): 11 | inp.wake(random.random()) 12 | 13 | 14 | @source_def() 15 | def printer(): 16 | (fut1, waker1) = create_future() 17 | yield waker1 18 | (fut2, waker2) = create_future() 19 | yield waker2 20 | 21 | [r1, r2] = join([lambda: fut1.wait(), lambda: fut2.wait()]) 22 | print('printer: ', r1, r2) 23 | 24 | -------------------------------------------------------------------------------- /flow-python/examples/basis_function/interactive/main.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding=utf-8 3 | 4 | from megflow import block_on, join, Graph, Envelope 5 | from megflow.func_op import map_def 6 | 7 | @map_def() 8 | def noop(x): 9 | return x 10 | 11 | if __name__ == '__main__': 12 | g = Graph(config_str=""" 13 | main = "noop" 14 | [[graphs]] 15 | name = "noop" 16 | inputs = [ 17 | { name = "inp", cap = 16, ports=["n:inp"] }, 18 | ] 19 | outputs = [ 20 | { name = "out", cap = 16, ports=["n:out"] }, 21 | ] 22 | [[graphs.nodes]] 23 | name = "n" 24 | ty = "Noop" 25 | """) 26 | 27 | n = 160 28 | def send(): 29 | inp = g.input('inp') 30 | for i in range(n): 31 | inp.send(Envelope.pack(i)) 32 | g.close() 33 | 34 | def recv(): 35 | out = g.output('out') 36 | recv_n = 0 37 | while True: 38 | envelope = out.recv() 39 | if envelope is None: 40 | break 41 | recv_n = envelope.msg 42 | assert recv_n == n - 1 43 | 44 | block_on(lambda: join([recv, send])) 45 | g.wait() 46 | print('finish') 47 | 48 | -------------------------------------------------------------------------------- /flow-python/examples/basis_function/map_reduce/map_reduce.toml: -------------------------------------------------------------------------------- 1 | main = "map_reduce" 2 | 3 | [[graphs]] 4 | name = "map_reduce" 5 | 6 | connections = [ 7 | { cap = 10, ports = ["rand:out", "mul1:inp", "mul2:inp"] }, 8 | { cap = 10, ports = ["mul1:out", "sum:inp"] }, 9 | { cap = 10, ports = ["mul2:out", "sum:inp"] }, 10 | { cap = 10, ports = ["sum:out", "printer:inp"] } 11 | ] 12 | 13 | [[graphs.nodes]] 14 | name = "mul1" 15 | ty = "Multiply" 16 | c = 1 17 | 18 | [[graphs.nodes]] 19 | name = "mul2" 20 | ty = "Multiply" 21 | c = 2 22 | 23 | [[graphs.nodes]] 24 | name = "sum" 25 | ty = "Summation" 26 | 27 | [[graphs.nodes]] 28 | name = "rand" 29 | ty = "Rnd" 30 | n = 3 31 | 32 | [[graphs.nodes]] 33 | name = "printer" 34 | ty = "Printer" 35 | -------------------------------------------------------------------------------- /flow-python/examples/basis_function/map_reduce/op.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding=utf-8 3 | 4 | from megflow.func_op import * 5 | import random 6 | 7 | @map_def() 8 | def multiply(inp, context=None): 9 | return inp * context.c 10 | 11 | 12 | @reduce_def() 13 | def summation(inp): 14 | return sum(inp, 0) 15 | 16 | @source_def() 17 | def rnd(context=None): 18 | for i in range(context.n): 19 | yield random.random() 20 | 21 | @sink_def() 22 | def printer(inp): 23 | print('printer: ', inp) 24 | -------------------------------------------------------------------------------- /flow-python/examples/basis_function/nest/nest.toml: -------------------------------------------------------------------------------- 1 | main = "nest" 2 | 3 | [[graphs]] 4 | name = "noop" 5 | inputs = [{name="inp", cap=16, ports=["noop:inp"]}] 6 | outputs = [{name="out", cap=16, ports=["noop:out"]}] 7 | 8 | [[graphs.nodes]] 9 | name = "noop" 10 | ty = "Noop" 11 | 12 | [[graphs]] 13 | name = "ppl" 14 | inputs = [{name="inp", cap=16, ports=["n1:inp"]}] 15 | outputs = [{name="out", cap=16, ports=["n5:out"]}] 16 | connections = [ 17 | { cap = 16, ports = ["n1:out", "n2:inp"] }, 18 | { cap = 16, ports = ["n2:out", "n3:inp"] }, 19 | { cap = 16, ports = ["n3:out", "n4:inp"] }, 20 | { cap = 16, ports = ["n4:out", "n5:inp"] }, 21 | ] 22 | 23 | [[graphs.nodes]] 24 | name = "n1" 25 | ty = "Noop" 26 | 27 | [[graphs.nodes]] 28 | name = "n2" 29 | ty = "noop" 30 | 31 | [[graphs.nodes]] 32 | name = "n3" 33 | ty = "Noop" 34 | 35 | [[graphs.nodes]] 36 | name = "n4" 37 | ty = "noop" 38 | 39 | [[graphs.nodes]] 40 | name = "n5" 41 | ty = "Noop" 42 | 43 | [[graphs]] 44 | name = "nest" 45 | 46 | connections = [ 47 | { cap = 16, ports = ["gen:out", "n:inp"] }, 48 | { cap = 16, ports = ["n:out", "printer:inp"] }, 49 | ] 50 | 51 | [[graphs.nodes]] 52 | name = "gen" 53 | ty = "Gen" 54 | n = 100 55 | 56 | [[graphs.nodes]] 57 | name = "printer" 58 | ty = "Printer" 59 | 60 | [[graphs.nodes]] 61 | name = "n" 62 | ty = "ppl" 63 | -------------------------------------------------------------------------------- /flow-python/examples/basis_function/nest/op.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding=utf-8 3 | from megflow.func_op import * 4 | 5 | @map_def(exclusive=True) 6 | def noop(x): 7 | # print('send', x) 8 | return x 9 | 10 | 11 | @source_def() 12 | def gen(context=None): 13 | for i in range(context.n): 14 | # print('first', i) 15 | yield i 16 | 17 | 18 | @sink_def() 19 | def printer(x): 20 | pass 21 | # print('last', x) 22 | -------------------------------------------------------------------------------- /flow-python/examples/basis_function/profile/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding=utf-8 3 | 4 | -------------------------------------------------------------------------------- /flow-python/examples/basis_function/profile/profile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MegEngine/MegFlow/41cb9f85828aa080f6004674904fe430d8268052/flow-python/examples/basis_function/profile/profile.png -------------------------------------------------------------------------------- /flow-python/examples/basis_function/profile/profile.toml: -------------------------------------------------------------------------------- 1 | main = "Example" 2 | [[graphs]] 3 | name = "Example" 4 | 5 | {{ Args = { 'Node': 10, 'Data': 1 } -}} 6 | 7 | [[graphs.connections]] 8 | cap = 64 9 | ports = ["src:out", "trans0:inp"] 10 | [[graphs.connections]] 11 | cap = 64 12 | ports = ["sink:inp", "trans{{Args.Node-1}}:out"] 13 | 14 | {% for i in range(Args.Node-1) %} 15 | [[graphs.connections]] 16 | cap = 64 17 | ports = ["trans{{i+1}}:inp", "trans{{i}}:out"] 18 | {% end for %} 19 | 20 | {% for i in range(Args.Node) %} 21 | [[graphs.nodes]] 22 | name = "trans{{i}}" 23 | ty = "Transport" 24 | {% end for %} 25 | 26 | [[graphs.nodes]] 27 | name = "src" 28 | ty = "Source" 29 | n = {{Args.Data}} 30 | 31 | [[graphs.nodes]] 32 | name = "sink" 33 | ty = "Sink" 34 | data_n = {{Args.Data}} 35 | node_n = {{Args.Node}} 36 | -------------------------------------------------------------------------------- /flow-python/examples/basis_function/profile/sink.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding=utf-8 3 | 4 | from megflow import register, Envelope 5 | import time 6 | 7 | @register(inputs=['inp']) 8 | class Sink: 9 | def __init__(self, name, args): 10 | self.data_n = args['data_n'] 11 | self.node_n = args['node_n'] 12 | self.count = 0 13 | 14 | def exec(self): 15 | envelope = self.inp.recv() 16 | 17 | if envelope is not None: 18 | self.count += 1 19 | if self.count == self.data_n: 20 | start = envelope.msg['time'] 21 | end = time.perf_counter() 22 | elapsed = end - start 23 | print('Time elapsed is {} ms for {} messages, {} nodes '.format(elapsed*1000, self.data_n, self.node_n)) 24 | 25 | -------------------------------------------------------------------------------- /flow-python/examples/basis_function/profile/source.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding=utf-8 3 | from megflow import register, Envelope 4 | import time 5 | 6 | @register(outputs=['out']) 7 | class Source: 8 | def __init__(self, name, args): 9 | self.n = args['n'] 10 | 11 | def exec(self): 12 | start = time.perf_counter() 13 | for i in range(self.n - 1): 14 | envelope = Envelope.pack({}) 15 | self.out.send(envelope) 16 | 17 | envelope = Envelope.pack({'time': start}) 18 | self.out.send(envelope) 19 | -------------------------------------------------------------------------------- /flow-python/examples/basis_function/profile/transport.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding=utf-8 3 | 4 | from megflow import register, Envelope 5 | @register(inputs=["inp"], outputs=["out"]) 6 | class Transport: 7 | def __init__(self, name, args): 8 | pass 9 | 10 | def exec(self): 11 | envelope = self.inp.recv() 12 | if envelope is not None: 13 | self.out.send(envelope) 14 | 15 | -------------------------------------------------------------------------------- /flow-python/examples/basis_function/unused/op.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding=utf-8 3 | from megflow.func_op import * 4 | 5 | @source_def() 6 | def gen(context=None): 7 | for i in range(context.n): 8 | print('gen', i) 9 | yield { 'id': i } 10 | -------------------------------------------------------------------------------- /flow-python/examples/basis_function/unused/unused.toml: -------------------------------------------------------------------------------- 1 | main = "unused" 2 | 3 | [[graphs]] 4 | name = "unused" 5 | 6 | [[graphs.nodes]] 7 | name = "gen" 8 | ty = "Gen" 9 | n = 10 10 | 11 | -------------------------------------------------------------------------------- /flow-python/examples/logical_test/__init__.py: -------------------------------------------------------------------------------- 1 | # MegFlow is Licensed under the Apache License, Version 2.0 (the "License") 2 | # 3 | # Copyright (c) 2019-2021 Megvii Inc. All rights reserved. 4 | # 5 | # Unless required by applicable law or agreed to in writing, 6 | # software distributed under the License is distributed on an 7 | # "AS IS" BASIS, WITHOUT ARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 8 | 9 | #!/usr/bin/env python 10 | # coding=utf-8 11 | -------------------------------------------------------------------------------- /flow-python/examples/logical_test/buffer.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding=utf-8 3 | 4 | from megflow import res_register 5 | 6 | 7 | @res_register() 8 | class Buffer: 9 | def __init__(self, name, args): 10 | self.name = name 11 | self.n = args['n'] 12 | self.i = 0 13 | 14 | def get(self): 15 | self.i += 1 16 | return self.i % self.n 17 | -------------------------------------------------------------------------------- /flow-python/examples/logical_test/logical_test.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MegEngine/MegFlow/41cb9f85828aa080f6004674904fe430d8268052/flow-python/examples/logical_test/logical_test.png -------------------------------------------------------------------------------- /flow-python/examples/logical_test/logical_test.toml: -------------------------------------------------------------------------------- 1 | main = "logical_test" 2 | 3 | [[resources]] 4 | name = "global_buf" 5 | ty = "Buffer" 6 | n = 10 7 | 8 | [[nodes]] 9 | name = "shared" 10 | ty = "RepeatProcess" 11 | 12 | [[graphs]] 13 | name = "TransGraph0" 14 | inputs = [{ name = "whatever", cap = 16, ports = ["shared:inp"] }] 15 | connections = [ 16 | { cap = 16, ports = ["shared:out", "trans:inp"] }, 17 | { cap = 16, ports = ["trans:out", "destination:inp"] } 18 | ] 19 | 20 | [[graphs.nodes]] 21 | name = "trans" 22 | ty = "Transform" 23 | 24 | [[graphs.nodes]] 25 | name = "destination" 26 | ty = "Printer" 27 | res = ["global_buf", "parent_buf", "buf"] 28 | 29 | [[graphs.resources]] 30 | name = "buf" 31 | ty = "Buffer" 32 | n = 5 33 | 34 | [[graphs]] 35 | name = "TransGraph1" 36 | inputs = [{ name = "whatever", cap = 16, ports = ["shared:inp"] }] 37 | connections = [ 38 | { cap = 16, ports = ["shared:out", "trans:inp"] }, 39 | { cap = 16, ports = ["trans:out", "destination:inp"] } 40 | ] 41 | 42 | [[graphs.nodes]] 43 | name = "trans" 44 | ty = "Transform" 45 | 46 | [[graphs.nodes]] 47 | name = "destination" 48 | ty = "Printer" 49 | res = ["global_buf", "parent_buf", "buf"] 50 | 51 | [[graphs.resources]] 52 | name = "buf" 53 | ty = "Buffer" 54 | n = 10 55 | 56 | [[graphs]] 57 | name = "logical_test" 58 | connections = [ 59 | { cap = 1, ports = ["source:out", "demux:inp"] }, 60 | { cap = 1, ports = ["demux:out", "destination:whatever"] } 61 | ] 62 | 63 | [[graphs.nodes]] 64 | name = "source" 65 | ty = "Source" 66 | n = 300 67 | 68 | [[graphs.nodes]] 69 | name = "demux" 70 | ty = "DynDemux" 71 | 72 | [[graphs.nodes]] 73 | name = "destination" 74 | ty = "TransGraph0|TransGraph1" 75 | res = ["parent_buf"] 76 | tag = "arg from graph" 77 | 78 | [[graphs.resources]] 79 | name = "parent_buf" 80 | ty = "Buffer" 81 | n = 2 82 | -------------------------------------------------------------------------------- /flow-python/examples/logical_test/printer.py: -------------------------------------------------------------------------------- 1 | # MegFlow is Licensed under the Apache License, Version 2.0 (the "License") 2 | # 3 | # Copyright (c) 2019-2021 Megvii Inc. All rights reserved. 4 | # 5 | # Unless required by applicable law or agreed to in writing, 6 | # software distributed under the License is distributed on an 7 | # "AS IS" BASIS, WITHOUT ARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 8 | 9 | #!/usr/bin/env python 10 | # coding=utf-8 11 | from megflow import register 12 | 13 | instance_id = 0 14 | 15 | 16 | @register('Printer', inputs=['inp']) 17 | class Node: 18 | def __init__(self, name, args): 19 | global instance_id 20 | self.id = instance_id 21 | instance_id += 1 22 | self.count = 0 23 | self.tag = args['tag'] 24 | 25 | def exec(self): 26 | envelope = self.inp.recv() 27 | if envelope is not None: 28 | self.count += 1 29 | gbuf = self.global_buf.get() 30 | pbuf = self.parent_buf.get() 31 | buf = self.buf.get() 32 | 33 | # print( 34 | # 'Printer[{}] get msg: {}, with tag: {}, buf(global, parent, local): ({}, {}, {})' 35 | # .format(self.id, envelope.msg['message'], self.tag, gbuf, pbuf, buf)) 36 | else: 37 | assert self.count == 10 38 | -------------------------------------------------------------------------------- /flow-python/examples/logical_test/process.py: -------------------------------------------------------------------------------- 1 | # MegFlow is Licensed under the Apache License, Version 2.0 (the "License") 2 | # 3 | # Copyright (c) 2019-2021 Megvii Inc. All rights reserved. 4 | # 5 | # Unless required by applicable law or agreed to in writing, 6 | # software distributed under the License is distributed on an 7 | # "AS IS" BASIS, WITHOUT ARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 8 | 9 | #!/usr/bin/env python 10 | # coding=utf-8 11 | from megflow import register 12 | import multiprocessing as mp 13 | from multiprocessing import Process, Pipe 14 | 15 | 16 | def repeat(n, s, r): 17 | while True: 18 | message = r.recv() 19 | 20 | if message is None: 21 | break 22 | 23 | for i in range(n): 24 | msg = {} 25 | msg['message'] = "a message[{}] repeat {} by process node".format( 26 | message['message'], i) 27 | s.send(msg) 28 | print('==== logical_test pass ====') 29 | 30 | 31 | @register(inputs=['inp'], outputs=['out'], exclusive=True) 32 | class RepeatProcess: 33 | def __init__(self, name, args): 34 | self.name = name 35 | s1, r1 = Pipe() 36 | s2, r2 = Pipe() 37 | self.send = s1 38 | self.recv = r2 39 | 40 | mp.set_start_method('fork') 41 | self.p = Process(target=repeat, args=(10, s2, r1)) 42 | self.p.start() 43 | 44 | def __del__(self): 45 | try: 46 | self.send.send(None) 47 | self.p.join() 48 | except: 49 | pass 50 | 51 | def exec(self): 52 | envelope = self.inp.recv() 53 | 54 | if envelope is None: 55 | return 56 | 57 | try: 58 | self.send.send(envelope.msg) 59 | except: 60 | pass 61 | 62 | for i in range(10): 63 | message = self.recv.recv() 64 | self.out.send(envelope.repack(message)) 65 | -------------------------------------------------------------------------------- /flow-python/examples/logical_test/source.py: -------------------------------------------------------------------------------- 1 | # MegFlow is Licensed under the Apache License, Version 2.0 (the "License") 2 | # 3 | # Copyright (c) 2019-2021 Megvii Inc. All rights reserved. 4 | # 5 | # Unless required by applicable law or agreed to in writing, 6 | # software distributed under the License is distributed on an 7 | # "AS IS" BASIS, WITHOUT ARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 8 | 9 | #!/usr/bin/env python 10 | # coding=utf-8 11 | from megflow import register, Envelope, sleep 12 | 13 | 14 | @register(outputs=['out']) 15 | class Source: 16 | def __init__(self, name, args): 17 | self.n = args['n'] 18 | 19 | def exec(self): 20 | for i in range(self.n): 21 | msg = {} 22 | msg['message'] = 'send to {}'.format(i) 23 | envelope = Envelope.pack(msg) 24 | envelope.to_addr = i 25 | envelope.tag = "TransGraph{}".format(i % 2) 26 | self.out.send(envelope) 27 | 28 | sleep(5) 29 | 30 | for i in range(self.n): 31 | envelope = Envelope.pack(None) 32 | envelope.to_addr = i 33 | self.out.send(envelope) 34 | -------------------------------------------------------------------------------- /flow-python/megflow/__init__.py: -------------------------------------------------------------------------------- 1 | # MegFlow is Licensed under the Apache License, Version 2.0 (the "License") 2 | # 3 | # Copyright (c) 2019-2021 Megvii Inc. All rights reserved. 4 | # 5 | # Unless required by applicable law or agreed to in writing, 6 | # software distributed under the License is distributed on an 7 | # "AS IS" BASIS, WITHOUT ARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 8 | 9 | #!/usr/bin/env python 10 | # coding=utf-8 11 | 12 | from .registry import register, collect, res_register 13 | from .megflow import * 14 | 15 | __version__ = version() 16 | -------------------------------------------------------------------------------- /flow-python/megflow/command_line.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding=utf-8 3 | 4 | import sys 5 | import os 6 | import subprocess 7 | import pkg_resources 8 | 9 | def megflow_run(): 10 | if pkg_resources.resource_exists('megflow', 'lib') and pkg_resources.resource_isdir('megflow', 'lib'): 11 | if 'MGF_CMDLINE_FLAG' not in os.environ: 12 | os.environ['MGF_CMDLINE_FLAG'] = '1' 13 | if 'LD_LIBRARY_PATH' in os.environ: 14 | os.environ['LD_LIBRARY_PATH'] = pkg_resources.resource_filename('megflow', 'lib') + ':' + os.environ['LD_LIBRARY_PATH'] 15 | else: 16 | os.environ['LD_LIBRARY_PATH'] = pkg_resources.resource_filename('megflow', 'lib') 17 | 18 | try: 19 | os.execv(sys.argv[0], sys.argv) 20 | return 21 | except Exception as exc: 22 | print('Failed re-exec:', exc) 23 | sys.exit(1) 24 | 25 | import argparse 26 | import megflow 27 | 28 | parser = argparse.ArgumentParser(prog='megflow_run', description='run a pipeline with plugins.') 29 | parser.add_argument('--dump', help='the path to dump graph', action='store_true') 30 | parser.add_argument('-p', '--plugin', required=True, type=str, help='plugin path') 31 | parser.add_argument('-m', '--module', type=str, help='module path') 32 | parser.add_argument('-c', '--config', type=str, help='config path') 33 | parser.add_argument('--dynamic', type=str, help='dynamic config path') 34 | parser.add_argument('--version', action='version', version='%(prog)s {version}'.format(version=megflow.__version__)) 35 | 36 | args = parser.parse_args() 37 | 38 | megflow.Graph( 39 | dump=args.dump, 40 | plugin_path=args.plugin, 41 | module_path=args.module, 42 | config_path=args.config, 43 | dynamic_path = args.dynamic 44 | ).wait() 45 | 46 | 47 | def run_with_plugins(): 48 | print('run_with_plugins has been renamed to megflow_run.') 49 | 50 | 51 | def megflow_quickstart(): 52 | bin_path = pkg_resources.resource_filename('megflow', 'megflow_quickstart_inner') 53 | sys.argv[0] = bin_path 54 | ret = subprocess.Popen(sys.argv) 55 | ret.wait() 56 | 57 | 58 | if __name__ == '__main__': 59 | megflow_run() 60 | -------------------------------------------------------------------------------- /flow-python/megflow/registry.py: -------------------------------------------------------------------------------- 1 | # MegFlow is Licensed under the Apache License, Version 2.0 (the "License") 2 | # 3 | # Copyright (c) 2019-2021 Megvii Inc. All rights reserved. 4 | # 5 | # Unless required by applicable law or agreed to in writing, 6 | # software distributed under the License is distributed on an 7 | # "AS IS" BASIS, WITHOUT ARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 8 | 9 | #!/usr/bin/env python 10 | # coding=utf-8 11 | from collections import Iterable 12 | 13 | def __register(name, inputs, outputs, exclusive, func): 14 | params = {} 15 | 16 | params['name'] = name 17 | params['code'] = func 18 | params['inputs'] = inputs 19 | params['outputs'] = outputs 20 | params['exclusive'] = exclusive 21 | 22 | return params 23 | 24 | 25 | def __res_register(name, func): 26 | params = {} 27 | params['name'] = name 28 | params['code'] = func 29 | 30 | return params 31 | 32 | 33 | __NODES_PLUGINS = 'nodes' 34 | __RESOURCE_PLUGINS = 'resources' 35 | __PLUGINS_DEFAULT = { 36 | __NODES_PLUGINS: [], 37 | __RESOURCE_PLUGINS: [], 38 | } 39 | _PLUGINS_REGISTRY = __PLUGINS_DEFAULT.copy() 40 | 41 | def register(name=None, inputs=[], outputs=[], exclusive=False): 42 | def decorator(func): 43 | nonlocal name 44 | global _PLUGINS_REGISTRY 45 | if name is None: 46 | name = func.__name__ 47 | _PLUGINS_REGISTRY[__NODES_PLUGINS].append(__register(name, inputs, outputs, exclusive, func)) 48 | return func 49 | 50 | return decorator 51 | 52 | 53 | def res_register(name=None): 54 | def decorator(func): 55 | nonlocal name 56 | global _PLUGINS_REGISTRY 57 | if name is None: 58 | name = func.__name__ 59 | _PLUGINS_REGISTRY[__RESOURCE_PLUGINS].append(__res_register(name, func)) 60 | return func 61 | 62 | return decorator 63 | 64 | 65 | def collect(): 66 | global _PLUGINS_REGISTRY 67 | plugins = _PLUGINS_REGISTRY.copy() 68 | _PLUGINS_REGISTRY = __PLUGINS_DEFAULT.copy() 69 | return plugins 70 | -------------------------------------------------------------------------------- /flow-quickstart/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "flow-quickstart" 3 | version = "0.3.5" 4 | edition = "2018" 5 | authors = ["megvii"] 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | lazy_static = "1.4.0" 11 | console = "0.14.1" 12 | clap = "3.0.0-beta.2" 13 | dirs = "4.0.0" 14 | indicatif = "0.16.2" 15 | git2 = "0.13" 16 | libgit2-sys = "0.12" 17 | walkdir = "2.3.2" 18 | remove_dir_all = "0.7.0" 19 | ignore = "0.4.18" 20 | url = "2.2.2" 21 | anyhow = "1.0.44" 22 | toml = "0.5.8" 23 | home = "0.5.3" 24 | regex = "1" 25 | 26 | [[bin]] 27 | path = "src/main.rs" 28 | name = "megflow_quickstart" 29 | -------------------------------------------------------------------------------- /flow-quickstart/src/log.rs: -------------------------------------------------------------------------------- 1 | /** 2 | * \file flow-quickstart/log.rs 3 | * MegFlow is Licensed under the Apache License, Version 2.0 (the "License") 4 | * 5 | * Copyright (c) 2019-2021 Megvii Inc. All rights reserved. 6 | * 7 | * Unless required by applicable law or agreed to in writing, 8 | * software distributed under the License is distributed on an 9 | * "AS IS" BASIS, WITHOUT ARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | */ 11 | use console::Emoji; 12 | 13 | pub static ERROR: Emoji<'_, '_> = Emoji("⛔ ", ""); 14 | pub static SPARKLE: Emoji<'_, '_> = Emoji("✨ ", ""); 15 | pub static WARN: Emoji<'_, '_> = Emoji("⚠️ ", ""); 16 | pub static WRENCH: Emoji<'_, '_> = Emoji("🔧 ", ""); 17 | pub static INFO: Emoji<'_, '_> = Emoji("💡 ", ""); 18 | 19 | #[macro_export] 20 | macro_rules! warn { 21 | ($($arg:tt)*) => ({ 22 | println!("{} {}", 23 | $crate::log::WARN, 24 | format!($($arg)*) 25 | ); 26 | }) 27 | } 28 | 29 | #[macro_export] 30 | macro_rules! retry { 31 | ($($arg:tt)*) => ({ 32 | println!("{} {}", 33 | $crate::log::WRENCH, 34 | format!($($arg)*) 35 | ); 36 | }) 37 | } 38 | 39 | #[macro_export] 40 | macro_rules! info { 41 | ($($arg:tt)*) => ({ 42 | println!("{} {}", 43 | $crate::log::INFO, 44 | format!($($arg)*) 45 | ); 46 | }) 47 | } 48 | 49 | #[macro_export] 50 | macro_rules! error { 51 | ($($arg:tt)*) => ({ 52 | println!("{} {}", 53 | $crate::log::ERROR, 54 | format!($($arg)*) 55 | ); 56 | }) 57 | } 58 | -------------------------------------------------------------------------------- /flow-rs/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "flow-rs" 3 | version = "0.3.5" 4 | authors = ["megvii"] 5 | edition = "2018" 6 | 7 | [features] 8 | python = ["stackful", "numpy", "pyo3"] 9 | debug = ["warp"] 10 | 11 | [dependencies.templar] 12 | git = "https://github.com/proctorlabs/templar.git" 13 | rev = "85469a4c" 14 | default_features=false 15 | features=["shared-context", "toml-extension"] 16 | 17 | [dependencies] 18 | flow-derive = { path="../flow-derive" } 19 | anyhow = "1.0" 20 | serde = { version = "1.0", features = ["derive"] } 21 | toml = "0.5" 22 | lazy_static = "1.4" 23 | ctor = "0.1" 24 | pyo3 = { version = "0.15", features = ["abi3"], optional=true } 25 | dyn-clone = "1.0" 26 | async-std = { version = "1.9", features = ["unstable", "attributes", "tokio1"] } 27 | stackful = { git = "https://github.com/nbdd0121/stackful.git", rev = "4789e26", optional=true } 28 | futures-util = "0.3" 29 | futures-core = "0.3" 30 | concurrent-queue = "1.2" 31 | event-listener = "2.4" 32 | numpy = { version = "0.15", optional=true } 33 | log = "0.4" 34 | oneshot = "0.1" 35 | warp = { version = "0.3", optional=true } 36 | serde_json = "1.0" 37 | unstructured = "0.5.1" 38 | 39 | [dev-dependencies] 40 | clap = "3.0.0-beta.2" 41 | rand = "0.8.4" 42 | tempfile = "3.2.0" 43 | -------------------------------------------------------------------------------- /flow-rs/src/channel/error.rs: -------------------------------------------------------------------------------- 1 | /** 2 | * \file flow-rs/src/channel/error.rs 3 | * MegFlow is Licensed under the Apache License, Version 2.0 (the "License") 4 | * 5 | * Copyright (c) 2019-2021 Megvii Inc. All rights reserved. 6 | * 7 | * Unless required by applicable law or agreed to in writing, 8 | * software distributed under the License is distributed on an 9 | * "AS IS" BASIS, WITHOUT ARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | */ 11 | pub enum BatchRecvError { 12 | Closed(Vec), 13 | } 14 | 15 | pub type SendError = super::inner::SendError; 16 | pub type RecvError = super::inner::RecvError; 17 | 18 | impl std::fmt::Debug for BatchRecvError { 19 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 20 | match *self { 21 | BatchRecvError::Closed(_) => f.write_str("BatchRecvError::Closed"), 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /flow-rs/src/config/presentation.rs: -------------------------------------------------------------------------------- 1 | /** 2 | * \file flow-rs/src/config/presentation.rs 3 | * MegFlow is Licensed under the Apache License, Version 2.0 (the "License") 4 | * 5 | * Copyright (c) 2019-2021 Megvii Inc. All rights reserved. 6 | * 7 | * Unless required by applicable law or agreed to in writing, 8 | * software distributed under the License is distributed on an 9 | * "AS IS" BASIS, WITHOUT ARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | */ 11 | use flow_rs::prelude::Parser; 12 | use serde::{Deserialize, Serialize}; 13 | use std::path::PathBuf; 14 | use toml::value::Table; 15 | 16 | #[derive(Serialize, Deserialize, Clone, Debug)] 17 | #[serde(deny_unknown_fields)] 18 | pub struct Connection { 19 | pub cap: usize, 20 | pub ports: Vec, 21 | } 22 | 23 | #[derive(Serialize, Deserialize, Clone, Debug)] 24 | pub struct NamedConn { 25 | pub name: String, 26 | #[serde(flatten)] 27 | pub conn: Connection, 28 | } 29 | 30 | #[derive(Serialize, Deserialize, Clone, Debug)] 31 | pub struct Entity { 32 | pub name: String, 33 | pub ty: String, 34 | #[serde(default, flatten)] 35 | pub args: Table, 36 | } 37 | 38 | #[derive(Serialize, Deserialize, Clone, Debug)] 39 | pub struct Node { 40 | #[serde(flatten)] 41 | pub entity: Entity, 42 | #[serde(default)] 43 | pub res: Vec, 44 | pub cloned: Option, 45 | } 46 | 47 | #[derive(Serialize, Deserialize, Clone, Debug, Parser)] 48 | #[serde(deny_unknown_fields)] 49 | pub struct Graph { 50 | pub name: String, 51 | #[serde(default)] 52 | pub resources: Vec, 53 | #[serde(default)] 54 | pub nodes: Vec, 55 | #[serde(default)] 56 | pub inputs: Vec, 57 | #[serde(default)] 58 | pub outputs: Vec, 59 | #[serde(default)] 60 | pub connections: Vec, 61 | } 62 | 63 | #[derive(Serialize, Deserialize, Debug, Parser)] 64 | #[serde(deny_unknown_fields)] 65 | pub struct Config { 66 | #[serde(default)] 67 | pub include: Vec, 68 | #[serde(default)] 69 | pub resources: Vec, 70 | #[serde(default)] 71 | pub nodes: Vec, 72 | pub graphs: Vec, 73 | pub main: String, 74 | } 75 | -------------------------------------------------------------------------------- /flow-rs/src/debug/feature.rs: -------------------------------------------------------------------------------- 1 | /** 2 | * \file flow-rs/src/debug/mod.rs 3 | * MegFlow is Licensed under the Apache License, Version 2.0 (the "License") 4 | * 5 | * Copyright (c) 2019-2021 Megvii Inc. All rights reserved. 6 | * 7 | * Unless required by applicable law or agreed to in writing, 8 | * software distributed under the License is distributed on an 9 | * "AS IS" BASIS, WITHOUT ARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | */ 11 | use event_listener::Event; 12 | use std::sync::atomic::{AtomicBool, Ordering}; 13 | 14 | pub struct Feature { 15 | enable: AtomicBool, 16 | lock_ops: Event, 17 | } 18 | 19 | impl Feature { 20 | pub const fn new() -> Feature { 21 | Feature { 22 | enable: AtomicBool::new(false), 23 | lock_ops: Event::new(), 24 | } 25 | } 26 | 27 | pub fn enable(&self) -> bool { 28 | self.enable.load(Ordering::Relaxed) 29 | } 30 | 31 | pub fn disable(&self) { 32 | self.enable.store(false, Ordering::Relaxed); 33 | } 34 | 35 | pub fn notify(&self) { 36 | if self 37 | .enable 38 | .compare_exchange(false, true, Ordering::SeqCst, Ordering::SeqCst) 39 | .is_ok() 40 | { 41 | self.lock_ops.notify(usize::MAX); 42 | } 43 | } 44 | #[allow(dead_code)] 45 | pub async fn wait(&self) { 46 | let mut listener = None; 47 | 48 | loop { 49 | if self.enable.load(Ordering::Relaxed) { 50 | return; 51 | } 52 | 53 | match listener.take() { 54 | None => { 55 | listener = Some(self.lock_ops.listen()); 56 | } 57 | Some(l) => { 58 | l.await; 59 | } 60 | } 61 | } 62 | } 63 | } 64 | 65 | pub struct FeatureCommand { 66 | pub start: Box, 67 | pub stop: Box, 68 | pub disable: Box, 69 | } 70 | crate::collect!(String, FeatureCommand); 71 | -------------------------------------------------------------------------------- /flow-rs/src/debug/mod.rs: -------------------------------------------------------------------------------- 1 | /** 2 | * \file flow-rs/src/debug/mod.rs 3 | * MegFlow is Licensed under the Apache License, Version 2.0 (the "License") 4 | * 5 | * Copyright (c) 2019-2021 Megvii Inc. All rights reserved. 6 | * 7 | * Unless required by applicable law or agreed to in writing, 8 | * software distributed under the License is distributed on an 9 | * "AS IS" BASIS, WITHOUT ARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | */ 11 | mod feature; 12 | mod protocol; 13 | mod server; 14 | 15 | use crate::prelude::feature; 16 | pub use protocol::*; 17 | pub use server::{Server, PORT}; 18 | 19 | feature!(QPS, { ratio: f32 }); 20 | -------------------------------------------------------------------------------- /flow-rs/src/debug/protocol.rs: -------------------------------------------------------------------------------- 1 | /** 2 | * \file flow-rs/src/debug/protocol.rs 3 | * MegFlow is Licensed under the Apache License, Version 2.0 (the "License") 4 | * 5 | * Copyright (c) 2019-2021 Megvii Inc. All rights reserved. 6 | * 7 | * Unless required by applicable law or agreed to in writing, 8 | * software distributed under the License is distributed on an 9 | * "AS IS" BASIS, WITHOUT ARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | */ 11 | use serde::{Deserialize, Serialize}; 12 | use serde_json::{Map, Value}; 13 | 14 | #[derive(Serialize, Deserialize)] 15 | pub struct ProtocolMessage { 16 | pub ty: String, 17 | #[serde(flatten, default)] 18 | pub others: Map, 19 | } 20 | 21 | #[derive(Serialize, Deserialize)] 22 | pub struct RequestMessage { 23 | pub feature: String, 24 | pub seq_id: usize, 25 | pub command: String, 26 | #[serde(flatten, default)] 27 | pub args: Map, 28 | } 29 | 30 | #[derive(Serialize, Deserialize)] 31 | pub struct EventMessage { 32 | pub event: String, 33 | #[serde(flatten, default)] 34 | pub args: Map, 35 | } 36 | 37 | #[derive(Serialize, Deserialize)] 38 | pub struct ResponseMessage { 39 | pub success: bool, 40 | pub feature: String, 41 | pub seq_id: usize, 42 | pub command: String, 43 | #[serde(flatten, default)] 44 | pub args: Map, 45 | } 46 | // message 47 | pub const TYPE_REQUEST: &str = "request"; 48 | pub const TYPE_EVENT: &str = "event"; 49 | pub const TYPE_RESPONSE: &str = "response"; 50 | // event 51 | pub const EVENT_INITIALIZED: &str = "initialized"; 52 | pub const EVENT_TERMINATED: &str = "terminated"; 53 | pub const EVENT_STOP: &str = "stop"; 54 | // command 55 | pub const CMD_START: &str = "start"; 56 | pub const CMD_STOP: &str = "stop"; 57 | pub const CMD_NOOP: &str = "noop"; 58 | -------------------------------------------------------------------------------- /flow-rs/src/envelope/mod.rs: -------------------------------------------------------------------------------- 1 | /** 2 | * \file flow-rs/src/envelope/mod.rs 3 | * MegFlow is Licensed under the Apache License, Version 2.0 (the "License") 4 | * 5 | * Copyright (c) 2019-2021 Megvii Inc. All rights reserved. 6 | * 7 | * Unless required by applicable law or agreed to in writing, 8 | * software distributed under the License is distributed on an 9 | * "AS IS" BASIS, WITHOUT ARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | */ 11 | mod any_envelope; 12 | #[allow(clippy::module_inception)] 13 | mod envelope; 14 | 15 | #[doc(hidden)] 16 | pub use any_envelope::*; 17 | pub use envelope::*; 18 | 19 | pub type SealedEnvelope = Box; 20 | 21 | #[cfg(test)] 22 | mod test { 23 | use super::*; 24 | 25 | #[test] 26 | fn test_basis() { 27 | let envelope = DummyEnvelope {}.seal(); 28 | assert!(envelope.is::()); 29 | let envelope = Envelope::new(0f32).seal(); 30 | assert!(!envelope.is::()); 31 | assert!(envelope.is::>()); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /flow-rs/src/future.rs: -------------------------------------------------------------------------------- 1 | /** 2 | * \file flow-rs/src/future.rs 3 | * MegFlow is Licensed under the Apache License, Version 2.0 (the "License") 4 | * 5 | * Copyright (c) 2019-2021 Megvii Inc. All rights reserved. 6 | * 7 | * Unless required by applicable law or agreed to in writing, 8 | * software distributed under the License is distributed on an 9 | * "AS IS" BASIS, WITHOUT ARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | */ 11 | use futures_util::stream::{FuturesUnordered, StreamExt}; 12 | use std::future::Future; 13 | 14 | // https://users.rust-lang.org/t/usage-of-futures-select-ok/46068/10 15 | pub(crate) async fn select_ok(futs: impl IntoIterator) -> Result 16 | where 17 | F: Future>, 18 | { 19 | let mut futs: FuturesUnordered = futs.into_iter().collect(); 20 | 21 | let mut last_error: Option = None; 22 | while let Some(next) = futs.next().await { 23 | match next { 24 | Ok(ok) => return Ok(ok), 25 | Err(err) => { 26 | last_error = Some(err); 27 | } 28 | } 29 | } 30 | Err(last_error.expect("Empty iterator.")) 31 | } 32 | 33 | #[cfg(test)] 34 | mod test { 35 | use super::*; 36 | use flow_rs::rt::channel::unbounded; 37 | 38 | #[flow_rs::rt::test] 39 | async fn test_basis() { 40 | let (s1, r1) = unbounded(); 41 | let (s2, r2) = unbounded(); 42 | 43 | s1.send(1).await.ok(); 44 | s2.send(2).await.ok(); 45 | 46 | let fut = vec![r1.recv(), r2.recv()]; 47 | assert_eq!(select_ok(fut).await, Ok(1)); 48 | let fut = vec![r1.recv(), r2.recv()]; 49 | assert_eq!(select_ok(fut).await, Ok(2)); 50 | 51 | s1.send(1).await.ok(); 52 | let fut = vec![r1.recv(), r2.recv()]; 53 | assert_eq!(select_ok(fut).await, Ok(1)); 54 | 55 | drop(s1); 56 | drop(s2); 57 | 58 | let fut = vec![r1.recv(), r2.recv()]; 59 | assert!(select_ok(fut).await.is_err()); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /flow-rs/src/graph/channel.rs: -------------------------------------------------------------------------------- 1 | /** 2 | * \file flow-rs/src/graph/channel.rs 3 | * MegFlow is Licensed under the Apache License, Version 2.0 (the "License") 4 | * 5 | * Copyright (c) 2019-2021 Megvii Inc. All rights reserved. 6 | * 7 | * Unless required by applicable law or agreed to in writing, 8 | * software distributed under the License is distributed on an 9 | * "AS IS" BASIS, WITHOUT ARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | */ 11 | use crate::channel::ChannelStorage; 12 | use crate::config::interlayer as config; 13 | use anyhow::Result; 14 | 15 | #[derive(Clone)] 16 | pub struct AnyChannel { 17 | storage: Option, 18 | info: config::Connection, 19 | } 20 | 21 | impl AnyChannel { 22 | pub fn new(cfg: &config::Connection) -> Result { 23 | Ok(AnyChannel { 24 | storage: None, 25 | info: cfg.clone(), 26 | }) 27 | } 28 | 29 | pub fn set(&mut self, storage: ChannelStorage) { 30 | self.storage = Some(storage) 31 | } 32 | 33 | pub fn make(&self) -> ChannelStorage { 34 | ChannelStorage::bound(self.info.cap) 35 | } 36 | 37 | pub fn get(&self) -> &ChannelStorage { 38 | self.storage.as_ref().unwrap() 39 | } 40 | 41 | #[allow(dead_code)] 42 | pub fn get_mut(&mut self) -> &mut ChannelStorage { 43 | self.storage.as_mut().unwrap() 44 | } 45 | 46 | pub fn info(&self) -> &config::Connection { 47 | &self.info 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /flow-rs/src/graph/context.rs: -------------------------------------------------------------------------------- 1 | /** 2 | * \file flow-rs/src/graph/mod.rs 3 | * MegFlow is Licensed under the Apache License, Version 2.0 (the "License") 4 | * 5 | * Copyright (c) 2019-2021 Megvii Inc. All rights reserved. 6 | * 7 | * Unless required by applicable law or agreed to in writing, 8 | * software distributed under the License is distributed on an 9 | * "AS IS" BASIS, WITHOUT ARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | */ 11 | use event_listener::Event; 12 | use std::sync::atomic::{AtomicBool, AtomicU64, Ordering}; 13 | use std::sync::Arc; 14 | 15 | pub struct ContextInner { 16 | pub name: String, 17 | pub ty: String, 18 | pub local_key: u64, 19 | pub id: u64, 20 | lock_ops: Event, 21 | is_closed: AtomicBool, 22 | } 23 | 24 | pub type Context = Arc; 25 | 26 | impl ContextInner { 27 | pub fn close(&self) { 28 | if self 29 | .is_closed 30 | .compare_exchange(false, true, Ordering::Release, Ordering::Relaxed) 31 | .is_ok() 32 | { 33 | self.lock_ops.notify(usize::MAX); 34 | } 35 | } 36 | pub fn is_closed(&self) -> bool { 37 | self.is_closed.load(Ordering::Relaxed) 38 | } 39 | pub async fn wait(&self) { 40 | let mut listener = None; 41 | 42 | loop { 43 | if self.is_closed.load(Ordering::Relaxed) { 44 | return; 45 | } 46 | 47 | match listener.take() { 48 | None => { 49 | listener = Some(self.lock_ops.listen()); 50 | } 51 | Some(l) => { 52 | l.await; 53 | } 54 | } 55 | } 56 | } 57 | } 58 | 59 | lazy_static::lazy_static! { 60 | static ref GRAPH_ID: AtomicU64 = AtomicU64::new(0); 61 | } 62 | 63 | pub(crate) fn context(name: String, ty: String, local_key: u64) -> Context { 64 | Arc::new(ContextInner { 65 | name, 66 | ty, 67 | local_key, 68 | id: GRAPH_ID.fetch_add(1, Ordering::Relaxed), 69 | lock_ops: Event::new(), 70 | is_closed: AtomicBool::new(false), 71 | }) 72 | } 73 | -------------------------------------------------------------------------------- /flow-rs/src/graph/node.rs: -------------------------------------------------------------------------------- 1 | /** 2 | * \file flow-rs/src/graph/node.rs 3 | * MegFlow is Licensed under the Apache License, Version 2.0 (the "License") 4 | * 5 | * Copyright (c) 2019-2021 Megvii Inc. All rights reserved. 6 | * 7 | * Unless required by applicable law or agreed to in writing, 8 | * software distributed under the License is distributed on an 9 | * "AS IS" BASIS, WITHOUT ARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | */ 11 | use crate::config::interlayer as config; 12 | use crate::config::table::merge_table; 13 | use crate::node::Actor; 14 | use anyhow::Result; 15 | use toml::value::Table; 16 | 17 | pub struct AnyNode { 18 | nodes: Vec>, 19 | #[allow(dead_code)] 20 | info: config::Node, 21 | } 22 | 23 | impl AnyNode { 24 | pub fn new(local_key: u64, mut info: config::Node, extra_args: Table) -> Result { 25 | info.entity.args = merge_table(extra_args, info.entity.args); 26 | Ok(AnyNode { 27 | nodes: crate::node::load_static(local_key, &info)?, 28 | info, 29 | }) 30 | } 31 | 32 | #[allow(dead_code)] 33 | pub fn first(&self) -> &dyn Actor { 34 | self.nodes.first().map(|n| n.as_ref()).unwrap() 35 | } 36 | 37 | #[allow(dead_code)] 38 | pub fn get(&self) -> &Vec> { 39 | &self.nodes 40 | } 41 | 42 | pub fn get_mut(&mut self) -> &mut Vec> { 43 | &mut self.nodes 44 | } 45 | 46 | pub fn get_into(&mut self) -> Vec> { 47 | std::mem::take(&mut self.nodes) 48 | } 49 | 50 | pub fn info(&self) -> &config::Node { 51 | &self.info 52 | } 53 | 54 | pub fn info_mut(&mut self) -> &mut config::Node { 55 | &mut self.info 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /flow-rs/src/helper/mod.rs: -------------------------------------------------------------------------------- 1 | #[cfg(feature = "python")] 2 | mod python; 3 | 4 | #[cfg(feature = "python")] 5 | pub use python::*; 6 | -------------------------------------------------------------------------------- /flow-rs/src/helper/python.rs: -------------------------------------------------------------------------------- 1 | /** 2 | * \file flow-rs/src/helper.rs 3 | * MegFlow is Licensed under the Apache License, Version 2.0 (the "License") 4 | * 5 | * Copyright (c) 2019-2021 Megvii Inc. All rights reserved. 6 | * 7 | * Unless required by applicable law or agreed to in writing, 8 | * software distributed under the License is distributed on an 9 | * "AS IS" BASIS, WITHOUT ARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | */ 11 | use numpy::array::PyArrayDyn; 12 | use numpy::Element; 13 | use pyo3::prelude::*; 14 | use std::ops::Deref; 15 | 16 | pub struct SliceGuard<'a, T> 17 | where 18 | T: Element, 19 | { 20 | slice: &'a [T], 21 | _ref: PyObject, 22 | } 23 | 24 | impl<'a, T> Deref for SliceGuard<'a, T> 25 | where 26 | T: Element, 27 | { 28 | type Target = [T]; 29 | fn deref(&self) -> &Self::Target { 30 | self.slice 31 | } 32 | } 33 | 34 | pub fn uget_slice<'a, T: Element>(py: Python, pyobject: &PyAny) -> PyResult> { 35 | let array: &PyArrayDyn = pyobject.extract()?; 36 | unsafe { 37 | let slice = array 38 | .as_slice() 39 | .map_err(|_| pyo3::exceptions::PyTypeError::new_err("not contiguous"))?; 40 | let slice = std::slice::from_raw_parts(slice.as_ptr(), slice.len()); 41 | Ok(SliceGuard { 42 | slice, 43 | _ref: pyobject.into_py(py), 44 | }) 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /flow-rs/src/loader/python/context.rs: -------------------------------------------------------------------------------- 1 | /** 2 | * \file flow-rs/src/loader/python/context.rs 3 | * MegFlow is Licensed under the Apache License, Version 2.0 (the "License") 4 | * 5 | * Copyright (c) 2019-2021 Megvii Inc. All rights reserved. 6 | * 7 | * Unless required by applicable law or agreed to in writing, 8 | * software distributed under the License is distributed on an 9 | * "AS IS" BASIS, WITHOUT ARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | */ 11 | use super::unlimited::{self, PyThreadStateUnlimited}; 12 | use pyo3::ffi; 13 | use pyo3::prelude::*; 14 | use std::cell::RefCell; 15 | 16 | thread_local! { 17 | static CTX: RefCell = RefCell::new(ContextPool { pool: vec![], freelist: vec![] }); 18 | } 19 | 20 | pub fn with_context(py: Python, f: F) -> R 21 | where 22 | F: FnOnce() -> R + Send, 23 | R: Send, 24 | { 25 | let id = CTX.with(|ctx| ctx.borrow_mut().store()); 26 | let r = py.allow_threads(f); 27 | CTX.with(|ctx| ctx.borrow_mut().restore(id)); 28 | r 29 | } 30 | 31 | struct Context { 32 | thread: *mut ffi::PyThreadState, 33 | ctx: PyThreadStateUnlimited, 34 | } 35 | 36 | struct ContextPool { 37 | pool: Vec, 38 | freelist: Vec, 39 | } 40 | 41 | impl ContextPool { 42 | fn store(&mut self) -> usize { 43 | let id = self.freelist.pop().unwrap_or(self.pool.len()); 44 | if id == self.pool.len() { 45 | self.pool.push(Context { 46 | thread: std::ptr::null_mut(), 47 | ctx: Default::default(), 48 | }) 49 | } 50 | unsafe { 51 | let context = self.pool.get_unchecked_mut(id); 52 | context.thread = ffi::PyThreadState_Get(); 53 | context.ctx = unlimited::store(context.thread); 54 | } 55 | id 56 | } 57 | 58 | fn restore(&mut self, id: usize) { 59 | unsafe { 60 | let context = self.pool.get_unchecked_mut(id); 61 | ffi::PyThreadState_Swap(context.thread); 62 | unlimited::restore(context.thread, &context.ctx); 63 | context.thread = std::ptr::null_mut(); 64 | context.ctx = Default::default(); 65 | } 66 | self.freelist.push(id); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /flow-rs/src/node/bcast.rs: -------------------------------------------------------------------------------- 1 | /** 2 | * \file flow-rs/src/node/bcast.rs 3 | * MegFlow is Licensed under the Apache License, Version 2.0 (the "License") 4 | * 5 | * Copyright (c) 2019-2021 Megvii Inc. All rights reserved. 6 | * 7 | * Unless required by applicable law or agreed to in writing, 8 | * software distributed under the License is distributed on an 9 | * "AS IS" BASIS, WITHOUT ARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | */ 11 | use anyhow::Result; 12 | use flow_rs::prelude::*; 13 | use toml::value::Table; 14 | 15 | #[inputs(inp)] 16 | #[outputs(out:[])] 17 | #[derive(Node, Actor, Default)] 18 | struct Bcast {} 19 | 20 | impl Bcast { 21 | fn new(_: String, _: &Table) -> Bcast { 22 | Default::default() 23 | } 24 | 25 | async fn initialize(&mut self, _: ResourceCollection) {} 26 | async fn finalize(&mut self) {} 27 | 28 | async fn exec(&mut self, _: &Context) -> Result<()> { 29 | if let Ok(msg) = self.inp.recv_any().await { 30 | for out in &self.out[0..self.out.len() - 1] { 31 | let msg_cloned = msg.clone(); 32 | out.send_any(msg_cloned).await.ok(); 33 | } 34 | if let Some(out) = self.out.last() { 35 | out.send_any(msg).await.ok(); 36 | } 37 | } 38 | Ok(()) 39 | } 40 | } 41 | 42 | node_register!("Bcast", Bcast); 43 | 44 | #[cfg(test)] 45 | mod test { 46 | use crate::envelope::Envelope; 47 | use crate::sandbox::Sandbox; 48 | #[flow_rs::rt::test] 49 | async fn test_bcast() { 50 | let bcast = Sandbox::pure("Bcast").unwrap(); 51 | let input = bcast.input("inp").unwrap(); 52 | let output = bcast.output("out").unwrap(); 53 | let handle = bcast.start(); 54 | input.send(Envelope::new(0usize)).await.ok(); 55 | let envelope = output.recv::().await; 56 | assert!(envelope.is_ok()); 57 | let envelope = envelope.unwrap(); 58 | assert_eq!(envelope.get_ref(), &0); 59 | assert!(output.is_empty()); 60 | input.close(); 61 | handle.await.unwrap(); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /flow-rs/src/node/noop.rs: -------------------------------------------------------------------------------- 1 | /** 2 | * \file flow-rs/src/node/noop.rs 3 | * MegFlow is Licensed under the Apache License, Version 2.0 (the "License") 4 | * 5 | * Copyright (c) 2019-2021 Megvii Inc. All rights reserved. 6 | * 7 | * Unless required by applicable law or agreed to in writing, 8 | * software distributed under the License is distributed on an 9 | * "AS IS" BASIS, WITHOUT ARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | */ 11 | use anyhow::Result; 12 | use flow_rs::prelude::*; 13 | use toml::value::Table; 14 | 15 | #[inputs] 16 | #[outputs(out)] 17 | #[derive(Node, Actor, Default)] 18 | struct NoopProducer {} 19 | 20 | impl NoopProducer { 21 | fn new(_name: String, _args: &Table) -> NoopProducer { 22 | Default::default() 23 | } 24 | 25 | async fn initialize(&mut self, _: ResourceCollection) {} 26 | async fn finalize(&mut self) {} 27 | async fn exec(&mut self, _: &Context) -> Result<()> { 28 | Ok(()) 29 | } 30 | } 31 | 32 | node_register!("NoopProducer", NoopProducer); 33 | 34 | #[inputs(inp)] 35 | #[outputs] 36 | #[derive(Node, Actor, Default)] 37 | struct NoopConsumer {} 38 | 39 | impl NoopConsumer { 40 | fn new(_name: String, _args: &Table) -> NoopConsumer { 41 | Default::default() 42 | } 43 | 44 | async fn initialize(&mut self, _: ResourceCollection) {} 45 | async fn finalize(&mut self) {} 46 | async fn exec(&mut self, _: &Context) -> Result<()> { 47 | self.inp.recv_any().await.ok(); 48 | Ok(()) 49 | } 50 | } 51 | 52 | node_register!("NoopConsumer", NoopConsumer); 53 | -------------------------------------------------------------------------------- /flow-rs/src/resource/any_resource.rs: -------------------------------------------------------------------------------- 1 | #[cfg(feature = "python")] 2 | use pyo3::Python; 3 | use std::any::{Any, TypeId}; 4 | use std::sync::Arc; 5 | 6 | pub trait DowncastArc { 7 | fn into_arc_any(self: Arc) -> Arc; 8 | } 9 | 10 | impl DowncastArc for T 11 | where 12 | T: 'static + Send + Sync, 13 | { 14 | fn into_arc_any(self: Arc) -> Arc { 15 | self 16 | } 17 | } 18 | 19 | pub trait Resource: Any + DowncastArc + Send + Sync { 20 | #[cfg(feature = "python")] 21 | fn to_python(&self, py: Python) -> pyo3::PyObject; 22 | } 23 | 24 | impl dyn Resource { 25 | pub fn is(&self) -> bool { 26 | let t = TypeId::of::(); 27 | let boxed = self.type_id(); 28 | 29 | t == boxed 30 | } 31 | #[inline] 32 | pub fn downcast_arc(self: Arc) -> Result, Arc> 33 | where 34 | Self: Send + Sync, 35 | { 36 | if self.is::() { 37 | Ok(DowncastArc::into_arc_any(self).downcast::().unwrap()) 38 | } else { 39 | Err(self) 40 | } 41 | } 42 | } 43 | 44 | pub type AnyResource = Arc; 45 | -------------------------------------------------------------------------------- /flow-rs/src/resource/lazy.rs: -------------------------------------------------------------------------------- 1 | /** 2 | * \file flow-rs/src/resource/lazy.rs 3 | * MegFlow is Licensed under the Apache License, Version 2.0 (the "License") 4 | * 5 | * Copyright (c) 2019-2021 Megvii Inc. All rights reserved. 6 | * 7 | * Unless required by applicable law or agreed to in writing, 8 | * software distributed under the License is distributed on an 9 | * "AS IS" BASIS, WITHOUT ARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | */ 11 | 12 | // Async initialized lazily 13 | pub struct LazyCell { 14 | cons: Box T + Send + Sync>, 15 | inner: Option, 16 | } 17 | 18 | impl LazyCell { 19 | pub fn new(cons: Box T + Send + Sync>) -> LazyCell { 20 | LazyCell { cons, inner: None } 21 | } 22 | 23 | pub fn revert(&mut self) { 24 | self.inner = None; 25 | } 26 | 27 | pub fn get(&mut self) -> &T { 28 | if self.inner.is_none() { 29 | self.inner = Some((self.cons)()); 30 | } 31 | self.inner.as_ref().unwrap() 32 | } 33 | 34 | pub fn view(&self) -> Option<&T> { 35 | self.inner.as_ref() 36 | } 37 | } 38 | 39 | // we will lock when we set UnsafeCell 40 | unsafe impl Sync for LazyCell where T: Send + Sync {} 41 | 42 | #[cfg(test)] 43 | mod test { 44 | use super::*; 45 | use std::sync::Arc; 46 | #[crate::rt::test] 47 | async fn test_basis() { 48 | let cap = 100; 49 | let mut raw = LazyCell::new(Box::new(move || Arc::new(Vec::::with_capacity(cap)))); 50 | assert!(raw.view().is_none()); 51 | let has_init = raw.get(); 52 | assert_eq!(has_init.capacity(), cap); 53 | assert!(raw.view().is_some()); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /flow-rs/tests/02-dyn-subgraph.rs: -------------------------------------------------------------------------------- 1 | mod nodes_ext; 2 | 3 | use anyhow::Result; 4 | use flow_rs::prelude::*; 5 | 6 | #[rt::test] 7 | async fn test_basis() -> Result<()> { 8 | let _ = Builder::default() 9 | .template( 10 | r#" 11 | main="test" 12 | [[graphs]] 13 | name="sub" 14 | nodes=[{name="b",ty="BinaryOpr"}] 15 | inputs=[ 16 | {name="a",cap=1,ports=["b:a"]}, 17 | {name="b",cap=1,ports=["b:b"]} 18 | ] 19 | outputs=[{name="c",cap=1,ports=["b:c"]}] 20 | [[graphs]] 21 | name="test" 22 | inputs=[{name="inp",cap=1,ports=["t1:inp","t2:inp"]}] 23 | outputs=[{name="out",cap=1,ports=["t3:out"]}] 24 | connections=[ 25 | {cap=1,ports=["t1:out", "sub:a"]}, 26 | {cap=1,ports=["t2:out", "sub:b"]}, 27 | {cap=1,ports=["t3:inp", "sub:c"]} 28 | ] 29 | nodes=[ 30 | {name="sub",ty="sub"}, 31 | {name="t1",ty="DynOutTransform"}, 32 | {name="t2",ty="DynOutTransform"}, 33 | {name="t3",ty="DynInTransform"} 34 | ] 35 | "# 36 | .to_owned(), 37 | ) 38 | .build()?; 39 | 40 | Ok(()) 41 | } 42 | -------------------------------------------------------------------------------- /flow-rs/tests/04-isolated.rs: -------------------------------------------------------------------------------- 1 | mod nodes_ext; 2 | 3 | use anyhow::Result; 4 | use flow_rs::prelude::*; 5 | 6 | #[rt::test] 7 | async fn test_unused() -> Result<()> { 8 | let mut graph = Builder::default() 9 | .template( 10 | r#" 11 | main="test" 12 | [[graphs]] 13 | name="test" 14 | [[graphs.nodes]] 15 | name="t" 16 | ty="Transform" 17 | "# 18 | .to_owned(), 19 | ) 20 | .build()?; 21 | graph.start().await?; 22 | Ok(()) 23 | } 24 | 25 | #[rt::test] 26 | async fn test_isolated() -> Result<()> { 27 | let mut graph = Builder::default() 28 | .template( 29 | r#" 30 | main="test" 31 | [[graphs]] 32 | name="test" 33 | nodes=[ 34 | {name="a", ty="Isolated"}, 35 | ] 36 | "# 37 | .to_owned(), 38 | ) 39 | .build()?; 40 | graph.start().await?; 41 | Ok(()) 42 | } 43 | 44 | #[rt::test] 45 | async fn test_isolated_in_global() -> Result<()> { 46 | let mut graph = Builder::default() 47 | .template( 48 | r#" 49 | main="test" 50 | [[nodes]] 51 | name="a" 52 | ty="IsolatedNever" 53 | [[graphs]] 54 | name="test" 55 | [[graphs.inputs]] 56 | name="inp" 57 | cap=1 58 | ports=["t:inp"] 59 | [[graphs.outputs]] 60 | name="out" 61 | cap=1 62 | ports=["t:out"] 63 | [[graphs.nodes]] 64 | name="t" 65 | ty="Transform" 66 | "# 67 | .to_owned(), 68 | ) 69 | .build()?; 70 | graph.start().await?; 71 | Ok(()) 72 | } 73 | -------------------------------------------------------------------------------- /logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MegEngine/MegFlow/41cb9f85828aa080f6004674904fe430d8268052/logo.png --------------------------------------------------------------------------------