basler / pypylon

The official python wrapper for the pylon Camera Software Suite
http://www.baslerweb.com
BSD 3-Clause "New" or "Revised" License
557 stars 207 forks source link

Hardware Triggering using pypylon #707

Open ManoelVictor9 opened 7 months ago

ManoelVictor9 commented 7 months ago

Describe what you want to implement and what the issue & the steps to reproduce it are:

Hi! The objective of my code is to capture images divided into 4 quadrants in a constant loop, I currently use the space key to capture, but I need to do it using a hardware trigger on "Line1" with "RisingEdge" and after days and days of research, I was unable to carry out this implementation. Thanks, any help would be appreciated!

Code:

from pypylon import pylon import cv2 import numpy as np

camera = pylon.InstantCamera(pylon.TlFactory.GetInstance().CreateFirstDevice())

try: camera.Open() camera.WidthMax.SetValue(camera.WidthMax.GetMax()) camera.HeightMax.SetValue(camera.HeightMax.GetMax()) except Exception as e: print(f"Erro ao definir a largura e a altura da câmera: {e}") finally: camera.Close()

camera.StartGrabbing(pylon.GrabStrategy_LatestImageOnly) converter = pylon.ImageFormatConverter()

converter.OutputPixelFormat = pylon.PixelType_BGR8packed converter.OutputBitAlignment = pylon.OutputBitAlignment_MsbAligned

num_images = 4

resized_images = []

capture_flag = False

current_image_index = 0

screen_width, screen_height = 1300, 760

while camera.IsGrabbing(): grabResult = camera.RetrieveResult(5000, pylon.TimeoutHandling_ThrowException)

if grabResult.GrabSucceeded():

Acessando os dados da imagem

image = converter.Convert(grabResult)
img = image.GetArray()

if capture_flag:
    if len(resized_images) < num_images:
        resized_images.append(img.copy())
    else:
        resized_images[current_image_index] = img.copy()
        current_image_index = (current_image_index + 1) % num_images
    capture_flag = False

combined_image = np.zeros((screen_height, screen_width, 3), dtype=np.uint8)

for i in range(len(resized_images)):
    h, w, _ = resized_images[i].shape
    h_ratio = screen_height // 2
    w_ratio = screen_width // 2
    row = i // 2
    col = i % 2

    resized_images[i] = cv2.resize(resized_images[i], (w_ratio, h_ratio))

    combined_image[row * h_ratio : (row + 1) * h_ratio, col * w_ratio : (col + 1) * w_ratio, :] = resized_images[i]

cv2.imshow('Combined Images', combined_image)

grabResult.Release()

key = cv2.waitKey(1) if key == 27: # Tecla 'esc' para sair break elif key == ord(' '): # Tecla de espaço para capturar a próxima imagem capture_flag = True camera.StopGrabbing()

cv2.waitKey(0) cv2.destroyAllWindows()

Image exemple: imagem_2024-01-19_101524228

thiesmoeller commented 7 months ago

In the companion repository pypylon-samples we have a demo for HW triggering. https://github.com/basler/pypylon-samples/blob/main/notebooks/USB_hardware_trigger_and_chunks.ipynb

And you can search under docs.baslerweb.com for triggered acquisition.

https://docs.baslerweb.com/triggered-image-acquisition

ManoelVictor9 commented 7 months ago

In the companion repository pypylon-samples we have a demo for HW triggering. https://github.com/basler/pypylon-samples/blob/main/notebooks/USB_hardware_trigger_and_chunks.ipynb

And you can search under docs.baslerweb.com for triggered acquisition.

https://docs.baslerweb.com/triggered-image-acquisition

Thanks for the help, but I've used these examples before and it didn't work well, I still don't know exactly how to implement the HW triggering in my code, could you tell me where I can change it and how I do it?

SMA2016a commented 7 months ago

you need to set just 3 parameters:

cam.TriggerSelector = "FrameStart" cam.TriggerSource = "Line1" cam.TriggerMode = "On"

ManoelVictor9 commented 7 months ago

you need to set just 3 parameters:

camera.TriggerSelector.SetValue(TriggerSelector_FrameStart); camera.TriggerMode.SetValue(TriggerMode_On); camera.TriggerSource.SetValue(TriggerSource_Line1);

