Error in CVB Logging GUI while Image Acquisition works as aspected

Hi,

I am using the following setup:

Ubuntu 22.04
Python 3.11
CVB 14.00.004

When capturing images via Python, each image generates an error log entry in the CVB Logging GUI. But the image capture works perfectly for my use case. Now I just want to check if I can optimise my workflow to get rid of the error before shipping the code to the customers.

One example error:

{
	"GroupName": "",
	"LogLevel": "fatal",
	"LogLevel [Hex]": "0x01",
	"LogMessage": "cto load shared library: /opt/cvb/drivers/genicam/..: cannot read file data: Is a directory",
	"LogPos": "/home/siderepos/jenkins/workspace/CVFactory.dll_release_cvb14.0/ubu2004-x86_64/src/../include/CVFactory/detail/DynLib.h(54)",
	"Module": "CVFactory.Open",
	"ProcessID": "54302",
	"SenderMsgID": "1",
	"ServiceMsgID": "655",
	"SourceMsgID": "1",
	"ThreadID": "140500728280640",
	"Timestamp": "19671680126151885"
}

ll /opt/cvb/bin returns:

drwxrwxr-x  3 root root    4096 Mai  8 09:48 ./
drwxrwxr-x 13 root root    4096 Mai  8 10:30 ../
lrwxrwxrwx  1 root root      12 Apr 26 08:57 CVMock.vin -> CVMock.vin.1
lrwxrwxrwx  1 root root      16 Apr 26 08:57 CVMock.vin.1 -> CVMock.vin.1.101
lrwxrwxrwx  1 root root      20 Apr 26 08:57 CVMock.vin.1.101 -> CVMock.vin.1.101.264
-rw-r--r--  1 root root 1614968 Apr 26 08:57 CVMock.vin.1.101.264
drwxrwxr-x  2 root root    4096 Mai  8 09:48 genicam/
lrwxrwxrwx  1 root root      13 Apr 26 08:57 GenICam.vin -> GenICam.vin.3
lrwxrwxrwx  1 root root      17 Apr 26 08:57 GenICam.vin.3 -> GenICam.vin.3.232
lrwxrwxrwx  1 root root      22 Apr 26 08:57 GenICam.vin.3.232 -> GenICam.vin.3.232.1687
-rw-r--r--  1 root root 6818272 Apr 26 08:57 GenICam.vin.3.232.1687

My python code:

camera_path = str(Path(cvb.install_path()) / "drivers" / "GenICam.vin")
with cvb.DeviceFactory.open(camera_path, port=0) as device:
	stream = device.stream()
	stream.start()
	image, status = stream.wait()
	with image:
		taken_image = cvb.as_array(image, copy=True)
	stream.abort()
        return taken_image

Is there any obvious error or fix?

You were talking about “each” image.
However I cannot see you doing anything in a loop here.

If we assume your snippet to be a method that is run in a loop and returns the image, it is usually not best practice to load the driver each time you want to acquire a new image.
It would be better to keep the stream as a member and start it once and have it running all the time.

You could also go for get_snapshot() rather than wait() as this basically starts the stream, gets an image and then stops the stream again without killing it.

Let me know if this changes your error output.

Cheers
Chris

@Chris is totally right, although I would add:
The “open” implicitely searches up the common TL path for existing TLIs. On Linux, this is implemented somehow like glob does path lookup. The resulting pathlist contains .(current directory), .. (parent directory), and those two elements are also at least tried to be loaded as tl - without success. That is the message is all about. You can ignore it. I’ll look whether we can pipe in a fix for that in the near future.

1 Like

The application is more or less embedded, as this can then run for days or even weeks, I wanted to be sure to always create a new camera instance instead of leaving the driver/resource open all the time. But I will give it a try. Thanks for the input.

I’ll try that with get_snapshot() too.

Good to know, thanks.

I tried to refactor it according to your input. But in the loop it gets only one image and then it stales.

camara.py

class Camera:
    def __init__(self):
        self.camera_path: str = str(Path(cvb.install_path()) / "drivers" / "GenICam.vin")
        self.device = cvb.DeviceFactory.open(self.camera_path, port=0)
        self.stream = self.device.stream()

    def take_image(self) -> np.array:
        try:
			image, status = self.stream.get_snapshot()
			with image:
				taken_image = cvb.as_array(image, copy=True)
				return taken_image
		except Exception as e:
			raise e

