QVideo 3.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.
- qvideo-3.0.0/LICENSE.md +17 -0
- qvideo-3.0.0/PKG-INFO +161 -0
- qvideo-3.0.0/QCamcorder.py +125 -0
- qvideo-3.0.0/QVideo.egg-info/PKG-INFO +161 -0
- qvideo-3.0.0/QVideo.egg-info/SOURCES.txt +115 -0
- qvideo-3.0.0/QVideo.egg-info/dependency_links.txt +1 -0
- qvideo-3.0.0/QVideo.egg-info/requires.txt +23 -0
- qvideo-3.0.0/QVideo.egg-info/top_level.txt +1 -0
- qvideo-3.0.0/README.md +117 -0
- qvideo-3.0.0/__init__.py +5 -0
- qvideo-3.0.0/cameras/Basler/QBaslerCamera.py +50 -0
- qvideo-3.0.0/cameras/Basler/QBaslerTree.py +67 -0
- qvideo-3.0.0/cameras/Basler/__init__.py +5 -0
- qvideo-3.0.0/cameras/Flir/QFlirCamera.py +48 -0
- qvideo-3.0.0/cameras/Flir/QFlirTree.py +70 -0
- qvideo-3.0.0/cameras/Flir/QListFlirCameras.py +31 -0
- qvideo-3.0.0/cameras/Flir/__init__.py +5 -0
- qvideo-3.0.0/cameras/Genicam/QGenicamCamera.py +382 -0
- qvideo-3.0.0/cameras/Genicam/QGenicamTree.py +224 -0
- qvideo-3.0.0/cameras/Genicam/__init__.py +5 -0
- qvideo-3.0.0/cameras/IDS/QIDSCamera.py +49 -0
- qvideo-3.0.0/cameras/IDS/QIDSTree.py +65 -0
- qvideo-3.0.0/cameras/IDS/__init__.py +5 -0
- qvideo-3.0.0/cameras/MV/QMVCamera.py +55 -0
- qvideo-3.0.0/cameras/MV/QMVTree.py +65 -0
- qvideo-3.0.0/cameras/MV/__init__.py +5 -0
- qvideo-3.0.0/cameras/Noise/QNoiseCamera.py +144 -0
- qvideo-3.0.0/cameras/Noise/QNoiseTree.py +35 -0
- qvideo-3.0.0/cameras/Noise/__init__.py +5 -0
- qvideo-3.0.0/cameras/OpenCV/QListCVCameras.py +73 -0
- qvideo-3.0.0/cameras/OpenCV/QOpenCVCamera.py +224 -0
- qvideo-3.0.0/cameras/OpenCV/QOpenCVTree.py +59 -0
- qvideo-3.0.0/cameras/OpenCV/__init__.py +5 -0
- qvideo-3.0.0/cameras/Picamera/QPicamera.py +229 -0
- qvideo-3.0.0/cameras/Picamera/QPicameraTree.py +47 -0
- qvideo-3.0.0/cameras/Picamera/__init__.py +5 -0
- qvideo-3.0.0/cameras/Vimbax/QVimbaXCamera.py +51 -0
- qvideo-3.0.0/cameras/Vimbax/QVimbaXTree.py +37 -0
- qvideo-3.0.0/cameras/Vimbax/__init__.py +5 -0
- qvideo-3.0.0/cameras/__init__.py +0 -0
- qvideo-3.0.0/dvr/QDVRWidget.py +354 -0
- qvideo-3.0.0/dvr/QHDF5Reader.py +93 -0
- qvideo-3.0.0/dvr/QHDF5Writer.py +89 -0
- qvideo-3.0.0/dvr/QOpenCVReader.py +98 -0
- qvideo-3.0.0/dvr/QOpenCVWriter.py +144 -0
- qvideo-3.0.0/dvr/__init__.py +14 -0
- qvideo-3.0.0/dvr/icons_rc.py +1082 -0
- qvideo-3.0.0/dvr/icons_rc_qt6.py +1106 -0
- qvideo-3.0.0/filters/Median.py +89 -0
- qvideo-3.0.0/filters/MoMedian.py +63 -0
- qvideo-3.0.0/filters/Normalize.py +142 -0
- qvideo-3.0.0/filters/QBlurFilter.py +101 -0
- qvideo-3.0.0/filters/QEdgeFilter.py +162 -0
- qvideo-3.0.0/filters/QRGBFilter.py +97 -0
- qvideo-3.0.0/filters/QSampleHold.py +141 -0
- qvideo-3.0.0/filters/_MedianBase.py +104 -0
- qvideo-3.0.0/filters/__init__.py +17 -0
- qvideo-3.0.0/lib/QCamera.py +387 -0
- qvideo-3.0.0/lib/QCameraTree.py +212 -0
- qvideo-3.0.0/lib/QFPSMeter.py +90 -0
- qvideo-3.0.0/lib/QFilterBank.py +118 -0
- qvideo-3.0.0/lib/QListCameras.py +87 -0
- qvideo-3.0.0/lib/QVideoReader.py +227 -0
- qvideo-3.0.0/lib/QVideoScreen.py +173 -0
- qvideo-3.0.0/lib/QVideoSource.py +196 -0
- qvideo-3.0.0/lib/QVideoWriter.py +123 -0
- qvideo-3.0.0/lib/VideoFilter.py +149 -0
- qvideo-3.0.0/lib/__init__.py +17 -0
- qvideo-3.0.0/lib/chooser.py +118 -0
- qvideo-3.0.0/lib/clickable.py +43 -0
- qvideo-3.0.0/lib/resolutions.py +46 -0
- qvideo-3.0.0/lib/types.py +6 -0
- qvideo-3.0.0/pyproject.toml +105 -0
- qvideo-3.0.0/setup.cfg +4 -0
- qvideo-3.0.0/tests/test_chooser.py +311 -0
- qvideo-3.0.0/tests/test_clickable.py +108 -0
- qvideo-3.0.0/tests/test_demo.py +46 -0
- qvideo-3.0.0/tests/test_filterdemo.py +60 -0
- qvideo-3.0.0/tests/test_icons_rc.py +24 -0
- qvideo-3.0.0/tests/test_median.py +205 -0
- qvideo-3.0.0/tests/test_momedian.py +142 -0
- qvideo-3.0.0/tests/test_normalize.py +134 -0
- qvideo-3.0.0/tests/test_qblurfilter.py +155 -0
- qvideo-3.0.0/tests/test_qcamcorder.py +104 -0
- qvideo-3.0.0/tests/test_qcamera.py +381 -0
- qvideo-3.0.0/tests/test_qcameratree.py +213 -0
- qvideo-3.0.0/tests/test_qdvrwidget.py +543 -0
- qvideo-3.0.0/tests/test_qedgefilter.py +192 -0
- qvideo-3.0.0/tests/test_qfilterbank.py +143 -0
- qvideo-3.0.0/tests/test_qfpsmeter.py +217 -0
- qvideo-3.0.0/tests/test_qgenicamcamera.py +816 -0
- qvideo-3.0.0/tests/test_qgenicamtree.py +637 -0
- qvideo-3.0.0/tests/test_qhdf5reader.py +224 -0
- qvideo-3.0.0/tests/test_qhdf5writer.py +165 -0
- qvideo-3.0.0/tests/test_qlistcameras.py +138 -0
- qvideo-3.0.0/tests/test_qlistcvcameras.py +253 -0
- qvideo-3.0.0/tests/test_qnoisecamera.py +266 -0
- qvideo-3.0.0/tests/test_qnoisetree.py +30 -0
- qvideo-3.0.0/tests/test_qopencvcamera.py +331 -0
- qvideo-3.0.0/tests/test_qopencvreader.py +192 -0
- qvideo-3.0.0/tests/test_qopencvtree.py +80 -0
- qvideo-3.0.0/tests/test_qopencvwriter.py +269 -0
- qvideo-3.0.0/tests/test_qpicamera.py +379 -0
- qvideo-3.0.0/tests/test_qrgbfilter.py +145 -0
- qvideo-3.0.0/tests/test_qsamplehold.py +168 -0
- qvideo-3.0.0/tests/test_qvideoreader.py +267 -0
- qvideo-3.0.0/tests/test_qvideoscreen.py +301 -0
- qvideo-3.0.0/tests/test_qvideosource.py +229 -0
- qvideo-3.0.0/tests/test_qvideowriter.py +223 -0
- qvideo-3.0.0/tests/test_qvimbaxcamera.py +259 -0
- qvideo-3.0.0/tests/test_resolutions.py +102 -0
- qvideo-3.0.0/tests/test_roidemo.py +97 -0
- qvideo-3.0.0/tests/test_videofilter.py +167 -0
- qvideo-3.0.0/version.py +4 -0
qvideo-3.0.0/LICENSE.md
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
GNU GENERAL PUBLIC LICENSE
|
|
2
|
+
Version 3, 29 June 2007
|
|
3
|
+
|
|
4
|
+
Copyright (c) 2022 David G. Grier
|
|
5
|
+
|
|
6
|
+
This program is free software: you can redistribute it and/or modify
|
|
7
|
+
it under the terms of the GNU General Public License as published by
|
|
8
|
+
the Free Software Foundation, either version 3 of the License, or
|
|
9
|
+
(at your option) any later version.
|
|
10
|
+
|
|
11
|
+
This program is distributed in the hope that it will be useful,
|
|
12
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
13
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
14
|
+
GNU General Public License for more details.
|
|
15
|
+
|
|
16
|
+
You should have received a copy of the GNU General Public License
|
|
17
|
+
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
qvideo-3.0.0/PKG-INFO
ADDED
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: QVideo
|
|
3
|
+
Version: 3.0.0
|
|
4
|
+
Summary: PyQt-based framework for integrating video cameras into research applications
|
|
5
|
+
Author-email: "David G. Grier" <david.grier@nyu.edu>
|
|
6
|
+
License: GPL-3.0-or-later
|
|
7
|
+
Project-URL: Homepage, https://github.com/davidgrier/QVideo
|
|
8
|
+
Project-URL: Repository, https://github.com/davidgrier/QVideo
|
|
9
|
+
Project-URL: Bug Tracker, https://github.com/davidgrier/QVideo/issues
|
|
10
|
+
Keywords: camera,video,imaging,PyQt5,scientific,GenICam,GigE Vision,raspberry pi
|
|
11
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
12
|
+
Classifier: Intended Audience :: Science/Research
|
|
13
|
+
Classifier: License :: OSI Approved :: GNU General Public License v3 (GPLv3)
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Topic :: Scientific/Engineering
|
|
19
|
+
Classifier: Topic :: Multimedia :: Video :: Capture
|
|
20
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
21
|
+
Requires-Python: >=3.10
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
License-File: LICENSE.md
|
|
24
|
+
Requires-Dist: h5py
|
|
25
|
+
Requires-Dist: numba
|
|
26
|
+
Requires-Dist: numpy
|
|
27
|
+
Requires-Dist: opencv-python
|
|
28
|
+
Requires-Dist: PyQt5
|
|
29
|
+
Requires-Dist: PyQt5-sip
|
|
30
|
+
Requires-Dist: pyqtgraph
|
|
31
|
+
Provides-Extra: genicam
|
|
32
|
+
Requires-Dist: harvesters; extra == "genicam"
|
|
33
|
+
Requires-Dist: genicam; extra == "genicam"
|
|
34
|
+
Provides-Extra: picamera
|
|
35
|
+
Requires-Dist: picamera2; extra == "picamera"
|
|
36
|
+
Provides-Extra: dev
|
|
37
|
+
Requires-Dist: pytest; extra == "dev"
|
|
38
|
+
Requires-Dist: pytest-cov; extra == "dev"
|
|
39
|
+
Provides-Extra: docs
|
|
40
|
+
Requires-Dist: furo; extra == "docs"
|
|
41
|
+
Requires-Dist: sphinx; extra == "docs"
|
|
42
|
+
Requires-Dist: sphinx-autodoc-typehints; extra == "docs"
|
|
43
|
+
Dynamic: license-file
|
|
44
|
+
|
|
45
|
+
# QVideo: PyQt support for video cameras
|
|
46
|
+
|
|
47
|
+
[](https://pypi.org/project/QVideo/)
|
|
48
|
+
[](https://pypi.org/project/QVideo/)
|
|
49
|
+
[](LICENSE.md)
|
|
50
|
+
[](https://github.com/davidgrier/QVideo/actions/workflows/test.yml)
|
|
51
|
+
|
|
52
|
+
**QVideo** is a framework for integrating video cameras into PyQt5 projects
|
|
53
|
+
for scientific research. It provides a unified, registration-based property
|
|
54
|
+
system so that every camera backend — USB webcams, GenICam devices, FLIR
|
|
55
|
+
cameras, Raspberry Pi cameras — is controlled through the same API. Property
|
|
56
|
+
trees, display widgets, and a digital video recorder are built on top of that
|
|
57
|
+
abstraction and require no camera-specific code.
|
|
58
|
+
|
|
59
|
+
<img src="docs/dvrdemo.png" width="75%" alt="QVideo interface demo">
|
|
60
|
+
|
|
61
|
+
## Features
|
|
62
|
+
|
|
63
|
+
- **Unified camera API** — `QCamera` subclasses expose adjustable parameters
|
|
64
|
+
via `registerProperty` / `registerMethod`; UI and recording layers consume
|
|
65
|
+
them without knowing the underlying hardware.
|
|
66
|
+
- **Auto-built property trees** — `QCameraTree` reads the registered property
|
|
67
|
+
map and builds a `pyqtgraph` parameter tree widget automatically.
|
|
68
|
+
- **Threaded video source** — `QVideoSource` wraps any camera in a `QThread`
|
|
69
|
+
and emits `newFrame(ndarray)` at acquisition rate.
|
|
70
|
+
- **Composable filter pipeline** — `VideoFilter` / `QFilterBank` sit between
|
|
71
|
+
source and display; filters include blur, edge detection, RGB channel
|
|
72
|
+
selection, sample-and-hold, and statistical median variants.
|
|
73
|
+
- **Digital video recorder** — lossless HDF5 (with timestamps) and OpenCV
|
|
74
|
+
video formats; `QDVRWidget` is the composite UI widget.
|
|
75
|
+
- **Live display** — `QVideoScreen` supports mouse-aware graphical overlays
|
|
76
|
+
for annotations, regions of interest, and user interaction.
|
|
77
|
+
|
|
78
|
+
## Installation
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
pip install QVideo
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### Optional hardware backends
|
|
85
|
+
|
|
86
|
+
| Backend | Extra | Notes |
|
|
87
|
+
|---------|-------|-------|
|
|
88
|
+
| GenICam cameras (Vimba, etc.) | `pip install QVideo[genicam]` | Requires a vendor-supplied `.cti` producer file |
|
|
89
|
+
| Raspberry Pi camera | `pip install QVideo[picamera]` | Requires `picamera2` |
|
|
90
|
+
| FLIR / Spinnaker cameras | — | Requires the proprietary PySpin SDK; install that separately |
|
|
91
|
+
|
|
92
|
+
## Quick start
|
|
93
|
+
|
|
94
|
+
```python
|
|
95
|
+
from pyqtgraph.Qt import QtWidgets
|
|
96
|
+
from QVideo.cameras.Noise import QNoiseSource
|
|
97
|
+
from QVideo.lib import QVideoScreen
|
|
98
|
+
|
|
99
|
+
app = QtWidgets.QApplication([])
|
|
100
|
+
|
|
101
|
+
source = QNoiseSource() # synthetic noise — no hardware needed
|
|
102
|
+
screen = QVideoScreen()
|
|
103
|
+
source.newFrame.connect(screen.setImage)
|
|
104
|
+
|
|
105
|
+
screen.show()
|
|
106
|
+
source.start()
|
|
107
|
+
app.exec()
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Replace `QNoiseSource` with `QOpenCVSource`, `QGenicamSource`, etc. to switch
|
|
111
|
+
hardware — the rest of the code is identical.
|
|
112
|
+
|
|
113
|
+
## Camera backends
|
|
114
|
+
|
|
115
|
+
| Backend | Class | Hardware |
|
|
116
|
+
|---------|-------|----------|
|
|
117
|
+
| `cameras/Noise` | `QNoiseCamera` | Synthetic — no hardware required |
|
|
118
|
+
| `cameras/OpenCV` | `QOpenCVCamera` | USB webcams via OpenCV |
|
|
119
|
+
| `cameras/Genicam` | `QGenicamCamera` | Abstract base for all GenICam/GigE Vision cameras |
|
|
120
|
+
| `cameras/Flir` | `QFlirCamera` | FLIR cameras via GenICam (Spinnaker GenTL producer) |
|
|
121
|
+
| `cameras/Basler` | `QBaslerCamera` | Basler cameras via GenICam (pylon GenTL producer) |
|
|
122
|
+
| `cameras/IDS` | `QIDSCamera` | IDS Imaging cameras via GenICam |
|
|
123
|
+
| `cameras/MV` | `QMVCamera` | Any GenICam camera via MATRIX VISION mvGenTLProducer |
|
|
124
|
+
| `cameras/Vimbax` | `QVimbaXCamera` | Allied Vision cameras via VimbaX GenTL producer |
|
|
125
|
+
| `cameras/Picamera` | `QPicamera` | Raspberry Pi camera module |
|
|
126
|
+
|
|
127
|
+
## Writing a new camera backend
|
|
128
|
+
|
|
129
|
+
Subclass `QCamera` and implement three methods:
|
|
130
|
+
|
|
131
|
+
```python
|
|
132
|
+
from QVideo.lib import QCamera
|
|
133
|
+
|
|
134
|
+
class MyCamera(QCamera):
|
|
135
|
+
|
|
136
|
+
def _initialize(self) -> bool:
|
|
137
|
+
self.device = open_my_hardware()
|
|
138
|
+
if not self.device:
|
|
139
|
+
return False
|
|
140
|
+
self.registerProperty('exposure',
|
|
141
|
+
getter=lambda: self.device.get_exposure(),
|
|
142
|
+
setter=lambda v: self.device.set_exposure(v),
|
|
143
|
+
ptype=float)
|
|
144
|
+
return True
|
|
145
|
+
|
|
146
|
+
def _deinitialize(self) -> None:
|
|
147
|
+
self.device.close()
|
|
148
|
+
|
|
149
|
+
def read(self):
|
|
150
|
+
ok, frame = self.device.read_frame()
|
|
151
|
+
return ok, frame
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
`QCameraTree` and `QVideoSource` work with `MyCamera` immediately — no
|
|
155
|
+
additional code needed.
|
|
156
|
+
|
|
157
|
+
## Acknowledgements
|
|
158
|
+
|
|
159
|
+
Work on this project at New York University is supported by the National
|
|
160
|
+
Science Foundation of the United States under award number DMR-2104837 and
|
|
161
|
+
by an award from the TAC Program of New York University.
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
'''Composite camcorder widget combining a video screen, camera controls, and DVR.
|
|
3
|
+
|
|
4
|
+
Run directly to launch a full camcorder application with camera selection::
|
|
5
|
+
|
|
6
|
+
python -m QVideo.QCamcorder [-c|-f|-s|-v] [cameraID]
|
|
7
|
+
|
|
8
|
+
Camera flags (mutually exclusive):
|
|
9
|
+
|
|
10
|
+
.. code-block:: text
|
|
11
|
+
|
|
12
|
+
-b [cameraID] Basler camera (requires pylon SDK)
|
|
13
|
+
-c [cameraID] OpenCV camera
|
|
14
|
+
-f [cameraID] FLIR camera (requires Spinnaker SDK)
|
|
15
|
+
-i [cameraID] IDS Imaging camera (requires IDS peak SDK)
|
|
16
|
+
-m [cameraID] MATRIX VISION mvGenTLProducer (universal GenICam, not FLIR)
|
|
17
|
+
-p [cameraID] Raspberry Pi camera module (requires picamera2)
|
|
18
|
+
-v [cameraID] Allied Vision VimbaX camera
|
|
19
|
+
|
|
20
|
+
If no flag is given, a noise camera is used as a fallback.
|
|
21
|
+
'''
|
|
22
|
+
|
|
23
|
+
from pyqtgraph.Qt.QtWidgets import QWidget
|
|
24
|
+
from pyqtgraph.Qt.QtCore import pyqtSlot
|
|
25
|
+
from pyqtgraph.Qt import QtGui, uic
|
|
26
|
+
from QVideo.lib import QCameraTree, QVideoSource
|
|
27
|
+
from pathlib import Path
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
__all__ = ['QCamcorder']
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class QCamcorder(QWidget):
|
|
34
|
+
'''A widget combining a video screen, camera controls, and DVR.
|
|
35
|
+
|
|
36
|
+
Lays out a :class:`~QVideo.lib.QVideoScreen.QVideoScreen` alongside
|
|
37
|
+
a :class:`~QVideo.dvr.QDVRWidget.QDVRWidget` and an arbitrary
|
|
38
|
+
:class:`~QVideo.lib.QCameraTree.QCameraTree` control panel. Live
|
|
39
|
+
frames from the camera source are routed to the screen; when the DVR
|
|
40
|
+
starts playback the screen is switched to the playback stream and the
|
|
41
|
+
camera controls are disabled until playback ends.
|
|
42
|
+
|
|
43
|
+
Parameters
|
|
44
|
+
----------
|
|
45
|
+
cameraWidget : QCameraTree
|
|
46
|
+
Camera control tree to embed in the controls panel.
|
|
47
|
+
*args :
|
|
48
|
+
Additional positional arguments forwarded to
|
|
49
|
+
:class:`~pyqtgraph.Qt.QtWidgets.QWidget`.
|
|
50
|
+
**kwargs :
|
|
51
|
+
Additional keyword arguments forwarded to
|
|
52
|
+
:class:`~pyqtgraph.Qt.QtWidgets.QWidget`.
|
|
53
|
+
'''
|
|
54
|
+
|
|
55
|
+
UIFILE = Path(__file__).parent / 'QCamcorder.ui'
|
|
56
|
+
|
|
57
|
+
def __init__(self,
|
|
58
|
+
cameraWidget: QCameraTree,
|
|
59
|
+
*args, **kwargs) -> None:
|
|
60
|
+
super().__init__(*args, **kwargs)
|
|
61
|
+
self.cameraWidget = cameraWidget
|
|
62
|
+
self._setupUi()
|
|
63
|
+
self._connectSignals()
|
|
64
|
+
self.screen.source = self.source
|
|
65
|
+
self.dvr.source = self.source
|
|
66
|
+
|
|
67
|
+
def _setupUi(self) -> None:
|
|
68
|
+
uic.loadUi(str(self.UIFILE), self)
|
|
69
|
+
self.controls.layout().addWidget(self.cameraWidget)
|
|
70
|
+
|
|
71
|
+
def _connectSignals(self) -> None:
|
|
72
|
+
self.dvr.playing.connect(self.dvrPlayback)
|
|
73
|
+
|
|
74
|
+
def closeEvent(self, event: QtGui.QCloseEvent) -> None:
|
|
75
|
+
'''Stop the camera source when the widget is closed.'''
|
|
76
|
+
self.cameraWidget.stop()
|
|
77
|
+
super().closeEvent(event)
|
|
78
|
+
|
|
79
|
+
@pyqtSlot(bool)
|
|
80
|
+
def dvrPlayback(self, playback: bool) -> None:
|
|
81
|
+
'''Switch the screen source between live camera and DVR playback.
|
|
82
|
+
|
|
83
|
+
Connected to :attr:`~QVideo.dvr.QDVRWidget.QDVRWidget.playing`.
|
|
84
|
+
When playback starts the camera source is disconnected from the
|
|
85
|
+
screen and the DVR's :attr:`~QVideo.dvr.QDVRWidget.QDVRWidget.newFrame`
|
|
86
|
+
signal is connected instead; the camera controls are disabled.
|
|
87
|
+
When playback ends the connections are reversed and the controls
|
|
88
|
+
are re-enabled.
|
|
89
|
+
|
|
90
|
+
Parameters
|
|
91
|
+
----------
|
|
92
|
+
playback : bool
|
|
93
|
+
``True`` when DVR playback begins, ``False`` when it ends.
|
|
94
|
+
'''
|
|
95
|
+
try:
|
|
96
|
+
if playback:
|
|
97
|
+
self.source.newFrame.disconnect(self.screen.setImage)
|
|
98
|
+
self.dvr.newFrame.connect(self.screen.setImage)
|
|
99
|
+
else:
|
|
100
|
+
self.dvr.newFrame.disconnect(self.screen.setImage)
|
|
101
|
+
self.source.newFrame.connect(self.screen.setImage)
|
|
102
|
+
except (RuntimeError, TypeError):
|
|
103
|
+
pass
|
|
104
|
+
self.cameraWidget.setDisabled(playback)
|
|
105
|
+
|
|
106
|
+
@property
|
|
107
|
+
def source(self) -> QVideoSource:
|
|
108
|
+
'''The :class:`~QVideo.lib.QVideoSource.QVideoSource` from the camera widget.'''
|
|
109
|
+
return self.cameraWidget.source
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def main() -> None: # pragma: no cover
|
|
113
|
+
'''Launch the camcorder with an interactively chosen camera.'''
|
|
114
|
+
import pyqtgraph as pg
|
|
115
|
+
from QVideo.lib import choose_camera
|
|
116
|
+
|
|
117
|
+
pg.mkQApp()
|
|
118
|
+
camera = choose_camera()
|
|
119
|
+
widget = QCamcorder(camera.start())
|
|
120
|
+
widget.show()
|
|
121
|
+
pg.exec()
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
if __name__ == '__main__':
|
|
125
|
+
main()
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: QVideo
|
|
3
|
+
Version: 3.0.0
|
|
4
|
+
Summary: PyQt-based framework for integrating video cameras into research applications
|
|
5
|
+
Author-email: "David G. Grier" <david.grier@nyu.edu>
|
|
6
|
+
License: GPL-3.0-or-later
|
|
7
|
+
Project-URL: Homepage, https://github.com/davidgrier/QVideo
|
|
8
|
+
Project-URL: Repository, https://github.com/davidgrier/QVideo
|
|
9
|
+
Project-URL: Bug Tracker, https://github.com/davidgrier/QVideo/issues
|
|
10
|
+
Keywords: camera,video,imaging,PyQt5,scientific,GenICam,GigE Vision,raspberry pi
|
|
11
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
12
|
+
Classifier: Intended Audience :: Science/Research
|
|
13
|
+
Classifier: License :: OSI Approved :: GNU General Public License v3 (GPLv3)
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Topic :: Scientific/Engineering
|
|
19
|
+
Classifier: Topic :: Multimedia :: Video :: Capture
|
|
20
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
21
|
+
Requires-Python: >=3.10
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
License-File: LICENSE.md
|
|
24
|
+
Requires-Dist: h5py
|
|
25
|
+
Requires-Dist: numba
|
|
26
|
+
Requires-Dist: numpy
|
|
27
|
+
Requires-Dist: opencv-python
|
|
28
|
+
Requires-Dist: PyQt5
|
|
29
|
+
Requires-Dist: PyQt5-sip
|
|
30
|
+
Requires-Dist: pyqtgraph
|
|
31
|
+
Provides-Extra: genicam
|
|
32
|
+
Requires-Dist: harvesters; extra == "genicam"
|
|
33
|
+
Requires-Dist: genicam; extra == "genicam"
|
|
34
|
+
Provides-Extra: picamera
|
|
35
|
+
Requires-Dist: picamera2; extra == "picamera"
|
|
36
|
+
Provides-Extra: dev
|
|
37
|
+
Requires-Dist: pytest; extra == "dev"
|
|
38
|
+
Requires-Dist: pytest-cov; extra == "dev"
|
|
39
|
+
Provides-Extra: docs
|
|
40
|
+
Requires-Dist: furo; extra == "docs"
|
|
41
|
+
Requires-Dist: sphinx; extra == "docs"
|
|
42
|
+
Requires-Dist: sphinx-autodoc-typehints; extra == "docs"
|
|
43
|
+
Dynamic: license-file
|
|
44
|
+
|
|
45
|
+
# QVideo: PyQt support for video cameras
|
|
46
|
+
|
|
47
|
+
[](https://pypi.org/project/QVideo/)
|
|
48
|
+
[](https://pypi.org/project/QVideo/)
|
|
49
|
+
[](LICENSE.md)
|
|
50
|
+
[](https://github.com/davidgrier/QVideo/actions/workflows/test.yml)
|
|
51
|
+
|
|
52
|
+
**QVideo** is a framework for integrating video cameras into PyQt5 projects
|
|
53
|
+
for scientific research. It provides a unified, registration-based property
|
|
54
|
+
system so that every camera backend — USB webcams, GenICam devices, FLIR
|
|
55
|
+
cameras, Raspberry Pi cameras — is controlled through the same API. Property
|
|
56
|
+
trees, display widgets, and a digital video recorder are built on top of that
|
|
57
|
+
abstraction and require no camera-specific code.
|
|
58
|
+
|
|
59
|
+
<img src="docs/dvrdemo.png" width="75%" alt="QVideo interface demo">
|
|
60
|
+
|
|
61
|
+
## Features
|
|
62
|
+
|
|
63
|
+
- **Unified camera API** — `QCamera` subclasses expose adjustable parameters
|
|
64
|
+
via `registerProperty` / `registerMethod`; UI and recording layers consume
|
|
65
|
+
them without knowing the underlying hardware.
|
|
66
|
+
- **Auto-built property trees** — `QCameraTree` reads the registered property
|
|
67
|
+
map and builds a `pyqtgraph` parameter tree widget automatically.
|
|
68
|
+
- **Threaded video source** — `QVideoSource` wraps any camera in a `QThread`
|
|
69
|
+
and emits `newFrame(ndarray)` at acquisition rate.
|
|
70
|
+
- **Composable filter pipeline** — `VideoFilter` / `QFilterBank` sit between
|
|
71
|
+
source and display; filters include blur, edge detection, RGB channel
|
|
72
|
+
selection, sample-and-hold, and statistical median variants.
|
|
73
|
+
- **Digital video recorder** — lossless HDF5 (with timestamps) and OpenCV
|
|
74
|
+
video formats; `QDVRWidget` is the composite UI widget.
|
|
75
|
+
- **Live display** — `QVideoScreen` supports mouse-aware graphical overlays
|
|
76
|
+
for annotations, regions of interest, and user interaction.
|
|
77
|
+
|
|
78
|
+
## Installation
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
pip install QVideo
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### Optional hardware backends
|
|
85
|
+
|
|
86
|
+
| Backend | Extra | Notes |
|
|
87
|
+
|---------|-------|-------|
|
|
88
|
+
| GenICam cameras (Vimba, etc.) | `pip install QVideo[genicam]` | Requires a vendor-supplied `.cti` producer file |
|
|
89
|
+
| Raspberry Pi camera | `pip install QVideo[picamera]` | Requires `picamera2` |
|
|
90
|
+
| FLIR / Spinnaker cameras | — | Requires the proprietary PySpin SDK; install that separately |
|
|
91
|
+
|
|
92
|
+
## Quick start
|
|
93
|
+
|
|
94
|
+
```python
|
|
95
|
+
from pyqtgraph.Qt import QtWidgets
|
|
96
|
+
from QVideo.cameras.Noise import QNoiseSource
|
|
97
|
+
from QVideo.lib import QVideoScreen
|
|
98
|
+
|
|
99
|
+
app = QtWidgets.QApplication([])
|
|
100
|
+
|
|
101
|
+
source = QNoiseSource() # synthetic noise — no hardware needed
|
|
102
|
+
screen = QVideoScreen()
|
|
103
|
+
source.newFrame.connect(screen.setImage)
|
|
104
|
+
|
|
105
|
+
screen.show()
|
|
106
|
+
source.start()
|
|
107
|
+
app.exec()
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Replace `QNoiseSource` with `QOpenCVSource`, `QGenicamSource`, etc. to switch
|
|
111
|
+
hardware — the rest of the code is identical.
|
|
112
|
+
|
|
113
|
+
## Camera backends
|
|
114
|
+
|
|
115
|
+
| Backend | Class | Hardware |
|
|
116
|
+
|---------|-------|----------|
|
|
117
|
+
| `cameras/Noise` | `QNoiseCamera` | Synthetic — no hardware required |
|
|
118
|
+
| `cameras/OpenCV` | `QOpenCVCamera` | USB webcams via OpenCV |
|
|
119
|
+
| `cameras/Genicam` | `QGenicamCamera` | Abstract base for all GenICam/GigE Vision cameras |
|
|
120
|
+
| `cameras/Flir` | `QFlirCamera` | FLIR cameras via GenICam (Spinnaker GenTL producer) |
|
|
121
|
+
| `cameras/Basler` | `QBaslerCamera` | Basler cameras via GenICam (pylon GenTL producer) |
|
|
122
|
+
| `cameras/IDS` | `QIDSCamera` | IDS Imaging cameras via GenICam |
|
|
123
|
+
| `cameras/MV` | `QMVCamera` | Any GenICam camera via MATRIX VISION mvGenTLProducer |
|
|
124
|
+
| `cameras/Vimbax` | `QVimbaXCamera` | Allied Vision cameras via VimbaX GenTL producer |
|
|
125
|
+
| `cameras/Picamera` | `QPicamera` | Raspberry Pi camera module |
|
|
126
|
+
|
|
127
|
+
## Writing a new camera backend
|
|
128
|
+
|
|
129
|
+
Subclass `QCamera` and implement three methods:
|
|
130
|
+
|
|
131
|
+
```python
|
|
132
|
+
from QVideo.lib import QCamera
|
|
133
|
+
|
|
134
|
+
class MyCamera(QCamera):
|
|
135
|
+
|
|
136
|
+
def _initialize(self) -> bool:
|
|
137
|
+
self.device = open_my_hardware()
|
|
138
|
+
if not self.device:
|
|
139
|
+
return False
|
|
140
|
+
self.registerProperty('exposure',
|
|
141
|
+
getter=lambda: self.device.get_exposure(),
|
|
142
|
+
setter=lambda v: self.device.set_exposure(v),
|
|
143
|
+
ptype=float)
|
|
144
|
+
return True
|
|
145
|
+
|
|
146
|
+
def _deinitialize(self) -> None:
|
|
147
|
+
self.device.close()
|
|
148
|
+
|
|
149
|
+
def read(self):
|
|
150
|
+
ok, frame = self.device.read_frame()
|
|
151
|
+
return ok, frame
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
`QCameraTree` and `QVideoSource` work with `MyCamera` immediately — no
|
|
155
|
+
additional code needed.
|
|
156
|
+
|
|
157
|
+
## Acknowledgements
|
|
158
|
+
|
|
159
|
+
Work on this project at New York University is supported by the National
|
|
160
|
+
Science Foundation of the United States under award number DMR-2104837 and
|
|
161
|
+
by an award from the TAC Program of New York University.
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
LICENSE.md
|
|
2
|
+
QCamcorder.py
|
|
3
|
+
README.md
|
|
4
|
+
__init__.py
|
|
5
|
+
pyproject.toml
|
|
6
|
+
version.py
|
|
7
|
+
./QCamcorder.py
|
|
8
|
+
./__init__.py
|
|
9
|
+
./version.py
|
|
10
|
+
QVideo.egg-info/PKG-INFO
|
|
11
|
+
QVideo.egg-info/SOURCES.txt
|
|
12
|
+
QVideo.egg-info/dependency_links.txt
|
|
13
|
+
QVideo.egg-info/requires.txt
|
|
14
|
+
QVideo.egg-info/top_level.txt
|
|
15
|
+
cameras/__init__.py
|
|
16
|
+
cameras/Basler/QBaslerCamera.py
|
|
17
|
+
cameras/Basler/QBaslerTree.py
|
|
18
|
+
cameras/Basler/__init__.py
|
|
19
|
+
cameras/Flir/QFlirCamera.py
|
|
20
|
+
cameras/Flir/QFlirTree.py
|
|
21
|
+
cameras/Flir/QListFlirCameras.py
|
|
22
|
+
cameras/Flir/__init__.py
|
|
23
|
+
cameras/Genicam/QGenicamCamera.py
|
|
24
|
+
cameras/Genicam/QGenicamTree.py
|
|
25
|
+
cameras/Genicam/__init__.py
|
|
26
|
+
cameras/IDS/QIDSCamera.py
|
|
27
|
+
cameras/IDS/QIDSTree.py
|
|
28
|
+
cameras/IDS/__init__.py
|
|
29
|
+
cameras/MV/QMVCamera.py
|
|
30
|
+
cameras/MV/QMVTree.py
|
|
31
|
+
cameras/MV/__init__.py
|
|
32
|
+
cameras/Noise/QNoiseCamera.py
|
|
33
|
+
cameras/Noise/QNoiseTree.py
|
|
34
|
+
cameras/Noise/__init__.py
|
|
35
|
+
cameras/OpenCV/QListCVCameras.py
|
|
36
|
+
cameras/OpenCV/QOpenCVCamera.py
|
|
37
|
+
cameras/OpenCV/QOpenCVTree.py
|
|
38
|
+
cameras/OpenCV/__init__.py
|
|
39
|
+
cameras/Picamera/QPicamera.py
|
|
40
|
+
cameras/Picamera/QPicameraTree.py
|
|
41
|
+
cameras/Picamera/__init__.py
|
|
42
|
+
cameras/Vimbax/QVimbaXCamera.py
|
|
43
|
+
cameras/Vimbax/QVimbaXTree.py
|
|
44
|
+
cameras/Vimbax/__init__.py
|
|
45
|
+
dvr/QDVRWidget.py
|
|
46
|
+
dvr/QHDF5Reader.py
|
|
47
|
+
dvr/QHDF5Writer.py
|
|
48
|
+
dvr/QOpenCVReader.py
|
|
49
|
+
dvr/QOpenCVWriter.py
|
|
50
|
+
dvr/__init__.py
|
|
51
|
+
dvr/icons_rc.py
|
|
52
|
+
dvr/icons_rc_qt6.py
|
|
53
|
+
filters/Median.py
|
|
54
|
+
filters/MoMedian.py
|
|
55
|
+
filters/Normalize.py
|
|
56
|
+
filters/QBlurFilter.py
|
|
57
|
+
filters/QEdgeFilter.py
|
|
58
|
+
filters/QRGBFilter.py
|
|
59
|
+
filters/QSampleHold.py
|
|
60
|
+
filters/_MedianBase.py
|
|
61
|
+
filters/__init__.py
|
|
62
|
+
lib/QCamera.py
|
|
63
|
+
lib/QCameraTree.py
|
|
64
|
+
lib/QFPSMeter.py
|
|
65
|
+
lib/QFilterBank.py
|
|
66
|
+
lib/QListCameras.py
|
|
67
|
+
lib/QVideoReader.py
|
|
68
|
+
lib/QVideoScreen.py
|
|
69
|
+
lib/QVideoSource.py
|
|
70
|
+
lib/QVideoWriter.py
|
|
71
|
+
lib/VideoFilter.py
|
|
72
|
+
lib/__init__.py
|
|
73
|
+
lib/chooser.py
|
|
74
|
+
lib/clickable.py
|
|
75
|
+
lib/resolutions.py
|
|
76
|
+
lib/types.py
|
|
77
|
+
tests/test_chooser.py
|
|
78
|
+
tests/test_clickable.py
|
|
79
|
+
tests/test_demo.py
|
|
80
|
+
tests/test_filterdemo.py
|
|
81
|
+
tests/test_icons_rc.py
|
|
82
|
+
tests/test_median.py
|
|
83
|
+
tests/test_momedian.py
|
|
84
|
+
tests/test_normalize.py
|
|
85
|
+
tests/test_qblurfilter.py
|
|
86
|
+
tests/test_qcamcorder.py
|
|
87
|
+
tests/test_qcamera.py
|
|
88
|
+
tests/test_qcameratree.py
|
|
89
|
+
tests/test_qdvrwidget.py
|
|
90
|
+
tests/test_qedgefilter.py
|
|
91
|
+
tests/test_qfilterbank.py
|
|
92
|
+
tests/test_qfpsmeter.py
|
|
93
|
+
tests/test_qgenicamcamera.py
|
|
94
|
+
tests/test_qgenicamtree.py
|
|
95
|
+
tests/test_qhdf5reader.py
|
|
96
|
+
tests/test_qhdf5writer.py
|
|
97
|
+
tests/test_qlistcameras.py
|
|
98
|
+
tests/test_qlistcvcameras.py
|
|
99
|
+
tests/test_qnoisecamera.py
|
|
100
|
+
tests/test_qnoisetree.py
|
|
101
|
+
tests/test_qopencvcamera.py
|
|
102
|
+
tests/test_qopencvreader.py
|
|
103
|
+
tests/test_qopencvtree.py
|
|
104
|
+
tests/test_qopencvwriter.py
|
|
105
|
+
tests/test_qpicamera.py
|
|
106
|
+
tests/test_qrgbfilter.py
|
|
107
|
+
tests/test_qsamplehold.py
|
|
108
|
+
tests/test_qvideoreader.py
|
|
109
|
+
tests/test_qvideoscreen.py
|
|
110
|
+
tests/test_qvideosource.py
|
|
111
|
+
tests/test_qvideowriter.py
|
|
112
|
+
tests/test_qvimbaxcamera.py
|
|
113
|
+
tests/test_resolutions.py
|
|
114
|
+
tests/test_roidemo.py
|
|
115
|
+
tests/test_videofilter.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
QVideo
|