Of course! This is a very common and powerful combination for computer vision applications. Let's break down how to use Python, OpenCV, and ONVIF together.

High-Level Overview: What's the Role of Each?
-
ONVIF (Open Network Video Interface Forum): This is a standard, not a library. It's a set of open specifications for IP-based physical security products. Think of it as a common language that allows different IP cameras (from different manufacturers like Hikvision, Axis, Dahua, etc.) to "talk" to each other. It's used for:
- Discovery: Finding cameras on your network.
- Configuration: Getting the camera's stream URL (RTSP), setting resolution, changing focus, moving PTZ (Pan-Tilt-Zoom) cameras, etc.
- Getting Metadata: Retrieving device information, video analytics, etc.
-
Python: The programming language that ties everything together. It's easy to use and has vast libraries for almost any task.
-
OpenCV (Open Source Computer Vision Library): This is the engine for processing video. It's not designed to find or configure IP cameras. Its main strengths are:
- Reading Video Streams: Once you have the stream's URL (from ONVIF), OpenCV can connect to it.
- Video Processing: Frame capture, decoding, encoding.
- Computer Vision: Object detection (like using YOLO or Haar Cascades), face recognition, motion detection, image filtering, etc.
The Workflow:

- Use an ONVIF Python Library to discover your camera on the network.
- Use the same library to ask the camera for its RTSP stream URL.
- Pass that URL to OpenCV to open and read the video frames.
- Process each frame with OpenCV's powerful functions to analyze the video content.
- (Optional) Use the ONVIF library again to control the camera, like panning or zooming based on your analysis.
Step 1: Install Necessary Libraries
You'll need opencv-python for video processing and a library to handle the ONVIF protocol. The most popular one is onvif-zeep.
pip install opencv-python pip install onvif-zeep
Step 2: Find Your Camera's IP Address
Before you can use ONVIF, you need to know your camera's IP address. You can usually find this in your router's admin panel (DHCP client list) or by using a network scanning tool like nmap or a dedicated IP camera scanner app.
Step 3: The Python Code - A Practical Example
Let's create a script that performs the following actions:
- Discovers all ONVIF devices on the local network.
- Connects to the first one it finds.
- Gets the RTSP stream URL.
- Opens the stream with OpenCV.
- Displays the live video feed in a window.
- Detects motion in the feed.
- (Bonus) Shows how to get camera information and control a PTZ camera.
Here is the complete, commented script.