where_it_gets_called.py

camera = Camera()
try:
	for i in range(3):
		t0 = time.time()
		np_image = camera.take_image()
		logging.debug(np_image)
		logging.debug(f"Image {i} took: {(time.time()-t0):2f}")
except Exception as e:
	raise e

What do I miss?

Even with the old stream.start() → stream.wait() → stream.abort() I can only get one image with my camera class.

Hi @sebwr ,

2 thoughts on this as your code lgtm.

  1. You have the triggermode activated in your camera (unlikely).
    You could check this by opening the camera in the CVBViewer or any of the CVB examples like the streaming_simple example in the ImageManager package:

%cvb%Tutorial\Image Manager\CVBPy

  1. Your loop runs faster than the exposureTime of the camera.
    I would also expect this to not be the case because get_snapshot internally falls back to wait() as well as far as I am aware. This means the acquisition should wait for the default timeout (8s I think) for a new image.
    You could check the status to see if a timeout occured.

Cheers
Chris

Edit:

In an older post we had a problem with the stream running out of scope more or less:
https://forum.commonvisionblox.com/t/get-snapshot-stops-working-after-first-acquisition/1537/10

Thinking about it, using the Software triggered solution might be the best way to go anyways.

This is currently off.

There is no timeout even after a minute. So I guess it is something internally with Python resource management.

I will try the software trigger approach next.

Yes I would also assume, that it is a problem with the ressource management then as we have already seen this behaviour in the linked thread.
Maybe you start from the simplest approach which would be to hold all variables the main routine and from there break it down to your class design.
Somewhere along the way you will eventually see the above described behaviour again.

Maybe it is as simple as putting the creation of the camera object into the try block… I havent tested this though.

1 Like

The software trigger only works when I use the

with cvb.DeviceFactory.open(camera_path, port=0) as device

everytime I want to get an image inside my loop, but if I try to set the device as a member variable (e.g.: self.device = cvb.DeviceFactory.open(self.camera_path, port=0)) the same behaviour as without the triggered mode occur.

