🧑🏻‍💻 Comment j'ai hacké des panneaux publicitaires ? 🖼️

Vincent Ogloblinsky - @vogloblinsky

Vincent Ogloblinsky

Compodoc maintainer

Web software architect

Indie hacker on side-projects

Démo

Agenda

1.

Le problème

2.

Trouver les 4 coins

3.

Le temps réel

4.

La précision

5.

Le rendu et l'art

6.

Développer avec une IA

7.

Ce que j'en retiens

Le problème

Le but : poser une œuvre à la place de la pub

Il faut savoir exactement où est la surface d'affichage, au pixel près.

De face : un rectangle

De biais : un quadrilatère

Dans les deux cas : 4 côtés, 4 coins.

Et vous, vous chercheriez quoi dans l'image ?

  • un rectangle englobant, « comme YOLO »
  • les pixels du panneau
  • les 4 bords
  • les 4 coins

Toutes ces idées, je les ai testées

Environnement technique

  • Navigateur mobile : ONNX dans un worker, OpenCV.js, WebGL
  • De vrais téléphones : iPhone 13 mini, Galaxy A52, TCL 403
  • Une surface plate : 4 coins suffisent pour tout reconstruire

Déjà fait ?

No Ad (2011 - NY USA) : œuvres pré-assignées à des panneaux connus.

Ici, n'importe quel panneau.

Trouver
les 4 coins

Ce qui existe : des CNN… pour une autre question

1. Trouver les coinsCNN
→
2. Les suivreflot optique
→
3. Les affineredge-fitting
→
4. Homographiewarp WebGL
→
5. Une œuvreWikidata
  • CNN (réseau de neurones convolutif) entraînés sur des millions d'images : ImageNet, COCO, Open Images
    CNN = un œil artificiel qui apprend tout seul, à partir d'exemples, à passer des pixels aux bords, des bords aux formes, et des formes aux objets.
  • YOLO (« You Only Look Once ») est une famille de CNN spécialisés dans la détection d'objets. Il ne répond que par une bbox : un rectangle droit, jamais en perspective
  • Aucun dataset de panneaux pub annotés par leurs 4 coins
bboxle vrai panneau

Donc : fabriquer mon propre dataset

~900panneaux annotés à la main
4clics par image, avec une loupe

Photos du web, d'Open Images, et de terrain à Rennes.
+ des images sans panneau, pour apprendre à ne rien voir

Un réseau de vision, c'est 2 parties

l'image « Qu'est-ce que c'est ? » → un panneau « Où sont les coins ? » → 4 cartes, 1 par coin
BACKBONE · l'œil
Regarde l'image et la résume, zone par zone : ici un bord, là un angle, là une surface claire.
Il décrit, il ne répond à aucune question. Grosse partie, apprise sur des millions de photos : on la réutilise.
TÊTE · le cerveau
Lit ce résumé et répond à la question posée, dans le format demandé.
Elle ne voit jamais l'image. Petite partie : on la change selon la question.

Même œil, autre question → autre réponse.

On garde l'œil, on change le cerveau

image 224 × 224 BACKBONE l'œil le résumé : 7 × 7 zones TÊTE ImageNet « quelle catégorie ? » 1 étiquette parmi 1 000 on jette ce cerveau NOTRE TÊTE « où sont les coins ? » agrandit le résumé × 8 4 cartes de 56 × 56 réutilisé + affiné neuve, apprise de zéro

Entraînement sur mes ~500 photos de panneaux annotées : l'œil et le cerveau apprennent ensemble. Quand un coin est mal placé, l'erreur remonte du cerveau jusqu'à l'œil, qui s'ajuste un peu.

Pourquoi ça marche avec si peu d'images : l'œil a déjà vu des millions de photos. L'essentiel à apprendre, c'est notre question, au cerveau.

Vos réponses, mesurées

Même œil, autre question : seul le cerveau (la tête) change.

