cv-frames 0.0.2__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.2 → 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.2/cv_frames.egg-info → cv_frames-0.0.4}/PKG-INFO +20 -2
- cv_frames-0.0.2/PKG-INFO → cv_frames-0.0.4/README.rst +10 -11
- cv_frames-0.0.2/README.rst → cv_frames-0.0.4/cv_frames.egg-info/PKG-INFO +29 -0
- {cv_frames-0.0.2 → 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.2 → cv_frames-0.0.4}/cvframes/cli.py +4 -3
- {cv_frames-0.0.2 → cv_frames-0.0.4}/cvframes/iterate.py +55 -21
- {cv_frames-0.0.2 → cv_frames-0.0.4}/cvframes/web/server.py +16 -11
- {cv_frames-0.0.2 → cv_frames-0.0.4}/pyproject.toml +33 -12
- {cv_frames-0.0.2 → cv_frames-0.0.4}/requirements.txt +1 -1
- {cv_frames-0.0.2 → cv_frames-0.0.4}/tests/test_iterate.py +2 -3
- cv_frames-0.0.2/.pre-commit-config.yaml +0 -39
- cv_frames-0.0.2/cv_frames.egg-info/requires.txt +0 -6
- {cv_frames-0.0.2 → cv_frames-0.0.4}/.github/workflows/publish.yml +0 -0
- {cv_frames-0.0.2 → cv_frames-0.0.4}/.gitignore +0 -0
- {cv_frames-0.0.2 → cv_frames-0.0.4}/cv_frames.egg-info/dependency_links.txt +0 -0
- {cv_frames-0.0.2 → cv_frames-0.0.4}/cv_frames.egg-info/entry_points.txt +0 -0
- {cv_frames-0.0.2 → cv_frames-0.0.4}/cv_frames.egg-info/top_level.txt +0 -0
- {cv_frames-0.0.2 → cv_frames-0.0.4}/cvframes/__init__.py +0 -0
- {cv_frames-0.0.2 → cv_frames-0.0.4}/cvframes/web/__init__.py +0 -0
- {cv_frames-0.0.2 → cv_frames-0.0.4}/cvframes/web/client.py +0 -0
- {cv_frames-0.0.2 → cv_frames-0.0.4}/cvframes/web/static/index.html +0 -0
- {cv_frames-0.0.2 → 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.2
|
|
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,17 +79,17 @@ 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
|
|
56
90
|
|
|
57
91
|
if not capture.is_opened():
|
|
58
|
-
|
|
92
|
+
raise RuntimeError(f"Cannot open video file: {ipath}")
|
|
59
93
|
|
|
60
94
|
try:
|
|
61
95
|
while True:
|
|
@@ -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,
|
|
@@ -33,14 +40,16 @@ async def broadcast_task(
|
|
|
33
40
|
frame = await frame_queue.get()
|
|
34
41
|
async with clients_lock:
|
|
35
42
|
to_remove = set()
|
|
43
|
+
tasks = []
|
|
36
44
|
for ws in clients:
|
|
37
45
|
if ws.closed:
|
|
38
46
|
to_remove.add(ws)
|
|
39
47
|
continue
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
48
|
+
|
|
49
|
+
tasks.append(
|
|
50
|
+
asyncio.create_task(send_frame(ws, frame, to_remove))
|
|
51
|
+
)
|
|
52
|
+
await asyncio.gather(*tasks, return_exceptions=True)
|
|
44
53
|
for ws in to_remove:
|
|
45
54
|
clients.remove(ws)
|
|
46
55
|
frame_queue.task_done()
|
|
@@ -71,7 +80,7 @@ async def handle_tcp_connection(
|
|
|
71
80
|
buffer = buffer[end:]
|
|
72
81
|
await frame_queue.put(jpg)
|
|
73
82
|
|
|
74
|
-
except Exception as e:
|
|
83
|
+
except Exception as e: # noqa: BLE001 - report and close the socket
|
|
75
84
|
print(f"[TCP] Error: {e}")
|
|
76
85
|
finally:
|
|
77
86
|
writer.close()
|
|
@@ -122,7 +131,7 @@ async def websocket_handler(request):
|
|
|
122
131
|
print(f"[WS] Error: {ws.exception()}")
|
|
123
132
|
break
|
|
124
133
|
|
|
125
|
-
except Exception as e:
|
|
134
|
+
except Exception as e: # noqa: BLE001 - report and drop the client
|
|
126
135
|
print(f"[WS] Receive error: {e}")
|
|
127
136
|
|
|
128
137
|
finally:
|
|
@@ -135,7 +144,6 @@ async def websocket_handler(request):
|
|
|
135
144
|
|
|
136
145
|
async def main(http_port: int, tcp_port: int):
|
|
137
146
|
app = web.Application()
|
|
138
|
-
|
|
139
147
|
frame_queue: asyncio.Queue = asyncio.Queue()
|
|
140
148
|
clients: set[web.WebSocketResponse] = set()
|
|
141
149
|
clients_lock = asyncio.Lock()
|
|
@@ -150,11 +158,8 @@ async def main(http_port: int, tcp_port: int):
|
|
|
150
158
|
|
|
151
159
|
# Get static paths relative to package
|
|
152
160
|
index = pkg_resources.files("cvframes.web.static").joinpath("index.html")
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
app.router.add_get("/", lambda request: web.FileResponse(index))
|
|
161
|
+
app.router.add_get("/", lambda request: web.FileResponse(str(index)))
|
|
156
162
|
app.router.add_get("/ws", websocket_handler)
|
|
157
|
-
app.router.add_static("/static/", path=static, name="static")
|
|
158
163
|
|
|
159
164
|
runner = web.AppRunner(app)
|
|
160
165
|
await runner.setup()
|
|
@@ -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
|