So I don`t understand what the triggered mode improve in my use case.

Yeah this is very likely due to the same ressource management problem.

Stick with your first approach for now (get_snapshot) and let us try getting rid of this problem first.
“Maybe it is as simple as putting the creation of the camera object into the try block… I havent tested this though.”

The software trigger approach is something to keep in mind for the future.

1 Like

Works:

from pathlib import Path
import cvb
import numpy as np


class CameraTest:
    def __init__(self):
        self.camera_path: str = str(Path(cvb.install_path()) / "drivers" / "GenICam.vin")

    def take_image(self):
        with cvb.DeviceFactory.open(self.camera_path, port=0) as device:
            stream = device.stream()
            image, status = stream.get_snapshot()
            print(f"The image status was: {status}")
            with image:
                taken_image = cvb.as_array(image, copy=True)
                print(np.shape(taken_image))


if __name__ == "__main__":
    try:
        camera = CameraTest()
        for i in range(3):
            camera.take_image()
    except Exception as e:
        print(e)

and returns:

The image status was: 0
(2048, 2560, 3)
The image status was: 0
(2048, 2560, 3)
The image status was: 0
(2048, 2560, 3)

But when I try to remove the with block directly breaks. So here I only tried to get an member variable which holds the device, to avoid reloading the driver for each image.

from pathlib import Path
import cvb
import numpy as np


class CameraTest:
    def __init__(self):
        self.camera_path: str = str(Path(cvb.install_path()) / "drivers" / "GenICam.vin")
        self.device = cvb.DeviceFactory.open(self.camera_path, port=0)

    def take_image(self):
        print(self.device)
        print(self.device.connection_state)
        print("Getting stream")
        stream = self.device.stream()
        print("Trying to get snapshot")
        image, status = stream.get_snapshot()
        print(f"The image status was: {status}")
        with image:
            taken_image = cvb.as_array(image, copy=True)
            print(np.shape(taken_image))


if __name__ == "__main__":
    try:
        camera = CameraTest()
        for i in range(3):
            camera.take_image()
    except Exception as e:
        print(e)

This may be a hint:
It hangs on the second get_snapshot and will not recover. And when executing the script again, the first get_snapshot is already hanging.

On the console the error is:

<cvb.VinDevice object at 0x7f1b87b37e40>
1
Getting stream
Trying to get snapshot

Since the image status is non-zero I guess this explains why get_snapshot hangs until eternity.

Solution:
I have to remove and re connect the energy to the camera to get it to do anything again, but then it only gets one picture until it hangs again.

With the creation of the camera inside the try block, have you tried to store the device.stream() as a member as well?
What happens if you do so?

class CameraTest:
    def __init__(self):
        self.camera_path: str = str(Path(cvb.install_path()) / "drivers" / "GenICam.vin")
        print("Init device")
        self.device = cvb.DeviceFactory.open(self.camera_path, port=0)
        print("Getting stream")
        self.stream = self.device.stream()

    def take_image(self):
        print(self.device)
        print(self.device.connection_state)
        print("Getting stream")
        print("Trying to get snapshot")
        image, status = self.stream.get_snapshot()
        print(f"The image status was: {status}")
        with image:
            taken_image = cvb.as_array(image, copy=True)
            print(np.shape(taken_image))
Init device
Getting stream
<cvb.VinDevice object at 0x7f46c083be40>
1
Getting stream
Trying to get snapshot
The image status was: 0
(2048, 2560, 3)
<cvb.VinDevice object at 0x7f46c083be40>
1
Getting stream
Trying to get snapshot

Same behavior…

1 Like

@s-woe @Andreas any ideas on this one?

Hi @sebwr !
From your description I’m pretty sure there is no need for a class, at least not for the device. Why don’t you just create a new device each time you start this (relatively) rare job? (see example below)

If you consider this, I also recommend to use our new stack. Then you would have to discover your device with the cvb.DeviceFactory.discover_from_root() function and filter out the access token to the desired device.

The simple switch to use our new recommended stack is to pass the cvb.AcquisitionStack.GenTL to the cvb.DeviceFactory.open() function.

The whole job would be the following:

import cvb
import numpy as np
from pathlib import Path
import cvb
from typing import Optional
import time

class CameraTest:
    def __init__(self):
        # use any unique substring which distinguishes you device from others inside the access token:
        self.camera_identifier: str = "CVMock"
        all_tokens = cvb.DeviceFactory.discover_from_root(cvb.DiscoverFlags.IgnoreVins)
        filtered_token = [token for token in all_tokens if self.camera_identifier in token.access_token]
        if not len(filtered_token):
            raise RuntimeError("No suitable camera found")
        self.token = filtered_token[0].access_token

    def rarely_take_image(self) -> Optional[np.ndarray]:
        # now open the first one from the filtered list:
        with cvb.DeviceFactory.open(self.token, cvb.AcquisitionStack.GenTL) as device:
            stream = device.stream(cvb.ImageStream)
            stream.start()
            image, status, nodemaps = stream.wait()
            taken_image = None
            if status == cvb.WaitStatus.Ok:
                vinbuffer_nodemap = nodemaps["VinBuffer"]
                timestamp = vinbuffer_nodemap.Timestamp
                print("Timestamp: "+str(timestamp.value))
                taken_image = cvb.as_array(image, copy=True)
            stream.abort()
            return taken_image



if __name__ == "__main__":
    camera = CameraTest()
    for i in range(3):
        try:
            image = camera.rarely_take_image()
            if image is not None:
                print("Acquistion #"+str(i)+" succeded")
                pass # do what you want
            # simulate long time
            time.sleep(10)



        except Exception as e:
            print("Acquistion #"+str(i)+" failed with error: " +str(e) )

Please be aware that you adapt your “camera identifier” with something, that is unique for your device (here it is “CVMock”, but this can be any substring in the access token, like camera serial number, vendor ID or similar).
The expected output should be something like:

Timestamp: 28178249299300
Acquistion #0 succeded
Timestamp: 28178420961700
Acquistion #1 succeded
Timestamp: 28178587217200
Acquistion #2 succeded
2 Likes

I had implemented it exactly this way when I wrote this thread, but @Chris pointed out that loading the driver often is not good practice. That was the only reason I tried the class approach.

Nevertheless, thanks for the snippet for the new approach. I think you should also put it somewhere where it can be found via Google. Because I started using CVB at the end of last year and only found examples using the old acquisition stack in this forum.

I will accept this answer for now and try the new acquisition stack the next time I am on site.

@Chris @s-woe Thank you very much for your time and for sharing your knowledge.

1 Like

Hi @sebwr,

you can have a look at the samples that are delivered with CVB.
They’re at $CVB/tutorial.

1 Like