In this tutorial you will use the Portenta Vision Shield to detect the presence and the position of objects in a camera image. To achieve this goal, you will use a technique that is often referred to as blob detection. You will write a MicroPython script and run it on the Portenta H7 with the help of the OpenMV IDE.
The OpenMV IDE is built for Machine Vision applications. It is meant to provide an Arduino like experience for simple computer vision tasks using a camera sensor. OpenMV comes with its own firmware that is built on MicroPython. Among other hardware, it supports the Portenta board. A statement from the creators of OpenMV on the reason why they built it:
Currently, doing anything serious involving computer vision requires a computer running an operating system running many layers of software and requiring much setup before you can get computer vision code working. This is all well and fine if you need to do many other things than just processing images, like connecting to the internet, running many different applications concurrently, etc.
But, what if, I just want to make an LED turn on if a red object appears in front of a white wall. Why do a need to build up a complex system for that? Or, what if I just want to turn on an LED when a face is in view?
This is where OpenMV comes in. Here you can read more about the OpenMV IDE.
Before you can start programming OpenMV scripts for the Portenta you need to download and install the OpenMV IDE.
IMPORTANT: Before you connect the Portenta to the OpenMV IDE make sure you update the bootloader as explained in the "Flashing the OpenMV Firmware" section!
Open the OpenMV download page in your browser and download the version that you need for your operating system. Alternatively you may use the following direct download links of the OpenMV IDE 2.6.5:
Follow the instructions of the installer.
Connect the Portenta to your computer via the USB-C® cable if you haven't done so yet. Make sure you first update the bootloader to the latest version using the STM32H747_updateBootloader sketch in the examples menu in the Arduino IDE.
Instructions on how to update the bootloader can be found in the Updating the Portenta Bootloader tutorial.
In bootloader versions 17 and older, there was a bug that could put the Portenta in a boot loop when the transmission aborted while flashing a large firmware file. This was fixed in the bootloader version 18. We strongly advise to update the bootloader before you proceed with the next step of this tutorial!
After updating the bootloader, put the Portenta in bootloader mode by double pressing the reset button on the board. The built-in green LED will start fading in and out. Now open the OpenMV IDE.
Click on the "connect" symbol at the bottom of the left toolbar.
A pop-up will ask you how you would like to proceed: "DFU bootloader(s) found. What would you like to do?". Select "Reset Firmware to Release Version". This will install the latest OpenMV firmware on the Portenta. If it asks you whether it should erase the internal file system you can click "No".
Portenta's green LED will start flashing while the OpenMV firmware is being uploaded on the board. A terminal window will open showing you the upload progress. Wait until the green LED stops flashing and fading. You will see a message saying "DFU firmware update complete!" when the process is done.
Installing the OpenMV firmware will overwrite any existing sketches in the internal Flash of Portenta. As a result the M7 port will not be exposed in the Arduino IDE anymore. To re-flash the M7 with an Arduino firmware, you need to put the board into bootloader mode. To do so, double press the reset button on the Portenta H7 board. The built-in green LED will start fading in and out. In bootloader mode you will see the Portenta M7 port again in the Arduino IDE.
The Portenta will start flashing its blue LED when it is ready to be connected. After confirming the completion dialog, the Portenta should already be connected to the OpenMV IDE, otherwise click the "connect" button once again.
In this section you will learn how to use the built-in blob detection algorithm to detect the location of objects in an image. That algorithm allows to detect areas in a digital image that differ in properties, such as brightness or color compared to surrounding areas. These areas are called blobs.
To do so, you need to feed an image from the camera to the algorithm. It will then analyze it and output the coordinates of the found blobs. You will visualize these coordinates directly on the image and indicate whether a blob was found by using the red and green LED.
Create a new script by clicking the "New File" button in the toolbar on the left side. Import the required modules:
1import pyb # Import module for board related functions2import sensor # Import the module for sensor related functions3import image # Import module containing machine vision algorithms4import time # Import module for tracking elapsed time
A module in Python® is a confined bundle of functionality. By importing it into the script, it gets made available.
In order to take a snapshot with the camera, it has to be configured in the script.
1sensor.reset() # Resets the sensor2sensor.set_pixformat(sensor.GRAYSCALE) # Sets the sensor to grayscale3sensor.set_framesize(sensor.QVGA) # Sets the resolution to 320x240 px4sensor.skip_frames(time = 2000) # Skip some frames to let the image stabilize
The most relevant functions in this snipped are
. The camera that comes with the Portenta Vision Shield only supports grey scale images. Therefore, you need to set it via the
The resolution of the camera needs to be set to a supported format both by the sensor and the algorithm. Algorithms which use a neural network are usually trained on a specific image resolution, to make them sensitive to the provided image snapshot resolution. The Portenta Vision Shield supports
, which you will use in this tutorial.
In order to feed the blob detection algorithm with an image, you have to take a snapshot from the camera or load the image from memory (e.g. SD card or internal Flash). In this case, you will take a snapshot using the
function. The resulting image needs then to be fed to the algorithm using the
function. You will notice that a list of tuples gets passed to the algorithm. In this list, you can specify the grey scale values (brightness) that are mostly contained in the object that you would like to track. For instance, if you wanted to detect white objects on a black background, the resulting range of brightness would be very narrow (e.g. from 200 - 255). Remember that 255 denotes the maximum brightness / white and 0 corresponds to the minimum brightness / black. If you are interested in a wider range of grey scale values to detect various objects, you can set the threshold range for example to (100, 255).
1thresholds = (100, 255) # Define the min/max gray scale values you are looking for2img = sensor.snapshot() # Takes a snapshot and saves it in the memory34# Find blobs with a minimal area of 15x15 = 200 px5# Overlapping blobs will not be merged6blobs = img.find_blobs([thresholds], area_threshold=225, merge=False)
Once the blobs are detected, you may be interested to see where they have been found in the images. This can be done by drawing directly on the camera image.
1# Draw blobs2for blob in blobs:3 # Draw a rectangle where the blob was found4 img.draw_rectangle(blob.rect(), color=255)5 # Draw a cross in the middle of the blob6 img.draw_cross(blob.cx(), blob.cy(), color=255)
The result of that will be visible in the Frame Buffer preview panel on the right side of the OpenMV IDE.
What if you want some visual feedback from the blob detection without any computer connected to your Portenta? You could use for example the built-in LEDs to indicate whether or not a blob was found in the camera image. Let's initialize the red and the green LEDs with the following code:
1ledRed = pyb.LED(1) # Initializes the red LED2ledGreen = pyb.LED(2) # Initializes the green LED
And then add the logic that will turn on the appropriate LED if a blob is present. This part of the code will be added after the "Draw Blobs" logic.
1# Turn the green LED on if a blob was found2if len(blobs) > 0:3 ledGreen.on()4 ledRed.off()5else:6# Turn the red LED on if no blob was found7 ledGreen.off()8 ledRed.on()
In this example, the green LED will light up when there is at least one blob found in the image. The red LED will light up if no blob could be found.
Let's program the Portenta with the complete script and test if the algorithm works. Copy the following script and paste it into the new script file that you created.
1import pyb # Import module for board related functions2import sensor # Import the module for sensor related functions3import image # Import module containing machine vision algorithms4import time # Import module for tracking elapsed time56sensor.reset() # Resets the sensor7sensor.set_pixformat(sensor.GRAYSCALE) # Sets the sensor to grayscale8sensor.set_framesize(sensor.QVGA) # Sets the resolution to 320x240 px9sensor.skip_frames(time = 2000) # Skip some frames to let the image stabilize1011thresholds = (100, 255) # Define the min/max gray scale values we're looking for12ledRed = pyb.LED(1) # Initializes the red LED13ledGreen = pyb.LED(2) # Initializes the green LED1415clock = time.clock() # Instantiates a clock object1617while(True):18 clock.tick() # Advances the clock19 img = sensor.snapshot() # Takes a snapshot and saves it in memory2021 # Find blobs with a minimal area of 15x15 = 225 px22 # Overlapping blobs won't be merged23 blobs = img.find_blobs([thresholds], area_threshold=225, merge=False)2425 # Draw blobs26 for blob in blobs:27 # Draw a rectangle where the blob was found28 img.draw_rectangle(blob.rect(), color=255)29 # Draw a cross in the middle of the blob30 img.draw_cross(blob.cx(), blob.cy(), color=255)3132 # Turn the green LED on if a blob was found33 if len(blobs) > 0:34 ledGreen.on()35 ledRed.off()36 else:37 # Turn the red LED on if no blob was found38 ledGreen.off()39 ledRed.on()4041 pyb.delay(50) # Pauses the execution for 50ms42 print(clock.fps()) # Prints the framerate to the serial console
Click on the "Play" button at the bottom of the left toolbar. Place some objects on your desk and check if the Portenta can detect them.
The MicroPython script does not get compiled and linked into an actual firmware. Instead it gets copied to the internal Flash of the Portenta where it gets compiled and executed on the fly.
In this tutorial you learned how to use the OpenMV IDE to develop MicroPython scripts that can run on the Portenta board. You also learned how to configure the camera of the Portenta Vision Shield for machine vision applications in OpenMV. Last but not least, you learned how to interact with the built-in LEDs in MicroPython on the OpenMV firmware.