Connexion Contactez-Nous

Retour sur Expérience de Data Visualisation 3D avec Blender

by Nicolas David

#Data #Big Data #Innovation

Blender propose des fonctions avancées de modélisation et d’animation. D’autres logiciels pourraient être considérés tel que 3DS max, maya, ou Cinema 4D parmi les plus connus, mais la différence pour ces expérimentations est que Blender est disponible gratuitement. Il est donc possible d'essayer sans plus attendre...

Fixons tout de suite le contexte. Dans cet article, Blender ne désigne bien sûr pas un robot mixeur pour la cuisine, mais le logiciel libre de modélisation 3D.

Blender propose des fonctions avancées de modélisation et d’animation. D’autres logiciels pourraient être considérés tel que 3DS max, maya, ou Cinema 4D parmi les plus connus, mais la différence pour ces expérimentations est que Blender est disponible gratuitement. Il est donc possible d'essayer sans plus attendre. L’objet n’est pas ici de faire une présentation de Blender (la chaîne youtube Blender Guru le fait très bien si le sujet vous intéresse et que vous maîtrisez l'anglais) mais plutôt de le mettre en regard avec notre activité : travailler avec la donnée.

En effet le monde de la donnée et de la 3D sont tous deux des domaines au coeur de l'actualité du numérique et les interactions entre ces deux domaines ne sont pas nouvelles. Le Big data VR challenge, était par exemple un concours organisé notamment par Epic Games (société éditrice du moteur Unreal Engine) au cours duquel des projets de visualisation de données en VR étaient développés. Aussi, il paraissait intéressant de proposer une initiation (sans prétention) et des technologies possibles.

Dans un premier temps, nous nous intéressons à Blender qui propose un langage de script embarqué qui n’est autre que Python. Le logiciel en lui-même n’est pas écrit en python, mais embarque, dans la version 2.79, sous linux 64 bit (sous Windows 10 64 bit également et vraisemblablement pour mac également) un interpréteur de python 3.5. Il est ainsi possible, par l’intermédiaire d’une fenêtre de scripting d’écrire et compiler du code python dans l’interface de Blender, et même de faire interagir les 2 en utilisant notamment une librairie dédiée bpy (pour Blender/Python API).

Pour avoir le retour console, il suffit de lancer Blender depuis la console. Celle-ci affichera alors les sorties une fois le script compilé dans Blender. Voici les différentes étapes afin d’installer des modules tiers fonctionnant dans Blender :

  1. Installation de Blender
  2. Vérification de la commande python (et ajout d’un alias éventuel dans .bashrc, par exemple : pythonb)[pastacode lang="bash" manual="path...%2Fblender-2.79a-linux-glibc219-x86_64%2F2.79%2Fpython%2Fbin%2Fpython3.5m" message="" highlight="" provider="manual"/]
  3. Compiler le script get-pip.py avec cet interpréteur si pip n’est pas déjà installé.
  4. Lancement de pip pour installer vos librairies préférées (vous noterez que je ne passe pas par un virtualenv puisque j’utilise l'interpréteur de Blender, les packages sont installés pour cet interpréteur et non pas pour le système). Attention, il faut bien ajouter -m pour lancer pip. Par exemple :[pastacode lang="bash" manual="pythonb%20-m%20pip%20install%20pandas" message="" highlight="" provider="manual"/]

Félicitations, votre interpréteur Python est prêt à accueillir vos modules python préférés ! (Une installation similaire fonctionne également sous Windows 10 avec Blender 2.79).

Pour interagir avec Blender, il est possible d'utiliser BPY (Blender PYthon module) préinstallé avec l’interpréteur. Il permet notamment d’accéder aux éléments de la scène 3D, de créer/manipuler des objets dans la scène, de simuler des entrées utilisateurs... (Voici également une vidéo d’introduction en anglais présentant des exemples de code simples desquels le code suivant s'inspire.)

Voici également un très petit jeu de données issu du projet Mister Isidor retravaillé assez rapidement.

Il permet de connaître la répartition des appartements en vente ou location ces derniers mois dans différentes villes en fonction de leur nombre de pièces.

