├── .cruft.json ├── .flake8 ├── .github └── workflows │ └── publish_PYPI_each_tag.yml ├── .gitignore ├── .pre-commit-config.yaml ├── LICENSE ├── MANIFEST.in ├── README.md ├── example_app ├── requirements.txt └── streamlit_app.py ├── pyproject.toml ├── requirements.txt ├── setup.py └── src └── camera_input_live ├── __init__.py └── frontend ├── index.html ├── main.js ├── streamlit-component-lib.js └── style.css /.cruft.json: -------------------------------------------------------------------------------- 1 | { 2 | "template": "https://github.com/blackary/cookiecutter-streamlit-component/", 3 | "commit": "c972054b7b0890a1aa1c02e792bedc71c525ea87", 4 | "checkout": null, 5 | "context": { 6 | "cookiecutter": { 7 | "author_name": "Zachary Blackwood", 8 | "author_email": "zachary@streamlit.io", 9 | "project_name": "Streamlit Camera Input Live", 10 | "package_name": "streamlit-camera-input-live", 11 | "import_name": "camera_input_live", 12 | "description": "Alternative version of st.camera_input which returns the webcam images live, without any button press needed", 13 | "deployment_via_github_actions": "y", 14 | "working_with_dataframes": "n", 15 | "open_source_license": "MIT license", 16 | "_template": "https://github.com/blackary/cookiecutter-streamlit-component/" 17 | } 18 | }, 19 | "directory": null 20 | } 21 | -------------------------------------------------------------------------------- /.flake8: -------------------------------------------------------------------------------- 1 | [flake8] 2 | max-line-length = 88 3 | select = 4 | E # pep8 errors 5 | F # pyflakes errors 6 | W # pep8 warnings 7 | B # flake8-bugbear warnings 8 | ignore = 9 | E501 # "Line lengths are recommended to be no greater than 79 characters" 10 | E203 # "Whitespace before ':'": conflicts with black 11 | W503 # "line break before binary operator": conflicts with black 12 | exclude = 13 | .git 14 | .vscode 15 | .pytest_cache 16 | .mypy_cache 17 | .venv 18 | .env 19 | .direnv 20 | per-file-ignores = -------------------------------------------------------------------------------- /.github/workflows/publish_PYPI_each_tag.yml: -------------------------------------------------------------------------------- 1 | 2 | # This workflows will upload a Python Package using Twine when a release is created 3 | # For more information see: https://help.github.com/en/actions/language-and-framework-guides/using-python-with-github-actions#publishing-to-package-registries 4 | 5 | name: Upload Python Package 6 | 7 | on: 8 | release: 9 | types: [created] 10 | 11 | jobs: 12 | deploy: 13 | runs-on: ubuntu-latest 14 | 15 | steps: 16 | - uses: actions/checkout@v2 17 | - name: Set up Python 18 | uses: actions/setup-python@v2 19 | with: 20 | python-version: "3.x" 21 | - name: Install dependencies 22 | run: | 23 | python -m pip install --upgrade pip 24 | pip install setuptools wheel twine 25 | - name: Build and publish 26 | env: 27 | TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }} 28 | TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }} 29 | run: | 30 | pwd 31 | python setup.py sdist bdist_wheel 32 | twine upload dist/* 33 | 34 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # OSX useful to ignore 7 | *.DS_Store 8 | .AppleDouble 9 | .LSOverride 10 | 11 | # Thumbnails 12 | ._* 13 | 14 | # Files that might appear in the root of a volume 15 | .DocumentRevisions-V100 16 | .fseventsd 17 | .Spotlight-V100 18 | .TemporaryItems 19 | .Trashes 20 | .VolumeIcon.icns 21 | .com.apple.timemachine.donotpresent 22 | 23 | # Directories potentially created on remote AFP share 24 | .AppleDB 25 | .AppleDesktop 26 | Network Trash Folder 27 | Temporary Items 28 | .apdisk 29 | 30 | # C extensions 31 | *.so 32 | 33 | # Distribution / packaging 34 | .Python 35 | env/ 36 | venv/ 37 | build/ 38 | develop-eggs/ 39 | dist/ 40 | downloads/ 41 | eggs/ 42 | .eggs/ 43 | lib/ 44 | lib64/ 45 | parts/ 46 | sdist/ 47 | var/ 48 | *.egg-info/ 49 | .installed.cfg 50 | *.egg 51 | 52 | # PyInstaller 53 | # Usually these files are written by a python script from a template 54 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 55 | *.manifest 56 | *.spec 57 | 58 | # Installer logs 59 | pip-log.txt 60 | pip-delete-this-directory.txt 61 | 62 | # Unit test / coverage reports 63 | htmlcov/ 64 | .tox/ 65 | .coverage 66 | .coverage.* 67 | .cache 68 | nosetests.xml 69 | coverage.xml 70 | *,cover 71 | .hypothesis/ 72 | .pytest_cache/ 73 | 74 | # Translations 75 | *.mo 76 | *.pot 77 | 78 | # Django stuff: 79 | *.log 80 | 81 | # Sphinx documentation 82 | docs/_build/ 83 | 84 | # IntelliJ Idea family of suites 85 | .idea 86 | *.iml 87 | ## File-based project format: 88 | *.ipr 89 | *.iws 90 | ## mpeltonen/sbt-idea plugin 91 | .idea_modules/ 92 | 93 | # PyBuilder 94 | target/ 95 | 96 | # Cookiecutter 97 | output/ 98 | python_boilerplate/ 99 | cookiecutter-pypackage-env/ 100 | 101 | # IDE settings 102 | .vscode/ 103 | 104 | # direnv 105 | .envrc 106 | .direnv/ 107 | 108 | # streamlit 109 | .streamlit/secrets.toml 110 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | repos: 2 | - repo: https://github.com/astral-sh/ruff-pre-commit 3 | rev: v0.8.3 4 | hooks: 5 | - id: ruff 6 | args: 7 | - --fix 8 | - id: ruff-format 9 | - repo: https://github.com/pre-commit/mirrors-mypy 10 | rev: v1.13.0 11 | hooks: 12 | - id: mypy 13 | args: 14 | - --ignore-missing-imports 15 | - --follow-imports=silent 16 | - repo: https://github.com/pre-commit/pre-commit-hooks 17 | rev: v5.0.0 # Use the ref you want to point at 18 | hooks: 19 | - id: trailing-whitespace 20 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022, Zachary Blackwood 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | recursive-include src/camera_input_live/frontend * 2 | include README.md 3 | include LICENSE 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # streamlit-camera-input-live 2 | 3 | [](https://pypi.org/project/streamlit-camera-input-live/) 4 | [](https://pypistats.org/packages/streamlit-camera-input-live) 5 | [](LICENSE) 6 | [](https://github.com/psf/black) 7 | 8 | Alternative version of st.camera_input which returns the webcam images live, without any button press needed 9 | 10 | [](https://camera.streamlitapp.com) 11 | 12 | ## Installation instructions 13 | 14 | ```sh 15 | pip install streamlit-camera-input-live 16 | ``` 17 | 18 | ## Usage instructions 19 | 20 | ```python 21 | import streamlit as st 22 | 23 | from camera_input_live import camera_input_live 24 | 25 | image = camera_input_live() 26 | 27 | if image: 28 | st.image(image) 29 | ``` 30 | -------------------------------------------------------------------------------- /example_app/requirements.txt: -------------------------------------------------------------------------------- 1 | streamlit-camera-input-live 2 | opencv-python-headless -------------------------------------------------------------------------------- /example_app/streamlit_app.py: -------------------------------------------------------------------------------- 1 | import cv2 2 | import numpy as np 3 | import streamlit as st 4 | 5 | from camera_input_live import camera_input_live 6 | 7 | "# Streamlit camera input live Demo" 8 | "## Try holding a qr code in front of your webcam" 9 | 10 | image = camera_input_live() 11 | 12 | if image is not None: 13 | st.image(image) 14 | bytes_data = image.getvalue() 15 | cv2_img = cv2.imdecode(np.frombuffer(bytes_data, np.uint8), cv2.IMREAD_COLOR) 16 | 17 | detector = cv2.QRCodeDetector() 18 | 19 | data, bbox, straight_qrcode = detector.detectAndDecode(cv2_img) 20 | 21 | if data: 22 | st.write("# Found QR code") 23 | st.write(data) 24 | with st.expander("Show details"): 25 | st.write("BBox:", bbox) 26 | st.write("Straight QR code:", straight_qrcode) 27 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.ruff] 2 | exclude = [ 3 | ".git", 4 | ".vscode", 5 | ".pytest_cache", 6 | ".bamboo", 7 | ".tecton", 8 | ".mypy_cache", 9 | ".env", 10 | ] 11 | ignore = [ 12 | "B008", 13 | "ISC001", 14 | "E501", 15 | "W191" 16 | ] 17 | line-length = 88 18 | select = [ 19 | "B", # https://pypi.org/project/flake8-bugbear/ 20 | "E", # https://pycodestyle.pycqa.org/en/latest/intro.html#error-codes 21 | "F", # https://flake8.pycqa.org/en/latest/user/error-codes.html 22 | "W", # https://pycodestyle.pycqa.org/en/latest/intro.html#error-codes 23 | "I", # https://pycqa.github.io/isort/ 24 | "N", # https://github.com/PyCQA/pep8-naming 25 | "C4", # https://github.com/adamchainz/flake8-comprehensions 26 | "EXE", # https://pypi.org/project/flake8-executable/ 27 | "ISC", # https://github.com/flake8-implicit-str-concat/flake8-implicit-str-concat 28 | "ICN", # https://pypi.org/project/flake8-import-conventions/ 29 | "PIE", # https://pypi.org/project/flake8-pie/ 30 | "PT", # https://github.com/m-burst/flake8-pytest-style 31 | "RET", # https://pypi.org/project/flake8-return/ 32 | "SIM", # https://pypi.org/project/flake8-simplify/ 33 | "ERA", # https://pypi.org/project/flake8-eradicate/ 34 | "PLC", # https://beta.ruff.rs/docs/rules/#convention-plc 35 | "RUF", # https://beta.ruff.rs/docs/rules/#ruff-specific-rules-ruf 36 | "ARG", # https://beta.ruff.rs/docs/rules/#flake8-unused-arguments-arg 37 | ] 38 | 39 | [tool.ruff.per-file-ignores] 40 | "__init__.py" = ["F401"] 41 | 42 | [tool.mypy] 43 | files = [ 44 | "**/*.py", 45 | ] 46 | follow_imports = "silent" 47 | ignore_missing_imports = true 48 | scripts_are_modules = true 49 | python_version = 3.9 50 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | streamlit 2 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from pathlib import Path 2 | 3 | import setuptools 4 | 5 | this_directory = Path(__file__).parent 6 | long_description = (this_directory / "README.md").read_text() 7 | 8 | setuptools.setup( 9 | name="streamlit-camera-input-live", 10 | version="0.2.0", 11 | author="Zachary Blackwood", 12 | author_email="zachary@streamlit.io", 13 | description="Alternative version of st.camera_input which returns the webcam images live, without any button press needed", 14 | long_description=long_description, 15 | long_description_content_type="text/markdown", 16 | packages=setuptools.find_packages(where="src"), 17 | package_dir={"": "src"}, 18 | include_package_data=True, 19 | classifiers=[], 20 | python_requires=">=3.7", 21 | install_requires=["streamlit>=1.2", "jinja2"], 22 | url="https://github.com/blackary/streamlit-camera-input-live", 23 | ) 24 | -------------------------------------------------------------------------------- /src/camera_input_live/__init__.py: -------------------------------------------------------------------------------- 1 | import base64 2 | from io import BytesIO 3 | from pathlib import Path 4 | from typing import Optional 5 | 6 | import streamlit as st 7 | import streamlit.components.v1 as components 8 | 9 | # Tell streamlit that there is a component called camera_input_live, 10 | # and that the code to display that component is in the "frontend" folder 11 | frontend_dir = (Path(__file__).parent / "frontend").absolute() 12 | _component_func = components.declare_component( 13 | "camera_input_live", path=str(frontend_dir) 14 | ) 15 | 16 | 17 | def camera_input_live( 18 | debounce: int = 1000, 19 | height: int = 530, 20 | width: int = 704, 21 | key: Optional[str] = None, 22 | show_controls: bool = True, 23 | start_label: str = "Start capturing", 24 | stop_label: str = "Pause capturing", 25 | ) -> Optional[BytesIO]: 26 | """ 27 | Add a descriptive docstring 28 | """ 29 | b64_data: Optional[str] = _component_func( 30 | height=height, 31 | width=width, 32 | debounce=debounce, 33 | showControls=show_controls, 34 | startLabel=start_label, 35 | stopLabel=stop_label, 36 | key=key, 37 | ) 38 | 39 | if b64_data is None: 40 | return None 41 | 42 | raw_data = b64_data.split(",")[1] # Strip the data: type prefix 43 | 44 | return BytesIO(base64.b64decode(raw_data)) 45 | 46 | 47 | def main(): 48 | st.write("## Example") 49 | 50 | image = camera_input_live(show_controls=True) 51 | 52 | if image is not None: 53 | st.image(image) 54 | 55 | 56 | if __name__ == "__main__": 57 | main() 58 | -------------------------------------------------------------------------------- /src/camera_input_live/frontend/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 | 6 | 7 |