Object Detection in Images using Hailo AI Kit for Raspberry Pi

Problem Description

While the Hailo Raspberry Pi 5 Examples repository contains example code for object detection in videos, adjusting that code to work with images turns out to be rather challenging as it is based on the GStreamer framework and would require to rewrite the pipeline.

Hailo Application Code Examples

An alternative is the Hailo Application Code Examples repository. It contains example code for object detection which takes an image as input, uses the Yolov7 model (by default) to detect objects in that image, annotates the image with bounding boxes for the detected objects, and writes the annotated image into an output directory. For each detected object, the model is expected to return a bounding box as coordinates of the lower left and upper right corner, a label (class), and a score how sure the model is that the object belongs to this class.

The problem is: the code is written for a Hailo8 device and doesn’t work out of the box with the Hailo8L accelerator for Raspberry Pi 5. Luckily, we can make it work with minimal changes.

Disclaimer

There is a more elegant solution than the one described below. If you adjust the code in object_detection.py (in process_output function) to run with the HailoRT version 4.18.0, you might not need to change anything in objection_detection_utils.py. In my case, I started debugging object_detection_utils.py and worked my way backwards leading to the changes described next.

Changes to object_detection.py

Select the right model

If you try to run object_detection.py with the Hailo AI Kit for Raspberry Pi 5, it will throw an error because the Yolov7, the default model used in that script, is built for Hailo8 architecture while the AI accelerator for Raspberry Pi has a Hailo8L architecture. You can see for yourself in the hailort.log log file which is automatically produced for every invocation of the AI accelerator.

So, to label the images using the Hailo8L accelerator for Raspberry Pi 5, you’ll need the yolov8s_h8l model. (the other model in the source code). You can download the model by checking out the Hailo Raspberry Pi 5 Examples repository and running the download_resources.sh script which will download the model into the resources directory.

You can then copy the yolov8s_h8l.hef file to the directory where your objeect_detection.py script is stored and adjust the parse_args function in object_detection.py like this:

def parse_args() -> argparse.Namespace:
    
    -- snip --
    
    parser.add_argument(
        "-n", "--net", 
        help="Path for the network in HEF format.",
        default="yolov8s_h8l.hef"
    )

    -- snip --

Remove the duplicate call to utils.extract_detections

The process_output function in object_detection.py apparently tries to differentiate between the version of the Hailo runtime (hailort). It looks like the runtime versions before 4.19.0 return the inference results (i.e., the object detection results) as an array of arrays. So, the outer array is stripped on lines 123-124:

        -- snip --

        # Deals with the expanded results from hailort versions < 4.19.0
        if len(infer_results) == 1:
            infer_results = infer_results[0]

        -- snip --

However, the utils.extract_detections function is called twice: the first call is on line 120 before infer_results is adjusted, and the second call is on line 126 after infer_results is adjusted (if the Hailo runtime’s version is lower than 4.19.0). Because the Raspberry Pi 5 AI Kit uses Hailo runtime 4.18.0, the second call to utils.extract_detections makes the program crash since infer_results is passed in two different forms.

We can fix this by simply commenting the lines 123, 124 and 126:

