The camera is fine. GStreamer plays it from the command line, the sensor enumerates under v4l2-ctl --list-devices, and still the Python capture dies with Failed to open camera! after printing Frame size 0 x 0 and FPS : 0.0. If you are integrating a CSI sensor module on a Jetson — or any Linux host where capture runs through GStreamer and processing runs in OpenCV — the cause is usually one line in the OpenCV build report. You will confirm it with one command, choose between uninstalling a wheel, re-flashing or rebuilding, and get frames flowing into Python again.
GStreamer shows live video, so why does OpenCV fail with a 0 x 0 frame?
Because cv2.VideoCapture on a Jetson CSI camera reaches the sensor through OpenCV's GStreamer backend, and an OpenCV built without GStreamer hands the wrapper an empty 0 x 0 stream at 0.0 fps while the sensor and its kernel driver keep working.
The failure isolates itself in three observations. Native GStreamer plays the feed: with an IMX519 autofocus module, nvarguscamerasrc delivers 1920 x 1080 without complaint. The sensor enumerates — v4l2-ctl --list-devices lists the vi-output node behind /dev/video0. And the vendor's own focus example fails exactly like your code, which rules the script out.
Those Frame size 0 x 0 and FPS : 0.0 lines are the wrapper reporting what cv2.VideoCapture handed back: an open that produced no stream, so the read returns nothing and the wrapper raises its RuntimeError. The camera was never the suspect. The library that cannot speak GStreamer is.
Two neighbours look similar and are not. When GStreamer itself runs but a viewer stays black, playback elements are missing rather than a backend — that is the case of a camera connected while the viewer shows no video over missing GStreamer plugins. When a pipeline opens and then crashes on caps, read fixing the GStreamer caps on a CSI camera. Here the wrapper cannot even start, which points straight at the build.
One boundary worth knowing: CSI modules live and die by their kernel driver on each host — IMX219 and IMX477 on Jetson built-in driver support and stereo sync maps which sensor modules the platform carries out of the box.
How do you check whether OpenCV was built with GStreamer?
Run python3 -c "import cv2; print(cv2.getBuildInformation())" and search the output for the GStreamer line: GStreamer: NO names this exact fault, while GStreamer: YES clears OpenCV and moves the search to your pipeline.
python3 -c "import cv2; print(cv2.getBuildInformation())" | grep -i gstreamer
getBuildInformation() prints the report compiled into the library: every backend it carries, from V4L2 through FFmpeg to GStreamer. The text is baked in at build time, so it cannot drift — the library was either linked against GStreamer when it was built or it never was.
GStreamer: YES clears the library; chase the pipeline string and its caps instead. GStreamer: NO is your answer, and the next question is where that binary came from.
Why does your OpenCV report GStreamer: NO when you never compiled it?
Because the cv2 your interpreter loads was built without it, and on a stock JetPack image that almost always means a pip wheel shadowing the system build — the published opencv-python wheels carry no GStreamer backend at all.
Find out which binary you are importing:
python3 -c "import cv2; print(cv2.__file__)"
A path under site-packages belongs to a wheel, and that wheel shadows the JetPack build sitting in the system's dist-packages. The wheels are plain about the reason: enabling GStreamer needs the GStreamer development libraries present at build time, and the published wheels are compiled without them, as the opencv-python project's own issue tracker records.
Two rarer causes sit behind the same NO: a flash or upgrade that dropped the stock build, and a from-source build with the GStreamer option switched off. The check tells you the state; the file path tells you the likely reason.
Should you rebuild OpenCV or re-flash the Jetson?
Re-flash JetPack when the system build itself prints GStreamer: NO; the stock image ships OpenCV with GStreamer enabled, and a clean re-flash of the same JetPack 6.2 image brought GStreamer: YES back with nothing else changed.
Work the cheapest fix first:
- Wheel shadowing — uninstall the wheel, re-run the check. If
GStreamer: YESreturns, you are done; the sensor driver and the example scripts were never at fault. - System build says NO — re-flash the JetPack image. Run the build check before you reinstall anything else, because a later pip install can shadow the fresh build again.
- Workstation Linux — build OpenCV yourself with the GStreamer development libraries installed first; the opencv-python project's issue tracker records that same approach for enabling the backend.
On Tegra hardware, treat the third option with respect. A from-source build must keep its CUDA and Tegra-specific pieces aligned, and a GStreamer-enabled self-build can still misbehave at runtime — such reports are on file in the opencv project's issue tracker. For a Jetson that has to run on Monday, the re-flash is the committed answer.
How do you open the camera once the build reports GStreamer: YES?
Pass the pipeline string to cv2.VideoCapture with the GStreamer backend selected — starting at nvarguscamerasrc, ending at appsink — then prove it with one read that prints your sensor's width, height and channel count.
Take the pipeline that already plays on your command line and terminate it at appsink instead of the display element; OpenCV reads from appsink. The backend is explicit in the call:
import cv2
pipeline = "nvarguscamerasrc ! <caps for your sensor> ! <conversion to BGR> ! appsink"
capture = cv2.VideoCapture(pipeline, cv2.CAP_GSTREAMER)
opened = capture.isOpened() # opened: bool
ok, frame = capture.read() # ok: bool, frame: numpy.ndarray or None
print(opened, ok, None if frame is None else frame.shape)
cv2.CAP_GSTREAMER is the constant that selects the GStreamer backend. pipeline is your own string; the placeholder elements in the middle set the size and convert the Jetson's camera buffers into BGR arrays. Copy the exact middle from the sensor vendor's example for your module, because element and caps names shift between releases.
The test is one read. frame.shape should print (1080, 1920, 3) for the 1080p feed nvarguscamerasrc already plays. From there the focus example drives the lens over I2C as before; if frames arrive but the lens stays put, the fault has moved to the driver — see diagnosing the I2C fault behind a voice coil motor autofocus driver.
The same backend opens network streams, where buffering adds a latency question of its own; the rtspsrc latency walkthrough for OpenCV VideoCapture covers that tuning.
Can you get frames into Python without touching the OpenCV build at all?
Yes — but not through cv2.VideoCapture for a CSI sensor: either read the working GStreamer pipeline from native code, or change camera class so capture never depends on the host's OpenCV.
The first route keeps your pipeline and moves the boundary. A small native component runs the nvarguscamerasrc pipeline into appsink and hands buffers to your Python process, which processes arrays with whatever cv2 it has; capture and processing stop sharing a build.
The second route changes the camera. Industrial GigE Vision and USB3 Vision cameras are not UVC devices, and cv2.VideoCapture cannot open them on any host — why OpenCV VideoCapture cannot open a GigE Vision camera explains the transport behind that. Their SDK hands frames to your code as arrays, so the host's OpenCV build never gates capture. That decoupling earns its keep on any machine you cannot re-flash.
For new embedded designs, board-level cameras let you pick the transport up front. The LS-BCM-0020 is a 6.3 MP GigE board-level camera with an IMX178 sensor, a 1/1.8" rolling shutter and 18 fps; the LS-BCM-0003 is a 3.1 MP USB3 board-level camera with an IMX252 sensor, a 1/1.8" global shutter and 120 fps.
Board-level cameras that keep capture out of the host's OpenCV:
- Sensor format
- 1/1.8"
- Lens mount
- CS-mount
- Sensor format
- 1/1.8"
- Lens mount
- C-mount
The same transport logic holds for full area scan cameras, and the camera finder filters by interface and mount. On choosing the board camera itself, board level camera selection for embedded vision machines walks the trade-offs.
Compact cameras to build in
120 models · from $179.00How do you keep a GStreamer-less build off the production line?
Add the check to commissioning: a golden image is not validated until python3 -c "import cv2; print(cv2.getBuildInformation())" prints GStreamer: YES, and your startup code asserts it before the first frame is requested.
Three habits do the rest. Never pip-install opencv-python onto the deployment image; if a tool demands it, give that tool its own virtual environment and leave the system cv2 alone. Run the build check as a step in image validation, before drivers and SDKs go on, and treat GStreamer: YES as a release gate. Keep the recovery rehearsed — the one-liner, the uninstall, the re-flash — so the fix is a procedure, not a diagnosis.
Choosing a transport that survives this whole class of fault is the longer-term move: board-level cameras with a standard vision interface, or a full system specced through the camera step of the configurator. More capture-and-transport problems sit under systems integration, and if the camera decision needs a second opinion, the contact page reaches the applications team.
The configurator matches a camera, lens, light, light controller and cables to each other, step by step, and lists only parts that fit what you already picked.
Questions
Can you check GStreamer support from inside a running script instead of the shell?
Yes. getBuildInformation() returns a plain string, so read it in Python: build_info = cv2.getBuildInformation(), then fail fast if the GStreamer line does not say YES. Doing this at startup turns a confusing 0 x 0 frame into a named error before the machine cycles.
Do any of the published pip wheels include GStreamer?
None do. The wheels published for opencv-python, including the headless and contrib builds, are compiled without GStreamer, because enabling it needs the GStreamer development libraries present at build time, as the project's own issue tracker states. Any cv2 with GStreamer came from your distribution, from JetPack, or from a build you made.
Will cv2.VideoCapture(0) open the CSI sensor through V4L2 instead?
Not usefully. Index-based capture is for UVC-class USB devices; a Jetson CSI node typically hands out the sensor's raw output and skips the ISP pipeline, so you lose the processed 1080p stream and the supported path. Keep CSI capture on the GStreamer backend and reserve V4L2 indices for UVC cameras.
Does the warning about nv_imx477.ko.bak during driver install mean the install failed?
No. The package's install script tried to back up a module file for a different sensor that was not on the system (ls: cannot access 'nv_imx477.ko.bak'), and the message is harmless for an IMX519 set-up. The sensor enumerating under v4l2-ctl --list-devices and playing through nvarguscamerasrc is the real proof the driver works.
Does this GStreamer gap affect C++ OpenCV applications too?
It affects whichever library the application links. A C++ program built against the same system OpenCV shares its backends, so a stock JetPack library serves C++ fine even while a Python pip wheel fails. The build information is per-binary: check it in the process that actually captures, not in a different interpreter or binary.

