Using QVideo in Jupyter#
Installation#
In a notebook, install QVideo with the %pip magic. Unlike pip in a
terminal, it installs into the environment that the notebook’s kernel uses,
so import QVideo works afterwards:
%pip install "QVideo[pyqt6,jupyter]"
The extras do two things. pyqt6 installs the Qt layer that QVideo
needs (without one, import QVideo stops with a message that names the
choices), and jupyter installs ipywidgets, which the camera
chooser, the property panel and the live view use. OpenCV and the other
required packages come with QVideo.
Restart the kernel after the installation (Kernel > Restart), and
reload the browser tab if you installed ipywidgets for the first
time, so that the notebook can draw its widgets.
Note
If import QVideo still fails, the notebook kernel probably runs in
another environment than the one you installed into. Check it:
import sys
print(sys.executable) # the interpreter where QVideo is installed
Install into the environment shown, or register a kernel from the right environment and select it in the kernel menu:
pip install ipykernel
python -m ipykernel install --user --name qvideo --display-name "QVideo"
Acquiring a frame#
from QVideo import Camera
camera = await Camera()
frame = camera.read()
await Camera() probes all installed backends and opens a camera that
works:
With one real camera, it opens that camera. The simulated Noise camera is only a fallback, and
Camera('Noise')selects it.With no camera, it says that no hardware was found and uses Noise.
With several real cameras, a chooser appears: select a camera from the drop-down and click Open. The cell shows
[*]until you do.
When ipywidgets is not installed the chooser falls back to a
printed summary:
Available cameras: Flir, OpenCV
Using Flir. To select a different one: Camera('OpenCV')
If the chooser prints HBox(...) instead of a drop-down, see
Widgets show up as text.
To see which cameras there are without opening one:
from QVideo import available_cameras
available_cameras()
# [('OpenCV', 0, 'FaceTime HD Camera'), ('Noise', 0, 'Noise')]
Each entry holds the arguments of Camera() and the name that the
device gives itself, so Camera('OpenCV', 0) opens the first. The
function opens and closes each camera to see that it works, so the light
of a camera may blink.
To open the first available camera without any output:
camera = Camera()
frame = camera.read()
To request a specific backend by name:
camera = Camera('OpenCV') # USB webcam
camera = Camera('Basler') # Basler pylon camera
camera = Camera('Noise') # synthetic noise — no hardware required
Displaying a frame#
import matplotlib.pyplot as plt
plt.imshow(frame, cmap='gray') # grayscale
plt.axis('off')
plt.show()
Omit cmap='gray' for color frames.
Adjusting camera properties#
Camera properties are accessible as attributes:
camera.fps = 30.0
camera.width = 1280
camera.exposure = 0.01 # seconds — camera-dependent
To list all properties the active camera exposes:
print(list(camera.settings))
Interactive property panel#
camera.controls() returns an ipywidgets-based property panel
that exposes all camera settings as interactive inputs. Place it as the
last expression in a cell to render it inline:
camera.controls()
Each registered property appears as an appropriate widget:
float with known bounds → slider
float without bounds → numeric text field
int with known bounds → integer slider
int without bounds → integer text field
bool → checkbox
string with a fixed set of values → dropdown
string free-form → text field
Read-only properties are shown but disabled. Click Refresh to re-read all values from the camera (useful after a resolution change or other external adjustment).
Note
ipywidgets must be installed for camera.controls() to work:
pip install ipywidgets
Live video feed#
camera.live_view() streams frames into an ipywidgets image widget
using an asyncio background loop — no matplotlib backend setup required:
live = camera.live_view()
To stop the feed:
live.stop()
Pass fps to control the display update rate (default 30):
live = camera.live_view(fps=10.0)
Note
ipywidgets must be installed (included in the jupyter extra):
pip install "QVideo[jupyter]"
Filtering frames#
camera.filters is a list of filters that camera.read() and
camera.live_view() apply to every frame, in order. Use any of the
filters in QVideo.filters, or any function that takes a frame and
returns a frame:
from QVideo.filters import DarkFrameFilter, GammaFilter
camera = await Camera()
gamma = GammaFilter()
camera.filters = [DarkFrameFilter(), gamma]
camera.live_view() # shows the filtered video
frame = camera.read() # a filtered frame
Change the list while the feed runs and the next frame uses it:
camera.filters.append(lambda frame: 255 - frame)
camera.filters = [] # back to raw frames
Filters have settings, which you change on the filter itself, for example
gamma.gamma = 0.5; the documentation of each filter names them.
A filter that raises is reported with a warning and removed from the list; the feed continues with the others.
A filter that needs several frames before it has a result, such as an average, makes
read()take more frames until it has one.Filters run in the notebook’s own process, one frame at a time, so a slow filter lowers the rate of the display but not what the camera captures.
Finding filters by name#
Filter() lists the filters that QVideo provides, and Filter('name')
builds them, so that a notebook needs no imports:
from QVideo import Camera, Filter
Filter() # ['Blob', 'Canny', 'Cartoon', ...]
camera = await Camera()
camera.filters = Filter('Smoothing', 'Canny')
camera.filters = Filter(['Smoothing', 'Canny']) # the same
Case and spaces do not matter ('darkframe' finds 'Dark Frame'), and
a name that does not exist raises an error that suggests the nearest ones.
The filters come with their default settings; change them through the list:
camera.filters[1].threshold1 = 50
A filter that needs a calibration, such as 'Dark Frame' or
'Flat Field', passes frames through until it has one. Keep the filter
and call capture() to take the calibration from the next frame:
camera.filters = Filter('Dark Frame')
camera.filters[0].capture()
Acquiring a sequence of frames#
import numpy as np
frames = [camera.read() for _ in range(50)]
stack = np.stack(frames) # shape: (50, height, width, ...)
Widgets show up as text#
If a cell prints something like HBox(children=(Dropdown(...)))
instead of a drop-down, the notebook received the widget but cannot draw
it. The Python side is fine; the browser side of ipywidgets is not
loaded. Try these in order:
Run
import ipywidgets; ipywidgets.IntSlider()in a cell. If a slider appears, the problem is in QVideo’s cell, not in Jupyter.Reload the browser tab, then rerun the cell.
Restart the kernel. If you installed
ipywidgetsafter JupyterLab started, restart JupyterLab as well.Install the browser side into the environment that runs JupyterLab:
pip install -U ipywidgets jupyterlab_widgets, then restart.Compare
sys.executablein a notebook cell withwhich jupyterin a terminal. If they name different environments, install into the one that the kernel uses (with%pip).
Meanwhile you do not need widgets: Camera('OpenCV') opens a camera by
name, and camera.read() returns a frame.
Environment notes#
Headless servers: Qt requires a display. On a headless server set
the platform plugin to offscreen before importing QVideo:
import os
os.environ['QT_QPA_PLATFORM'] = 'offscreen'
from QVideo import Camera
Multiple USB cameras: pass cameraID to select a specific device
index:
camera = Camera('OpenCV', cameraID=1) # second USB camera