Sure, I tried that, but what is the variable that changes when I press the button? Bcs at the moment the variable is a flag when I press the space bar

...

if capture_flag: if len(resized_images) < num_images: resized_images.append(img.copy()) else: resized_images[current_image_index] = img.copy() current_image_index = (current_image_index + 1) % num_images capture_flag = False

...

if key == 27: break elif key == ord(' '): #capture the next image capture_flag = True camera.StopGrabbing()

HighImp commented 7 months ago

Hi Manoel, I hope I understood your problem, you want to replace the spacebar click directly with an external trigger, right?

To solve the problem that you are waiting for an image and handling the cv2 function at the same time, the best way is to use the ImageEventHandler.

Nevertheless, I tried to change your code a bit to achieve your goal in a somewhat hacky way by using the WaitObject.

Please forgive me for rearranging a few lines and translating your comments, my Portuguese is terrible ;-)
A note for readability: please surround all your code with Python Markdown in github comments, so the indentations will survive.

Best regards

from pypylon import pylon
import cv2
import numpy as np

camera = pylon.InstantCamera(pylon.TlFactory.GetInstance().CreateFirstDevice())

# changing this to True changes the Trigger to Line1 instead of the SpaceKey
EXTERNAL_TRIGGER = False

try:
    # setup camera aoi
    camera.Open()
    camera.Width.Value = camera.Width.Max
    camera.Height.Value = camera.Height.Max

    # set camera to frame trigger mode on Line1
    camera.TriggerSelector.Value = "FrameStart"
    if EXTERNAL_TRIGGER:
        camera.TriggerSource.Value = "Line1"
    else:
        camera.TriggerSource.Value = "Software"
    camera.TriggerMode.Value = "On"

except Exception as e:
    print(f"Error when configuring the camera: {e}")

# constant values
num_images = 4
screen_width, screen_height = 1300, 760

# runtime values
resized_images = []
current_image_index = 0
combined_image = np.zeros((screen_height, screen_width, 3), dtype=np.uint8)

# configure image converter
converter = pylon.ImageFormatConverter()
converter.OutputPixelFormat = pylon.PixelType_BGR8packed
converter.OutputBitAlignment = pylon.OutputBitAlignment_MsbAligned

camera.StartGrabbing(pylon.GrabStrategy_LatestImageOnly)

while camera.IsGrabbing():
    while current_image_index < num_images:

        cv2.imshow('Combined Images', combined_image)
        key = cv2.waitKey(1)
        if key == 27:  # Esc key to exit
            camera.StopGrabbing()
            break
        elif not EXTERNAL_TRIGGER and key == ord(" "):
            camera.ExecuteSoftwareTrigger()

        # you cant check your key entry and wait for the next image in one thread at the same time,
        # so you can use this wait-object to check for new images and skip the 5 sec Timeout during RecieveResult
        if not camera.GetGrabResultWaitObject().Wait(10):
            continue

        try:
            # use the context handler, so you dont have to call "grabResult.Release" at the end
            with camera.RetrieveResult(5000, pylon.TimeoutHandling_ThrowException) as grabResult:
                assert grabResult.GrabSucceeded()
                # Accessing image data
                image = converter.Convert(grabResult)
                img = image.GetArray()
        except pylon.TimeoutException as timeout_error:
            raise AssertionError("Timeout error, this should not happen, "
                                 "because we waited for the image in the wait object before!") from timeout_error

        except AssertionError as assertion_error:
            raise AssertionError("Unsuccessful grab, this should not happen at all!") from assertion_error

        # we dont need the capture flag variable anymore,
        # because of every image is grabbed on purpose, just add every incoming image
        if len(resized_images) < num_images:
            resized_images.append(img.copy())
        else:
            resized_images[current_image_index] = img.copy()
            current_image_index = (current_image_index + 1) % num_images

        for i in range(len(resized_images)):
            h, w, _ = resized_images[i].shape
            h_ratio = screen_height // 2
            w_ratio = screen_width // 2
            row = i // 2
            col = i % 2

            resized_images[i] = cv2.resize(resized_images[i], (w_ratio, h_ratio))
            combined_image[row * h_ratio: (row + 1) * h_ratio, col * w_ratio: (col + 1) * w_ratio, :] = resized_images[
                i]

