Image Aquisition inside QThreads

Hi,

I use the camera control code inside a machine that has a PySide6 (QT) UI. To keep the UI from frezzing I need to do all the calculations inside a QThread that runs independently from the main UI thread.

Based on the forum entry “Getting Started with CVBpy” I implemented my blah which also runs and terminates without problems on the main thread of a python script.

However, as soon as I run this thread the application crashes after everything is done. That means in the moment where the thread is cleaned up it comes to a segfault (Process finished with exit code 139 (interrupted by signal 11: SIGSEGV)).

By using gdb I know that it happens exactly when the thread is cleaned up. So I suspect that I am doing something wrong with the camera code and therefore some resource is still open and this then leads to a crash during thread termination.

My environment:
Ubuntu 22.04
Python 3.9.16
CVB 13.04.005

Code of a minimal (not) working example :

camera.py

from dataclasses import dataclass
from pathlib import Path
import cvb
import numpy as np

@dataclass
class Camera:
    """
    Class which holds the logic of the camera. Since there is currently only on type of camera deployed the path to
    the driver is hardcoded and set to port 0
    """
    camera_path = Path(cvb.install_path()) / "drivers" / "GenICam.vin"
    def take_image(self) -> np.array:
        """
        Calls the CVB driver and retrieves am image from the there stored camera on port 0. Since the image is
        provided by an image stream, a copy of the matrix must be done, which is then returned.
        :return np.array: captured image from the driver which is stored inside a `np.array
        """
        try:
            with cvb.DeviceFactory.open(str(self.camera_path), port=0) as device:
                stream = device.stream()
                stream.start()
                image, status = stream.wait()
                stream.try_abort()
                if status == cvb.WaitStatus.Ok:
                    image.unlock()
                    return cvb.as_array(image, copy=True)
                else:
                    print(f"Image acquisition status was not ok. It was {status}")
        except Exception as e:
            print(f"Exception while taking picture! {e}")
        finally:
            pass
            # del device
            # del stream
            # del status

runner.py

import time
from PySide6.QtCore import QRunnable, Slot
from cvb_bug.camera import Camera

class Runner(QRunnable):
    @Slot()
    def run(self) -> None:
        try:
            camera = Camera()
            for x in range(5):
                image = camera.take_image()
                print(image.shape)
                time.sleep(0.5)
        except Exception as e:
            print(e)

main_threaded.py

import sys
from PySide6.QtCore import QThreadPool
from PySide6.QtWidgets import QMainWindow, QVBoxLayout, QPushButton, QWidget, QApplication
from cvb_bug.runner import Runner

class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()

        self.threadpool = None
        self.runner = None

        layout = QVBoxLayout()

        btn = QPushButton("Start")
        btn.pressed.connect(self.start)

        layout.addWidget(btn)
        w = QWidget()
        w.setLayout(layout)
        self.setCentralWidget(w)
        self.show()

    def start(self):
        print("START")
        self.threadpool = QThreadPool()
        self.runner = Runner()
        self.threadpool.start(self.runner)

if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = MainWindow()
    app.exec()

Since I described above that it works without thread here is a main script which called my camera control without threads.

import time
from cvb_bug.camera import Camera

if __name__ == "__main__":
    camera = Camera()
    for x in range(5):
        image = camera.take_image()
        print(image.shape)
        time.sleep(0.5)

Last but not least here is the dump from gdb where you can see the crash when thread 26046 terminates. Since it is lenghtly here is a pastebin link

I hope you have any ideas what I have done with the camera code.
I could also provide the complete example as a github repo if this helps.

Greetings Sebastian

Hi @sebwr ,
thanks for your question.

Unfortunately, in my replication of your setup I cannot see that failure. Does it occur consistently at your site?

What kind of confused me was, that you try_abort() your stream before copying the image. Could you retest your application, with that being done afterwards? Maybe this already helps.

Also unlock shouldn’t be used with our new acquisition stack. Please look at the following guide for transforming: Acquisition Stack 3