Votre réponse Ce que j'ai entraîné Erreur / coin
en px, image 224 × 224
« Un rectangle, comme YOLO » bbox, puis les coins dedans 12,5 px
« Les pixels du panneau » segmentation, puis coins du masque 12,0 px
« Les 4 coins » régression directe : 8 nombres 11,7 px
« Les 4 bords » une carte par côté, coins aux croisements 7,7 px
« Les 4 coins » en mieux une carte par coin (heatmaps) 7,2 px

Erreurs mesurées dans l'image réduite vue par le modèle (224 × 224), pas dans la photo d'origine.
Même jeu de test fixe de 119 images, même backbone.

Même cible, 11,7 px contre 7,2 px

Régression : des coordonnées

La tête répond directement la position : 8 nombres, un (x, y) par coin.

haut-gauche → (57, 50)

Une seule réponse, sans nuance.

Heatmap : une carte de chaleur

La tête répond une image par coin. Chaque case dit « le coin est-il ici ? », du froid au chaud.

le coin = la case la plus chaude

Et quand le réseau hésite entre le bord du cadre et le bord de l'affiche ?

il répond la moyenne :un point dans le vide
deux zones s'allument :on garde la plus forte

Analogie · « Où sont tes clés ? » Cuisine ou entrée… Une seule réponse : « dans le couloir ». Une carte : on entoure les deux, on va voir la plus probable.

À quoi ressemblent ces cartes

L'image, puis une carte par coin : haut-gauche, haut-droit, bas-droit, bas-gauche.

D'où vient le gain ?

régression + ShuffleNetV2
11,7 px
→
on change
la tête
−4,5 px
heatmaps + ShuffleNetV2
7,2 px
→
on change
l'œil
−1,5 px
heatmaps + MnasNet-A1
5,7 px

Erreur moyenne par coin, en px de l'image 224 × 224 vue par le modèle. On ne change qu'une chose à la fois.

Pourquoi changer la tête rapporte 3 fois plus que changer l'œil ?

Elle parle la langue de l'œil

L'œil décrit l'image zone par zone, comme une carte. La heatmap répond aussi par une carte : chaque case dit juste « c'est ici ». La régression doit tout résumer en 8 nombres.

Elle a le droit d'hésiter

Deux candidats ? Deux zones s'allument, on garde la plus forte. La régression, elle, coupe la poire en deux et tombe dans le vide.

Elle apprend plus par image

À chaque photo, des milliers de cases sont corrigées, pas 8 nombres. Précieux quand on n'a que quelques centaines d'images.

Analogie · montrer le coin du doigt sur la photo, plutôt que dicter ses coordonnées GPS.

Choisir l'œil : un compromis

On ne peut pas tout avoir : un œil plus fin voit mieux, mais met plus de temps à regarder. Et sur téléphone, chaque milliseconde compte.

Erreur par coin · px (224 × 224)
plus court = plus précis
Temps par image · iPhone 13 mini
plus court = plus rapide
EfficientNet-Lite0
5,1 px
67 ms
MnasNet-A1 ★ retenu
5,7 px
58 ms
MobileNetV2
6,1 px
57 ms
ShuffleNetV2
7,2 px
55 ms

Barres à l'échelle, à partir de zéro (0–9 px et 0–90 ms).

MnasNet-A1 : −1,5 px par rapport à ShuffleNetV2, pour seulement +3 ms avec une image d'entrée de 320px
EfficientNet-Lite0 : encore −0,6 px… mais +9 ms de plus. Ça ne vaut pas le coût.

Et plus léger ne veut pas dire mieux : MobileNetV3-Small, conçu pour le mobile, fait 11,1 px, aussi mal que la régression.

Le temps réel

De la photo à la vidéo

1. Trouver les coinsCNN heatmaps
→
2. Les suivreflot optique
→
3. Les affineredge-fitting
→
4. Homographiewarp WebGL
→
5. Une œuvreWikidata

Jusqu'ici : des photos. Maintenant : une vidéo, à ~25 images par seconde.

