morefigs / pymba

Python wrapper for Allied Vision's Vimba C API
MIT License
105 stars 84 forks source link

MultiThreading help #105

Closed fabestla closed 5 years ago

fabestla commented 5 years ago

Hi there, I'm fairly new to Python and OpenCV so my apologies if this seems basic.

Without multithreading, the examples from here work perfectly, except that it trashes many frames when I display the video. If I comment the cv2.show from the display_frame example, it doesn't trash any more frames.

I am trying as the documentation suggests for camera.arm, to make a function with a separate thread to show the image. I started from the example display_frame from this code. I didn't change the opencv_acquire_streaming_images.py other than the line to call the camera.arm: camera.arm('Continuous', display_frame.start)

The error I'm getting is: [Traceback (most recent call last): File "_ctypes/callbacks.c", line 234, in 'calling callback function' File "/usr/local/lib/python3.6/dist-packages/pymba/frame.py", line 72, in frame_callback_wrapper self._frame_callback(self) File "/usr/local/lib/python3.6/dist-packages/pymba/camera.py", line 272, in _streaming_callback self._user_callback(frame) File "/home/sti/sunlight/Main_MV_display_frame.py", line 20, in start threading.Thread(target=self.show, args=()).start() AttributeError: 'Frame' object has no attribute 'show' ]

Below is what I wrote for display_frame: from typing import Optional import cv2 from pymba import Frame import threading

todo add more colours

PIXEL_FORMATS_CONVERSIONS = { 'BayerRG8': cv2.COLOR_BAYER_RG2RGB, }

class display_frame:

def __init__(self, frame: Frame =None)-> None:
    self.frame: Frame = frame
    self.stopped = False

def start(self):
    threading.Thread(target=self.show, args=()).start()
    return self

def show(self) -> None:
    """
    Displays the acquired frame.
    :param frame: The frame object to display.
    :param delay: Display delay in milliseconds, use 0 for indefinite.
    """
    while not self.stopped:
        print('frame {}'.format(frame.data.frameID))
         # get a copy of the frame data
        image = frame.buffer_data_numpy()
        # convert colour space if desired
        try:
            image = cv2.cvtColor(image, PIXEL_FORMATS_CONVERSIONS[frame.pixel_format])
        except KeyError:
            pass
         # display image
        cv2.imshow('Image', image)
        if cv2.waitKey(1) == ord("q"):
            self.stopped = True

def stop(self):
    self.stopped = True

Any help is appreciated.

morefigs commented 5 years ago

Good idea to try a thread, but your code seems a little buggy:

I would instead suggest using a thread safe queue, and have the camera thread add frames to and a second thread gets frames from to display. You may need to be careful when using OpenCV functions in a thread, and from memory any OpenCV windows opened in a thread need to be closed from that same thread.

SunnyAVT commented 5 years ago

You can try the file in the attachment, rename txt to py file format in your test. opencv_liveview.txt

fabestla commented 5 years ago

Thank you, This is resolved. The way that worked was by using global variables for each thread (callback then other methods doing more work on the image). Also, to synchronize the threads as they work together, I used the Event method from threading.