FrameBuffer: precisely sync LED flash with camera exposure at high FPS on H7 R2 + MT9V034

sensor.reset()
sensor.set_pixformat(sensor.GRAYSCALE)
sensor.set_framesize(sensor.QQVGA)  # 160x120
sensor.set_auto_gain(False)
sensor.set_auto_exposure(False, exposure_us=2500)
sensor.set_framebuffers(1)
sensor.skip_frames(time=2000)

My goal is to accurately capture LEDs that are blinking sequentially at high speed. For that, I need to achieve tight synchronization between LED ON timing and camera exposure

pin1.value(1) # turn on LED
time.sleep_us(SLEEP_TIME_FIRST)
start = time.ticks_us()
img = sensor.snapshot()
end = time.ticks_us()
print("Snapshot took:", time.ticks_diff(end, start), "us")
pin1.value(0) # turn off LED
time.sleep_us(SLEEP_TIME_SECOND)

My observations:

  1. When sensor.set_framebuffers(3) (default), sensor.snapshot() only takes 40us, and I can reach ~146 FPS, but the snapshot might starts before IR LED turns on, causing frame mismatch.

  2. When sensor.set_framebuffers(1) (single buffer mode), the snapshot captures the correct exposure with IR LED, but it becomes very slow: ~8000–9000us per frame, and FPS drops to ~63.

  3. Adjusting exposure time alone does not help reduce snapshot duration in single-buffer mode.

My question:

Is there any way to:

  1. Modify the firmware to reduce the snapshot time required in single buffer mode?"

  2. Maintain a high FPS (120+) while keeping the exposure tightly aligned with LED state?

Any advice or guidance would be greatly appreciated!

Thanks in advance.

Hi, see this for triggered mode: openmv/scripts/examples/01-Camera/04-Global-Shutter/triggered_mode.py at master · openmv/openmv · GitHub

The frame rate will be capped at like ~40 through for the full res without lowering the exposure. If you go to a lower res where pixel binning is used you can go 2x-4x faster.

Here’s a combined example:

# This work is licensed under the MIT license.
# Copyright (c) 2013-2023 OpenMV LLC. All rights reserved.
# https://github.com/openmv/openmv/blob/master/LICENSE
#
# Global Shutter Triggered Mode Example
#
# This example shows off setting the global shutter camera into triggered mode. In triggered mode
# snapshot() controls EXACTLY when integration of the camera pixels start such that you can sync
# taking pictures to some external movement. Since the camera captures all pixels at the same time
# (as it is a global shutter camera versus a rolling shutter camera) movement in the image will
# only be captured for the integration time and not the integration time multiplied by the number
# of rows in the image. Additionally, sensor noise is reduced in triggered mode as the camera will
# not read out rows until after exposing which results in a higher quality image.
#
# That said, your maximum frame rate will be reduced by 2 to 3 as frames are no longer generated
# continuously by the camera and because you have to wait for the integration to finish before
# readout of the frame.

import sensor
import time

# Change this value to adjust the exposure. Try 10.0/0.1/etc.
EXPOSURE_TIME_SCALE = 0.3

sensor.reset()  # Reset and initialize the sensor.
sensor.set_pixformat(sensor.GRAYSCALE)  # Set pixel format to GRAYSCALE
sensor.set_framesize(sensor.QQVGA)  # Set frame size to VGA (640x480)
sensor.skip_frames(time=2000)  # Wait for settings take effect.
clock = time.clock()  # Create a clock object to track the FPS.

sensor.ioctl(sensor.IOCTL_SET_TRIGGERED_MODE, True)

# Print out the initial exposure time for comparison.
print("Initial exposure == %d" % sensor.get_exposure_us())

clock = time.clock()  # Create a clock object to track the FPS.

# You have to turn automatic gain control and automatic white balance off
# otherwise they will change the image gains to undo any exposure settings
# that you put in place...
sensor.set_auto_gain(False)
sensor.set_auto_whitebal(False)
# Need to let the above settings get in...
sensor.skip_frames(time=500)

current_exposure_time_in_microseconds = sensor.get_exposure_us()
print("Current Exposure == %d" % current_exposure_time_in_microseconds)

# Auto exposure control (AEC) is enabled by default. Calling the below function
# disables sensor auto exposure control. The additionally "exposure_us"
# argument then overrides the auto exposure value after AEC is disabled.
sensor.set_auto_exposure(
    False, exposure_us=int(current_exposure_time_in_microseconds * EXPOSURE_TIME_SCALE))

while True:
    clock.tick()  # Update the FPS clock.
    img = sensor.snapshot()  # Take a picture and return the image.
    print(clock.fps())  # Note: OpenMV Cam runs about half as fast when connected
    # to the IDE. The FPS should increase once disconnected.

Notice how you have to set the exposure low to get the fast FPS.