Closed zeesyk closed 1 year ago
I suggest to create an array of 10 numpy objects and pass that to the callback. In the callback you convert the incoming buffer into the array elements and after you got 10 images, you can save the image safely to harddisc using OpenCV. How well this works depends on the camera's frame rate and the speed of your computer. The speed of your hard disc does not matter using that approach. You may look at https://github.com/TheImagingSource/IC-Imaging-Control-Samples/blob/master/Python/tisgrabber/samples/32-trigger-callback.py for a rudimentary example.
Stefan
Thank you for your quick response, Stefan! Instead, I did a for loop rather than creating an array. Maybe your suggestion might be better because when I run the program, nothing gets captured nor saved. Also, do you prefer that I don't use the "burst" function? I'm referring to ic.IC_SetPropertyValue(hGrabber,tis.T("Trigger"),tis.T("Burst Count"), 10). I'm not entirely sure how I would implement this either if I'm simply setting the camera to take 10 images rather actually telling it to take this burst.
-Ale
This is my current code, it runs with no errors but it still does not save any images.
"""
External trigger code
"""
import ctypes
import tisgrabber as tis
import numpy as np
import cv2
import os
ic = ctypes.cdll.LoadLibrary("./tisgrabber_x64.dll")
tis.declareFunctions(ic)
ic.IC_InitLibrary(0)
hGrabber = ic.IC_CreateGrabber()
def CreateUserData(ud, camera):
''' Create the user data for callback for the passed camera
:param ud User data to create
:param camera The camera connected to the user data
'''
ud.width = ctypes.c_long()
ud.height = ctypes.c_long()
iBitsPerPixel = ctypes.c_int()
colorformat = ctypes.c_int()
# Query the values
ic.IC_GetImageDescription( camera, ud.width, ud.height,
iBitsPerPixel, colorformat )
ud.BytesPerPixel = int( iBitsPerPixel.value / 8.0 )
ud.buffer_size = ud.width.value * ud.height.value * ud.BytesPerPixel
ud.getNextImage = 0
class CallbackUserdata(ctypes.Structure):
def __init__(self,):
self.width = 0
self.height = 0
self.BytesPerPixel = 0
self.buffer_size = 0
self.getNextImage = 0
self.image = None
def frameReadyCallback(hGrabber,pBuffer, framenumber, pData):
"""
Parameters
----------
hGrabber : This is the real pointer to the grabber object
pBuffer : Pointer to the first pixel's first byte
framenumber : Number of frames since stream started.
pData : Pointer to additional user data structure.
"""
imgs_array = np.zeros((pData.height.value,pData.width.value))
for i in range(10): #take 10 images
if pData.buffer_size > 0:
print("data available")
imagePtr = ic.Ic_GetImagePtr(hGrabber)
imagedata = ctypes.cast(imagePtr, ctypes.POINTER(ctypes.c_ubyte *
pData.buffer_size))
#Image data gets converted into CV Matrix
pData.cvMat = np.ndarray(buffer = imagedata.contents,
dtype = np.uint8,
shape = (pData.height.value,
pData.width.value,
pData.BytersPerPixel)) #image
image = pData.cvMat
imgs_array += image
#imgs_array.append(image)
else:
print("Acquisition failed")
cv2.imwrite("./image{:04}",imgs_array)
##########################################################################
frameReadyCallbackfunc = ic.FRAMEREADYCALLBACK(frameReadyCallback)
userdata = CallbackUserdata() #create instance of CallbackUserdata class
#############################################################################
hGrabber = ic.IC_CreateGrabber()
frame = ic.IC_OpenVideoCaptureDevice(hGrabber,
tis.T("DMK 33UX287"))#manually opening camera
index = 0
if ic.IC_IsDevValid(hGrabber):
##Camera Properties
ic.IC_SetFrameReadyCallback(hGrabber, frameReadyCallbackfunc, userdata)
ic.IC_SetVideoFormat(hGrabber, tis.T("RGB24 (640x480)")) # resolution
ic.IC_SetFrameRate(hGrabber, ctypes.c_float(600.0)) #framerate
ic.IC_SetPropertyValue(hGrabber,tis.T("Exposure"), tis.T("Value"),
ctypes.c_float(1/600))
#Exposure time t =1/1000 = 0.001 seconds or 1ms
ic.IC_SetPropertySwitch(hGrabber, tis.T("Trigger"),
tis.T("Enable"), 1) # hardware trigger: 1
ic.IC_SetContinuousMode(hGrabber, 0)
ic.IC_StartLive(hGrabber, 0)#0starts stream but does not show live video, 1 shows live video
ic.IC_SetPropertySwitch(hGrabber, tis.T("Trigger"),
tis.T("Line1"),1)
ic.IC_StopLive(hGrabber)
else:
print("Could not activate hardware trigger")
ic.IC_ReleaseGrabber(hGrabber)
pData cvMat is not declared. It should be declared in the CallbackUserdata:
class CallbackUserdata(ctypes.Structure):
def __init__(self,):
self.width = 0
self.height = 0
self.BytesPerPixel = 0
self.buffer_size = 0
self.getNextImage = 0
self.image = None
self.cvMat = None
However, I am working on a sample for you, but I was extremely interrupted today. I am sorry.
Stefan
Hello
this is my first approach:
'''
This sample demonstrates how to create a callback, which is automatically
called for each incoming frame.
'''
import ctypes
import time
import numpy as np
import cv2 as cv2
import tisgrabber as tis
ic = ctypes.cdll.LoadLibrary("./tisgrabber_x64.dll")
tis.declareFunctions(ic)
class CallbackUserdata(ctypes.Structure):
""" Example for user data passed to the callback function. """
def __init__(self):
self.count = 0
self.camera = None # Reference to a camera/grabber object
self.images = []
def FrameCallback(hGrabber, pBuffer, framenumber, pData):
""" This is an example callback function
The image is saved in test.jpg and the pData.Value1 is
incremented by one.
:param: hGrabber: This is the real pointer to the grabber object. Do not use.
:param: pBuffer : Pointer to the first pixel's first byte
:param: framenumber : Number of the frame since the stream started
:param: pData : Pointer to additional user data structure
"""
Width = ctypes.c_long()
Height = ctypes.c_long()
BitsPerPixel = ctypes.c_int()
colorformat = ctypes.c_int()
# Query the image description values
ic.IC_GetImageDescription(hGrabber, Width, Height, BitsPerPixel,
colorformat)
# Calculate the buffer size
bpp = int(BitsPerPixel.value/8.0)
buffer_size = Width.value * Height.value * bpp
if buffer_size > 0:
image = ctypes.cast(pBuffer,
ctypes.POINTER(
ctypes.c_ubyte * buffer_size))
cvMat = np.ndarray(buffer=image.contents,
dtype=np.uint8,
shape=(Height.value,
Width.value,
bpp))
pData.images.append(cvMat)
pData.count = pData.count + 1
print("Callback called: ", pData.count)
Userdata = CallbackUserdata()
# Create the function pointer.
Callbackfuncptr = ic.FRAMEREADYCALLBACK(FrameCallback)
ic.IC_InitLibrary(0)
hGrabber = ic.IC_CreateGrabber()
ic.IC_OpenVideoCaptureDevice(hGrabber, tis.T("DMK 33GR0521"))#enter name of camera
if(ic.IC_IsDevValid(hGrabber)):
ic.IC_SetFrameReadyCallback(hGrabber, Callbackfuncptr, Userdata)
ic.IC_SetContinuousMode(hGrabber, 0)
ic.IC_SetVideoFormat(hGrabber, tis.T("RGB24 (640x480)")) # resolution
ic.IC_SetFrameRate(hGrabber, ctypes.c_float(50.0)) #framerate
ic.IC_SetPropertyValue(hGrabber,tis.T("Exposure"), tis.T("Value"),
ctypes.c_float(1/400))
ic.IC_SetPropertySwitch(hGrabber, tis.T("Trigger"),
tis.T("Enable"), 1)
ic.IC_SetPropertyValue(hGrabber,tis.T("Trigger"),
tis.T("Burst Count"), 10)
ic.IC_StartLive(hGrabber, 1)
while Userdata.count < 10:
time.sleep(0.1)
ic.IC_StopLive(hGrabber)
i = 0
for image in Userdata.images:
i += 1
filename= "img{}.jpg".format(i)
cv2.imwrite(filename, image)
else:
ic.IC_MsgBox(tis.T("No device opened"), tis.T("Callback"))
ic.IC_ReleaseGrabber(hGrabber)
But this far to slow. I can use a maximum frame rate of ~50 fps in order to handle all images. So no go for you. Give me a little time, I have a second approach, but I must test it,
Stefan
pData cvMat is not declared. It should be declared in the CallbackUserdata:
class CallbackUserdata(ctypes.Structure): def __init__(self,): self.width = 0 self.height = 0 self.BytesPerPixel = 0 self.buffer_size = 0 self.getNextImage = 0 self.image = None self.cvMat = None
However, I am working on a sample for you, but I was extremely interrupted today. I am sorry.
Stefan
No problem, I appreciate you taking the time to help me!
Here we go. This version works with higher frame rates, it should even work with your 500 fps. I tried 130, because the camera i used is too slow.
triggerburstcount2.zip
The sample contains a different tisgrabber.dll. This DLL implements membuffer handling
In the line
ic.IC_SetRingBufferSize(hGrabber,50)
you define, how many buffers shall be allocated internally. Allocate only so much buffers, your computer has free RAM for, otherwise Windows starts swapping. In your case, even 12 buffers will be fine, because you want to capture only 10 images per trigger.
The trick is saving the pointers to the memory buffers only and do the numpy conversion stuff later.
The code is like the IC-Express code from https://www.theimagingsource.com/support/downloads-for-windows/end-user-software/icexpress/
Stefan
Thank you your input. I ran your code and I set the buffer size to 12 like you had suggested; however, no images were saved. I have my camera connected to a function generator as my trigger source so most likely my issue is related to that.
Did you saw the live video window during capture? (I suppose, I passed a "1" to ic.IC_StartLive(hGrabber, 1)
Stefan
Yes! But the live window stopped responding almost immediately after running the program.
This is expected, because the camera waits for the trigger. After you triggered the camera, you will see it running shortly and then the images are to be saved.
Stefan
Thank you for your help. I was able to get it working today.
I'm writing code to activate hardware trigger for my camera. Ideally, for every trigger pulse, I want the camera to take 10 images and save them into a folder. I've looked at the posted examples however I'm still struggling to implement this portion. I'm rather new to python. I know that using ic.IC_SnapImage() isn't effective for triggered images, so I want to use the bursts function ic.IC_SetPropertyValue(hGrabber,tis.T("Trigger"),tis.T("Burst Count"), 10).