PTZ Cameras with Python

PTZ cameras are often controllable using a protocol called onvif. One python3 module for this protocol is onvif-zeep. The protocol is a little awkward and verbose, even with this module, so this page mainly serves to record some examples of its use. For the camera I used this with, the web interface to setting the PTZ features required a Windows-only binary browser plugin, and, as the camera lived in a sealed dome, there was no other way of aiming it.

The module can be installed with

pip3 install --user onvif-zeep

Absolute Move

#!/usr/bin/python3
import sys
from onvif import ONVIFCamera

mycam = ONVIFCamera('192.168.1.80', 80, 'user', 'password')
media = mycam.create_media_service()
ptz = mycam.create_ptz_service()
media_profile = media.GetProfiles()[0]

moverequest = ptz.create_type('AbsoluteMove')
moverequest.ProfileToken = media_profile.token
moverequest.Position=ptz.GetStatus({'ProfileToken': media_profile.token}).Position

try: 
    moverequest.Position.PanTilt.x = float(sys.argv[1])
    moverequest.Position.PanTilt.y = float(sys.argv[2])
    moverequest.Position.Zoom.x = float(sys.argv[3])
except:
    pass
    
ptz.AbsoluteMove(moverequest)

No argument checking, comments, help text, etc., but usage is

  abs_move x [y [z]]

where x is horizontal position, y is vertical, and z is zoom.

The permitted range for these three parameters will depend on your camera. The positions may range from -1 to +1, and the zoom from 0 to 1. Or your camera may be different.

Note that the username specified must be an account which has onvif enabled. The camera may default to no account having onvif enabled, not even the admin account.

WDR

If you camera supports a wide dynamic range setting, then the following may be useful.

#!/usr/bin/python3
import sys
from onvif import ONVIFCamera
mycam = ONVIFCamera('192.168.1.80', 80, 'user', 'password')
imaging=mycam.create_imaging_service()
media = mycam.create_media_service()
video_sources=media.GetVideoSources()

image=mycam.create_imaging_service()
imagingrequest=image.create_type('SetImagingSettings')
imagingrequest.VideoSourceToken=video_sources[0].token

try:
  imagingrequest.ImagingSettings={'WideDynamicRange': {'Mode' : sys.argv[1],
                                                       'Level' : float(sys.argv[2])}}
except:
  imagingrequest.ImagingSettings={'WideDynamicRange': {'Mode' : sys.argv[1] }}

image.SetImagingSettings(imagingrequest)

The first argument should be "ON" of "OFF", and the second the level, which, in the case of the camera tested, is a float ranging from 0 to 100.

Refocus

The camera tested did not support the onvif commands for moving to an absolute focus position. But one can refocus it by turning autofocus on, waiting a few seconds, and then turning it off again. This is certainly necessary after changing the zoom setting. (One could also leave autofocus on, and risk the camera hunting occasionally.)

#!/usr/bin/python3
import time
from onvif import ONVIFCamera

mycam = ONVIFCamera('192.168.1.80', 80, 'user', 'password')
imaging=mycam.create_imaging_service()
media = mycam.create_media_service()
video_sources=media.GetVideoSources()

image=mycam.create_imaging_service()
imagingrequest=image.create_type('SetImagingSettings')
imagingrequest.VideoSourceToken=video_sources[0].token
imagingrequest.ImagingSettings={'Focus': {'AutoFocusMode' : 'AUTO'}}
image.SetImagingSettings(imagingrequest)

time.sleep(6)

imagingrequest=image.create_type('SetImagingSettings')
imagingrequest.VideoSourceToken=video_sources[0].token
imagingrequest.ImagingSettings={'Focus': {'AutoFocusMode' : 'MANUAL'}}
image.SetImagingSettings(imagingrequest)