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:
- ¿Alguien ha experimentado problemas similares con las cámaras?
- ¿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?
- ¿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.
- Agregar