Comments (13)
Hello
Currently I am changing the approach of using tisgrabber, so I changed the tisgrabber.py.
Please download the file(s)
- tisgrabber.zip
from https://we.tl/t-n30hFUvEHj
The link is valid for 7 days.
Please see samples/40-qt-stereo.py, maybe it is, what you are asking for.
It shows, how to
- use qt5 widgets to display live video in
- select and configure two devices
- use callbacks and create an opencv mat
- restore devices from saved xml files
- capture images from both cameras and save them to hard disc.
There are more samples. The main trick is making the image part of the user data struct used in the callback.
The images are handled in the main loop in this case in the GUI thread.
I hope this helps.
from ic-imaging-control-samples.
Thank you for the updated library. It was helpful, however I wonder what happen to Camera.open("camera unique name") function. Its really helpful to be able to open cameras using unique names in multi-camera setup.
I also noticed that the global variables are used in the stereo camera example. It would be really hard to manage such when using signal_slot mechanism in pyqt5 or pyqt6. For example, if the multiple cameras are used with hardware trigger and each camera worker has to run in background and send the acquired image through signal/slot to the main UI.
wouldn't it be much better to simply assign the camera to a class like this and send the acquired image to the main UI? however following example is not working, I suspect it has to ctypes but I cannot debug this since, I would not know how the data is handled in DLL.
'''background worker class for camera data acquisition'''
from PyQt5 import QtCore
from PyQt5.QtCore import QThread, pyqtSignal, QTimer, QObject
import numpy as np
import sys, os
# for tis grabber library
import ctypes
import tisgrabber as tis
# initialize the module (dll)
# get the current directory absolute path
abs_path_to_directory = os.path.abspath(os.path.dirname(__file__))
# add the directory to the os PATH
os.environ['PATH'] = abs_path_to_directory + os.pathsep + os.environ['PATH']
ic = ctypes.cdll.LoadLibrary("tisgrabber_x64.dll")
tis.declareFunctions(ic)
ic.IC_InitLibrary(0)
class WorkerSignals(QObject):
'''
Defines the signals available from a running worker thread.
Supported signals are:
finished
No data
error
`tuple` (exctype, value, traceback.format_exc() )
result
`object` data returned from processing, anything
progress
`int` to show the progress
info
`str` to show the messages or update about the procress
'''
finished = pyqtSignal()
error = pyqtSignal(tuple)
result = pyqtSignal(object)
progress = pyqtSignal(int)
info = pyqtSignal(str)
alive = pyqtSignal(bool)
connected = pyqtSignal(str)
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):
""" Example for user data passed to the callback function. """
def __init__(self):
self.width = 0
self.height = 0
self.BytesPerPixel = 0
self.buffer_size = 0
self.oldbrightness = 0
self.getNextImage = 0
self.cvMat = None
def Callback(hGrabber, pBuffer, framenumber, pData):
"""
: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("State reset")
if pData.buffer_size > 0:
print("data available")
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
class CameraWorker(QThread):
signals = WorkerSignals()
def __init__(self,parent=None,camera_id='tis',hardware_trigger = False, exposure = 1/30, fps = 30.0, video_format ="Y800 (1280x720)", gain= 1):
super(CameraWorker, self).__init__(parent)
self.ok = False
self.data = None
self.threadactive = True
self.camera_id = camera_id
self.click_image= False
self.name = 'Camera{}'.format(camera_id)
print('from thread',self.camera_id)
# initialize the camera as the worker class attribute
self.Camera = ic.IC_CreateGrabber()
ic.IC_OpenVideoCaptureDevice(self.Camera, camera_id.encode("utf-8")) # it opens the camera with camera model but the unique id (with serial number would have been more stable for multi-camera setup)
if ic.IC_IsDevValid(self.Camera) == 1:
# the code passes the DevValid check point no problem until here.
print('camera is valid')
ic.IC_SetFrameRate(self.Camera, ctypes.c_float(fps))
ic.IC_SetVideoFormat(self.Camera, video_format.encode("utf-8"))
ic.IC_SetPropertySwitch(self.Camera, "Exposure".encode("utf-8"), "Auto".encode("utf-8"), 0)
ic.IC_SetPropertyAbsoluteValue(self.Camera, "Exposure".encode("utf-8"), "Value".encode("utf-8"), ctypes.c_float(exposure))
ic.IC_SetPropertySwitch(self.Camera, "Gain".encode("utf-8"), "Auto".encode("utf-8"), 0)
ic.IC_SetPropertyAbsoluteValue(self.Camera, "Gain".encode("utf-8"), "Value".encode("utf-8"), ctypes.c_float(gain))
if hardware_trigger:
ic.IC_SetPropertySwitch(self.Camera, "Trigger".encode("utf-8"), "Enable".encode("utf-8"), 1) #switch to harware trigger mode
else:
ic.IC_SetPropertySwitch(self.Camera, "Trigger".encode("utf-8"), "Enable".encode("utf-8"), 0)
# passes the set properties
ic.IC_SetContinuousMode(self.Camera, 1)
ic.IC_StartLive( self.Camera, 0)
# camera starts
# ########
# this part passes as well
Callbackfunc = ic.FRAMEREADYCALLBACK(Callback)
self.CameraState = CallbackUserdata()
ic.IC_SetFrameReadyCallback(self.Camera, Callbackfunc, self.CameraState)
CreateUserData(self.CameraState, self.Camera)
@QtCore.pyqtSlot()
def run(self):
"""start the camera acquisition"""
i = 0
fps = []
self.CameraState.getNextImage = 1
while self.threadactive:
if self.CameraState.getNextImage == 0:
# ########
# this should return (emit) the image data to the main UI but nothing happens.
print('State Changed')
image = self.CameraState.cvMat
self.data = {"id":self.camera_id, "image":image}
self.signals.result.emit(self.data)
self.CameraState.getNextImage = 1
QThread.msleep(1)
ic.IC_StopLive(self.Camera)
ic.IC_ReleaseGrabber(self.Camera)
self.signals.finished.emit()
# self.stop()
self.signals.alive.emit(False)
def stop(self):
self.threadactive = False
QThread.msleep(100)
self.quit()
self.wait()
self.exit()
from ic-imaging-control-samples.
The function is IC_OpenDevByUniqueName() please see the updated tisgrabber.chm in the tisgrabber directory of this reposity. I uploaded it yesterday.
wouldn't it be much better to simply assign the camera to a class like this and send the acquired image to the main UI?
Yes. The tisgrabber.dll C wrapper is not object oriented, therefore, I did not implement, rather removed, a camera class. The problem is, if I change the tisgrabber.dll, I also have to change the Python classes. This is a little bit too much work overload for me.
But you can implement that class.
example is not working
What does that mean in detail? Which error message do you receive?
I am sure, my stereo sample works, so you may missed something?
Stefan
from ic-imaging-control-samples.
example is not working
I am working on a multi-camera setup (4 cameras DFM 42BUC03-ML). I am trying to run the four CameraWorker class in the background thread and get the data out of the pyqt5 signal/slot method (see the "def run" method). Because the default structure of I have to assign the camera and CallbackUserdata to an object in CameraWorker class like following:
self.CameraState = CallbackUserdata()
ic.IC_SetFrameReadyCallback(self.Camera, Callbackfunc, self.CameraState)
CreateUserData(self.CameraState, self.Camera)
After calling the worker class in main thread, the init method works alright without any errors. but calling the "run" method doesn't return the cvMat object as it should in the example. The stereo example works, I believe because global variables are used and no "class" is used. In a more specialized setting, I want to be able to control each camera and their functions individually and not rely on global variables. In my code, no errors occur but also camera state never updates :
@QtCore.pyqtSlot()
def run(self):
"""start the camera acquisition"""
i = 0
fps = []
self.CameraState.getNextImage = 1
while self.threadactive:
i+=1
print("while loop count {}".format(i)) # this counter works and update the value of i
if self.CameraState.getNextImage == 0:
# the self.CameraState.getNextImage does not update and stays same
# CallBackUserData assigned to the callback never updates and doesn't shows any errors.
#
# this should return (emit) the image data to the main UI but nothing happens.
print('State Changed')
image = self.CameraState.cvMat
self.data = {"id":self.camera_id, "image":image}
self.signals.result.emit(self.data)
self.CameraState.getNextImage = 1
QThread.msleep(1)
from ic-imaging-control-samples.
The stereo sample works, because the grabber objects are in the same thread and there is only one thread used.
In an own multithready application with many cameras I called Initlibrary() in the thread of a camera. It looks like this:
def camerathread( data ):
'''
For each camera a thread is created. The in data.configfile saved camera.xml file
is used to open the camera.
The main loop runs for ever.
If a device lost happens, the main loop tries to reopen the camera.
Image saving to the "files" subdir is done here on demand.
The snapped image data is compressed to jpeg in memory and saved in the passed "data.lockedimage" (limg) object.
'''
global ic
ic.IC_InitLibrary(0)
while data.running == True :
hGrabber = ic.IC_CreateGrabber()
ic.IC_LoadDeviceStateFromFile(hGrabber,tis.T(data.configfile))
if( ic.IC_IsDevValid(hGrabber)):
# Add callbacks for framerate (not used) and device lost
cb = ic.FRAMEREADYCALLBACK(Callback)
devlostfunc = ic.DEVICELOSTCALLBACK(deviceLostCallback)
ic.IC_SetCallbacks(hGrabber,cb,None,devlostfunc, data )
ic.IC_StartLive(hGrabber,0)
(Ignore parts of the comment, the code is from a webserver showing many cameras in a browser)
( I am not sure, whether that "gobal ic" is necessary. I am not a Python expert, I am a beginner rather)
This works fine so far.
I wonder, why you run the cameras in own threads. Each grabber (camera) uses its own thread, which calls the callback functions within that thread. Thus it is not necessary to create a new thread for the camera thread that already exists.
Within the callback you can call self.signals.result.emit(self.data). I made that at https://github.com/TheImagingSource/IC-Imaging-Control-Samples/blob/master/Python/Python%20NET/qt5-simple.py
You could create a list containing the grabber objects and additional data in your main thread, open and configure the cameras, set the callback function (which is implemented once only and used by all cameras) and send the signal with the additional data and the cv::Mat (which is part of the additional data). You do not need an extra loop for snapping images in this case. Shall we proceed like this? Please let me know. If yes, I create a sample for you based on your code above.
If you want to stay with your worker threas, I would like to suggest, that I rewrite your code to changed tisgrabber.py
I will use the IC_OpenDevByUniqueName() function instead of IC_LoadDeviceStateFromFile(), So you do not need to create a device state file (IC_SaveDeviceStatetoFile() ).
Please let me know, how to proceed.
Stefan
from ic-imaging-control-samples.
Within the callback you can call self.signals.result.emit(self.data).
Yes this what I am trying to figure out how to do that.
You could create a list containing the grabber objects and additional data in your main thread, open and configure the cameras, set the callback function (which is implemented once only and used by all cameras) and send the signal with the additional data and the cv::Mat (which is part of the additional data). You do not need an extra loop for snapping images in this case. Shall we proceed like this? Please let me know. If yes, I create a sample for you based on your code above.
I think this method will be ok as long as the pyqtsignal can be emitted from callback and received in the main UI thread. And I can be able to differentiate which camera the images belong to (probably using unique names?). Thank you very much for your help.
from ic-imaging-control-samples.
Hello
Well, I will start now. Base will be the stereo sample (I like copy&paste).
can be able to differentiate which camera the images belong to (probably using unique names?). T
That can all be written in the user data. I would not suggest to use different signals for each camera, because without that, you can use as many cameras as you want without changing the code.
Please give me some time. (I will handle my easier support cases first, so I am more concentrated.)
Stefan
from ic-imaging-control-samples.
Hello
Here we go: https://github.com/TheImagingSource/IC-Imaging-Control-Samples/tree/master/Python/tisgrabber/samples
it is 41-qt-triggering.py. The name suggests, there is something with trigger, but I did not implement enabling of trigger (you can do in the properties....)
The sample does:
- count the number of connected cameras
- create for each camera a select and properties menu entry
- create a video window and a brightness progress bar
- implements a camera class for
- select device
- device properties
- start, stop
- save device state to file
- restore from file
- create callback data
- set callback
- create user data
- still missing: device lost event handler
- callback class
- create the cv mat
- send a signal to main ui thread on new image
- image processing in main UI thread, show result in progressbar
- restores last used devices at program start.
I suppose, the sample does, what you want to do.
There is a warning regarding IC_SetHwnd and internal conversion from void* to int. Thus, the sample works with Python 3.8, but not with 3.10. I think, the second parameter must be casted implicitly to int.
I must admit, that signaling was tricky. I think the namespace thing was necessary.
Stefan
from ic-imaging-control-samples.
Thank you for the sample. I tried to the run the sample, however, I am getting an error from the frameReadyCallback.
def frameReadyCallback(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
and with cv.mean() the average brightness of the image is
measuered.
: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
"""
# print("camera {}". format(pData.index))
Width = ctypes.c_long()
Height = ctypes.c_long()
BitsPerPixel = ctypes.c_int()
colorformat = ctypes.c_int()
# Query the image description values
print(hGrabber) # --> This returns an integer value
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))
pData.cvMat = np.ndarray(buffer=image.contents,
dtype=np.uint8,
shape=(Height.value,
Width.value,
bpp))
pData.signals.newimage.emit(pData)
The error output is as follow:
Traceback (most recent call last):
File "_ctypes/callbacks.c", line 232, in 'calling callback function'
File "///", line 54, in frameReadyCallback
ic.IC_GetImageDescription(hGrabber, Width, Height, BitsPerPixel, colorformat)
ctypes.ArgumentError: argument 1: <class 'TypeError'>: expected LP_HGRABBER instance instead of int
from ic-imaging-control-samples.
Hello
You are right. Its in line 95 of tisgrabber.py. It must be
ic.FRAMEREADYCALLBACK = ctypes.CFUNCTYPE(ctypes.c_void_p, ctypes.POINTER(HGRABBER), ctypes.POINTER( ctypes.c_ubyte), ctypes.c_ulong, ctypes.py_object )
I forgot to push this file. (There are some more differences, since it also support device lost events.)
The IC_SetHWnd() argtypes issue for Python 3.10 is not solved yet.
The tisgrabber.py is updated in the repository now.
Stefan
from ic-imaging-control-samples.
thank you very much, its working without any errors.
from ic-imaging-control-samples.
And does the sample do, what you have in mind?
from ic-imaging-control-samples.
And does the sample do, what you have in mind?
Not out of the box but I have modified it to work with my user inference. And new callback function returns the images in both continuous and hardware trigger mode. Which helps a lot. Thank you very much for the support.
from ic-imaging-control-samples.
Related Issues (20)
- Framesnapsink/Framequesink HOT 3
- Noise Reduction HOT 18
- Taking images after external trigger for a fixed time HOT 13
- How to operate multiple cameras at the same time get image through python HOT 2
- Knowing when callback fails HOT 8
- Detailed trigger mode setting HOT 2
- Capture AVI program is not working HOT 12
- Activate/deactivate external trigger using python script HOT 4
- Exception: A set operation on a VCDProperty object failed HOT 10
- IC_StartLive fail HOT 3
- Cannot get Map Strings for Strobe Mode in python HOT 18
- Get crashed on create MediaStreamSink object in C# in .net core 7 HOT 2
- Web browser stream HOT 5
- Getting the exposure time HOT 3
- IC Imaging source upgrade to 3.5 from 3.4 HOT 1
- Ic imaging control 3.5 HOT 3
- tisfrabber LoadDeviceStateFromFile() and SnapImage HOT 1
- Python (tisgrabber): interaction with camera livestream IC_StartLive HOT 5
- Wondering if it's possible to make the video window scrollable in python HOT 1
- Exposure Mode to Trigger Width with Device Trigger enabled using 3.4 SDK HOT 8
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from ic-imaging-control-samples.