cv2.waitKey(0)
cv2.destroyAllWindows()
camera.Close()
ManoelVictor9 commented 7 months ago

Hi Manoel, I hope I understood your problem, you want to replace the spacebar click directly with an external trigger, right?

To solve the problem that you are waiting for an image and handling the cv2 function at the same time, the best way is to use the ImageEventHandler.

Nevertheless, I tried to change your code a bit to achieve your goal in a somewhat hacky way by using the WaitObject.

Please forgive me for rearranging a few lines and translating your comments, my Portuguese is terrible ;-) A note for readability: please surround all your code with Python Markdown in github comments, so the indentations will survive.

Best regards

from pypylon import pylon
import cv2
import numpy as np

camera = pylon.InstantCamera(pylon.TlFactory.GetInstance().CreateFirstDevice())

# changing this to True changes the Trigger to Line1 instead of the SpaceKey
EXTERNAL_TRIGGER = False

try:
    # setup camera aoi
    camera.Open()
    camera.Width.Value = camera.Width.Max
    camera.Height.Value = camera.Height.Max

    # set camera to frame trigger mode on Line1
    camera.TriggerSelector.Value = "FrameStart"
    if EXTERNAL_TRIGGER:
        camera.TriggerSource.Value = "Line1"
    else:
        camera.TriggerSource.Value = "Software"
    camera.TriggerMode.Value = "On"

except Exception as e:
    print(f"Error when configuring the camera: {e}")

# constant values
num_images = 4
screen_width, screen_height = 1300, 760

# runtime values
resized_images = []
current_image_index = 0
combined_image = np.zeros((screen_height, screen_width, 3), dtype=np.uint8)

# configure image converter
converter = pylon.ImageFormatConverter()
converter.OutputPixelFormat = pylon.PixelType_BGR8packed
converter.OutputBitAlignment = pylon.OutputBitAlignment_MsbAligned

camera.StartGrabbing(pylon.GrabStrategy_LatestImageOnly)

while camera.IsGrabbing():
    while current_image_index < num_images:

        cv2.imshow('Combined Images', combined_image)
        key = cv2.waitKey(1)
        if key == 27:  # Esc key to exit
            camera.StopGrabbing()
            break
        elif not EXTERNAL_TRIGGER and key == ord(" "):
            camera.ExecuteSoftwareTrigger()

        # you cant check your key entry and wait for the next image in one thread at the same time,
        # so you can use this wait-object to check for new images and skip the 5 sec Timeout during RecieveResult
        if not camera.GetGrabResultWaitObject().Wait(10):
            continue

        try:
            # use the context handler, so you dont have to call "grabResult.Release" at the end
            with camera.RetrieveResult(5000, pylon.TimeoutHandling_ThrowException) as grabResult:
                assert grabResult.GrabSucceeded()
                # Accessing image data
                image = converter.Convert(grabResult)
                img = image.GetArray()
        except pylon.TimeoutException as timeout_error:
            raise AssertionError("Timeout error, this should not happen, "
                                 "because we waited for the image in the wait object before!") from timeout_error

        except AssertionError as assertion_error:
            raise AssertionError("Unsuccessful grab, this should not happen at all!") from assertion_error

        # we dont need the capture flag variable anymore,
        # because of every image is grabbed on purpose, just add every incoming image
        if len(resized_images) < num_images:
            resized_images.append(img.copy())
        else:
            resized_images[current_image_index] = img.copy()
            current_image_index = (current_image_index + 1) % num_images

        for i in range(len(resized_images)):
            h, w, _ = resized_images[i].shape
            h_ratio = screen_height // 2
            w_ratio = screen_width // 2
            row = i // 2
            col = i % 2

            resized_images[i] = cv2.resize(resized_images[i], (w_ratio, h_ratio))
            combined_image[row * h_ratio: (row + 1) * h_ratio, col * w_ratio: (col + 1) * w_ratio, :] = resized_images[
                i]

cv2.waitKey(0)
cv2.destroyAllWindows()
camera.Close()

It worked perfectly, Thank you so Much!