Dataset synthétique grâce au rendu 3D dans Blender

Créer un dataset spécifique à un besoin peut parfois s’avérer difficile en plus d’être coûteux en temps. Spécifiquement pour les dataset d’images, on peut être contraint par le droit d’auteur et le droit à l’image, ne pas avoir le matériel pour prendre des photos, ne pas avoir le temps de labelliser le dataset à la main.

On peut alors se tourner vers la donnée synthétique, générée à partir de rien, et labellisée automatiquement. Aujourd’hui je vais vous présenter comment j’ai généré un dataset synthétique en utilisant Blender.

Blender est un logiciel libre de modélisation, animation et rendu d’images 3D (entre autres).

L’objectif était de créer un dataset d’entraînement pour la reconnaissance d’objets, spécifiquement pour la reconnaissance de piétons et de véhicules transpalettes.
Cet article détaille l’entraînement d’un réseau de neurone avec ce dataset : Fine tuning pour la reconnaissance de piétons et véhicules dans un entrepôt

Extrait du dataset créé, à gauche une image d’entrée et à droite le masque qui décrit les objets à reconnaitre

Pour reproduire ce résultat, vous aurez besoin des connaissances de base de Blender, et de pouvoir lancer un script python.

Modélisation 3D

J’ai commencé par la modélisation en 3D d’un entrepôt. En modélisant les objets 3D les plus simples, et en utilisant des assets libre de droit pour les plus compliqués. J’ai notamment utilisé :

Animation

J’ai ensuite animé la caméra, un personnage et un transpalette pour que le dataset final contienne des angles de vue variés des objets qu’il faudra reconnaître.

Pour cela, j’ai d’abord donné une animation simple aux objets, puis dans l’éditeur de courbes, j’ai appliqué le modificateur de bruit à la position X et Y.

(1) Editeur de graph; (2) Ajouter un modificateur; (3) Bruit

Avec ce modificateur, il y a certaines frames où les objets sortent de l’écran ou alors sont superposés. Il m’a fallu bake l’animation pour pouvoir éditer uniquement les frames problématiques.

Rendu des images

À ce point, j’ai pu faire le rendu de mes images:

Je me suis ensuite attelé au rendu des masks.

Compositing des masks

Un mask est une image en noir et blanc dont la valeur d’un pixel représente un objet à reconnaître. On va donc créer des images où le background aura la valeur 0, un transpalette la valeur 1 et un piéton la valeur 2.

Pour cela il faut changer les paramètres de rendu suivants:

Pouvoir différencier les objets par leurs matériaux

View Layer >
Passes >
Data >
Material Index

Retirer les filtres de couleurs artistiques de Blender

Render >
Color Management >
View Transform = Standard
Look = None
Sequencer = Non-Color

Passer le mode de rendu en Noir et Blanc

Output >
Output >
Color = BW

J’ai activé la passe Material Index pour différencier mes objets grâce à leurs matériaux. Il faut donc donner un index différent aux matériaux de mes objets à différencier (0 pour le background, 1 pour le transpalette et 2 pour le piéton)


En sélectionnant un objet : Material > Settings > Pass Index

Maintenant que les index de matériaux sont configurés, on peut les utiliser dans le graph de composition.

  • (1), j’utilise la node ID Mask pour avoir des mask qui séparent les index de matériaux.
  • (2), je combine ces masks et leur donne les valeurs 0, 0.5 et 1. Cela me permet de visualiser le masks avec des couleurs visibles.
  • (3), je map les valeurs 0, 0.5 et 1 aux valeurs 0, 1/255 et 2/255 grâce à une node Color Ramp en mode continue.

Blender code les valeurs de couleurs rouge verte et bleu en valeurs flottantes entre 0 et 1, mais l’image que l’on veut créé sera codée en noir et blanc en valeurs entière entre 0 et 255. C’est pour ça que dans la node Color Ramp, je divise les valeurs voulues par 255.

Malheureusement, quand j’inspecte les masks rendus, je vois que le background est représentés par des pixels de couleur 0 et 1; le transpalette par des pixels de couleur 12, 13, 14 et le piéton par des pixels de couleur 21, 22, 23.

Je n’ai pas trouvé de solution à ce problème, j’ai donc traité les masks avec un script python ⬇

Traitement des masks

mask_processor.py

# imports
from PIL import Image
import os
import argparse


#### Parse arguments
parser = argparse.ArgumentParser(
                    prog='ProgramName',
                    description='What the program does',
                    epilog='Text at the bottom of help')
parser.add_argument('--source', type=str, default="", help='Source folder')
parser.add_argument('--output', type=str, default="processed_masks", help='Output folder')
args = parser.parse_args()
source_folder = args.source
output_folder = args.output
if not os.path.exists(source_folder) or len(os.listdir(source_folder)) == 0:
    print('Given source folder does not exist or is empty')
    exit
if not os.path.exists(output_folder):
    os.makedirs(output_folder)
    print(f'Created output folder {output_folder}')


#### Count the occurence of color in the input imagges
color_count = {i: 0 for i in range(256)}
for filename in os.listdir(source_folder):
    img = Image.open(os.path.join(source_folder, filename))
    px = img.load()
    for x in range(img.size[0]):
        for y in range(img.size[1]):
            color_count[px[x, y]] += 1


#### Use the color count to identify the ranges of clustered colors
ranges = []
current_range = []
for i in range(256):
    if color_count[i] > 0:
        current_range.append(i)
    elif len(current_range) > 0:
        ranges.append(current_range)
        current_range = []


#### Process the images to map the fuzy values to distinct values
for filename in os.listdir(source_folder):
    img = Image.open(os.path.join(source_folder, filename))
    px = img.load()
    for x in range(img.size[0]):
        for y in range(img.size[1]):
            for color_range in ranges:
                if px[x, y] in color_range:
                    px[x, y] = ranges.index(color_range)
                    break

    img.save(os.path.join(output_folder, filename))
    print(f"Processed {filename}")


#### Display ranges info
for color_range in ranges:
    print(f'colors {color_range} have been mapped to {ranges.index(color_range)}')

Vous pouvez utiliser ce script avec la commande suivante:

python mask_processor.py --source [chemin vers le dossier contenant vos masks]

Conclusion

J’ai créé dans Blender un dataset synthétique d’images pour l’entraînement d’un réseau de neurone à la reconnaissance d’objets spécifiques.

Vous pouvez retrouver les détails de l’entraînement dans cet article : Fine tuning pour la reconnaissance de piétons et véhicules dans un entrepôt – AI Squad by Reboot Conseil (rebootia.com)

Partager l'article:

Autres articles