Budget par frame
40 ms
CNN sur téléphone
~200 ms · 5× trop lent

Un détective et un suiveur

CNNflotflotflotflotflotflotflotflotflot CNNflotflotflotflotflotflotflotflotflot

CNN : 1 frame sur 10 flot optique + homographie : les autres

Entre deux images, un panneau plat bouge un peu, et de façon prévisible.

Analogie · on cherche un ami une fois dans la foule, puis on le suit des yeux.

Le « pipeline hybride » : un réseau de neurones + de la vision classique sans apprentissage.

Le moment « et merde ! 😡 »

Le suivi, censé alléger le travail, rendait l'app 7× plus lente que le CNN seul sur iPhone.

L'indice : démarrer le suivi et continuer le suivi coûtaient pareil. Le coupable est donc l'étape commune.

Démarrer
préparer l'image
→
chercher des points remarquables sur le panneau
Chaque image
préparer l'image
→
retrouver ces points dans l'image suivante

Préparer l'image = copier l'image de la caméra, puis la passer en noir et blanc (le suivi ne regarde que la luminosité).

Avant
toute l'image caméra,
en pleine résolution,
à chaque image
Après
seulement la zone du panneau
(+ une marge), réduite
à 320 px maximum
758 ms → ~40 ms
temps du suivi par image
(le CNN seul : 55 ms)

Profiler avant de juger une architecture.

Et encore…

Le flot (optique) = le suivi entre deux passages du CNN : on mesure de combien chaque point du panneau a bougé d'une image à la suivante.

Gels
Sur téléphone lent, à chaque passage du CNN, l'image se fige ~430 ms.
Le CNN calcule en arrière-plan, le suivi continue. Sa réponse arrive en retard (elle décrit une image vieille de 200 à 400 ms) : on la décale du mouvement mesuré par le flot entre-temps pour la remettre à jour.
Tremblements (« jitter »)
Téléphone immobile, les coins bougent quand même de quelques pixels : l'œuvre frétille.
Une moyenne glissante (EMA) : à chaque image, on n'avance que de 35 % vers la nouvelle position. Les petites secousses s'annulent. −41 à −47 % de tremblement, sans coût en temps.
Limite : panneau déroulant
L'affiche défile à l'intérieur du cadre.
Le flot suit les points de l'affiche qui défile… et emporte les coins avec elle, jusqu'à 150 px, jusqu'au passage suivant du CNN. Pas encore résolu.

La précision

Pourquoi maintenant ?

1. Trouver les coinsCNN heatmaps
→
2. Les suivreflot optique
→
3. Les affineredge-fitting
→
4. Homographiewarp WebGL
→
5. Une œuvreWikidata

Dans la rue : l'œuvre déborde du cadre, un liseré de pub dépasse.

7 pxmesurés dans l'image réduite du modèle
~15 pxsur la vraie vidéo, à l'écran

Pourquoi ? Le modèle ne voit qu'une image réduite, et répond avec une grille de cases :

Vidéo du téléphone
1080 × 1920 px
→
réduite
÷ 6
Image du modèle
320 px de haut
→
découpée
÷ 4
Carte de chaleur
80 × 80 cases
=
1 case = 24 px
de vidéo (6 × 4)

Le modèle désigne une case, pas un pixel : ~15 px d'erreur, c'est à peine plus d'une demi-case.

Analogie · un point juste sur une carte au 1/100 000 devient une erreur de plusieurs maisons sur le plan du quartier.

Fausse bonne idée : « augmente la résolution »

Au-delà de 448 px, des coins partent sur un mât, un kiosque.

Les 4 cartes deviennent identiques : le backbone a appris à voir à une certaine échelle.

Un premier correctif « marchait » sur 3 exemples… et échouait sur les 119.

Ce qui marche : l'edge-fitting

1. Le CNN : « où est le panneau ? »
Il le trouve dans toute l'image… à ~15 px près.
→
2. L'edge-fitting : « où est exactement son bord ? »
Il cherche juste autour, dans la vidéo en pleine résolution.

