Using Images¶
After device handling the next step is getting images. This chapter shows how to query images from the camera and how to process them by OpenCV.
Snap and Save an Image from LiveStream¶
Code : 10-save-image.py
This sample shows how to snap an image from the live stream of a video capture device and save it as JPEG image. It also uses the above mentioned tisgrabber.py. That reduces the code of the sample. It is started by
import ctypes
import tisgrabber as tis
ic = ctypes.cdll.LoadLibrary("./tisgrabber_x64.dll")
tis.declareFunctions(ic)
The program is ready to start now. A video capture device is opened and checked whether it is valid by
hGrabber = tis.openDevice(ic)
if( ic.IC_IsDevValid(hGrabber)):
The stream is started an a little menu is shown:
ic.IC_StartLive(hGrabber,1)
key = ""
while key != "q":
print("s: Save an image")
print("q: End program")
key = input('Enter your choice:')
Snapping and saving a frame is done with two functions:
ic.IC_SnapImage(hGrabber, 2000)
This waits for an image with a 2 seconds timeout and returns an error, if no frame was received. If a frame was received, it can be saved by
ic.IC_SaveImage(hGrabber, tis.T("test.jpg"),
tis.ImageFileTypes['JPEG'], 90)
The parameter tis.ImageFileTypes['JPEG']
indicates, that the image is to be saved as JPEG image. The other possibility is Bitmap: tis.ImageFileTypes['BMP']
.
The last parameter, here “90” sets the JPEG quality. It is ignored, if the image is saved as bitmap.
It is used in the menu loop as follows:
ic.IC_InitLibrary(0)
hGrabber = tis.openDevice(ic)
if( ic.IC_IsDevValid(hGrabber)):
ic.IC_StartLive(hGrabber,1)
key = ""
while key != "q":
print("s: Save an image")
print("q: End program")
key = input('Enter your choice:')
if key == "s":
if ic.IC_SnapImage(hGrabber, 2000) == tis.IC_SUCCESS:
ic.IC_SaveImage(hGrabber, tis.T("test.jpg"),
tis.ImageFileTypes['JPEG'] , 90)
print("Image saved.")
else:
print("No frame received in 2 seconds.")
ic.IC_StopLive(hGrabber)
else:
ic.IC_MsgBox(tis,T("No device opened"), tis.T("Simple Live Video"))
ic.IC_ReleaseGrabber(hGrabber)
The function IC_SnapImage()
is not suitable, if the camera is in trigger mode, because
frames can be missed. This function also blocks the program execution while no frame is delivered.
Please use a frame delivery callback function for triggered cameras . How to do this is explained later.
Snapping an Image and Process it with OpenCV¶
Code : 11-image-processing
This sample shows, how to
get an image descriptions of width, height and bits per pixel
get a pointer to the image data and convert it to Python use
create a
numpy
array for OpenCV image processing
OpenCV uses numpy
, therefore numpy
must be installed and imported.
The related functions in the tisgrabber DLL are
IC_GetImageDescription
IC_GetImagePtr
The function IC_GetImageDescription
receives pointers to variables, therefore, they must
be declared first:
Width = ctypes.c_long()
Height = ctypes.c_long()
BitsPerPixel = ctypes.c_int()
colorformat = ctypes.c_int()
BitsPerPixel
is 8 on Y800, 24 on RGB24 and 32 on RGB32 formats. The default format in
memory is RGB24. The format used by camera will be converted automatically to RGB24.
Next step is querying these values and calculate the buffer size of an image:
# Query values of image description
ic.IC_GetImageDescription(hGrabber, Width, Height,
BitsPerPixel, colorformat)
# Calculate the buffer size
bpp = int(BitsPerPixel.value / 8.0)
buffer_size = Width.value * Height.value * BitsPerPixel.value
Then the pointer to the image data is queried and casted into something Python can handle:
# Get the image data
imagePtr = ic.IC_GetImagePtr( hGrabber )
imagedata = ctypes.cast(imagePtr,
ctypes.POINTER(ctypes.c_ubyte *
buffer_size))
With imagedata
the numpy
array is created:
# Create the numpy array
image = np.ndarray(buffer=imagedata.contents,
dtype=np.uint8,
shape=(Height.value,
Width.value,
bpp))
The image
can be used by OpenCV functions now:
# Apply some OpenCV functions on the image
image = cv2.flip(image, 0)
image = cv2.erode(image, np.ones((11, 11)))
cv2.imshow('Window', image)
cv2.waitKey(10)
The complete sample code is:
import ctypes
import tisgrabber as tis
import cv2
import numpy as np
ic = ctypes.cdll.LoadLibrary("./tisgrabber_x64.dll")
tis.declareFunctions(ic)
ic.IC_InitLibrary(0)
hGrabber = tis.openDevice(ic)
if(ic.IC_IsDevValid(hGrabber)):
ic.IC_StartLive(hGrabber, 1)
key = ""
while key != "q":
print("p: Process an image")
print("q: End program")
key = input('Enter your choice:')
if key == "p":
if ic.IC_SnapImage(hGrabber, 2000) == tis.IC_SUCCESS:
# Declare variables of image description
Width = ctypes.c_long()
Height = ctypes.c_long()
BitsPerPixel = ctypes.c_int()
colorformat = ctypes.c_int()
# Query the values of image description
ic.IC_GetImageDescription(hGrabber, Width, Height,
BitsPerPixel, colorformat)
# Calculate the buffer size
bpp = int(BitsPerPixel.value / 8.0)
buffer_size = Width.value * Height.value * BitsPerPixel.value
# Get the image data
imagePtr = ic.IC_GetImagePtr(hGrabber)
imagedata = ctypes.cast(imagePtr,
ctypes.POINTER(ctypes.c_ubyte *
buffer_size))
# Create the numpy array
image = np.ndarray(buffer=imagedata.contents,
dtype=np.uint8,
shape=(Height.value,
Width.value,
bpp))
# Apply some OpenCV functions on the image
image = cv2.flip(image, 0)
image = cv2.erode(image, np.ones((11, 11)))
cv2.imshow('Window', image)
cv2.waitKey(10)
else:
print("No frame received in 2 seconds.")
ic.IC_StopLive(hGrabber)
cv2.destroyWindow('Window')
else:
ic.IC_MsgBox(tis.T("No device opened"), tis.T("Simple Live Video"))
ic.IC_ReleaseGrabber(hGrabber)
16 bit support is currently not implemented.
Frameready Callback¶
Code: 20-Callback.py
IC Imaging Control and tisgrabber.dll can call a callback function for each incoming frame (image) from a video capture device.
In case a camera is used in trigger mode, the usage of a callback is recommended. The program does not need to poll for new frames.
A callback function runs in the thread of the hGrabber
object, not in the main thread
of the program.
A callback using program needs three things at least:
- callback function
The function to be called.
- callback user data
A simple class with user data to be passed to the callback function.
- callback user function pointer
A pointer to the callback function. Sounds complicate, but is one line of code only.
Callback User data¶
The user data class is forwarded to the callback. In the callback the user data can be used to store information about image processing for the main thread. It can be create as
class CallbackUserdata(ctypes.Structure):
""" Example for user data passed to the callback function. """
def __init__(self):
self.Value1 = 42
self.Value2 = 0
self.camera = None # Reference to a camera/grabber object
Callback Function¶
The callback function has a fixed parameter list and must be implement as follows:
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
"""
print("Callback called", pData.Value1)
pData.Value1 = pData.Value1 + 1
At least the definition must be implemented in this way.
Callback Function Pointer¶
The callback function pointer declaration is in tisgrabber.py and it is used in the main script as:
# Create the function pointer.
Callbackfuncptr = tis.FRAMEREADYCALLBACK(FrameCallback)
Also do not forget to instatiate the user data object:
Userdata = CallbackUserdata()
After the hGrabber
object has been created, the callback variables can be passed
to it:
ic.IC_SetFrameReadyCallback(hGrabber, Callbackfuncptr, Userdata)
ic.IC_SetContinuousMode(hGrabber, 0)
The ic.IC_SetContinuousMode(hGrabber, 0)
advises the hGrabber
object to call the
frame callback function automatically, which means without an ic.IC_SnapImage()
call.
(Yes, I know, I switched “0” and “1”, when writing the tisgrabber.dll in the
ic.IC_SetContinuousMode
function. I am sorry for that.)
The following callback sample shows, how to create a numpy
array for OpenCV:
def LeftCallback(hGrabber, pBuffer, framenumber, pData):
""" This is an example callback function for image processing with
opencv. The image data in pBuffer is converted into a cv Matrix.
:param: hGrabber: This is the real pointer to the grabber object.
: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
"""
if pData.getNextImage == 1:
pData.getNextImage = 2
print(" Left")
if pData.buffer_size > 0:
image = ctypes.cast(pBuffer, ctypes.POINTER(ctypes.c_ubyte * pData.buffer_size))
pData.cvMat = np.ndarray(buffer=image.contents,
dtype=np.uint8,
shape=(pData.height.value,
pData.width.value,
pData.BytesPerPixel))
pData.getNextImage = 0
It is copied from the 40-qt-stereo.py example. The 41-qt-triggering.py sample shows how to signal the main Qt thread from within the callback. The sample shows also, how to use the trigger correctly.
It is recommended to perform image processing in the callback, because each hGrabber
object has its own thread and if many cameras are used, the callback run in own threads.
That saves CPU load.