python camera driver ctypes framebuffer

python - Reading Frame Buffer desde la cámara usando ctypes



camera driver (0)

Tengo problemas para leer el buffer de cuadro de una cámara usando Python . La cámara (Xenics) está conectada a través de USB y hay un dll que se envía con la cámara. Accedo a este dll usando ctypes. El código que estoy usando está inspirado principalmente en un módulo de python para lantz ( lantz_drivers_xenics ).
El dll proporciona la función XC_CopyFrameBuffer que estoy usando para copiar el framebuffer en una matriz numpy.

Si bien, en principio, la adquisición funciona bien, el problema es leer continuamente el framebuffer a velocidad moderada o alta. El software compilado que se envía con la cámara puede leer la cámara a 24 fps. En mi programa, me gustaría leer el framebuffer en algo entre 5 y 25 fps. Sin embargo, no puedo alcanzar este valor, porque el tiempo que toma llamar a la función XC_CopyFrameBuffer varía entre 0 y ~ 0.5 segundos ; es en su mayoría baja, pero 0.5 segundos para aproximadamente cada tercer cuadro más o menos.

Mi programa real es más complejo, usa hilos y procesa las imágenes adquiridas. Pero he reducido el código a un ejemplo mínimo que reproduce el problema y que he adjuntado. Su texto y salida gráfica se muestran a continuación. Mi pregunta es:

  1. ¿Alguien ha experimentado problemas similares con las cámaras?
  2. ¿Alguien ha usado una cámara Xenics, conectada a ella a través de ctypes en python y podría proporcionar un ejemplo de eso?
  3. ¿Hay algún problema aparente u oculto en la forma en que I (y / o lantz_drivers_xenics) utiliza el dll?

Este es el código que estoy usando:

import time import ctypes import threading import numpy as np from numpy.ctypeslib import ndpointer calibration_file = r"C:/Path/to/some/calibrationfile.xca" dll = "C://Programme//X-Control//xcamera.dll" lib = ctypes.WinDLL(dll) #for xcamera we need ctypes.WinDLL exposure_time = 300 #microseconds (us) readlock = threading.Lock() #add types lib.XC_OpenCamera.argtypes = [ctypes.c_uint] lib.XC_CloseCamera.argtypes = [ctypes.c_uint] lib.XC_IsInitialised.argtypes = [ctypes.c_uint] lib.XC_LoadFromFile.argtypes = [ctypes.c_uint, ctypes.c_char_p] lib.XC_LoadCalibrationPack.argtypes = [ctypes.c_uint, ctypes.c_char_p] lib.XC_SetGainCamera.argtypes = [ctypes.c_uint, ctypes.c_double] lib.XC_SetIntegrationTime.argtypes = [ctypes.c_uint, ctypes.c_ulong] lib.XC_SetFan.argtypes = [ctypes.c_uint, ctypes.c_bool] lib.XC_StartCapture.argtypes = [ctypes.c_uint] lib.XC_StopCapture.argtypes = [ctypes.c_uint] lib.XC_GetFrameSizeInBytes.argtypes = [ctypes.c_uint] lib.XC_CloseCamera.restype = ctypes.c_void_p lib.XC_StopCapture.restype = ctypes.c_void_p xcamera_id = lib.XC_OpenCamera(0) #lib.XC_LoadFromFile(xcamera_id, self.config_file) lib.XC_LoadCalibrationPack(xcamera_id, calibration_file) height = lib.XC_GetHeight(xcamera_id) width = lib.XC_GetWidth(xcamera_id) shape = (height, width) lib.XC_CopyFrameBuffer.argtypes = [ctypes.c_uint, ndpointer(dtype=np.uint16, shape=shape),ctypes.c_uint] frame_size = lib.XC_GetFrameSizeInBytes(xcamera_id) fbuffer = np.zeros(shape=shape, dtype=np.uint16) lib.XC_SetIntegrationTime(xcamera_id, exposure_time) lib.XC_SetFan(xcamera_id, True) lib.XC_StartCapture(xcamera_id) times = [] for i in range(150): time.sleep( exposure_time/1000000. ) # 1./25 if readlock.acquire() : start_time = time.time() (lib.XC_CopyFrameBuffer(xcamera_id, fbuffer, frame_size) ) #time.sleep(0.0002) now = time.time() readlock.release() print "Frame {nr}/ttime: {time}".format(time=now-start_time, nr=i) times.append(now-start_time) else: time.sleep(0.001) print "Try again" #This gets never printed, so readlock works fine. ###### CLOSING ########### lib.XC_StopCapture(xcamera_id) lib.XC_SetFan(xcamera_id, False) lib.XC_CloseCamera(xcamera_id) ###### Plot ########### import matplotlib.pyplot as plt fig = plt.figure() ax = fig.add_subplot(121) ax.imshow(fbuffer, interpolation="none") ax2 = fig.add_subplot(122) ax2.plot(times) plt.savefig("testing/readout.png") plt.show()

Una salida típica se ve así, donde el tiempo es el tiempo que se tarda en copiar el búfer de cuadros.

Frame 72 time: 0.0 Frame 73 time: 0.0 Frame 74 time: 0.000999927520752 Frame 75 time: 0.512000083923 Frame 76 time: 0.000999927520752 Frame 77 time: 0.0 Frame 78 time: 0.516000032425 Frame 79 time: 0.0 Frame 80 time: 0.000999927520752 Frame 81 time: 0.516000032425 Frame 82 time: 0.0 Frame 83 time: 0.0 Frame 84 time: 0.514000177383 Frame 85 time: 0.0 Frame 86 time: 0.0759999752045

Y el gráfico trazado es este (donde la imagen se ve exactamente como se supone). El gráfico de la derecha muestra el tiempo en segundos para cada fotograma

Comentarios:

  • Estoy trabajando en Python 2.7
  • Jugué con algunos de los parámetros:
    • Agregar time.sleep(x) con diferentes valores para x en diferentes posiciones en el ciclo tiene el efecto de aumentar el número de cuadros que toman 0.5s para leer. Cuanto más largo sea el tiempo de sueño, más largo será el marco.
    • Ommiting time.sleep( exposure_time/1000000. ) Proporciona marcos vacíos.
    • El problema es independiente de si uso readlock.acquire() o no.
    • El problema es independiente del número de bucles.