Un complément, pas un remplacement : seul, l'edge-fitting serait perdu parmi les milliers de bords d'une rue.

cadre (sombre)affiche (claire) côté estimé par le CNN droite ajustée = vrai bord
1. Le point de départ
Le côté donné par le CNN (pointillés bleus) : proche du bord, mais pas dessus.
2. On sonde
16 petits traits en travers du côté (en jaune). Chacun explore 18 px de part et d'autre.
3. On trouve le bord
Sur chaque trait, on garde l'endroit où l'image passe le plus brusquement du sombre au clair : c'est là qu'est le vrai bord.
4. On trace la droite
Une droite passe au mieux par ces points (en vert), les points aberrants (reflet, poteau) sont écartés. On refait ça sur les 4 côtés : les coins = les croisements des 4 droites.

Résultat

15,0 px → 6,7 pxerreur moyenne, 84 frames de vraies vidéos

Coût : ~90 ms sur iPhone… mais seulement 1 frame sur 10. Fluidité quasi intacte.

Analogie · le CNN montre du doigt le coin du tableau, de loin. L'edge-fitting s'approche et pose une règle le long de chaque bord.

Le CNN dit « à peu près où », la géométrie dit « exactement où ».

Puis la rue complique tout

L'abribus à double cadre : gris clair, puis bordure noire

fond cadre gris clair bordure noire affiche vers l'intérieur → CNN zone de recherche (±18 px) ✗ gris → noir : s'assombrit ✓ noir → affiche : s'éclaircit
Le piège
Le CNN pose le coin au milieu de la bordure noire. Ses deux bords tombent dans la zone de recherche.
Le symptôme
Chaque recherche prend le bord le plus net du moment : la droite saute de l'un à l'autre, l'œuvre se décale de 25 à 40 px.
La règle
En allant vers l'intérieur, ne garder que les bords qui passent du sombre au clair. Seul le bord de l'affiche passe le test.

Le rendu
et l'art

4 coins → une œuvre

1. Trouver les coinsCNN heatmaps
→
2. Les suivreflot optique
→
3. Les affineredge-fitting
→
4. Homographiewarp WebGL
→
5. Une œuvreWikidata
œuvre H (3×3)

Homographie : la formule qui déforme un rectangle pour le poser sur les 4 coins du panneau. Calculée par la carte graphique (WebGL).

D'où viennent les œuvres ?

Wikidata : la base de connaissances libre derrière Wikipédia. Une fiche par tableau : titre, artiste, musée, dimensions en cm (donc son ratio).
Wikimedia Commons : la photo du tableau, en domaine public.
Ce qu'on garde : des peintures uniquement, avec une image et des dimensions, conservées aujourd'hui dans un musée français. Classées en 7 univers selon le mouvement de l'artiste (fauvisme, cubisme, pointillisme…).

V1 : interroger Wikidata en direct

SPARQL = le langage pour interroger Wikidata, le « SQL » des données liées. À chaque scan, l'app demandait (simplifié) :

