basler / pypylon

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

Final Frame of Retrive Result takes about 10-11 ms higher than other frames #640

Open ShiddhamSharma opened 1 year ago

ShiddhamSharma commented 1 year ago

Hi, I have acA1440-73gm. I am currently trying to reduce the AOI width and height to increase fps. Now I have set it to 280 fps. So, I am using Software Trigger in one thread and in another thread I am taking the results. What I am seeing is that more or less each frame is between 1-2 ms and most are 0.0. I believe it is because of the images being in the bufer. But every time when I see the last frame takes 12 ms to grab. I don't understand why this is happening. My Code:

from pypylon import pylon
import cv2
import time
import numpy as np

# conecting to the first available camera
camera = pylon.InstantCamera(pylon.TlFactory.GetInstance().CreateFirstDevice())
camera.Open()
camera.GainAuto.SetValue('Continuous')
camera.AcquisitionFrameRateEnable = True
camera.AcquisitionFrameRateAbs = 280
camera.ExposureTimeAbs = 80
camera.TriggerSelector = 'FrameStart'
camera.TriggerSource = 'Software'
camera.TriggerMode = 'On'
camera.AcquisitionMode = 'Continuous'
import threading
camera.Height = 125
camera.Width = 100
import numpy as np

grab_start = []
grab_timer = []
trigger_timer = []
def GrabImage():
    camera.StartGrabbingMax(280, pylon.GrabStrategy_OneByOne)
    while camera.IsGrabbing():
        grab_start.append(time.time())
        grabResult = camera.RetrieveResult(5000, pylon.TimeoutHandling_ThrowException)
        grab_timer.append(time.time())
        if len(grab_timer) == 280:
            break
    print('Done')
    camera.StopGrabbing()

def TriggerImage():
    # print('Trigger Time: {}'.format(time.time()))
    time.sleep(1)
    for i in range(280):
        start = time.time()
        camera.ExecuteSoftwareTrigger()
        # print(time.time() - start)
        trigger_timer.append(time.time())
        time.sleep(0.003)

background_thread = threading.Thread(target=GrabImage)
background_thread.daemon = True
background_thread.start()

background_thread_2 = threading.Thread(target=TriggerImage)
background_thread_2.daemon = True
background_thread_2.start()

array1 = np.array(grab_timer)
array2 = np.array(trigger_timer)

element_wise_subtraction = (array1 - array2)*1000
print(element_wise_subtraction.tolist())

Screenshot (2)

thiesmoeller commented 1 year ago

What is the exposure time of your camera ?

ShiddhamSharma commented 1 year ago

Right now I am testing with 80 microseconds.

thiesmoeller commented 1 year ago

Look at the timestamps in the GrabResult. Each frame has a precision timestamp when the exposure started. The timestamp can be converted to seconds by reading the feature GevTimestampTickFrequency. If you are not using PTP, the timestamp counter is running at 125MHz.

This will allow you to measure the exact delta_t between your frames.

Using software Trigger at these high frame rates is not very deterministic as you are in a normal userland process that might be scheduled away by the OS.

If you just want 280fps, let the camera run free with the acquisitionframerateabs set to your target FPS like you already did. But check the resultingframerateabs feature to check the final FPS.

ShiddhamSharma commented 1 year ago

I will try using this. So in my application I will be giving hardware trigger to get single frame which can require my camera to work at 280 fps. I will be sending one trigger for one frame. I was trying to test this with software trigger first. Do you suggest any other method?

thiesmoeller commented 1 year ago

If you go to hw trigger later this is ok.

For your software trigger test there is still one issue you should fix:

The camera is not queueing trigger requests. It has to be ready to be triggered ( not in exposure ) for this you have to query the state first.

For this we have a convinience function available. See here: https://github.com/basler/pypylon/blob/270ab28bfccb513e4fae43870e027f65b7fde4c5/samples/grabstrategies.py#L88

thiesmoeller commented 1 year ago

.. and use time.perf_counter() to get high precision system time. time.time() is very imprecise...

ShiddhamSharma commented 1 year ago

I made these changes and checked the time differences:

def GrabImage():
    camera.StartGrabbingMax(280, pylon.GrabStrategy_OneByOne)
    while camera.IsGrabbing():
        grab_start.append(time.perf_counter())
        grabResult = camera.RetrieveResult(5000, pylon.TimeoutHandling_ThrowException)
        timestamp.append(grabResult.TimeStamp)
        grab_timer.append(time.perf_counter())
        if len(grab_timer) == 280:
            break
    print('Done')
    camera.StopGrabbing()

def TriggerImage():
    # print('Trigger Time: {}'.format(time.time()))
    time.sleep(1)
    for i in range(280):
        start = time.perf_counter()
        if camera.WaitForFrameTriggerReady(200, pylon.TimeoutHandling_ThrowException):
            camera.ExecuteSoftwareTrigger()
        software_trigger.append(time.perf_counter() - start)
        trigger_timer.append(time.perf_counter())
        # print(time.time() - start)

        time.sleep(0.0001)

Now when I check the time difference in both: timestamp and trgger_timer. I see the difference is 4ms which means the trigger is working fine. But still the difference between grab_timer and trigger_timer is abut 10 ms. Screenshot (5)

ShiddhamSharma commented 1 year ago

The thing is lets say I put grab max as 281 and I break the start grabbing loop as soon as I get 280 frames the last frame takes 1ms. So not sure but feels like this is an issue with StartGrabbingMax(). So any other suggestions? Because I am fine with using break as well.

ShiddhamSharma commented 1 year ago

I have now used StartGrabbing() function after breaking the loop I am getting around 1 ms for the last frame. So I believe this is a bug in StartGrabbingMax() function. Also my issue can be closed as I can use Start Grabbing function.

thiesmoeller commented 1 year ago

Start grabbing (max)takes time to start up. There grab engine has to be initialized, video buffers allocated.

Start grabbing max will just stop the acquisition after max frames automatically.

ShiddhamSharma commented 1 year ago

I am fine if it only takes some time to start up rather than it taking time at the last frame.