cv-frames 0.0.0__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.
@@ -0,0 +1,33 @@
1
+ name: publish
2
+
3
+ on:
4
+ release:
5
+ types: [created]
6
+
7
+ jobs:
8
+ deploy:
9
+
10
+ runs-on: ubuntu-latest
11
+
12
+ permissions:
13
+ id-token: write # Required for OIDC authentication
14
+
15
+ steps:
16
+ - uses: actions/checkout@v4
17
+ - name: Set up Python
18
+ uses: actions/setup-python@v4
19
+ with:
20
+ python-version: '3.x'
21
+
22
+ - name: Install dependencies
23
+ run: |
24
+ python -m pip install --upgrade pip
25
+ pip install build
26
+
27
+ - name: Build package
28
+ run: python -m build
29
+
30
+ - name: Publish to PyPI
31
+ uses: pypa/gh-action-pypi-publish@release/v1
32
+ with:
33
+ verbose: true
@@ -0,0 +1,30 @@
1
+ name: tests
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - "**"
7
+
8
+ jobs:
9
+ build:
10
+ runs-on: ubuntu-latest
11
+
12
+ steps:
13
+ - uses: actions/checkout@v3
14
+
15
+ - name: Set up Python
16
+ uses: actions/setup-python@v4
17
+ with:
18
+ python-version: "3.10"
19
+
20
+ - name: Install dependencies
21
+ run: |
22
+ pip install -r requirements.txt
23
+ pip install -e .
24
+
25
+ - name: Run the tests
26
+ run: |
27
+ PYTHONFAULTHANDLER=2 pytest --cov=cvframes --cov-report term-missing tests/
28
+
29
+ - name: Run pre-commit checks
30
+ uses: pre-commit/action@v3.0.1
@@ -0,0 +1,5 @@
1
+ *.egg*
2
+ __pycache__/
3
+ .python-version
4
+ .coverage
5
+ *.mp4
@@ -0,0 +1,39 @@
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: 4.0.1
36
+ hooks:
37
+ - id: flake8
38
+ # args: ["--config", "pyproject.toml"]
39
+ args: ["--max-line-length=79"]
@@ -0,0 +1,6 @@
1
+ Metadata-Version: 2.4
2
+ Name: cv-frames
3
+ Version: 0.0.0
4
+ Summary: Read frames from OpenCV like humans
5
+ Requires-Dist: opencv-python
6
+ Requires-Dist: numpy
@@ -0,0 +1 @@
1
+ # Read frames from OpenCV like humans
@@ -0,0 +1,6 @@
1
+ Metadata-Version: 2.4
2
+ Name: cv-frames
3
+ Version: 0.0.0
4
+ Summary: Read frames from OpenCV like humans
5
+ Requires-Dist: opencv-python
6
+ Requires-Dist: numpy
@@ -0,0 +1,15 @@
1
+ .gitignore
2
+ .pre-commit-config.yaml
3
+ README.md
4
+ pyproject.toml
5
+ requirements.txt
6
+ .github/workflows/publish.yml
7
+ .github/workflows/tests.yml
8
+ cv_frames.egg-info/PKG-INFO
9
+ cv_frames.egg-info/SOURCES.txt
10
+ cv_frames.egg-info/dependency_links.txt
11
+ cv_frames.egg-info/requires.txt
12
+ cv_frames.egg-info/top_level.txt
13
+ cvframes/__init__.py
14
+ cvframes/iterate.py
15
+ tests/test_iterate.py
@@ -0,0 +1,2 @@
1
+ opencv-python
2
+ numpy
@@ -0,0 +1 @@
1
+ cvframes
File without changes
@@ -0,0 +1,106 @@
1
+ from pathlib import Path
2
+ from typing import Callable, Generator, Optional, Tuple, TypeVar
3
+
4
+ import cv2
5
+ import numpy as np
6
+
7
+ T = TypeVar("T")
8
+
9
+
10
+ class IOCapture:
11
+ def __init__(self, source: str | Path, oname: str | Path = ""):
12
+ self.icap = cv2.VideoCapture(str(source))
13
+ self.ocap = (
14
+ cv2.VideoWriter(
15
+ str(oname),
16
+ cv2.VideoWriter_fourcc(*"mp4v"),
17
+ 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
+ ),
22
+ )
23
+ if oname
24
+ else None
25
+ )
26
+
27
+ def is_opened(self) -> bool:
28
+ return self.icap.isOpened()
29
+
30
+ def read(self) -> Tuple[bool, np.ndarray]:
31
+ return self.icap.read()
32
+
33
+ def write(self, frame: np.ndarray) -> None:
34
+ if self.ocap is not None:
35
+ self.ocap.write(frame)
36
+
37
+ def release(self) -> None:
38
+ self.icap.release()
39
+ if self.ocap is not None:
40
+ self.ocap.release()
41
+
42
+ def set(self, prop_id: int, value: float) -> None:
43
+ self.icap.set(prop_id, value)
44
+
45
+
46
+ def iterate_generic(
47
+ ipath: Path,
48
+ opath: Optional[Path],
49
+ start_frame: int,
50
+ stop_frame: int,
51
+ process_frames: Callable[[np.ndarray], T],
52
+ ) -> Generator[tuple[IOCapture, T], None, None]:
53
+ capture = IOCapture(str(ipath), oname=opath or "")
54
+ capture.set(cv2.CAP_PROP_POS_FRAMES, start_frame)
55
+ count = start_frame
56
+
57
+ if not capture.is_opened():
58
+ return
59
+
60
+ try:
61
+ while True:
62
+ ret, frame = capture.read()
63
+ count += 1
64
+ if not ret:
65
+ break
66
+ if stop_frame > 0 and count >= stop_frame:
67
+ break
68
+
69
+ yield capture, process_frames(frame)
70
+ finally:
71
+ capture.release()
72
+
73
+
74
+ def iterate(
75
+ ipath: Path,
76
+ opath: Optional[Path] = None,
77
+ start_frame: int = -1,
78
+ stop_frame: int = -1,
79
+ ) -> Generator[tuple[IOCapture, np.ndarray], None, None]:
80
+ return iterate_generic(
81
+ ipath,
82
+ opath,
83
+ start_frame,
84
+ stop_frame,
85
+ lambda frame: frame,
86
+ )
87
+
88
+
89
+ def iterate_sbs(
90
+ ipath: Path,
91
+ opath: Optional[Path] = None,
92
+ start_frame: int = -1,
93
+ stop_frame: int = -1,
94
+ ) -> Generator[Tuple[IOCapture, Tuple[np.ndarray, np.ndarray]], None, None]:
95
+ def processor(frame: np.ndarray) -> Tuple[np.ndarray, np.ndarray]:
96
+ _, width, _ = frame.shape
97
+ mid = width // 2
98
+ return frame[:, :mid, :], frame[:, mid:, :]
99
+
100
+ return iterate_generic(
101
+ ipath,
102
+ opath,
103
+ start_frame,
104
+ stop_frame,
105
+ processor,
106
+ )
@@ -0,0 +1,41 @@
1
+ [tool.black]
2
+ line-length = 79
3
+
4
+ [tool.isort]
5
+ profile = "black"
6
+ line_length = 79
7
+
8
+ [tool.mypy]
9
+ python_version = "3.10"
10
+ strict = false
11
+ ignore_missing_imports = true
12
+
13
+ [tool.flake8]
14
+ max-line-length = 79
15
+ ignore = [
16
+ "E203", # Whitespace before ':'
17
+ "W503", # Line break before binary operator
18
+ ]
19
+
20
+ [tool.pytest.ini_options]
21
+ addopts = "--cov=cvframes --cov-report=term-missing"
22
+
23
+ [build-system]
24
+ requires = ["setuptools", "wheel", "setuptools_scm"]
25
+ build-backend = "setuptools.build_meta"
26
+
27
+ [project]
28
+ # NB: Keep it the same as in PyPI
29
+ name = "cv-frames"
30
+ dynamic = ["version"]
31
+ description = "Read frames from OpenCV like humans"
32
+ dependencies = [
33
+ "opencv-python",
34
+ "numpy"
35
+ ]
36
+
37
+ [tool.setuptools]
38
+ packages = ["cvframes"]
39
+
40
+ [tool.setuptools_scm]
41
+ version_scheme = "post-release" # Uses Git tags for versioning
@@ -0,0 +1,3 @@
1
+ pytest
2
+ pytest-coverage
3
+ pre-commit
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,56 @@
1
+ from pathlib import Path
2
+ from typing import Optional
3
+
4
+ import cv2
5
+ import numpy as np
6
+ import pytest
7
+
8
+ from cvframes.iterate import iterate, iterate_sbs
9
+
10
+
11
+ @pytest.fixture
12
+ def video(tmp_path: Path):
13
+ opath = tmp_path / "test_video.mp4"
14
+ ovideo = cv2.VideoWriter(
15
+ str(opath),
16
+ cv2.VideoWriter_fourcc(*"mp4v"),
17
+ 30, # FPS
18
+ (640, 480),
19
+ )
20
+
21
+ for _ in range(5):
22
+ ovideo.write(np.zeros((480, 640, 3), dtype=np.uint8))
23
+ ovideo.release()
24
+ return opath
25
+
26
+
27
+ @pytest.mark.parametrize(
28
+ "opath",
29
+ [
30
+ None,
31
+ Path("output.mp4"),
32
+ ],
33
+ )
34
+ def test_iterate(video: Path, opath: Optional[Path]):
35
+ # sourcery skip: no-loop-in-tests
36
+ for capture, frame in iterate(video, opath=opath):
37
+ capture.write(frame)
38
+ assert frame.shape == (480, 640, 3)
39
+
40
+
41
+ @pytest.mark.skip("skipping")
42
+ @pytest.mark.parametrize(
43
+ "opath",
44
+ [
45
+ None,
46
+ Path("output.mp4"),
47
+ ],
48
+ )
49
+ def test_iterate_sbs(video: Path, opath: Optional[Path]):
50
+ # sourcery skip: no-loop-in-tests
51
+ for capture, (lframe, rframe) in iterate_sbs(
52
+ Path("input.mp4"), opath=opath
53
+ ):
54
+ capture.write(lframe)
55
+ assert lframe.shape == (480, 320, 3)
56
+ assert rframe.shape == (480, 320, 3)