Hi @s-woe,
thank you for your response. I have tried a few things that I have derived from your answer (See below). Unfortunately without success.

 def take_image(self) -> np.array:
        try:
            taken_image = None
            with cvb.DeviceFactory.open(self.camera_path, port=0) as device:
                stream = device.stream()
                stream.start()
                image, status = stream.wait()
                if status == cvb.WaitStatus.Ok:
                    taken_image = cvb.as_array(image, copy=True)
                    print(taken_image.flags["OWNDATA"])
                else:
                    print(f"Image acquisition status was not ok. The status was {status}")
                stream.stop()
            return taken_image
        except Exception as e:
            print(f"Exception while taking picture! {e}")

Generates still the same error. (Have removed the try abort and the unlock)

    def take_image(self) -> np.array:
        with cvb.DeviceFactory.open(self.camera_path, port=0) as device:
            stream = device.stream()
            try:
                stream.start()
                image, status = stream.wait()
                with image:
                    taken_image = cvb.as_array(image, copy=True)
                    print(taken_image.flags["OWNDATA"])
                    return taken_image
            except Exception as e:
                print(f"Exception while taking picture! {e}")
            finally:
                stream.abort()

The with code does not bring any improvement either. I also ran it without the return to make sure it wasn’t the return, but even without the return it crashed after a few seconds.

    def take_image(self) -> np.array:
        with cvb.DeviceFactory.open(self.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)
                print(taken_image.flags["OWNDATA"])
                print(taken_image.shape)
            stream.abort()

Without try catch, and stop the stream directly also makes no difference…

    def take_image(self) -> np.array:
        devices = cvb.DeviceFactory.discover_from_root(cvb.DiscoverFlags.IgnoreVins)
        with cvb.DeviceFactory.open(devices[0].access_token, cvb.AcquisitionStack.GenTL) as device:
            stream = device.stream(cvb.ImageStream)
            stream.start()
            image, status, node_maps = stream.wait()
            with image:
                taken_image = cvb.as_array(image, copy=True)
                print(taken_image.flags["OWNDATA"])
                print(taken_image.shape)
            stream.abort()

Acquisition Stack 3 also generates a segmentation fault after some time.

If I have missed any of your suggestions please let me know. If you know anything else I could try I would be happy if you let me know.

after some time - how long approximately?

~30 seconds after last image was taken
~38 seconds after the thread was started

Have the measured times stopped with the cell phone, so these are not one hundred percent accurate.

Do you have the possiblity to upgrade to 14.00.002 ?

Which camera do you use?

I retest that, I didn’t wait that long.

1 Like

Tried to go to 14.x but I guess this is not currently possible because of ubuntu 22.04 since they dropped support for libssl 1.x (they only support 3.x). And your install script uses something from microsoft which depents on libssl 1.x. But I tried on thursday, so I may be wrong.

Stemmer Imaging GO-5000C-USB JAI Ltd., Japan U50234B

Here is a repository, which contains both second and third gen code:
https://github.com/sebwr/cvb_qthread_test

you can simply edit the install script to not load the .net core runtime if you don’t need it.

thanks, already had a working example with your code. I just retested longer, the app still doesn’t crash. How often do you trigger the 5 image acquisition thread (i.e. how often do you hit “Start” button)?

1 Like

Once :thinking: pretty sure.
But it can’t really be the button, since it was executed in the actual machine at some point after umpteen other steps and was noticed there. And there were no buttons involved, at least not directly.

Ok I will try this next

What CVB version did you use for testing?
I just installed 14.00.002 and It seems like it’s working. The gen2 and gen3 version each ran for over 5 minutes.

I had only installed the new version and used the old wheel by mistake and that already works, so the bug is probably somewhere in the C code, which would explain the nonspeaking segfault.

I will now test with the correct wheel. After that I will test on the actual machine and I hope that the bug is there also gone

1 Like

Yeah sorry, I still cannot reproduce it.

didn’t mean to blame the button :joy:

Obviously, we have a bug back in 13.04.005, I will track it down. Thanks for upgrading.

1 Like

Just rechecked with 13.04.005, no segfault and same behavior, except that the acquisition was slower.

Ok, that’s interesting. So maybe it is Ubuntu 22.04.
Nevertheless, thank you for your input and help.