QVideo 3.2.3__tar.gz → 3.4.1__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.
- {qvideo-3.2.3 → qvideo-3.4.1}/PKG-INFO +25 -10
- {qvideo-3.2.3 → qvideo-3.4.1}/QVideo.egg-info/PKG-INFO +25 -10
- {qvideo-3.2.3 → qvideo-3.4.1}/QVideo.egg-info/SOURCES.txt +10 -3
- {qvideo-3.2.3 → qvideo-3.4.1}/QVideo.egg-info/requires.txt +21 -2
- {qvideo-3.2.3 → qvideo-3.4.1}/README.md +6 -6
- {qvideo-3.2.3 → qvideo-3.4.1}/cameras/OpenCV/_resolution_tree.py +4 -0
- {qvideo-3.2.3 → qvideo-3.4.1}/demos/__init__.py +14 -0
- {qvideo-3.2.3 → qvideo-3.4.1}/demos/demo.py +4 -2
- {qvideo-3.2.3 → qvideo-3.4.1}/demos/filterdemo.py +1 -11
- qvideo-3.4.1/demos/trackpydemo.py +55 -0
- qvideo-3.4.1/demos/yolodemo.py +61 -0
- {qvideo-3.2.3 → qvideo-3.4.1}/dvr/QDVRWidget.py +3 -2
- {qvideo-3.2.3 → qvideo-3.4.1}/filters/QBlurFilter.py +2 -2
- {qvideo-3.2.3 → qvideo-3.4.1}/filters/QEdgeFilter.py +6 -6
- {qvideo-3.2.3 → qvideo-3.4.1}/filters/QRGBFilter.py +2 -2
- {qvideo-3.2.3 → qvideo-3.4.1}/filters/QSampleHold.py +7 -5
- qvideo-3.4.1/filters/QYOLOFilter.py +100 -0
- {qvideo-3.2.3 → qvideo-3.4.1}/filters/_MedianBase.py +1 -1
- {qvideo-3.2.3 → qvideo-3.4.1}/filters/__init__.py +2 -0
- {qvideo-3.2.3 → qvideo-3.4.1}/lib/QFilterBank.py +10 -3
- qvideo-3.2.3/lib/VideoFilter.py → qvideo-3.4.1/lib/QVideoFilter.py +14 -14
- {qvideo-3.2.3 → qvideo-3.4.1}/lib/QVideoScreen.py +14 -8
- {qvideo-3.2.3 → qvideo-3.4.1}/lib/__init__.py +3 -3
- {qvideo-3.2.3 → qvideo-3.4.1}/lib/resolutions.py +3 -1
- qvideo-3.4.1/overlays/__init__.py +6 -0
- qvideo-3.4.1/overlays/trackpy.py +251 -0
- qvideo-3.4.1/overlays/yolo.py +259 -0
- {qvideo-3.2.3 → qvideo-3.4.1}/pyproject.toml +13 -8
- {qvideo-3.2.3 → qvideo-3.4.1}/tests/test_demo.py +8 -3
- {qvideo-3.2.3 → qvideo-3.4.1}/tests/test_qblurfilter.py +1 -1
- {qvideo-3.2.3 → qvideo-3.4.1}/tests/test_qdvrwidget.py +2 -3
- {qvideo-3.2.3 → qvideo-3.4.1}/tests/test_qedgefilter.py +1 -1
- {qvideo-3.2.3 → qvideo-3.4.1}/tests/test_qfilterbank.py +3 -3
- {qvideo-3.2.3 → qvideo-3.4.1}/tests/test_qopencvresolutiontree.py +15 -1
- {qvideo-3.2.3 → qvideo-3.4.1}/tests/test_qrgbfilter.py +1 -1
- {qvideo-3.2.3 → qvideo-3.4.1}/tests/test_qsamplehold.py +1 -1
- {qvideo-3.2.3 → qvideo-3.4.1}/tests/test_resolutions.py +20 -4
- qvideo-3.4.1/tests/test_trackpy_overlay.py +346 -0
- {qvideo-3.2.3 → qvideo-3.4.1}/tests/test_videofilter.py +6 -6
- qvideo-3.4.1/tests/test_yolo_overlay.py +315 -0
- qvideo-3.2.3/dvr/icons_rc_qt6.py +0 -1106
- {qvideo-3.2.3 → qvideo-3.4.1}/LICENSE.md +0 -0
- {qvideo-3.2.3 → qvideo-3.4.1}/QCamcorder.py +0 -0
- {qvideo-3.2.3 → qvideo-3.4.1}/QVideo.egg-info/dependency_links.txt +0 -0
- {qvideo-3.2.3 → qvideo-3.4.1}/QVideo.egg-info/top_level.txt +0 -0
- {qvideo-3.2.3 → qvideo-3.4.1}/__init__.py +0 -0
- {qvideo-3.2.3 → qvideo-3.4.1}/cameras/Basler/__init__.py +0 -0
- {qvideo-3.2.3 → qvideo-3.4.1}/cameras/Basler/_camera.py +0 -0
- {qvideo-3.2.3 → qvideo-3.4.1}/cameras/Basler/_tree.py +0 -0
- {qvideo-3.2.3 → qvideo-3.4.1}/cameras/Flir/QListFlirCameras.py +0 -0
- {qvideo-3.2.3 → qvideo-3.4.1}/cameras/Flir/__init__.py +0 -0
- {qvideo-3.2.3 → qvideo-3.4.1}/cameras/Flir/_camera.py +0 -0
- {qvideo-3.2.3 → qvideo-3.4.1}/cameras/Flir/_tree.py +0 -0
- {qvideo-3.2.3 → qvideo-3.4.1}/cameras/Genicam/__init__.py +0 -0
- {qvideo-3.2.3 → qvideo-3.4.1}/cameras/Genicam/_camera.py +0 -0
- {qvideo-3.2.3 → qvideo-3.4.1}/cameras/Genicam/_tree.py +0 -0
- {qvideo-3.2.3 → qvideo-3.4.1}/cameras/IDS/__init__.py +0 -0
- {qvideo-3.2.3 → qvideo-3.4.1}/cameras/IDS/_camera.py +0 -0
- {qvideo-3.2.3 → qvideo-3.4.1}/cameras/IDS/_tree.py +0 -0
- {qvideo-3.2.3 → qvideo-3.4.1}/cameras/MV/__init__.py +0 -0
- {qvideo-3.2.3 → qvideo-3.4.1}/cameras/MV/_camera.py +0 -0
- {qvideo-3.2.3 → qvideo-3.4.1}/cameras/MV/_tree.py +0 -0
- {qvideo-3.2.3 → qvideo-3.4.1}/cameras/Noise/__init__.py +0 -0
- {qvideo-3.2.3 → qvideo-3.4.1}/cameras/Noise/_camera.py +0 -0
- {qvideo-3.2.3 → qvideo-3.4.1}/cameras/Noise/_tree.py +0 -0
- {qvideo-3.2.3 → qvideo-3.4.1}/cameras/OpenCV/QListCVCameras.py +0 -0
- {qvideo-3.2.3 → qvideo-3.4.1}/cameras/OpenCV/__init__.py +0 -0
- {qvideo-3.2.3 → qvideo-3.4.1}/cameras/OpenCV/_camera.py +0 -0
- {qvideo-3.2.3 → qvideo-3.4.1}/cameras/OpenCV/_tree.py +0 -0
- {qvideo-3.2.3 → qvideo-3.4.1}/cameras/Picamera/__init__.py +0 -0
- {qvideo-3.2.3 → qvideo-3.4.1}/cameras/Picamera/_camera.py +0 -0
- {qvideo-3.2.3 → qvideo-3.4.1}/cameras/Picamera/_tree.py +0 -0
- {qvideo-3.2.3 → qvideo-3.4.1}/cameras/Vimbax/__init__.py +0 -0
- {qvideo-3.2.3 → qvideo-3.4.1}/cameras/Vimbax/_camera.py +0 -0
- {qvideo-3.2.3 → qvideo-3.4.1}/cameras/Vimbax/_tree.py +0 -0
- {qvideo-3.2.3 → qvideo-3.4.1}/cameras/__init__.py +0 -0
- {qvideo-3.2.3 → qvideo-3.4.1}/demos/ROIdemo.py +0 -0
- {qvideo-3.2.3 → qvideo-3.4.1}/dvr/QHDF5Reader.py +0 -0
- {qvideo-3.2.3 → qvideo-3.4.1}/dvr/QHDF5Writer.py +0 -0
- {qvideo-3.2.3 → qvideo-3.4.1}/dvr/QOpenCVReader.py +0 -0
- {qvideo-3.2.3 → qvideo-3.4.1}/dvr/QOpenCVWriter.py +0 -0
- {qvideo-3.2.3 → qvideo-3.4.1}/dvr/__init__.py +0 -0
- {qvideo-3.2.3 → qvideo-3.4.1}/dvr/icons_rc.py +0 -0
- {qvideo-3.2.3 → qvideo-3.4.1}/filters/Median.py +0 -0
- {qvideo-3.2.3 → qvideo-3.4.1}/filters/MoMedian.py +0 -0
- {qvideo-3.2.3 → qvideo-3.4.1}/filters/Normalize.py +0 -0
- {qvideo-3.2.3 → qvideo-3.4.1}/lib/QCamera.py +0 -0
- {qvideo-3.2.3 → qvideo-3.4.1}/lib/QCameraTree.py +0 -0
- {qvideo-3.2.3 → qvideo-3.4.1}/lib/QFPSMeter.py +0 -0
- {qvideo-3.2.3 → qvideo-3.4.1}/lib/QListCameras.py +0 -0
- {qvideo-3.2.3 → qvideo-3.4.1}/lib/QVideoReader.py +0 -0
- {qvideo-3.2.3 → qvideo-3.4.1}/lib/QVideoSource.py +0 -0
- {qvideo-3.2.3 → qvideo-3.4.1}/lib/QVideoWriter.py +0 -0
- {qvideo-3.2.3 → qvideo-3.4.1}/lib/chooser.py +0 -0
- {qvideo-3.2.3 → qvideo-3.4.1}/lib/clickable.py +0 -0
- {qvideo-3.2.3 → qvideo-3.4.1}/lib/types.py +0 -0
- {qvideo-3.2.3 → qvideo-3.4.1}/setup.cfg +0 -0
- {qvideo-3.2.3 → qvideo-3.4.1}/tests/test_chooser.py +0 -0
- {qvideo-3.2.3 → qvideo-3.4.1}/tests/test_clickable.py +0 -0
- {qvideo-3.2.3 → qvideo-3.4.1}/tests/test_filterdemo.py +0 -0
- {qvideo-3.2.3 → qvideo-3.4.1}/tests/test_icons_rc.py +0 -0
- {qvideo-3.2.3 → qvideo-3.4.1}/tests/test_median.py +0 -0
- {qvideo-3.2.3 → qvideo-3.4.1}/tests/test_momedian.py +0 -0
- {qvideo-3.2.3 → qvideo-3.4.1}/tests/test_normalize.py +0 -0
- {qvideo-3.2.3 → qvideo-3.4.1}/tests/test_qbaslercamera.py +0 -0
- {qvideo-3.2.3 → qvideo-3.4.1}/tests/test_qcamcorder.py +0 -0
- {qvideo-3.2.3 → qvideo-3.4.1}/tests/test_qcamera.py +0 -0
- {qvideo-3.2.3 → qvideo-3.4.1}/tests/test_qcameratree.py +0 -0
- {qvideo-3.2.3 → qvideo-3.4.1}/tests/test_qflircamera.py +0 -0
- {qvideo-3.2.3 → qvideo-3.4.1}/tests/test_qfpsmeter.py +0 -0
- {qvideo-3.2.3 → qvideo-3.4.1}/tests/test_qgenicamcamera.py +0 -0
- {qvideo-3.2.3 → qvideo-3.4.1}/tests/test_qgenicamtree.py +0 -0
- {qvideo-3.2.3 → qvideo-3.4.1}/tests/test_qhdf5reader.py +0 -0
- {qvideo-3.2.3 → qvideo-3.4.1}/tests/test_qhdf5writer.py +0 -0
- {qvideo-3.2.3 → qvideo-3.4.1}/tests/test_qidscamera.py +0 -0
- {qvideo-3.2.3 → qvideo-3.4.1}/tests/test_qlistcameras.py +0 -0
- {qvideo-3.2.3 → qvideo-3.4.1}/tests/test_qlistcvcameras.py +0 -0
- {qvideo-3.2.3 → qvideo-3.4.1}/tests/test_qmvcamera.py +0 -0
- {qvideo-3.2.3 → qvideo-3.4.1}/tests/test_qnoisecamera.py +0 -0
- {qvideo-3.2.3 → qvideo-3.4.1}/tests/test_qnoisetree.py +0 -0
- {qvideo-3.2.3 → qvideo-3.4.1}/tests/test_qopencvcamera.py +0 -0
- {qvideo-3.2.3 → qvideo-3.4.1}/tests/test_qopencvreader.py +0 -0
- {qvideo-3.2.3 → qvideo-3.4.1}/tests/test_qopencvtree.py +0 -0
- {qvideo-3.2.3 → qvideo-3.4.1}/tests/test_qopencvwriter.py +0 -0
- {qvideo-3.2.3 → qvideo-3.4.1}/tests/test_qpicamera.py +0 -0
- {qvideo-3.2.3 → qvideo-3.4.1}/tests/test_qvideoreader.py +0 -0
- {qvideo-3.2.3 → qvideo-3.4.1}/tests/test_qvideoscreen.py +0 -0
- {qvideo-3.2.3 → qvideo-3.4.1}/tests/test_qvideosource.py +0 -0
- {qvideo-3.2.3 → qvideo-3.4.1}/tests/test_qvideowriter.py +0 -0
- {qvideo-3.2.3 → qvideo-3.4.1}/tests/test_qvimbaxcamera.py +0 -0
- {qvideo-3.2.3 → qvideo-3.4.1}/tests/test_roidemo.py +0 -0
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: QVideo
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.4.1
|
|
4
4
|
Summary: PyQt-based framework for integrating video cameras into research applications
|
|
5
5
|
Author-email: "David G. Grier" <david.grier@nyu.edu>
|
|
6
6
|
License: GPL-3.0-or-later
|
|
7
7
|
Project-URL: Homepage, https://github.com/davidgrier/QVideo
|
|
8
8
|
Project-URL: Repository, https://github.com/davidgrier/QVideo
|
|
9
9
|
Project-URL: Bug Tracker, https://github.com/davidgrier/QVideo/issues
|
|
10
|
-
Keywords: camera,video,imaging,PyQt5,scientific,GenICam,GigE Vision,raspberry pi
|
|
10
|
+
Keywords: camera,video,imaging,PyQt5,PyQt6,scientific,GenICam,GigE Vision,raspberry pi
|
|
11
11
|
Classifier: Development Status :: 5 - Production/Stable
|
|
12
12
|
Classifier: Intended Audience :: Science/Research
|
|
13
13
|
Classifier: License :: OSI Approved :: GNU General Public License v3 (GPLv3)
|
|
@@ -24,15 +24,30 @@ License-File: LICENSE.md
|
|
|
24
24
|
Requires-Dist: h5py
|
|
25
25
|
Requires-Dist: numpy
|
|
26
26
|
Requires-Dist: opencv-python
|
|
27
|
-
Requires-Dist:
|
|
28
|
-
Requires-Dist: PyQt5-sip
|
|
27
|
+
Requires-Dist: pandas
|
|
29
28
|
Requires-Dist: pyqtgraph
|
|
29
|
+
Provides-Extra: pyqt5
|
|
30
|
+
Requires-Dist: PyQt5; extra == "pyqt5"
|
|
31
|
+
Requires-Dist: PyQt5-sip; extra == "pyqt5"
|
|
32
|
+
Provides-Extra: pyqt6
|
|
33
|
+
Requires-Dist: PyQt6; extra == "pyqt6"
|
|
30
34
|
Provides-Extra: genicam
|
|
31
35
|
Requires-Dist: harvesters; extra == "genicam"
|
|
32
36
|
Requires-Dist: genicam; extra == "genicam"
|
|
33
37
|
Provides-Extra: picamera
|
|
34
38
|
Requires-Dist: picamera2; extra == "picamera"
|
|
39
|
+
Provides-Extra: dev-pyqt5
|
|
40
|
+
Requires-Dist: PyQt5; extra == "dev-pyqt5"
|
|
41
|
+
Requires-Dist: PyQt5-sip; extra == "dev-pyqt5"
|
|
42
|
+
Requires-Dist: pytest; extra == "dev-pyqt5"
|
|
43
|
+
Requires-Dist: pytest-cov; extra == "dev-pyqt5"
|
|
44
|
+
Provides-Extra: dev-pyqt6
|
|
45
|
+
Requires-Dist: PyQt6; extra == "dev-pyqt6"
|
|
46
|
+
Requires-Dist: pytest; extra == "dev-pyqt6"
|
|
47
|
+
Requires-Dist: pytest-cov; extra == "dev-pyqt6"
|
|
35
48
|
Provides-Extra: dev
|
|
49
|
+
Requires-Dist: PyQt5; extra == "dev"
|
|
50
|
+
Requires-Dist: PyQt5-sip; extra == "dev"
|
|
36
51
|
Requires-Dist: pytest; extra == "dev"
|
|
37
52
|
Requires-Dist: pytest-cov; extra == "dev"
|
|
38
53
|
Provides-Extra: docs
|
|
@@ -48,8 +63,9 @@ Dynamic: license-file
|
|
|
48
63
|
[](LICENSE.md)
|
|
49
64
|
[](https://github.com/davidgrier/QVideo/actions/workflows/test.yml)
|
|
50
65
|
[](https://qvideo.readthedocs.io/en/latest/)
|
|
66
|
+
[](https://doi.org/10.5281/zenodo.19239402)
|
|
51
67
|
|
|
52
|
-
**QVideo** is a framework for integrating video cameras into
|
|
68
|
+
**QVideo** is a framework for integrating video cameras into PyQt projects
|
|
53
69
|
for scientific research. It provides a unified, registration-based property
|
|
54
70
|
system so that every camera backend — USB webcams, GenICam devices, FLIR
|
|
55
71
|
cameras, Raspberry Pi cameras — is controlled through the same API. Property
|
|
@@ -87,16 +103,15 @@ pip install QVideo
|
|
|
87
103
|
|---------|-------|-------|
|
|
88
104
|
| GenICam cameras (Vimba, etc.) | `pip install QVideo[genicam]` | Requires a vendor-supplied `.cti` producer file |
|
|
89
105
|
| Raspberry Pi camera | `pip install QVideo[picamera]` | Requires `picamera2` |
|
|
90
|
-
| FLIR / Spinnaker cameras | — | Requires the proprietary PySpin SDK; install that separately |
|
|
91
106
|
|
|
92
107
|
## Quick start
|
|
93
108
|
|
|
94
109
|
```python
|
|
95
|
-
|
|
110
|
+
import pyqtgraph as pg
|
|
96
111
|
from QVideo.cameras.Noise import QNoiseSource
|
|
97
112
|
from QVideo.lib import QVideoScreen
|
|
98
113
|
|
|
99
|
-
|
|
114
|
+
pg.mkApp()
|
|
100
115
|
|
|
101
116
|
source = QNoiseSource() # synthetic noise — no hardware needed
|
|
102
117
|
screen = QVideoScreen()
|
|
@@ -104,7 +119,7 @@ source.newFrame.connect(screen.setImage)
|
|
|
104
119
|
|
|
105
120
|
screen.show()
|
|
106
121
|
source.start()
|
|
107
|
-
|
|
122
|
+
pg.exec()
|
|
108
123
|
```
|
|
109
124
|
|
|
110
125
|
Replace `QNoiseSource` with `QOpenCVSource`, `QGenicamSource`, etc. to switch
|
|
@@ -157,5 +172,5 @@ additional code needed.
|
|
|
157
172
|
## Acknowledgements
|
|
158
173
|
|
|
159
174
|
Work on this project at New York University is supported by the National
|
|
160
|
-
Science Foundation of the United States under award number DMR-
|
|
175
|
+
Science Foundation of the United States under award number DMR-2428983 and
|
|
161
176
|
by an award from the TAC Program of New York University.
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: QVideo
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.4.1
|
|
4
4
|
Summary: PyQt-based framework for integrating video cameras into research applications
|
|
5
5
|
Author-email: "David G. Grier" <david.grier@nyu.edu>
|
|
6
6
|
License: GPL-3.0-or-later
|
|
7
7
|
Project-URL: Homepage, https://github.com/davidgrier/QVideo
|
|
8
8
|
Project-URL: Repository, https://github.com/davidgrier/QVideo
|
|
9
9
|
Project-URL: Bug Tracker, https://github.com/davidgrier/QVideo/issues
|
|
10
|
-
Keywords: camera,video,imaging,PyQt5,scientific,GenICam,GigE Vision,raspberry pi
|
|
10
|
+
Keywords: camera,video,imaging,PyQt5,PyQt6,scientific,GenICam,GigE Vision,raspberry pi
|
|
11
11
|
Classifier: Development Status :: 5 - Production/Stable
|
|
12
12
|
Classifier: Intended Audience :: Science/Research
|
|
13
13
|
Classifier: License :: OSI Approved :: GNU General Public License v3 (GPLv3)
|
|
@@ -24,15 +24,30 @@ License-File: LICENSE.md
|
|
|
24
24
|
Requires-Dist: h5py
|
|
25
25
|
Requires-Dist: numpy
|
|
26
26
|
Requires-Dist: opencv-python
|
|
27
|
-
Requires-Dist:
|
|
28
|
-
Requires-Dist: PyQt5-sip
|
|
27
|
+
Requires-Dist: pandas
|
|
29
28
|
Requires-Dist: pyqtgraph
|
|
29
|
+
Provides-Extra: pyqt5
|
|
30
|
+
Requires-Dist: PyQt5; extra == "pyqt5"
|
|
31
|
+
Requires-Dist: PyQt5-sip; extra == "pyqt5"
|
|
32
|
+
Provides-Extra: pyqt6
|
|
33
|
+
Requires-Dist: PyQt6; extra == "pyqt6"
|
|
30
34
|
Provides-Extra: genicam
|
|
31
35
|
Requires-Dist: harvesters; extra == "genicam"
|
|
32
36
|
Requires-Dist: genicam; extra == "genicam"
|
|
33
37
|
Provides-Extra: picamera
|
|
34
38
|
Requires-Dist: picamera2; extra == "picamera"
|
|
39
|
+
Provides-Extra: dev-pyqt5
|
|
40
|
+
Requires-Dist: PyQt5; extra == "dev-pyqt5"
|
|
41
|
+
Requires-Dist: PyQt5-sip; extra == "dev-pyqt5"
|
|
42
|
+
Requires-Dist: pytest; extra == "dev-pyqt5"
|
|
43
|
+
Requires-Dist: pytest-cov; extra == "dev-pyqt5"
|
|
44
|
+
Provides-Extra: dev-pyqt6
|
|
45
|
+
Requires-Dist: PyQt6; extra == "dev-pyqt6"
|
|
46
|
+
Requires-Dist: pytest; extra == "dev-pyqt6"
|
|
47
|
+
Requires-Dist: pytest-cov; extra == "dev-pyqt6"
|
|
35
48
|
Provides-Extra: dev
|
|
49
|
+
Requires-Dist: PyQt5; extra == "dev"
|
|
50
|
+
Requires-Dist: PyQt5-sip; extra == "dev"
|
|
36
51
|
Requires-Dist: pytest; extra == "dev"
|
|
37
52
|
Requires-Dist: pytest-cov; extra == "dev"
|
|
38
53
|
Provides-Extra: docs
|
|
@@ -48,8 +63,9 @@ Dynamic: license-file
|
|
|
48
63
|
[](LICENSE.md)
|
|
49
64
|
[](https://github.com/davidgrier/QVideo/actions/workflows/test.yml)
|
|
50
65
|
[](https://qvideo.readthedocs.io/en/latest/)
|
|
66
|
+
[](https://doi.org/10.5281/zenodo.19239402)
|
|
51
67
|
|
|
52
|
-
**QVideo** is a framework for integrating video cameras into
|
|
68
|
+
**QVideo** is a framework for integrating video cameras into PyQt projects
|
|
53
69
|
for scientific research. It provides a unified, registration-based property
|
|
54
70
|
system so that every camera backend — USB webcams, GenICam devices, FLIR
|
|
55
71
|
cameras, Raspberry Pi cameras — is controlled through the same API. Property
|
|
@@ -87,16 +103,15 @@ pip install QVideo
|
|
|
87
103
|
|---------|-------|-------|
|
|
88
104
|
| GenICam cameras (Vimba, etc.) | `pip install QVideo[genicam]` | Requires a vendor-supplied `.cti` producer file |
|
|
89
105
|
| Raspberry Pi camera | `pip install QVideo[picamera]` | Requires `picamera2` |
|
|
90
|
-
| FLIR / Spinnaker cameras | — | Requires the proprietary PySpin SDK; install that separately |
|
|
91
106
|
|
|
92
107
|
## Quick start
|
|
93
108
|
|
|
94
109
|
```python
|
|
95
|
-
|
|
110
|
+
import pyqtgraph as pg
|
|
96
111
|
from QVideo.cameras.Noise import QNoiseSource
|
|
97
112
|
from QVideo.lib import QVideoScreen
|
|
98
113
|
|
|
99
|
-
|
|
114
|
+
pg.mkApp()
|
|
100
115
|
|
|
101
116
|
source = QNoiseSource() # synthetic noise — no hardware needed
|
|
102
117
|
screen = QVideoScreen()
|
|
@@ -104,7 +119,7 @@ source.newFrame.connect(screen.setImage)
|
|
|
104
119
|
|
|
105
120
|
screen.show()
|
|
106
121
|
source.start()
|
|
107
|
-
|
|
122
|
+
pg.exec()
|
|
108
123
|
```
|
|
109
124
|
|
|
110
125
|
Replace `QNoiseSource` with `QOpenCVSource`, `QGenicamSource`, etc. to switch
|
|
@@ -157,5 +172,5 @@ additional code needed.
|
|
|
157
172
|
## Acknowledgements
|
|
158
173
|
|
|
159
174
|
Work on this project at New York University is supported by the National
|
|
160
|
-
Science Foundation of the United States under award number DMR-
|
|
175
|
+
Science Foundation of the United States under award number DMR-2428983 and
|
|
161
176
|
by an award from the TAC Program of New York University.
|
|
@@ -45,6 +45,8 @@ demos/ROIdemo.py
|
|
|
45
45
|
demos/__init__.py
|
|
46
46
|
demos/demo.py
|
|
47
47
|
demos/filterdemo.py
|
|
48
|
+
demos/trackpydemo.py
|
|
49
|
+
demos/yolodemo.py
|
|
48
50
|
dvr/QDVRWidget.py
|
|
49
51
|
dvr/QHDF5Reader.py
|
|
50
52
|
dvr/QHDF5Writer.py
|
|
@@ -52,7 +54,6 @@ dvr/QOpenCVReader.py
|
|
|
52
54
|
dvr/QOpenCVWriter.py
|
|
53
55
|
dvr/__init__.py
|
|
54
56
|
dvr/icons_rc.py
|
|
55
|
-
dvr/icons_rc_qt6.py
|
|
56
57
|
filters/Median.py
|
|
57
58
|
filters/MoMedian.py
|
|
58
59
|
filters/Normalize.py
|
|
@@ -60,6 +61,7 @@ filters/QBlurFilter.py
|
|
|
60
61
|
filters/QEdgeFilter.py
|
|
61
62
|
filters/QRGBFilter.py
|
|
62
63
|
filters/QSampleHold.py
|
|
64
|
+
filters/QYOLOFilter.py
|
|
63
65
|
filters/_MedianBase.py
|
|
64
66
|
filters/__init__.py
|
|
65
67
|
lib/QCamera.py
|
|
@@ -67,16 +69,19 @@ lib/QCameraTree.py
|
|
|
67
69
|
lib/QFPSMeter.py
|
|
68
70
|
lib/QFilterBank.py
|
|
69
71
|
lib/QListCameras.py
|
|
72
|
+
lib/QVideoFilter.py
|
|
70
73
|
lib/QVideoReader.py
|
|
71
74
|
lib/QVideoScreen.py
|
|
72
75
|
lib/QVideoSource.py
|
|
73
76
|
lib/QVideoWriter.py
|
|
74
|
-
lib/VideoFilter.py
|
|
75
77
|
lib/__init__.py
|
|
76
78
|
lib/chooser.py
|
|
77
79
|
lib/clickable.py
|
|
78
80
|
lib/resolutions.py
|
|
79
81
|
lib/types.py
|
|
82
|
+
overlays/__init__.py
|
|
83
|
+
overlays/trackpy.py
|
|
84
|
+
overlays/yolo.py
|
|
80
85
|
tests/test_chooser.py
|
|
81
86
|
tests/test_clickable.py
|
|
82
87
|
tests/test_demo.py
|
|
@@ -120,4 +125,6 @@ tests/test_qvideowriter.py
|
|
|
120
125
|
tests/test_qvimbaxcamera.py
|
|
121
126
|
tests/test_resolutions.py
|
|
122
127
|
tests/test_roidemo.py
|
|
123
|
-
tests/
|
|
128
|
+
tests/test_trackpy_overlay.py
|
|
129
|
+
tests/test_videofilter.py
|
|
130
|
+
tests/test_yolo_overlay.py
|
|
@@ -1,11 +1,23 @@
|
|
|
1
1
|
h5py
|
|
2
2
|
numpy
|
|
3
3
|
opencv-python
|
|
4
|
-
|
|
5
|
-
PyQt5-sip
|
|
4
|
+
pandas
|
|
6
5
|
pyqtgraph
|
|
7
6
|
|
|
8
7
|
[dev]
|
|
8
|
+
PyQt5
|
|
9
|
+
PyQt5-sip
|
|
10
|
+
pytest
|
|
11
|
+
pytest-cov
|
|
12
|
+
|
|
13
|
+
[dev-pyqt5]
|
|
14
|
+
PyQt5
|
|
15
|
+
PyQt5-sip
|
|
16
|
+
pytest
|
|
17
|
+
pytest-cov
|
|
18
|
+
|
|
19
|
+
[dev-pyqt6]
|
|
20
|
+
PyQt6
|
|
9
21
|
pytest
|
|
10
22
|
pytest-cov
|
|
11
23
|
|
|
@@ -20,3 +32,10 @@ genicam
|
|
|
20
32
|
|
|
21
33
|
[picamera]
|
|
22
34
|
picamera2
|
|
35
|
+
|
|
36
|
+
[pyqt5]
|
|
37
|
+
PyQt5
|
|
38
|
+
PyQt5-sip
|
|
39
|
+
|
|
40
|
+
[pyqt6]
|
|
41
|
+
PyQt6
|
|
@@ -5,8 +5,9 @@
|
|
|
5
5
|
[](LICENSE.md)
|
|
6
6
|
[](https://github.com/davidgrier/QVideo/actions/workflows/test.yml)
|
|
7
7
|
[](https://qvideo.readthedocs.io/en/latest/)
|
|
8
|
+
[](https://doi.org/10.5281/zenodo.19239402)
|
|
8
9
|
|
|
9
|
-
**QVideo** is a framework for integrating video cameras into
|
|
10
|
+
**QVideo** is a framework for integrating video cameras into PyQt projects
|
|
10
11
|
for scientific research. It provides a unified, registration-based property
|
|
11
12
|
system so that every camera backend — USB webcams, GenICam devices, FLIR
|
|
12
13
|
cameras, Raspberry Pi cameras — is controlled through the same API. Property
|
|
@@ -44,16 +45,15 @@ pip install QVideo
|
|
|
44
45
|
|---------|-------|-------|
|
|
45
46
|
| GenICam cameras (Vimba, etc.) | `pip install QVideo[genicam]` | Requires a vendor-supplied `.cti` producer file |
|
|
46
47
|
| Raspberry Pi camera | `pip install QVideo[picamera]` | Requires `picamera2` |
|
|
47
|
-
| FLIR / Spinnaker cameras | — | Requires the proprietary PySpin SDK; install that separately |
|
|
48
48
|
|
|
49
49
|
## Quick start
|
|
50
50
|
|
|
51
51
|
```python
|
|
52
|
-
|
|
52
|
+
import pyqtgraph as pg
|
|
53
53
|
from QVideo.cameras.Noise import QNoiseSource
|
|
54
54
|
from QVideo.lib import QVideoScreen
|
|
55
55
|
|
|
56
|
-
|
|
56
|
+
pg.mkApp()
|
|
57
57
|
|
|
58
58
|
source = QNoiseSource() # synthetic noise — no hardware needed
|
|
59
59
|
screen = QVideoScreen()
|
|
@@ -61,7 +61,7 @@ source.newFrame.connect(screen.setImage)
|
|
|
61
61
|
|
|
62
62
|
screen.show()
|
|
63
63
|
source.start()
|
|
64
|
-
|
|
64
|
+
pg.exec()
|
|
65
65
|
```
|
|
66
66
|
|
|
67
67
|
Replace `QNoiseSource` with `QOpenCVSource`, `QGenicamSource`, etc. to switch
|
|
@@ -114,5 +114,5 @@ additional code needed.
|
|
|
114
114
|
## Acknowledgements
|
|
115
115
|
|
|
116
116
|
Work on this project at New York University is supported by the National
|
|
117
|
-
Science Foundation of the United States under award number DMR-
|
|
117
|
+
Science Foundation of the United States under award number DMR-2428983 and
|
|
118
118
|
by an award from the TAC Program of New York University.
|
|
@@ -117,8 +117,12 @@ class QOpenCVResolutionTree(QCameraTree):
|
|
|
117
117
|
if change == 'value' and param.name() == 'resolution':
|
|
118
118
|
if value in self._resolutionMap:
|
|
119
119
|
w, h = self._resolutionMap[value]
|
|
120
|
+
fps = (self.camera.fps
|
|
121
|
+
if 'fps' in self.camera._properties else None)
|
|
120
122
|
self.camera.set('width', w)
|
|
121
123
|
self.camera.set('height', h)
|
|
124
|
+
if fps is not None:
|
|
125
|
+
self.camera.set('fps', fps)
|
|
122
126
|
return
|
|
123
127
|
super()._sync(root, changes)
|
|
124
128
|
|
|
@@ -21,6 +21,16 @@ ROIdemo
|
|
|
21
21
|
rectangular ROI overlay. Only the cropped region is saved when
|
|
22
22
|
the DVR records, making it easy to capture a sub-region of interest.
|
|
23
23
|
|
|
24
|
+
trackpydemo
|
|
25
|
+
Extends :mod:`demo` with a :class:`~QVideo.overlays.trackpy.QTrackpyWidget`
|
|
26
|
+
panel so that trackpy particle positions are overlaid on the live feed
|
|
27
|
+
in real time and locate results are available via a signal.
|
|
28
|
+
|
|
29
|
+
yolodemo
|
|
30
|
+
Extends :mod:`demo` with a :class:`~QVideo.overlays.yolo.QYoloWidget`
|
|
31
|
+
panel so that YOLO object-detection bounding boxes are overlaid on the
|
|
32
|
+
live feed in real time and detection results are available via a signal.
|
|
33
|
+
|
|
24
34
|
Running
|
|
25
35
|
-------
|
|
26
36
|
Each demo can be launched directly::
|
|
@@ -28,6 +38,8 @@ Each demo can be launched directly::
|
|
|
28
38
|
python -m QVideo.demos.demo
|
|
29
39
|
python -m QVideo.demos.filterdemo
|
|
30
40
|
python -m QVideo.demos.ROIdemo
|
|
41
|
+
python -m QVideo.demos.trackpydemo
|
|
42
|
+
python -m QVideo.demos.yolodemo
|
|
31
43
|
|
|
32
44
|
Camera selection
|
|
33
45
|
----------------
|
|
@@ -59,4 +71,6 @@ Example::
|
|
|
59
71
|
from .demo import Demo
|
|
60
72
|
from .filterdemo import FilterDemo
|
|
61
73
|
from .ROIdemo import ROIFilter, ROIDemo
|
|
74
|
+
from .trackpydemo import TrackpyDemo
|
|
75
|
+
from .yolodemo import YoloDemo
|
|
62
76
|
|
|
@@ -7,7 +7,7 @@ Run directly::
|
|
|
7
7
|
'''
|
|
8
8
|
|
|
9
9
|
from QVideo.lib import QVideoScreen, QCameraTree
|
|
10
|
-
from pyqtgraph.Qt.QtWidgets import QWidget, QHBoxLayout
|
|
10
|
+
from pyqtgraph.Qt.QtWidgets import QWidget, QHBoxLayout, QVBoxLayout
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
__all__ = ['Demo']
|
|
@@ -40,9 +40,11 @@ class Demo(QWidget):
|
|
|
40
40
|
def _setupUi(self) -> None:
|
|
41
41
|
layout = QHBoxLayout(self)
|
|
42
42
|
layout.addWidget(self.screen)
|
|
43
|
-
|
|
43
|
+
self._controls = QVBoxLayout()
|
|
44
|
+
layout.addLayout(self._controls)
|
|
44
45
|
layout.setStretch(0, 1) # screen takes all surplus horizontal space
|
|
45
46
|
layout.setStretch(1, 0) # controls stay at their natural width
|
|
47
|
+
self._controls.addWidget(self.cameraTree)
|
|
46
48
|
|
|
47
49
|
|
|
48
50
|
def main() -> None: # pragma: no cover
|
|
@@ -8,7 +8,6 @@ Run directly::
|
|
|
8
8
|
|
|
9
9
|
from QVideo.demos.demo import Demo
|
|
10
10
|
from QVideo.lib import QCameraTree
|
|
11
|
-
from pyqtgraph.Qt.QtWidgets import QHBoxLayout, QVBoxLayout
|
|
12
11
|
|
|
13
12
|
|
|
14
13
|
__all__ = ['FilterDemo']
|
|
@@ -38,15 +37,6 @@ class FilterDemo(Demo):
|
|
|
38
37
|
super().__init__(cameraTree, **kwargs)
|
|
39
38
|
self.addFilters(filters)
|
|
40
39
|
|
|
41
|
-
def _setupUi(self) -> None:
|
|
42
|
-
layout = QHBoxLayout(self)
|
|
43
|
-
layout.addWidget(self.screen)
|
|
44
|
-
self._controls = QVBoxLayout()
|
|
45
|
-
layout.addLayout(self._controls)
|
|
46
|
-
layout.setStretch(0, 1) # screen takes all surplus horizontal space
|
|
47
|
-
layout.setStretch(1, 0) # controls stay at their natural width
|
|
48
|
-
self._controls.addWidget(self.cameraTree)
|
|
49
|
-
|
|
50
40
|
def addFilters(self, filters: list[str]) -> None:
|
|
51
41
|
'''Register filters by name and show the filter bank.
|
|
52
42
|
|
|
@@ -68,7 +58,7 @@ def main() -> None: # pragma: no cover
|
|
|
68
58
|
|
|
69
59
|
pg.mkQApp()
|
|
70
60
|
camera = choose_camera().start()
|
|
71
|
-
filters = 'QRGBFilter QBlurFilter
|
|
61
|
+
filters = 'QYOLOFilter QRGBFilter QSampleHold QBlurFilter QEdgeFilter'.split()
|
|
72
62
|
widget = FilterDemo(camera, filters)
|
|
73
63
|
widget.show()
|
|
74
64
|
pg.exec()
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
'''Demo combining a live video feed, camera controls, and a trackpy overlay.
|
|
3
|
+
|
|
4
|
+
Run directly::
|
|
5
|
+
|
|
6
|
+
python -m QVideo.demos.trackpydemo
|
|
7
|
+
|
|
8
|
+
See :mod:`QVideo.overlays.trackpy` for literature references.
|
|
9
|
+
'''
|
|
10
|
+
|
|
11
|
+
from QVideo.demos.demo import Demo
|
|
12
|
+
from QVideo.lib import QCameraTree
|
|
13
|
+
from QVideo.overlays import QTrackpyWidget
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
__all__ = ['TrackpyDemo']
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class TrackpyDemo(Demo):
|
|
20
|
+
'''Extends :class:`~QVideo.demos.demo.Demo` with a trackpy overlay.
|
|
21
|
+
|
|
22
|
+
Adds a :class:`~QVideo.overlays.trackpy.QTrackpyWidget` control panel
|
|
23
|
+
below the camera control tree. Detected particle positions are
|
|
24
|
+
rendered in real time as a scatter-plot overlay on the video screen.
|
|
25
|
+
|
|
26
|
+
Parameters
|
|
27
|
+
----------
|
|
28
|
+
cameraTree : QCameraTree
|
|
29
|
+
The camera control tree widget to display alongside the video feed.
|
|
30
|
+
**kwargs :
|
|
31
|
+
Additional keyword arguments forwarded to :class:`~QVideo.demos.demo.Demo`.
|
|
32
|
+
'''
|
|
33
|
+
|
|
34
|
+
def __init__(self, cameraTree: QCameraTree, **kwargs) -> None:
|
|
35
|
+
super().__init__(cameraTree, **kwargs)
|
|
36
|
+
self.trackpy = QTrackpyWidget(self)
|
|
37
|
+
self.trackpy.source = self.screen.source
|
|
38
|
+
self.trackpy.attachTo(self.screen)
|
|
39
|
+
self._controls.addWidget(self.trackpy)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def main() -> None: # pragma: no cover
|
|
43
|
+
'''Launch the trackpy demo with an interactively chosen camera.'''
|
|
44
|
+
import pyqtgraph as pg
|
|
45
|
+
from QVideo.lib import choose_camera
|
|
46
|
+
|
|
47
|
+
pg.mkQApp()
|
|
48
|
+
camera = choose_camera().start()
|
|
49
|
+
widget = TrackpyDemo(camera)
|
|
50
|
+
widget.show()
|
|
51
|
+
pg.exec()
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
if __name__ == '__main__': # pragma: no cover
|
|
55
|
+
main()
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
'''Demo combining a live video feed, camera controls, and a YOLO overlay.
|
|
3
|
+
|
|
4
|
+
Run directly::
|
|
5
|
+
|
|
6
|
+
python -m QVideo.demos.yolodemo
|
|
7
|
+
|
|
8
|
+
See :mod:`QVideo.overlays.yolo` for literature references.
|
|
9
|
+
'''
|
|
10
|
+
|
|
11
|
+
from QVideo.demos.demo import Demo
|
|
12
|
+
from QVideo.lib import QCameraTree
|
|
13
|
+
from QVideo.overlays import QYoloWidget
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
__all__ = ['YoloDemo']
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class YoloDemo(Demo):
|
|
20
|
+
'''Extends :class:`~QVideo.demos.demo.Demo` with a YOLO overlay.
|
|
21
|
+
|
|
22
|
+
Adds a :class:`~QVideo.overlays.yolo.QYoloWidget` control panel
|
|
23
|
+
below the camera control tree. Detected object bounding boxes are
|
|
24
|
+
rendered in real time as a rectangle overlay on the video screen.
|
|
25
|
+
|
|
26
|
+
Parameters
|
|
27
|
+
----------
|
|
28
|
+
cameraTree : QCameraTree
|
|
29
|
+
The camera control tree widget to display alongside the video feed.
|
|
30
|
+
model_name : str
|
|
31
|
+
YOLO model weights file passed to :class:`~QVideo.overlays.yolo.QYoloWidget`.
|
|
32
|
+
Default: ``'yolo11n.pt'``.
|
|
33
|
+
**kwargs :
|
|
34
|
+
Additional keyword arguments forwarded to :class:`~QVideo.demos.demo.Demo`.
|
|
35
|
+
'''
|
|
36
|
+
|
|
37
|
+
def __init__(self,
|
|
38
|
+
cameraTree: QCameraTree,
|
|
39
|
+
model_name: str = 'yolo11n.pt',
|
|
40
|
+
**kwargs) -> None:
|
|
41
|
+
super().__init__(cameraTree, **kwargs)
|
|
42
|
+
self.yolo = QYoloWidget(self, model_name=model_name)
|
|
43
|
+
self.yolo.source = self.screen.source
|
|
44
|
+
self.yolo.attachTo(self.screen)
|
|
45
|
+
self._controls.addWidget(self.yolo)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def main() -> None: # pragma: no cover
|
|
49
|
+
'''Launch the YOLO demo with an interactively chosen camera.'''
|
|
50
|
+
import pyqtgraph as pg
|
|
51
|
+
from QVideo.lib import choose_camera
|
|
52
|
+
|
|
53
|
+
pg.mkQApp()
|
|
54
|
+
camera = choose_camera().start()
|
|
55
|
+
widget = YoloDemo(camera)
|
|
56
|
+
widget.show()
|
|
57
|
+
pg.exec()
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
if __name__ == '__main__': # pragma: no cover
|
|
61
|
+
main()
|
|
@@ -120,7 +120,7 @@ class QDVRWidget(QtWidgets.QFrame):
|
|
|
120
120
|
def __init__(self,
|
|
121
121
|
*args,
|
|
122
122
|
source: QVideoSource | None = None,
|
|
123
|
-
filename: str
|
|
123
|
+
filename: str = '',
|
|
124
124
|
**kwargs) -> None:
|
|
125
125
|
super().__init__(*args, **kwargs)
|
|
126
126
|
self._source = None
|
|
@@ -130,7 +130,7 @@ class QDVRWidget(QtWidgets.QFrame):
|
|
|
130
130
|
self._setupUi()
|
|
131
131
|
self._connectSignals()
|
|
132
132
|
self.source = source
|
|
133
|
-
self.filename = filename if filename
|
|
133
|
+
self.filename = filename if filename else str(
|
|
134
134
|
Path.home() / self.FILENAME)
|
|
135
135
|
|
|
136
136
|
def _setupUi(self) -> None:
|
|
@@ -335,6 +335,7 @@ class QDVRWidget(QtWidgets.QFrame):
|
|
|
335
335
|
return
|
|
336
336
|
if not (self.isRecording() or self.isPlaying()):
|
|
337
337
|
self.saveEdit.setText(filename)
|
|
338
|
+
self.playname = filename
|
|
338
339
|
|
|
339
340
|
@QtCore.pyqtProperty(str)
|
|
340
341
|
def playname(self) -> str:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
from pyqtgraph.Qt import QtCore, QtWidgets
|
|
2
2
|
from pyqtgraph import SpinBox
|
|
3
|
-
from QVideo.lib.
|
|
3
|
+
from QVideo.lib.QVideoFilter import VideoFilter, QVideoFilter
|
|
4
4
|
from QVideo.lib.types import Image
|
|
5
5
|
import cv2
|
|
6
6
|
|
|
@@ -67,7 +67,7 @@ class QBlurFilter(QVideoFilter):
|
|
|
67
67
|
'''
|
|
68
68
|
|
|
69
69
|
def __init__(self, parent: QtWidgets.QWidget | None = None) -> None:
|
|
70
|
-
super().__init__('Gaussian Blur',
|
|
70
|
+
super().__init__(parent, 'Gaussian Blur', BlurFilter())
|
|
71
71
|
|
|
72
72
|
def _setupUi(self) -> None:
|
|
73
73
|
super()._setupUi()
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
from pyqtgraph.Qt import QtCore, QtWidgets
|
|
2
2
|
from pyqtgraph import SpinBox
|
|
3
|
-
from QVideo.lib.
|
|
3
|
+
from QVideo.lib.QVideoFilter import VideoFilter, QVideoFilter
|
|
4
4
|
from QVideo.lib.types import Image
|
|
5
5
|
import cv2
|
|
6
6
|
import logging
|
|
@@ -49,8 +49,8 @@ class EdgeFilter(VideoFilter):
|
|
|
49
49
|
def low(self, low: int) -> None:
|
|
50
50
|
low = max(1, int(low))
|
|
51
51
|
if low >= self._high:
|
|
52
|
-
logger.warning('low (
|
|
53
|
-
'
|
|
52
|
+
logger.warning(f'low ({low}) must be less than '
|
|
53
|
+
f'high ({self._high}); ignoring')
|
|
54
54
|
return
|
|
55
55
|
self._low = low
|
|
56
56
|
|
|
@@ -63,8 +63,8 @@ class EdgeFilter(VideoFilter):
|
|
|
63
63
|
def high(self, high: int) -> None:
|
|
64
64
|
high = max(2, int(high))
|
|
65
65
|
if high <= self._low:
|
|
66
|
-
logger.warning('high (
|
|
67
|
-
'
|
|
66
|
+
logger.warning(f'high ({high}) must be greater than '
|
|
67
|
+
f'low ({self._low}); ignoring')
|
|
68
68
|
return
|
|
69
69
|
self._high = high
|
|
70
70
|
|
|
@@ -106,7 +106,7 @@ class QEdgeFilter(QVideoFilter):
|
|
|
106
106
|
'''
|
|
107
107
|
|
|
108
108
|
def __init__(self, parent: QtWidgets.QWidget | None = None) -> None:
|
|
109
|
-
super().__init__('Canny Edge Detection',
|
|
109
|
+
super().__init__(parent, 'Canny Edge Detection', EdgeFilter())
|
|
110
110
|
|
|
111
111
|
def _setupUi(self) -> None:
|
|
112
112
|
super()._setupUi()
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
from pyqtgraph.Qt import QtCore, QtWidgets
|
|
2
|
-
from QVideo.lib.
|
|
2
|
+
from QVideo.lib.QVideoFilter import VideoFilter, QVideoFilter
|
|
3
3
|
from QVideo.lib.types import Image
|
|
4
4
|
|
|
5
5
|
|
|
@@ -61,7 +61,7 @@ class QRGBFilter(QVideoFilter):
|
|
|
61
61
|
'''
|
|
62
62
|
|
|
63
63
|
def __init__(self, parent: QtWidgets.QWidget | None = None) -> None:
|
|
64
|
-
super().__init__('Color Channel',
|
|
64
|
+
super().__init__(parent, 'Color Channel', RGBFilter())
|
|
65
65
|
|
|
66
66
|
def _setupUi(self) -> None:
|
|
67
67
|
super()._setupUi()
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from pyqtgraph.Qt import QtCore, QtWidgets
|
|
2
|
-
from QVideo.lib.VideoFilter import QVideoFilter
|
|
3
|
-
from QVideo.lib.types import Image
|
|
4
2
|
from QVideo.filters.Normalize import Normalize
|
|
3
|
+
from QVideo.lib.QVideoFilter import QVideoFilter
|
|
4
|
+
from QVideo.lib.types import Image
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
__all__ = ['SampleHold', 'QSampleHold']
|
|
@@ -87,14 +87,16 @@ class QSampleHold(QVideoFilter):
|
|
|
87
87
|
'''
|
|
88
88
|
|
|
89
89
|
def __init__(self, parent: QtWidgets.QWidget | None = None) -> None:
|
|
90
|
-
super().__init__('Sample and Hold',
|
|
90
|
+
super().__init__(parent, 'Sample and Hold', SampleHold())
|
|
91
91
|
|
|
92
92
|
def _setupUi(self) -> None:
|
|
93
93
|
super()._setupUi()
|
|
94
94
|
self._layout.addWidget(QtWidgets.QLabel('order'))
|
|
95
|
-
self._orderButtons = [
|
|
95
|
+
self._orderButtons = [
|
|
96
|
+
QtWidgets.QRadioButton(str(n)) for n in (1, 2, 3)]
|
|
96
97
|
for n, button in enumerate(self._orderButtons, start=1):
|
|
97
|
-
button.toggled.connect(
|
|
98
|
+
button.toggled.connect(
|
|
99
|
+
lambda checked, n=n: self.setOrder(checked, n))
|
|
98
100
|
self._layout.addWidget(button)
|
|
99
101
|
self._orderButtons[self.filter.order - 1].setChecked(True)
|
|
100
102
|
self._resetButton = QtWidgets.QPushButton('Reset', self)
|