def process_output(

        -- snip --

        processed_image, infer_results = result
        detections = utils.extract_detections(infer_results)

        # Deals with the expanded results from hailort versions < 4.19.0
        #if len(infer_results) == 1:
        #    infer_results = infer_results[0]

        #detections = utils.extract_detections(infer_results)
        
        utils.visualize(

        -- snip --

Changes to object_detection_utils.py

Because the raw detections from the model are passed to utils.extract_detections function defined in object_detect_utils.py as an array of arrays, we need to strip the outer array before entering the for det in detection loop. To do this, we can adjust extract_detections to look like this:

def extract_detections(self, input_data: list, threshold: float = 0.5) -> dict:

    -- snip --

    for detections in input_data:
        if len(detections) == 0:
            continue

        for i, detection in enumerate(detections):
            for det in detection:
                bbox, score = det[:4], det[4]

                if score >= threshold:
                    boxes.append(bbox)
                    scores.append(score)
                    classes.append(i)
                    num_detections += 1

    -- snip --

With these changes, you can run the object_detection.py to detect objects in images using the Hailo AI Kit for Raspberry Pi 5.

1 Like

Thanks for the post, it really helps!

1 Like

Sir just want to add a point .May help others .With custom detection model based on yolov8s I had to add square brackets to detections list as shown below
“if len(detections) == 0:
continue
detections=[detections]”
as it was throwing some error .Note that my custom model had only one class and i was using detection tracker example

2 Likes

Hey @armtronix2021 ,

thanks for bringing this up. Definitely useful for with custom models!

Since I’m new to Hailo AI, do you have any good resources in mind for building custom models for Hailo AI accelerators? I would specifically be interested in building models for the Hailo8L accelerator for Raspberry Pi.

1 Like

I am not sure if there is any difference between retraining and creating your own custom model .But this is what i did

The important point to note is you will need to use the docker in creating the pt model . Never worked for me if i created a pt model without using the docker.
There are a few other changes which I think hailo guys have updated.
Anyway leaving the link there in case the model it doesnt work

4 Likes

Just adding one more link

here you will find more details

2 Likes

Great, thank you! I will try the link

Excuse me! I thought that Raspberry pi + Hailo-8 was only feasible for performing inferences. Do you also use it for training? How efficient is this device for training?

Is there any comparison regarding the performance of a PC with GPU (for example an RTX 4060 8GB of vRAM) or something similar vs Raspberry PI+Hailo-8 (26TOPS)?

retraining was done using docker running on i7 rtx4060i not on rasperry pi . Once you the get hef(hailo executable file) you can run it on raspberry pi

2 Likes

Raspberry Pi + HAILO 8 is primarily for inferencing. You can, in principle, train a model using Raspberry Pi. But it is, depending on the size of your model, time consuming. The key step to deploy your trained model is to convert it to the HEF file, which requires dataflow compiler, which further has certain hardware requirement.

1 Like

Unfortunately, I’m still having trouble getting inference to work even with those changes. Does anyone know how to fix the following error?

line 133, in extract_detections
bbox, score = det[:4], det[4]
IndexError: string index out of range

use brackets before the for loop and try

detections=[detections]
for det in detection:
                bbox, score = det[:4], det[4]

Hello! Thanks so much for the info. I tried your modifications, with yolov8s and yolov8n and it works fine. One question though: compared with the hailortcli run *.hef I get very poor performance regarding FPS (for example, with yolov8n, expected 95, I get 4 with the previous script, not doing the print/save of the output image). Do you have any idea what it could be? I checked and I am using PCI gen3, everything is working fine.

Hey @esahin ,

did you manage to resolve the issue? If not, just post your exact code and the error you get from the Python interpreter.

Best regards,
Paul

Hi @ana.frapiccini ,

you’re welcome – happy to hear the write-up was useful for you.

Regarding your question, I do have a hypothesis. Python is a programming language which is interpreted during runtime rather than compiled beforehand (like what you have with languages like C, C++ or Rust). So, Python is kind of notoriously famous for its lack of performance compared to, for example, C. The comic in this reddit illustrates this very nicely :slight_smile:

In contrast, when you run the command line executable, it’s for sure a compiled binary which will be way faster than a Python script.

So, to summarise, I don’t think you have any hardware issues (otherwise, the Python script wouldn’t work). I think this is simply the matter of the performance in a compiled language vs interpreted language.

Best regards,
Paul

Thank you! Yeah, i come from the Fortran community (numerical methods, hpc) and personally working with Python is a new adventure. I have isolated the issue to the creation of the binding in the model. I have separated the loading of the hef model, which took about 400 ms from the inference. I will keep exploring the issue. The thing is this code will replace a previous code which run in the Raspberry Pi, interacting with a camera, and i need it to run in python from script.

Ah, numerical methods & high-performance computing – that’s interesting! Is Raspberry Pi only for getting images using camera or do you intend to compute something on the Hailo accelerator?

Regarding Python’s performance: If you have to use Python, maybe it’s worth looking into Cython. I haven’t used it myself, but to my understanding it’s basically an extension (and, probably, a compiler) that allows you to write a script in Python and then compile it into an executable. According to the Wikipedia article, Cython’s performance is comparable to that of C.

An alternative would be to use Python in combination with Rust. I recently stumbled upon a new book on speeding up Python with Rust. I haven’t managed to read it yet, but Rust also has C-like performance since it is a compiled language and doesn’t have a garbage collector.

1 Like