cv-frames 0.0.3__tar.gz → 0.0.4__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {cv_frames-0.0.3 → cv_frames-0.0.4}/.github/workflows/tests.yml +3 -3
- cv_frames-0.0.4/.pre-commit-config.yaml +24 -0
- {cv_frames-0.0.3/cv_frames.egg-info → cv_frames-0.0.4}/PKG-INFO +20 -2
- cv_frames-0.0.3/PKG-INFO → cv_frames-0.0.4/README.rst +10 -11
- cv_frames-0.0.3/README.rst → cv_frames-0.0.4/cv_frames.egg-info/PKG-INFO +29 -0
- {cv_frames-0.0.3 → cv_frames-0.0.4}/cv_frames.egg-info/SOURCES.txt +2 -0
- cv_frames-0.0.4/cv_frames.egg-info/requires.txt +17 -0
- cv_frames-0.0.4/cv_frames.egg-info/scm_file_list.json +19 -0
- cv_frames-0.0.4/cv_frames.egg-info/scm_version.json +8 -0
- {cv_frames-0.0.3 → cv_frames-0.0.4}/cvframes/cli.py +4 -3
- {cv_frames-0.0.3 → cv_frames-0.0.4}/cvframes/iterate.py +54 -20
- {cv_frames-0.0.3 → cv_frames-0.0.4}/cvframes/web/server.py +12 -9
- {cv_frames-0.0.3 → cv_frames-0.0.4}/pyproject.toml +33 -12
- {cv_frames-0.0.3 → cv_frames-0.0.4}/requirements.txt +1 -1
- {cv_frames-0.0.3 → cv_frames-0.0.4}/tests/test_iterate.py +2 -3
- cv_frames-0.0.3/.pre-commit-config.yaml +0 -39
- cv_frames-0.0.3/cv_frames.egg-info/requires.txt +0 -6
- {cv_frames-0.0.3 → cv_frames-0.0.4}/.github/workflows/publish.yml +0 -0
- {cv_frames-0.0.3 → cv_frames-0.0.4}/.gitignore +0 -0
- {cv_frames-0.0.3 → cv_frames-0.0.4}/cv_frames.egg-info/dependency_links.txt +0 -0
- {cv_frames-0.0.3 → cv_frames-0.0.4}/cv_frames.egg-info/entry_points.txt +0 -0
- {cv_frames-0.0.3 → cv_frames-0.0.4}/cv_frames.egg-info/top_level.txt +0 -0
- {cv_frames-0.0.3 → cv_frames-0.0.4}/cvframes/__init__.py +0 -0
- {cv_frames-0.0.3 → cv_frames-0.0.4}/cvframes/web/__init__.py +0 -0
- {cv_frames-0.0.3 → cv_frames-0.0.4}/cvframes/web/client.py +0 -0
- {cv_frames-0.0.3 → cv_frames-0.0.4}/cvframes/web/static/index.html +0 -0
- {cv_frames-0.0.3 → cv_frames-0.0.4}/setup.cfg +0 -0
|
@@ -11,7 +11,7 @@ jobs:
|
|
|
11
11
|
|
|
12
12
|
strategy:
|
|
13
13
|
matrix:
|
|
14
|
-
python-version: ["3.
|
|
14
|
+
python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
|
|
15
15
|
|
|
16
16
|
steps:
|
|
17
17
|
- uses: actions/checkout@v3
|
|
@@ -24,11 +24,11 @@ jobs:
|
|
|
24
24
|
- name: Install dependencies
|
|
25
25
|
run: |
|
|
26
26
|
pip install -r requirements.txt
|
|
27
|
-
pip install -e .
|
|
27
|
+
pip install -e .[opencv]
|
|
28
28
|
|
|
29
29
|
- name: Run the tests
|
|
30
30
|
run: |
|
|
31
|
-
PYTHONFAULTHANDLER=2 pytest
|
|
31
|
+
PYTHONFAULTHANDLER=2 pytest tests/
|
|
32
32
|
|
|
33
33
|
- name: Run pre-commit checks
|
|
34
34
|
uses: pre-commit/action@v3.0.1
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
repos:
|
|
2
|
+
- repo: https://github.com/pre-commit/pre-commit-hooks
|
|
3
|
+
rev: v5.0.0
|
|
4
|
+
hooks:
|
|
5
|
+
- id: check-added-large-files
|
|
6
|
+
args: ["--maxkb=1000"]
|
|
7
|
+
- id: check-json
|
|
8
|
+
- id: check-docstring-first
|
|
9
|
+
- id: end-of-file-fixer
|
|
10
|
+
- id: trailing-whitespace
|
|
11
|
+
|
|
12
|
+
- repo: https://github.com/astral-sh/ruff-pre-commit
|
|
13
|
+
rev: v0.16.3
|
|
14
|
+
hooks:
|
|
15
|
+
- id: ruff-check
|
|
16
|
+
args: ["--fix"]
|
|
17
|
+
- id: ruff-format
|
|
18
|
+
|
|
19
|
+
- repo: https://github.com/pre-commit/mirrors-mypy
|
|
20
|
+
rev: v1.14.1
|
|
21
|
+
hooks:
|
|
22
|
+
- id: mypy
|
|
23
|
+
additional_dependencies: ["types-PyYAML"]
|
|
24
|
+
args: ["--config-file", "pyproject.toml"]
|
|
@@ -1,13 +1,21 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: cv-frames
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.4
|
|
4
4
|
Summary: Read frames from OpenCV like humans
|
|
5
|
+
Requires-Python: >=3.10
|
|
5
6
|
Description-Content-Type: text/x-rst
|
|
6
|
-
Requires-Dist: opencv-python
|
|
7
7
|
Requires-Dist: numpy
|
|
8
8
|
Requires-Dist: click
|
|
9
9
|
Provides-Extra: web
|
|
10
10
|
Requires-Dist: aiohttp; extra == "web"
|
|
11
|
+
Provides-Extra: opencv
|
|
12
|
+
Requires-Dist: opencv-python; extra == "opencv"
|
|
13
|
+
Provides-Extra: opencv-contrib
|
|
14
|
+
Requires-Dist: opencv-contrib-python; extra == "opencv-contrib"
|
|
15
|
+
Provides-Extra: opencv-headless
|
|
16
|
+
Requires-Dist: opencv-python-headless; extra == "opencv-headless"
|
|
17
|
+
Provides-Extra: opencv-contrib-headless
|
|
18
|
+
Requires-Dist: opencv-contrib-python-headless; extra == "opencv-contrib-headless"
|
|
11
19
|
|
|
12
20
|
cv-frames
|
|
13
21
|
=========
|
|
@@ -34,6 +42,16 @@ Install via pip:
|
|
|
34
42
|
|
|
35
43
|
pip install cv-frames
|
|
36
44
|
|
|
45
|
+
If you install ``cv-frames`` standalone, you need to specify an OpenCV backend:
|
|
46
|
+
|
|
47
|
+
.. code-block:: bash
|
|
48
|
+
|
|
49
|
+
pip install cv-frames[opencv]
|
|
50
|
+
|
|
51
|
+
If ``cv-frames`` is part of a project that already depends on OpenCV
|
|
52
|
+
(e.g. ``opencv-python`` or ``opencv-contrib-python`` is listed in your
|
|
53
|
+
``requirements.txt``), no optional dependency is needed.
|
|
54
|
+
|
|
37
55
|
----
|
|
38
56
|
|
|
39
57
|
Command Line Usage
|
|
@@ -1,14 +1,3 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: cv-frames
|
|
3
|
-
Version: 0.0.3
|
|
4
|
-
Summary: Read frames from OpenCV like humans
|
|
5
|
-
Description-Content-Type: text/x-rst
|
|
6
|
-
Requires-Dist: opencv-python
|
|
7
|
-
Requires-Dist: numpy
|
|
8
|
-
Requires-Dist: click
|
|
9
|
-
Provides-Extra: web
|
|
10
|
-
Requires-Dist: aiohttp; extra == "web"
|
|
11
|
-
|
|
12
1
|
cv-frames
|
|
13
2
|
=========
|
|
14
3
|
|
|
@@ -34,6 +23,16 @@ Install via pip:
|
|
|
34
23
|
|
|
35
24
|
pip install cv-frames
|
|
36
25
|
|
|
26
|
+
If you install ``cv-frames`` standalone, you need to specify an OpenCV backend:
|
|
27
|
+
|
|
28
|
+
.. code-block:: bash
|
|
29
|
+
|
|
30
|
+
pip install cv-frames[opencv]
|
|
31
|
+
|
|
32
|
+
If ``cv-frames`` is part of a project that already depends on OpenCV
|
|
33
|
+
(e.g. ``opencv-python`` or ``opencv-contrib-python`` is listed in your
|
|
34
|
+
``requirements.txt``), no optional dependency is needed.
|
|
35
|
+
|
|
37
36
|
----
|
|
38
37
|
|
|
39
38
|
Command Line Usage
|
|
@@ -1,3 +1,22 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: cv-frames
|
|
3
|
+
Version: 0.0.4
|
|
4
|
+
Summary: Read frames from OpenCV like humans
|
|
5
|
+
Requires-Python: >=3.10
|
|
6
|
+
Description-Content-Type: text/x-rst
|
|
7
|
+
Requires-Dist: numpy
|
|
8
|
+
Requires-Dist: click
|
|
9
|
+
Provides-Extra: web
|
|
10
|
+
Requires-Dist: aiohttp; extra == "web"
|
|
11
|
+
Provides-Extra: opencv
|
|
12
|
+
Requires-Dist: opencv-python; extra == "opencv"
|
|
13
|
+
Provides-Extra: opencv-contrib
|
|
14
|
+
Requires-Dist: opencv-contrib-python; extra == "opencv-contrib"
|
|
15
|
+
Provides-Extra: opencv-headless
|
|
16
|
+
Requires-Dist: opencv-python-headless; extra == "opencv-headless"
|
|
17
|
+
Provides-Extra: opencv-contrib-headless
|
|
18
|
+
Requires-Dist: opencv-contrib-python-headless; extra == "opencv-contrib-headless"
|
|
19
|
+
|
|
1
20
|
cv-frames
|
|
2
21
|
=========
|
|
3
22
|
|
|
@@ -23,6 +42,16 @@ Install via pip:
|
|
|
23
42
|
|
|
24
43
|
pip install cv-frames
|
|
25
44
|
|
|
45
|
+
If you install ``cv-frames`` standalone, you need to specify an OpenCV backend:
|
|
46
|
+
|
|
47
|
+
.. code-block:: bash
|
|
48
|
+
|
|
49
|
+
pip install cv-frames[opencv]
|
|
50
|
+
|
|
51
|
+
If ``cv-frames`` is part of a project that already depends on OpenCV
|
|
52
|
+
(e.g. ``opencv-python`` or ``opencv-contrib-python`` is listed in your
|
|
53
|
+
``requirements.txt``), no optional dependency is needed.
|
|
54
|
+
|
|
26
55
|
----
|
|
27
56
|
|
|
28
57
|
Command Line Usage
|
|
@@ -10,6 +10,8 @@ cv_frames.egg-info/SOURCES.txt
|
|
|
10
10
|
cv_frames.egg-info/dependency_links.txt
|
|
11
11
|
cv_frames.egg-info/entry_points.txt
|
|
12
12
|
cv_frames.egg-info/requires.txt
|
|
13
|
+
cv_frames.egg-info/scm_file_list.json
|
|
14
|
+
cv_frames.egg-info/scm_version.json
|
|
13
15
|
cv_frames.egg-info/top_level.txt
|
|
14
16
|
cvframes/__init__.py
|
|
15
17
|
cvframes/cli.py
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"files": [
|
|
3
|
+
".github/workflows/publish.yml",
|
|
4
|
+
".github/workflows/tests.yml",
|
|
5
|
+
".gitignore",
|
|
6
|
+
".pre-commit-config.yaml",
|
|
7
|
+
"README.rst",
|
|
8
|
+
"cvframes/__init__.py",
|
|
9
|
+
"cvframes/cli.py",
|
|
10
|
+
"cvframes/iterate.py",
|
|
11
|
+
"cvframes/web/__init__.py",
|
|
12
|
+
"cvframes/web/client.py",
|
|
13
|
+
"cvframes/web/server.py",
|
|
14
|
+
"cvframes/web/static/index.html",
|
|
15
|
+
"pyproject.toml",
|
|
16
|
+
"requirements.txt",
|
|
17
|
+
"tests/test_iterate.py"
|
|
18
|
+
]
|
|
19
|
+
}
|
|
@@ -13,9 +13,10 @@ def main():
|
|
|
13
13
|
|
|
14
14
|
@main.command(name="show")
|
|
15
15
|
@click.argument("filepath", type=click.Path(exists=True))
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
16
|
+
@click.option("--start-frame", type=int, default=0)
|
|
17
|
+
def show(filepath, start_frame: int):
|
|
18
|
+
for i, (_, frame) in enumerate(iterate(filepath, start_frame=start_frame)):
|
|
19
|
+
click.echo(f"Frame: {i + start_frame}")
|
|
19
20
|
cv2.imshow("cvframes-show", frame)
|
|
20
21
|
|
|
21
22
|
# Wait for a key press and check if it's 'q' to exit
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
from collections.abc import Callable, Generator
|
|
1
2
|
from pathlib import Path
|
|
2
|
-
from typing import
|
|
3
|
+
from typing import TypeVar
|
|
3
4
|
|
|
4
5
|
import cv2
|
|
5
6
|
import numpy as np
|
|
@@ -8,34 +9,67 @@ T = TypeVar("T")
|
|
|
8
9
|
|
|
9
10
|
|
|
10
11
|
class IOCapture:
|
|
11
|
-
def __init__(
|
|
12
|
+
def __init__(
|
|
13
|
+
self,
|
|
14
|
+
source: str | Path,
|
|
15
|
+
oname: str | Path = "",
|
|
16
|
+
oshape: tuple[int, int] | None = None,
|
|
17
|
+
):
|
|
12
18
|
self.icap = cv2.VideoCapture(str(source))
|
|
13
|
-
self.
|
|
14
|
-
|
|
15
|
-
|
|
19
|
+
self.oname = str(oname) if oname else None
|
|
20
|
+
self.oshape = oshape
|
|
21
|
+
self._ocap: cv2.VideoWriter | None = None
|
|
22
|
+
|
|
23
|
+
@property
|
|
24
|
+
def ocap(self) -> cv2.VideoWriter | None:
|
|
25
|
+
if self.oname is None:
|
|
26
|
+
return None
|
|
27
|
+
|
|
28
|
+
if self.oshape is None:
|
|
29
|
+
return None
|
|
30
|
+
|
|
31
|
+
if self._ocap is None:
|
|
32
|
+
self._ocap = cv2.VideoWriter(
|
|
33
|
+
self.oname,
|
|
16
34
|
cv2.VideoWriter_fourcc(*"mp4v"),
|
|
17
35
|
self.icap.get(cv2.CAP_PROP_FPS),
|
|
18
|
-
|
|
19
|
-
int(self.icap.get(cv2.CAP_PROP_FRAME_WIDTH)),
|
|
20
|
-
int(self.icap.get(cv2.CAP_PROP_FRAME_HEIGHT)),
|
|
21
|
-
),
|
|
36
|
+
self.oshape,
|
|
22
37
|
)
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
38
|
+
|
|
39
|
+
if not self._ocap.isOpened():
|
|
40
|
+
raise RuntimeError(
|
|
41
|
+
f"Cannot open output video file: {self.oname}"
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
return self._ocap
|
|
26
45
|
|
|
27
46
|
def is_opened(self) -> bool:
|
|
28
47
|
return self.icap.isOpened()
|
|
29
48
|
|
|
30
|
-
def read(self) ->
|
|
49
|
+
def read(self) -> tuple[bool, np.ndarray]:
|
|
31
50
|
return self.icap.read()
|
|
32
51
|
|
|
33
52
|
def write(self, frame: np.ndarray) -> None:
|
|
53
|
+
height, width = frame.shape[:2]
|
|
54
|
+
shape = (width, height)
|
|
55
|
+
|
|
56
|
+
if self.oshape is None:
|
|
57
|
+
self.oshape = shape
|
|
58
|
+
return
|
|
59
|
+
|
|
60
|
+
if shape != self.oshape:
|
|
61
|
+
raise ValueError(
|
|
62
|
+
"All output frames must have the same resolution. "
|
|
63
|
+
f"Expected {self.oshape[0]}x{self.oshape[1]}, "
|
|
64
|
+
f"got {width}x{height}."
|
|
65
|
+
)
|
|
66
|
+
|
|
34
67
|
if self.ocap is not None:
|
|
35
68
|
self.ocap.write(frame)
|
|
36
69
|
|
|
37
70
|
def release(self) -> None:
|
|
38
71
|
self.icap.release()
|
|
72
|
+
|
|
39
73
|
if self.ocap is not None:
|
|
40
74
|
self.ocap.release()
|
|
41
75
|
|
|
@@ -45,11 +79,11 @@ class IOCapture:
|
|
|
45
79
|
|
|
46
80
|
def iterate_generic(
|
|
47
81
|
ipath: Path,
|
|
48
|
-
opath:
|
|
82
|
+
opath: Path | None,
|
|
49
83
|
start_frame: int,
|
|
50
84
|
stop_frame: int,
|
|
51
85
|
process_frames: Callable[[np.ndarray], T],
|
|
52
|
-
) -> Generator[
|
|
86
|
+
) -> Generator[tuple[IOCapture, T], None, None]:
|
|
53
87
|
capture = IOCapture(str(ipath), oname=opath or "")
|
|
54
88
|
capture.set(cv2.CAP_PROP_POS_FRAMES, start_frame)
|
|
55
89
|
count = start_frame
|
|
@@ -73,10 +107,10 @@ def iterate_generic(
|
|
|
73
107
|
|
|
74
108
|
def iterate(
|
|
75
109
|
ipath: Path,
|
|
76
|
-
opath:
|
|
110
|
+
opath: Path | None = None,
|
|
77
111
|
start_frame: int = -1,
|
|
78
112
|
stop_frame: int = -1,
|
|
79
|
-
) -> Generator[
|
|
113
|
+
) -> Generator[tuple[IOCapture, np.ndarray], None, None]:
|
|
80
114
|
return iterate_generic(
|
|
81
115
|
ipath,
|
|
82
116
|
opath,
|
|
@@ -88,11 +122,11 @@ def iterate(
|
|
|
88
122
|
|
|
89
123
|
def iterate_sbs(
|
|
90
124
|
ipath: Path,
|
|
91
|
-
opath:
|
|
125
|
+
opath: Path | None = None,
|
|
92
126
|
start_frame: int = -1,
|
|
93
127
|
stop_frame: int = -1,
|
|
94
|
-
) -> Generator[
|
|
95
|
-
def processor(frame: np.ndarray) ->
|
|
128
|
+
) -> Generator[tuple[IOCapture, tuple[np.ndarray, np.ndarray]], None, None]:
|
|
129
|
+
def processor(frame: np.ndarray) -> tuple[np.ndarray, np.ndarray]:
|
|
96
130
|
_, width, _ = frame.shape
|
|
97
131
|
mid = width // 2
|
|
98
132
|
return frame[:, :mid, :], frame[:, mid:, :]
|
|
@@ -24,6 +24,13 @@ MAPPING = {
|
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
|
|
27
|
+
async def send_frame(ws, frame: bytes, to_remove: set):
|
|
28
|
+
try:
|
|
29
|
+
await ws.send_bytes(frame)
|
|
30
|
+
except Exception: # noqa: BLE001 - a broken client must not stop the rest
|
|
31
|
+
to_remove.add(ws)
|
|
32
|
+
|
|
33
|
+
|
|
27
34
|
async def broadcast_task(
|
|
28
35
|
frame_queue: asyncio.Queue,
|
|
29
36
|
clients: set,
|
|
@@ -39,13 +46,9 @@ async def broadcast_task(
|
|
|
39
46
|
to_remove.add(ws)
|
|
40
47
|
continue
|
|
41
48
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
except Exception:
|
|
46
|
-
to_remove.add(ws)
|
|
47
|
-
|
|
48
|
-
tasks.append(asyncio.create_task(send(ws)))
|
|
49
|
+
tasks.append(
|
|
50
|
+
asyncio.create_task(send_frame(ws, frame, to_remove))
|
|
51
|
+
)
|
|
49
52
|
await asyncio.gather(*tasks, return_exceptions=True)
|
|
50
53
|
for ws in to_remove:
|
|
51
54
|
clients.remove(ws)
|
|
@@ -77,7 +80,7 @@ async def handle_tcp_connection(
|
|
|
77
80
|
buffer = buffer[end:]
|
|
78
81
|
await frame_queue.put(jpg)
|
|
79
82
|
|
|
80
|
-
except Exception as e:
|
|
83
|
+
except Exception as e: # noqa: BLE001 - report and close the socket
|
|
81
84
|
print(f"[TCP] Error: {e}")
|
|
82
85
|
finally:
|
|
83
86
|
writer.close()
|
|
@@ -128,7 +131,7 @@ async def websocket_handler(request):
|
|
|
128
131
|
print(f"[WS] Error: {ws.exception()}")
|
|
129
132
|
break
|
|
130
133
|
|
|
131
|
-
except Exception as e:
|
|
134
|
+
except Exception as e: # noqa: BLE001 - report and drop the client
|
|
132
135
|
print(f"[WS] Receive error: {e}")
|
|
133
136
|
|
|
134
137
|
finally:
|
|
@@ -1,22 +1,39 @@
|
|
|
1
|
-
[tool.
|
|
1
|
+
[tool.ruff]
|
|
2
2
|
line-length = 79
|
|
3
|
+
target-version = "py310"
|
|
3
4
|
|
|
4
|
-
[tool.
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
[tool.ruff.format]
|
|
6
|
+
quote-style = "double"
|
|
7
|
+
line-ending = "lf"
|
|
8
|
+
indent-style = "space"
|
|
9
|
+
skip-magic-trailing-comma = false
|
|
10
|
+
|
|
11
|
+
[tool.ruff.lint]
|
|
12
|
+
extend-select = [
|
|
13
|
+
"E", # pycodestyle
|
|
14
|
+
"F", # pyflakes
|
|
15
|
+
"I", # isort
|
|
16
|
+
"UP", # pyupgrade
|
|
17
|
+
# "B", # bugbear
|
|
18
|
+
# "C90", # mccabe
|
|
19
|
+
# "D", # pydocstyle
|
|
20
|
+
"ERA", # eradicate
|
|
21
|
+
]
|
|
22
|
+
ignore = [
|
|
23
|
+
"E203",
|
|
24
|
+
"B008", # Do not perform function call
|
|
25
|
+
"RUF009", # Don not perform partials
|
|
26
|
+
# "W503",
|
|
27
|
+
]
|
|
28
|
+
|
|
29
|
+
[tool.ruff.lint.isort]
|
|
30
|
+
combine-as-imports = true
|
|
7
31
|
|
|
8
32
|
[tool.mypy]
|
|
9
33
|
python_version = "3.10"
|
|
10
34
|
strict = false
|
|
11
35
|
ignore_missing_imports = true
|
|
12
36
|
|
|
13
|
-
[tool.flake8]
|
|
14
|
-
max-line-length = 79
|
|
15
|
-
ignore = [
|
|
16
|
-
"E203", # Whitespace before ':'
|
|
17
|
-
"W503", # Line break before binary operator
|
|
18
|
-
]
|
|
19
|
-
|
|
20
37
|
[tool.pytest.ini_options]
|
|
21
38
|
addopts = "--cov=cvframes --cov-report=term-missing"
|
|
22
39
|
|
|
@@ -30,15 +47,19 @@ name = "cv-frames"
|
|
|
30
47
|
dynamic = ["version"]
|
|
31
48
|
description = "Read frames from OpenCV like humans"
|
|
32
49
|
readme = "README.rst"
|
|
50
|
+
requires-python = ">=3.10"
|
|
33
51
|
|
|
34
52
|
dependencies = [
|
|
35
|
-
"opencv-python",
|
|
36
53
|
"numpy",
|
|
37
54
|
"click",
|
|
38
55
|
]
|
|
39
56
|
|
|
40
57
|
[project.optional-dependencies]
|
|
41
58
|
web = ["aiohttp"]
|
|
59
|
+
opencv = ["opencv-python"]
|
|
60
|
+
opencv-contrib = ["opencv-contrib-python"]
|
|
61
|
+
opencv-headless = ["opencv-python-headless"]
|
|
62
|
+
opencv-contrib-headless = ["opencv-contrib-python-headless"]
|
|
42
63
|
|
|
43
64
|
[project.scripts]
|
|
44
65
|
cv-frames = "cvframes.cli:main"
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
from pathlib import Path
|
|
2
|
-
from typing import Optional
|
|
3
2
|
|
|
4
3
|
import cv2
|
|
5
4
|
import numpy as np
|
|
@@ -31,7 +30,7 @@ def video(tmp_path: Path):
|
|
|
31
30
|
Path("output.mp4"),
|
|
32
31
|
],
|
|
33
32
|
)
|
|
34
|
-
def test_iterate(video: Path, opath:
|
|
33
|
+
def test_iterate(video: Path, opath: Path | None):
|
|
35
34
|
# sourcery skip: no-loop-in-tests
|
|
36
35
|
for capture, frame in iterate(video, opath=opath):
|
|
37
36
|
capture.write(frame)
|
|
@@ -46,7 +45,7 @@ def test_iterate(video: Path, opath: Optional[Path]):
|
|
|
46
45
|
Path("output.mp4"),
|
|
47
46
|
],
|
|
48
47
|
)
|
|
49
|
-
def test_iterate_sbs(video: Path, opath:
|
|
48
|
+
def test_iterate_sbs(video: Path, opath: Path | None):
|
|
50
49
|
# sourcery skip: no-loop-in-tests
|
|
51
50
|
for capture, (lframe, rframe) in iterate_sbs(
|
|
52
51
|
Path("input.mp4"), opath=opath
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
repos:
|
|
2
|
-
- repo: https://github.com/pre-commit/pre-commit-hooks
|
|
3
|
-
rev: v4.0.1
|
|
4
|
-
hooks:
|
|
5
|
-
- id: check-added-large-files
|
|
6
|
-
args: ["--maxkb=1000"]
|
|
7
|
-
- id: check-json
|
|
8
|
-
- id: check-docstring-first
|
|
9
|
-
- id: end-of-file-fixer
|
|
10
|
-
- id: trailing-whitespace
|
|
11
|
-
|
|
12
|
-
- repo: https://github.com/pycqa/isort
|
|
13
|
-
rev: 5.12.0
|
|
14
|
-
hooks:
|
|
15
|
-
- id: isort
|
|
16
|
-
name: isort (python)
|
|
17
|
-
args: ["--settings-path", "pyproject.toml"]
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
- repo: https://github.com/psf/black
|
|
21
|
-
rev: 22.3.0
|
|
22
|
-
hooks:
|
|
23
|
-
- id: black
|
|
24
|
-
args: ["--config", "pyproject.toml"]
|
|
25
|
-
|
|
26
|
-
- repo: https://github.com/pre-commit/mirrors-mypy
|
|
27
|
-
rev: v0.921
|
|
28
|
-
hooks:
|
|
29
|
-
- id: mypy
|
|
30
|
-
additional_dependencies: ["types-PyYAML"]
|
|
31
|
-
args: ["--config-file", "pyproject.toml"]
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
- repo: https://github.com/pycqa/flake8
|
|
35
|
-
rev: 7.1.2
|
|
36
|
-
hooks:
|
|
37
|
-
- id: flake8
|
|
38
|
-
# args: ["--config", "pyproject.toml"]
|
|
39
|
-
args: ["--max-line-length=79"]
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|