import cv2
import time
import onvif
from onvif import ONVIFCamera
# --- Configuration ---
# Replace with your camera's IP address, username, and password
# You can find these in your camera's web interface.
CAMERA_IP = '192.168.1.100'
USERNAME = 'admin'
PASSWORD = 'password'
# --- Part 1: Discovering ONVIF Devices (Optional) ---
def discover_onvif_devices():
"""Scans the network for ONVIF devices."""
print("Scanning for ONVIF devices...")
devices = onvif.discover()
if devices:
print(f"Found {len(devices)} device(s):")
for device in devices:
print(f" - IP: {device['X_Addr']}, Name: {device['Name']}")
else:
print("No ONVIF devices found.")
return devices
# --- Part 2: Connecting to the Camera and Getting Stream URL ---
def get_rtsp_stream(camera_ip, username, password):
"""Connects to a camera and retrieves its RTSP stream URL."""
try:
# Create an ONVIF camera object
# The port is usually 80 for HTTP and 554 for RTSP, but can vary.
# onvif-zeep will try to figure it out.
camera = ONVIFCamera(camera_ip, 80, username, password)
print(f"Successfully connected to camera at {camera_ip}")
# Get the device information (optional)
device_info = camera.get_device_information()
print(f"Camera Model: {device_info['model']}")
print(f"Manufacturer: {device_info['manufacturer']}")
# Get the media profile (this contains the stream URL)
media_service = camera.create_media_service()
profiles = media_service.GetProfiles()
if not profiles:
raise Exception("No media profiles found on the camera.")
# Use the first profile found
profile_token = profiles[0].token
stream_url = media_service.GetStreamUri({'StreamSetup': {'Stream': 'RTP-Unicast', 'Transport': 'RTSP'}, 'ProfileToken': profile_token})
print(f"RTSP Stream URL: {stream_url.uri}")
return stream_url.uri
except Exception as e:
print(f"Error connecting to camera or getting stream URL: {e}")
return None
# --- Part 3: Processing the Stream with OpenCV ---
def process_stream(rtsp_url):
"""Connects to the RTSP stream and processes it with OpenCV."""
if not rtsp_url:
print("No RTSP URL provided. Exiting.")
return
# Create a VideoCapture object
cap = cv2.VideoCapture(rtsp_url)
# Check if the VideoCapture object was opened successfully
if not cap.isOpened():
print(f"Error: Could not open video stream from {rtsp_url}")
return
print("Stream opened successfully. Press 'q' to quit.")
# For motion detection, we need a reference frame
ret, frame1 = cap.read()
if not ret:
print("Error: Could not read the first frame from the stream.")
cap.release()
return
gray1 = cv2.cvtColor(frame1, cv2.COLOR_BGR2GRAY)
gray1 = cv2.GaussianBlur(gray1, (21, 21), 0)
while True:
# Read a new frame
ret, frame2 = cap.read()
if not ret:
print("Stream ended. Trying to reconnect...")
time.sleep(2) # Wait before trying to reconnect
cap.release()
cap = cv2.VideoCapture(rtsp_url)
continue
# Process the frame (e.g., motion detection)
gray2 = cv2.cvtColor(frame2, cv2.COLOR_BGR2GRAY)
gray2 = cv2.GaussianBlur(gray2, (21, 21), 0)
# Compute the difference between the current frame and the reference frame
delta_frame = cv2.absdiff(gray1, gray2)
thresh_frame = cv2.threshold(delta_frame, 30, 255, cv2.THRESH_BINARY)[1]
thresh_frame = cv2.dilate(thresh_frame, None, iterations=2)
# Find contours of the moving objects
contours, _ = cv2.findContours(thresh_frame.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
# Draw rectangles around moving objects
for contour in contours:
if cv2.contourArea(contour) > 500: # Filter out small noise
(x, y, w, h) = cv2.boundingRect(contour)
cv2.rectangle(frame2, (x, y), (x + w, y + h), (0, 255, 0), 3)
# Update the reference frame
gray1 = gray2
# Display the resulting frame
cv2.imshow('ONVIF Camera Feed - Motion Detection', frame2)
# Press 'q' to exit
if cv2.waitKey(1) & 0xFF == ord('q'):
break
# Release everything when done
cap.release()
cv2.destroyAllWindows()
# --- Part 4: Controlling a PTZ Camera (Bonus) ---
def control_ptz(camera_ip, username, password):
"""Demonstrates how to control a PTZ-enabled camera."""
try:
camera = ONVIFCamera(camera_ip, 80, username, password)
ptz_service = camera.create_ptz_service()
media_service = camera.create_media_service()
# Get the PTZ configuration options for a profile
profiles = media_service.GetProfiles()
if not profiles:
print("No profiles found for PTZ control.")
return
profile_token = profiles[0].token
# Get the PTZ configuration options
ptz_configuration = ptz_service.GetConfigurationOptions({'ProfileToken': profile_token})
# Example: Move the camera to a preset position
# First, you need to know the name of a preset. Let's assume 'Home' exists.
presets = ptz_service.GetPresets({'ProfileToken': profile_token})
if presets:
home_preset = next((p for p in presets if p.Name == 'Home'), None)
if home_preset:
print(f"Moving camera to preset: {home_preset.Name}")
ptz_service.GotoPreset({
'ProfileToken': profile_token,
'PresetToken': home_preset.Token
})
else:
print("Preset 'Home' not found. Available presets:")
for p in presets:
print(f" - {p.Name}")
else:
print("No presets found on this camera.")
except Exception as e:
print(f"Error during PTZ control: {e}")
# --- Main Execution ---
if __name__ == '__main__':
# 1. Discover devices (optional)
# discover_onvif_devices()
# 2. Get the RTSP stream URL
rtsp_url = get_rtsp_stream(CAMERA_IP, USERNAME, PASSWORD)
# 3. Process the stream
if rtsp_url:
# Uncomment the line below if you want to test PTZ control first
# control_ptz(CAMERA_IP, USERNAME, PASSWORD)
process_stream(rtsp_url)
How to Run the Script
- Save the code: Save the code above as a Python file (e.g.,
onvif_opencv.py). - Update Configuration: Change
CAMERA_IP,USERNAME, andPASSWORDto match your camera's credentials. - Run from Terminal:
python onvif_opencv.py
You should see a window pop up with your camera's live feed, and green rectangles will appear around any moving objects. Press q to close the window.
Common Pitfalls and Troubleshooting
- Connection Timeouts: If the script hangs, it might be trying to connect to a camera that's offline or on a different subnet. Double-check the IP address.
- Authentication Failed: Make sure your username and password are correct. Some cameras have different user levels (e.g., a user with "viewer" rights can get the stream but not change PTZ settings).
- No Media Profiles: This can happen if the camera's ONVIF service is not enabled or is misconfigured. Check your camera's web interface for an "ONVIF" or "Network" section and ensure it's turned on.
- ONVIF Port Mismatch: The default port is 80, but some cameras might use a different one (e.g., 8899). If discovery fails, you might need to specify the port manually when creating the
ONVIFCameraobject:ONVIFCamera(camera_ip, 8899, username, password). - OpenCV Can't Connect to RTSP: The URL you get from ONVIF might be incorrect. Try pasting the
rtsp://...URL directly into a media player like VLC. If VLC can't play it, the URL is wrong, and the problem is with the ONVIF part, not OpenCV. If VLC works, then the issue is likely with your network or thecv2.VideoCapturecall itself.
This combination is extremely powerful for building automated surveillance systems, smart home applications, or any project that requires analyzing video from a network camera.