[pastacode lang="bash" manual="%22city%22%2C1%2C2%2C3%2C4%2C5%2C6%2C7%2C8%2C9%2C10%2C11%2C12%2C14%2C16%0A%22lyon%22%2C482%2C714%2C1161%2C887%2C419%2C73%2C13%2C9%2C1%2C3%2C0%2C4%2C1%2C0%0A%22nantes%22%2C413%2C707%2C686%2C334%2C119%2C38%2C9%2C10%2C3%2C1%2C0%2C0%2C0%2C1%0A%22strasbourg%22%2C137%2C365%2C590%2C378%2C144%2C29%2C17%2C7%2C4%2C0%2C2%2C0%2C0%2C0" message="data_for_blender.csv" highlight="" provider="manual"/]

L’idée ici est simplement de visualiser par ville (axe des x) et par type d’appartement (axe des y) le nombre d’appartement étudiés (axe des z).

Ci-après le code utilisé pour le rendu :

[pastacode lang="python" manual="import%20bpy%0Aimport%20csv%0Afrom%20math%20import%20radians%0A%0Abpy.context.space_data.font_size%20%3D%2012%0AfPath%20%20%20%3D%20'~%2FDocuments%2Fdata_for_blender.csv'%0AcsvFile%20%3D%20csv.reader(%20open(%20fPath%20)%20)%0Adata%20%20%20%20%3D%20%5B%20row%20for%20row%20in%20csvFile%20%5D%5B1%3A%5D%0A%0AtotalAppt%20%3D%200%0Afor%20k%20in%20range(1%2C14)%3A%0A%20%20%20%20totalAppt%20%2B%3D%20sum(%5B%20int(%20row%5Bk%5D%20)%20for%20row%20in%20data%20%5D)%0A%0Afor%20i%2C%20row%20in%20enumerate(%20data%20)%3A%20%23%20enumeration%20of%20cities%0A%20%20%20%20print(i)%0A%20%20%20%20city%20%3D%20row%5B0%5D%0A%20%20%20%20for%20j%20in%20range(1%2Clen(row))%3A%20%23%20enumeration%20of%20nb%20of%20rooms%0A%20%20%20%20%20%20%20%20current_criterium_appt%20%3D%20row%5Bj%5D%0A%0A%20%20%20%20%20%20%20%20%23%20compute%20an%20appropriate%20ratio%20to%20display%20the%20height%20of%20the%20cube%0A%20%20%20%20%20%20%20%20current_height%20%3D%20(%20int(%20current_criterium_appt%20)%20%2F%20totalAppt%20)%20*%2010%0A%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%23%20create%20the%20cube%20to%20represent%20the%20current%20city%20and%20type%20of%20appt%0A%20%20%20%20%20%20%20%20bpy.ops.mesh.primitive_cube_add(%20%0A%20%20%20%20%20%20%20%20%20%20%20%20radius%20%20%20%3D%200.5%2C%20%20%20%20%20%20%23%20Set%20base%20size%0A%20%20%20%20%20%20%20%20%20%20%20%20location%20%3D%20(%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20i%20*%201.1%2C%20%20%20%20%20%20%20%20%20%23%201.1%20to%20live%20a%20small%20space%20between%20the%20cubes%20%20%20%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20j%20*%201.1%2C%20%20%20%20%20%20%20%20%20%23%201.1%20to%20live%20a%20small%20space%20between%20the%20cubes%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20current_height%2F2%20%20%20%23%20the%20half%20of%20height%2C%20indeed%20it's%20the%20position%20of%20the%20center%20of%20the%20cube%0A%20%20%20%20%20%20%20%20%20%20%20%20)%0A%20%20%20%20%20%20%20%20)%0A%20%20%20%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20cube%20%3D%20bpy.context.object%0A%20%20%20%20%20%20%20%20cube.dimensions.z%20%3D%20current_height%20%23%20the%20height%20that%20we%20have%20computed%0A%20%20%20%20%20%20%20%20if%20i%20%3D%3D%200%20%3A%20%23%20so%20only%20once%20per%20type%20of%20appt%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%23%20Add%20type%20of%20appt%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20bpy.ops.object.text_add(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20location%20%3D%20(%20-1%2C%20j%20*%201.1-0.25%2C%20current_height)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20bpy.context.scene.objects.active.scale%20%3D%20(0.5%2C0.5%2C%200)%20%20%20%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20text%20%3D%20bpy.context.object%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20text.rotation_euler.z%20%3D%20radians(90)%20%23%20Rotate%20text%20by%2090%20degrees%20along%20Z%20axis%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20text.rotation_euler.x%20%3D%20radians(90)%20%23%20Rotate%20text%20by%2090%20degrees%20along%20X%20axis%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20text.data.extrude%20%20%20%20%20%3D%200.05%20%20%20%20%20%20%20%20%23%20depth%20of%20the%20text%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20text.data.bevel_depth%20%3D%200.01%20%20%20%20%20%20%20%20%23%20bevel%20effect%20that%20smoothes%20text's%20edges%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20text.data.body%20%20%20%20%20%20%20%20%3D%20%22T%22%2Bstr(j)%20%20%23%20the%20current%20type%20of%20appt%0A%0A%20%20%20%20%20%20%20%20%23%20Add%20the%20number%20of%20appts%0A%20%20%20%20%20%20%20%20bpy.ops.object.text_add(%0A%20%20%20%20%20%20%20%20%20%20%20%20location%20%3D%20(%20i%20*%201.1%20%2B%200.25%2C%20j%20*%201.1-0.25%2C%20current_height%20%2B%200.2)%0A%20%20%20%20%20%20%20%20)%0A%20%20%20%20%20%20%20%20bpy.context.scene.objects.active.scale%20%3D%20(0.3%2C0.3%2C%200)%20%20%20%20%0A%20%20%20%20%20%20%20%20text%20%3D%20bpy.context.object%0A%20%20%20%20%20%20%20%20text.rotation_euler.z%20%3D%20radians(90)%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20text.data.extrude%20%20%20%20%20%3D%200.05%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20text.data.bevel_depth%20%3D%200.01%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20text.data.body%20%20%20%20%20%20%20%20%3D%20str(current_criterium_appt)%20%23%20total%20number%0A%20%20%20%20%20%20%20%20activeObject%20%3D%20bpy.context.active_object%20%20%20%20%20%20%20%20%20%20%20%20%23%20set%20active%20object%20to%20variable%0A%20%20%20%20%20%20%20%20mat%20%3D%20bpy.data.materials.new(name%3D%22red_mat%22)%20%20%20%20%20%20%20%20%23%20set%20new%20material%20to%20variable%0A%20%20%20%20%20%20%20%20mat.diffuse_color%20%3D%20(1%2C%200%2C%200)%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%23%20change%20color%0A%20%20%20%20%20%20%20%20text.data.materials.append(mat)%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%23%20add%20the%20material%20to%20the%20object%0A%20%20%20%20%20%20%20%20%0A%20%20%20%20%23%20add%20name%20of%20the%20city%0A%20%20%20%20bpy.ops.object.text_add(%0A%20%20%20%20%20%20%20%20location%20%3D%20(%20i%20*%201.1%2B0.2%2C%20-5%2C%200%20)%0A%20%20%20%20)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%0A%20%20%20%20text%20%3D%20bpy.context.object%0A%20%20%20%20text.rotation_euler.z%20%3D%20radians(90)%0A%20%20%20%20text.data.extrude%20%20%20%20%20%3D%200.05%20%20%20%20%20%20%20%20%0A%20%20%20%20text.data.bevel_depth%20%3D%200.01%20%20%20%20%20%20%20%20%0A%20%20%20%20text.data.body%20%20%20%20%20%20%20%20%3D%20city%20%20%20%20%20%20%20%20%23%20the%20city%20name" message="script_blender.py" highlight="" provider="manual"/]

On obtient donc une représentation hybride entre la heat matrix et l'histogramme. Ci-après vous trouverez un rendu Gif du model 3d (le rendu est peu travaillé).

A noter que, par rapport au script précédent, une camera, une lumière et des images clés de rotation ont été ajoutées de manière à réaliser cet export.

L’idée serait bien sûr de pousser le concept plus loin pour arriver à un rendu à la fois pédagogique, scientifiquement juste mais s’adressant plus efficacement au ressenti de chacun, voire captant plus facilement l’intérêt via des animations.

Dans les idées futures, il serait notamment intéressant de :

  • Monter en compétences en modélisation et scripting pour proposer des visualisation plus pédagogiques/attrayantes.
  • Proposer des vidéos de visualisation 3D
  • Proposer des rendus 3D dans la page web en utilisant blend4Web ou opengl voire en intégrant les graphiques obtenus dans des moteurs de jeux pour les explorer en temps réels ou dans des expériences VR

par Nicolas DAVID, Ingénieur R&D et Data Scientist chez Verteego

Inscrivez-vous à la newsletter