Prathamesh1420 commited on
Commit
6e040df
·
verified ·
1 Parent(s): b4358b7

Upload 16 files

Browse files
.cruft.json ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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": "[email protected]",
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
+ }
.flake8 ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 =
.gitignore ADDED
@@ -0,0 +1,109 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
.pre-commit-config.yaml ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
LICENSE ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+
MANIFEST.in ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ recursive-include src/camera_input_live/frontend *
2
+ include README.md
3
+ include LICENSE
example_app/requirements.txt ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ streamlit-camera-input-live
2
+ opencv-python-headless
example_app/streamlit_app.py ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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)
pyproject.toml ADDED
@@ -0,0 +1,49 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
requirements.txt ADDED
@@ -0,0 +1 @@
 
 
1
+ streamlit
setup.py ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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="[email protected]",
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
+ )
src/camera_input_live/__init__.py ADDED
@@ -0,0 +1,57 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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()
src/camera_input_live/frontend/index.html ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta http-equiv="X-UA-Compatible" content="IE=edge" />
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
+ <title>streamlit-camera-input-live</title>
8
+ <script src="./streamlit-component-lib.js"></script>
9
+ <script src="./main.js"></script>
10
+ <link rel="stylesheet" href="./style.css" />
11
+ </head>
12
+ <body>
13
+ <button id="button">Stop</button>
14
+ <div class="padding"></div>
15
+ <div id="container">
16
+ <video id="video" autoplay="true"></video>
17
+ <canvas id="canvas"> </canvas>
18
+ </div>
19
+ </body>
20
+ </html>
src/camera_input_live/frontend/main.js ADDED
@@ -0,0 +1,111 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // The `Streamlit` object exists because our html file includes
2
+ // `streamlit-component-lib.js`.
3
+ // If you get an error about "Streamlit" not being defined, that
4
+ // means you're missing that file.
5
+
6
+ function sendValue(value) {
7
+ Streamlit.setComponentValue(value)
8
+ }
9
+
10
+
11
+
12
+ /**
13
+ * The component's render function. This will be called immediately after
14
+ * the component is initially loaded, and then again every time the
15
+ * component gets new data from Python.
16
+ */
17
+ function onRender(event) {
18
+ // Only run the render code the first time the component is loaded.
19
+ if (!window.rendered) {
20
+ // You most likely want to get the data passed in like this
21
+ const {height, width, debounce, showControls, startLabel, stopLabel} = event.detail.args
22
+
23
+ if (showControls) {
24
+ Streamlit.setFrameHeight(45)
25
+ }
26
+
27
+ if (isNaN(height)) {
28
+ height = width / (4/3);
29
+ }
30
+
31
+ let video = document.getElementById('video');
32
+ let canvas = document.getElementById('canvas');
33
+ let button = document.getElementById('button');
34
+
35
+ let stopped = false;
36
+
37
+ video.setAttribute('width', width);
38
+ video.setAttribute('height', height);
39
+ canvas.setAttribute('width', width);
40
+ canvas.setAttribute('height', height);
41
+
42
+ function takepicture() {
43
+ if (stopped) {
44
+ return;
45
+ }
46
+ let context = canvas.getContext('2d');
47
+ canvas.width = width;
48
+ canvas.height = height;
49
+ context.drawImage(video, 0, 0, width, height);
50
+
51
+ var data = canvas.toDataURL('image/png');
52
+ sendValue(data);
53
+ }
54
+
55
+
56
+ function stopVideo() {
57
+ video.pause();
58
+ video.srcObject.getTracks()[0].stop();
59
+ stopped = true;
60
+ }
61
+
62
+ function startVideo() {
63
+ navigator.mediaDevices.getUserMedia({ video: true })
64
+ .then(function(stream) {
65
+ video.srcObject = stream;
66
+ video.play();
67
+ })
68
+ .catch(function(err) {
69
+ console.log("An error occurred: " + err);
70
+ });
71
+ }
72
+
73
+ function toggleVideo() {
74
+ if (stopped) {
75
+ startVideo();
76
+ stopped = false;
77
+ } else {
78
+ stopVideo();
79
+ stopped = true;
80
+ }
81
+ // Toggle the button text
82
+ button.textContent = stopped ? startLabel : stopLabel;
83
+ }
84
+
85
+ if (navigator.mediaDevices.getUserMedia) {
86
+ navigator.mediaDevices
87
+ .getUserMedia({ video: true })
88
+ .then(function (stream) {
89
+ video.srcObject = stream;
90
+ })
91
+ .catch(function (error) {
92
+ console.log("Something went wrong!");
93
+ console.error(error);
94
+ });
95
+ }
96
+
97
+ button.addEventListener('click', toggleVideo);
98
+ button.textContent = stopped ? startLabel : stopLabel;
99
+
100
+ takepicture();
101
+ setInterval(takepicture, debounce);
102
+ window.rendered = true
103
+ }
104
+ }
105
+
106
+ // Render the component whenever python send a "render event"
107
+ Streamlit.events.addEventListener(Streamlit.RENDER_EVENT, onRender)
108
+ // Tell Streamlit that the component is ready to receive events
109
+ Streamlit.setComponentReady()
110
+ // Don't actually need to display anything, so set the height to 0
111
+ Streamlit.setFrameHeight(0)
src/camera_input_live/frontend/streamlit-component-lib.js ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ // Borrowed minimalistic Streamlit API from Thiago
3
+ // https://discuss.streamlit.io/t/code-snippet-create-components-without-any-frontend-tooling-no-react-babel-webpack-etc/13064
4
+ function sendMessageToStreamlitClient(type, data) {
5
+ console.log(type, data)
6
+ const outData = Object.assign({
7
+ isStreamlitMessage: true,
8
+ type: type,
9
+ }, data);
10
+ window.parent.postMessage(outData, "*");
11
+ }
12
+
13
+ const Streamlit = {
14
+ setComponentReady: function() {
15
+ sendMessageToStreamlitClient("streamlit:componentReady", {apiVersion: 1});
16
+ },
17
+ setFrameHeight: function(height) {
18
+ sendMessageToStreamlitClient("streamlit:setFrameHeight", {height: height});
19
+ },
20
+ setComponentValue: function(value) {
21
+ sendMessageToStreamlitClient("streamlit:setComponentValue", {value: value});
22
+ },
23
+ RENDER_EVENT: "streamlit:render",
24
+ events: {
25
+ addEventListener: function(type, callback) {
26
+ window.addEventListener("message", function(event) {
27
+ if (event.data.type === type) {
28
+ event.detail = event.data
29
+ callback(event);
30
+ }
31
+ });
32
+ }
33
+ }
34
+ }
src/camera_input_live/frontend/style.css ADDED
@@ -0,0 +1,43 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ button {
2
+ text-size-adjust: 100%;
3
+ -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
4
+ -webkit-font-smoothing: auto;
5
+ box-sizing: border-box;
6
+ font-size: inherit;
7
+ font-family: inherit;
8
+ overflow: visible;
9
+ text-transform: none;
10
+ appearance: button;
11
+ display: inline-flex;
12
+ -webkit-box-align: center;
13
+ align-items: center;
14
+ -webkit-box-pack: center;
15
+ justify-content: center;
16
+ font-weight: 400;
17
+ padding: 0.25rem 0.75rem;
18
+ border-radius: 0.25rem;
19
+ margin: 0px;
20
+ line-height: 1.6;
21
+ color: inherit;
22
+ width: auto;
23
+ user-select: none;
24
+ background-color: rgb(255, 255, 255);
25
+ border: 1px solid rgba(49, 51, 63, 0.2);
26
+ cursor: pointer;
27
+ position: absolute;
28
+ left: 0;
29
+ }
30
+
31
+ button:hover {
32
+ border-color: rgb(255, 75, 75);
33
+ color: rgb(255, 75, 75);
34
+ }
35
+ button:active {
36
+ color: rgb(255, 255, 255);
37
+ border-color: rgb(255, 75, 75);
38
+ background-color: rgb(255, 75, 75);
39
+ }
40
+
41
+ .padding {
42
+ height: 100px;
43
+ }