SELECT ?tableau ?image WHERE { ?tableau wdt:P31 wd:Q3305213 ; # une peinture wdt:P195 ?musee ; # dans ce musée wdt:P18 ?image ; # avec une image wdt:P2048 ?h ; wdt:P2049 ?l . FILTER(?l / ?h > 0.59 && ?l / ?h < 0.78) VALUES ?musee { … 10 musées proches … } }

« Une peinture, avec image, au ratio du panneau, dans un musée proche. »

Ce qu'on a mesuré

6 à 17 spar requête, avec 20 à 100 musées. Les yeux sur le panneau, à attendre.
> 100musées dans une requête : refus (429) ou délai dépassé (504), à coup sûr. On en a ~200.
~60 %d'échecs un jour de panne du service, avec des attentes de 50 s et plus.
Les rustines
10 musées maximum par requête, coupure à 12 s, relance d'un nouveau scan en cas d'échec. Ça tient… tant que le service va bien.
Le vrai problème
Wikidata est un service public et partagé, gratuit, sans garantie de temps de réponse. Ce n'est pas notre serveur.
L'idée
Les tableaux ne changent pas de musée tous les jours. Interroger Wikidata une fois, chez moi, et embarquer le résultat dans l'app.

V2 : un catalogue embarqué

5 690
peintures
541
artistes
220
musées français
7
univers
1 478
avec une histoire

Construit une fois, chez moi

Un script « aspire » Wikidata (~5 min) puis les textes Wikipédia des œuvres (2 à 3 h).

→ un fichier JSON de ~950 Ko (~270 Ko compressé), livré avec l'app.

Au scan : tirage local et instantané, même sans réseau. Seule la photo vient encore de Commons, en miniature.

Mettre à jour = relancer le script.

Le tirage, à chaque scan

5 690 peintures
↓
L'univers choisi (ex. Lumière & plein air : 1 287)
↓
Le ratio du panneau, ±15 % (un abribus : 23 % du catalogue)
↓
Autour de moi : les 10 musées les plus proches, le plus près a le plus de chances
↓
Bonus ×3 si le tableau a une histoire, jamais 2 fois le même musée de suite

Au final

Développer
avec une IA

Beaucoup moins de temps qu'il y a 1 ou 2 ans

Ce qui a changé : pas la quantité de code, la vitesse d'itération

L'IA

  • l'outillage : annotation, banc de test sur téléphone, évaluation
  • l'exploration large : 5 têtes, ~10 backbones
  • l'analyse des résultats, l'expérience suivante

Moi

  • le terrain : filmer, tester dans la rue
  • l'annotation
  • « il y a un truc qui cloche »
  • les arbitrages

Le nouveau problème : piloter

5 hypothèses et 3 expériences par jour : qu'est-ce qui est prouvé ? écarté ? pourquoi ?

knowledge/ : un mini graphe de connaissances en Markdown, dans le repo.

terrain
(moi)
→
IA : analyse
+ hypothèse
→
expérience
mesurée
→
IA : verdict
 
→
décision
(moi)
41affirmations
35expériences
17décisions, en 2 semaines

Deux règles contre l'enthousiasme

« L'auteur n'est pas une preuve. »

Une hypothèse reste une opinion tant qu'aucune expérience ne l'a testée.

On ne supprime pas une hypothèse réfutée.

Elle explique pourquoi on n'a pas pris ce chemin.

Ce que j'en retiens

  1. Le plaisir de donner vie à une idée folle
    celle qu'on ne lance jamais : vision, deep learning, mobile, web, données d'art
  2. On va encore plus vite qu'avant
    investiguer → prototyper → déployer → mesurer → améliorer → livrer
  3. Mais attention
    au « yes man » de l'IA, aux super-pouvoirs qu'on croit avoir…
    les utilisateurs finaux auront le dernier mot

Thanks for your attention ! 

Any questions ?

Crédit photos - Unsplash.com

🧑🏻‍💻 Comment j'ai hacké des panneaux publicitaires ? 🖼️

By Vincent Ogloblinsky

🧑🏻‍💻 Comment j'ai hacké des panneaux publicitaires ? 🖼️

Remplacer en direct toute la pollution visuelle des panneaux publicitaires par un chef-d'œuvre du domaine public, directement depuis votre téléphone, c'est pas cool ? Moi je trouvais l'idée fun, alors j'ai récemment fabriqué un pipeline de computer vision capable de repérer un panneau dans une vidéo, d'en suivre les 4 coins en temps réel, et d'y projeter une œuvre d'art, le tout dans un simple navigateur web, sur votre téléphone. Venez découvrir la sorcellerie derrière ce joli hack, entre réseau de neurones à convolution (CNN), suivi optique, homographie et API d'œuvres d'art, je vous raconte les galères, les fausses bonnes idées et ce que j'en ai appris pour faire du temps réel sur mobile sans un datacenter derrière.

  • 241