Le stock de vues immersives

Bonsoir,
Ce fil a pour vocation de partager un inventaire des stocks de vues immersives, au gré des apports des uns et des autres. Je l’amorce avec un état de prises de vues réalisées essentiellement en lien avec la contribution à Mapillary, où chaque contributeur a gardé une version des photos envoyées à la plateforme.

Les quelques chiffres et cartes sont issus de l’exploitation des données EXIF de chaque image, essentiellement ici les coordonnées, l’horodatage et le nom de chaque contributeur.

On dénombre 6.2 millions de photos, prises par 15 contributeurs, et traversant 3464 communes de métropole. Il existe aussi dans le stock des PDV hors des frontières : New York, Irlande, et Belgique notamment.

Vue à raison d’une couleur par contributeur

Vue des communes avec au moins 1 cliché

5 « J'aime »

Ça peut paraître vide, comme cette carte:

Il s’agissait d’OpenStreetMap au 1er janvier 2007…

Ce stock de photos permet à minima de faire des tests, d’expérimenter. Il y a sûrement d’autres gisements déjà existants car les collectivités ont souvent fait réaliser ce type de prises de vues.

2 « J'aime »

Ci-dessous état des lieux évolutif de la couverture en photos 360° sur Rennes Métropole.
État non figé car il nous reste encore quelques Go à téléverser. Nous avons commencé il y a quelques mois et ce n’est pas fini. Les photos s’étalent de 2017 à 2021. Il y aura du 2022. A ce jour : pas de mise à jour programmée de façon aussi systématique.

Aucune idée du volume ni du nb de photos traitées. Si qqn sait comment extraire cette info depuis mapillary : je suis preneur.

lien direct

1 « J'aime »

Avez-vous conservé une copie des photos ?

Je pose la question car on ne peut plus récupérer les version pleine définition comme avant (le rachat par Facebook/Meta).

Si vous avez ça sur disque, un petit coup d’exiftool en mode récursif permet de récupérer toutes les métadonnées en json et sans effort.

Bien sûr que nous avons le stock de To de photos sur nos dizaines de DD externes : tkt :stuck_out_tongue_winking_eye:

Et en plus avec de bonnes métadonnées / en-têtes dorénavant grâce à @PanierAvide

2 « J'aime »

Est-ce qu’il ne serait pas aussi intéressant d’avoir stockage très long terme (cold storage) pour garder toutes les images.

1 « J'aime »

Le stockage sur disques durs rangés dans une armoire devient rapidement économique vus les tarifs actuels de cold storage (le tarif C14 de scaleway, revient à 24€ HT/an pour 1To).

Pour la sécurité des données, je confierai ça à ZFS avec 1 disque de redondance sur un pool de 3 minimum, ou 2 disques pour un plus gros pool.

C’est aussi un moyen de réutiliser des disques un peu anciens à la capacité devenue limitée. J’ai des 2 et 3To qui terminent leur carrière de plus en plus comme ça, remplacés par des 6 ou 10To voire plus qui consomment 3 fois moins d’énergie à volume stocké équivalent.

Les 18To de photos récupérées de Mapillary il y a deux ans par une vingtaine de contributeur OSM sont ainsi restés sur un pool ZFS de 6+2 disques de 3To rangés dans un carton et maintenant remis dans un serveur.

1 « J'aime »

Comme d’autres j’ai du stock de vues en provenance de mapillary et kartaview (openstreetcam), comment leur faire rejoindre le stock de vues immersives, y’a-t-il un lieu qui existe pour les déposer ?

Je cherche aussi des moyens récents de faire de l’export de mapillary et kartaview pour les anciennes captures, en attendant je fais des copies des fichiers mon tel avant envoi sur les dits services.

1 « J'aime »

Bonjour,

J’ai compris que les premiers script de récupération des photos sur Mapillary sont obsolètes depuis la dernière version de l’API.

Maintenant que la version V4 est disponible, est-il possible de revoir les codes du script ?

En effet, j’ai des photos sur Mapillary dont je n’ai aucune sauvegarde.

TLDR: Ce n’est pas possible autrement qu’en très basse définition.

Mapillary ne conserve plus la version pleine définition non floutée, et apparemment ne conserve pas non plus la version non “tuilée”. Donc il n’y a matériellement plus de solution simple pour récupérer une photo pleine def.

Il y a quelques hack qui circulent, qui permettent de récupérer l’intégralité des tuiles d’une photo dont on connait l’identifiant, mais rien de bien probant et rien pour passer à l’échelle.

1 « J'aime »

Bonjour,

Pour l’extraction depuis Mapillary, j’ai trouvé un URL qui permet d’obtenir toutes les photos d’une séquence donnée au format au choix dont le format original dans un JSON.
Il certainement possible de créer un script qui récupère toutes les séquences d’un utilisateur “access_token” et de balayer ces séquences pour récupérer les images.
Pour obtenir un “access_token” il faut créer un compte développeur à partir de son compte
Les images récupérées sont floutées mais est-ce un problème ? je n’ai pas trouvé de paramètre pour trouver les non floutées.

Dans le script, certainement prévoir un paramètre de “quantité” pour éviter les limitations imposé par mapillary https://www.mapillary.com/developer/api-documentation#rate-limits

https://graph.mapillary.com/images?access_token=MLY|&fields=id,altitude,atomic_scale,camera_parameters,camera_type,captured_at,compass_angle,computed_altitude,computed_compass_angle,computed_geometry,computed_rotation,exif_orientation,height,width,merge_cc,sfm_cluster,sequence,geometry,mesh,detections,thumb_256_url,thumb_1024_url,thumb_2048_url,thumb_original_url&sequence_ids=dbu7kk2xxvx5t42pxgnizb

Tu as regardé quelle était la résolution des images récupérées ? Ce sont certainement les versions basse définition, et tout le problème est là.

Bonjour,

Il y a un paramètre visible dans le lien ci-dessus “thumb_xxx”
Plusieurs taille d’image dispo jusqu’au format original “thumb_original” (je n’ai pas tester les 360).

2 « J'aime »

Merci !
J’ai creusé un peu, et je suis tombé sur cet exemple : Download Mapilarry images with API v4 - #24 by bgo_eiu - Imagery - Mapillary Community Forum
J’ai réussi à récupérer une image 360 en pleine définition (12998x6499). Elle est un peu plus compressée que l’original, est floutée, et ne contient plus aucune métadonnées. C’est déjà un pas en avant, car on ne récupère pas les tuiles séparées mais bien l’image complète. Quant aux métadonnées, on les reçoit lors de l’appel à l’Api, ce qui permet de les réinjecter par la suite.
A surveiller aussi : GitHub - tobesucht/mapillary_downloader: The tool provides the option to download from Mapillary v4 all kind of vector data and street imagery for a given region

800km d’images disponibles sur notre Agglo et une nouvelle campagne de mise à jour à partir du 1er mars sur nos secteurs à enjeux.

5 « J'aime »

Ça avance !

J’ai un script qui permet de récupérer les photos en définition originale d’une séquence (un poil plus compressée), et de réinjecter certaines métadonnées dedans (géoloc, direction, timestamp, panoramique ou pas).
En argument du script, on donne un jeton créé depuis son compte Mapillary, ainsi que l’identifiant d’une séquence.

Ici un exemple avec une séquence de 156 photos

Je vais essayer de trouver un peu de temps pour mettre le script un peu plus au propre, car pour l’instant c’est vraiment du quick en dirty

Une fonctionnalité manquante pour le moment, c’est de récupérer facilement les séquences d’un contributeurs. Le “hack” que j’ai trouvé semble ne plus fonctionner. Si quelqu’un souhaite se pencher sur ce problème, voici des explications :

2 « J'aime »

Ça pourrait m’intéresser de récupérer mes séquences, au moins depuis que je suis en RTK. Peut-être pas tout d’un coup question place mais quand même. À la rigueur, je dois pouvoir refaire la géoloc. Juste une question de temps :sweat_smile:.

Un script (merci ChatGPT) pour récupérer des séquences et rajouter les EXIF dans les images

import requests
from PIL import Image
import piexif
from io import BytesIO

def to_deg(value, loc):
    """Convert decimal coordinates to degrees, minutes, seconds tuple"""
    if value < 0:
        loc_value = loc[0]
    else:
        loc_value = loc[1]
    abs_value = abs(value)
    deg = int(abs_value)
    t1 = (abs_value - deg) * 60
    min = int(t1)
    sec = round((t1 - min) * 60, 5)
    return (deg, min, sec, loc_value)

# URL de l'API Mapillary
url = "https://graph.mapillary.com/images?access_token=MLY|XXXXXXXXXXXXXXXXXXXXXXXXXAPIXXXXXXXX&fields=id,altitude,atomic_scale,camera_parameters,camera_type,captured_at,compass_angle,computed_altitude,computed_compass_angle,computed_geometry,computed_rotation,exif_orientation,height,width,merge_cc,sfm_cluster,sequence,geometry,mesh,detections,thumb_256_url,thumb_1024_url,thumb_2048_url,thumb_original_url&sequence_ids=Pvdg_DcsRrqwdVk8ZrcLeg"

# Récupérer les données JSON
response = requests.get(url)
data = response.json()

# Parcourir les données
for image_data in data['data']:
    img_url = image_data['thumb_original_url']
    coordinates = image_data['computed_geometry']['coordinates']
    
    # Télécharger l'image
    img_response = requests.get(img_url)
    img = Image.open(BytesIO(img_response.content))
    
    # Modifier les données EXIF
    exif_dict = piexif.load(img.info["exif"])
    
    lat_deg = to_deg(coordinates[1], ["S", "N"])
    lng_deg = to_deg(coordinates[0], ["W", "E"])
    
    exif_dict["GPS"][piexif.GPSIFD.GPSLatitudeRef] = lat_deg[3]
    exif_dict["GPS"][piexif.GPSIFD.GPSLatitude] = (int(lat_deg[0]), 1), (int(lat_deg[1]), 1), (int(lat_deg[2]*100000), 100000)
    exif_dict["GPS"][piexif.GPSIFD.GPSLongitudeRef] = lng_deg[3]
    exif_dict["GPS"][piexif.GPSIFD.GPSLongitude] = (int(lng_deg[0]), 1), (int(lng_deg[1]), 1), (int(lng_deg[2]*100000), 100000)
    
    exif_bytes = piexif.dump(exif_dict)
    
    # Sauvegarder l'image avec les nouvelles données EXIF
    img.save(f"{image_data['id']}.jpg", exif=exif_bytes)

print("Téléchargement et modification des images terminés.")

Résultat dans QGIS (plugin import photos)

Ca marche plutôt bien

La page envoyer aime pas cet exif bricolé, avec cette correction ça a l’air de mieux marcher l’import :

import requests
from PIL import Image
import piexif
from io import BytesIO

# Fonction pour convertir les coordonnées décimales en degrés, minutes, secondes
def to_deg(value, loc):
    if value < 0:
        loc_value = loc[0]
    else:
        loc_value = loc[1]
    abs_value = abs(value)
    deg = int(abs_value)
    t1 = (abs_value - deg) * 60
    min = int(t1)
    sec = round((t1 - min) * 60, 5)
    return (deg, min, sec, loc_value)

# URL de l'API Mapillary
url = "https://graph.mapillary.com/images?access_token=MLY|XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX&fields=id,altitude,atomic_scale,camera_parameters,camera_type,captured_at,compass_angle,computed_altitude,computed_compass_angle,computed_geometry,computed_rotation,exif_orientation,height,width,merge_cc,sfm_cluster,sequence,geometry,mesh,detections,thumb_256_url,thumb_1024_url,thumb_2048_url,thumb_original_url&sequence_ids=Pvdg_DcsRrqwdVk8ZrcLeg"

# Récupérer les données JSON
response = requests.get(url)
data = response.json()

# Parcourir les données
for image_data in data['data']:
    img_url = image_data['thumb_original_url']
    coordinates = image_data['computed_geometry']['coordinates']
    
    # Télécharger l'image
    img_response = requests.get(img_url)
    img = Image.open(BytesIO(img_response.content))
    
    # Créer un dictionnaire EXIF fictif pour un appareil Google Pixel
    exif_dict = {
        "0th": {
            piexif.ImageIFD.Make: "Google",
            piexif.ImageIFD.Model: "Pixel",
        },
        "Exif": {
            piexif.ExifIFD.DateTimeOriginal: "2023:09:19 10:00:00",
            piexif.ExifIFD.LensMake: "Google",
            piexif.ExifIFD.LensModel: "Pixel Lens",
        },
        "GPS": {}
    }
    
    # Ajouter les coordonnées GPS
    lat_deg = to_deg(coordinates[1], ["S", "N"])
    lng_deg = to_deg(coordinates[0], ["W", "E"])
    
    exif_dict["GPS"][piexif.GPSIFD.GPSLatitudeRef] = lat_deg[3]
    exif_dict["GPS"][piexif.GPSIFD.GPSLatitude] = (int(lat_deg[0]), 1), (int(lat_deg[1]), 1), (int(lat_deg[2]*100000), 100000)
    exif_dict["GPS"][piexif.GPSIFD.GPSLongitudeRef] = lng_deg[3]
    exif_dict["GPS"][piexif.GPSIFD.GPSLongitude] = (int(lng_deg[0]), 1), (int(lng_deg[1]), 1), (int(lng_deg[2]*100000), 100000)
    
    # Convertir le dictionnaire EXIF en bytes
    exif_bytes = piexif.dump(exif_dict)
    
    # Sauvegarder l'image avec les nouvelles données EXIF
    img.save(f"{image_data['id']}.jpg", exif=exif_bytes)

print("Téléchargement et modification des images terminés.")


par contre la séquence est moche, faut aussi qu’on récupère date/heure

Ajout de la date dans les fichiers, mais pas mieux dans la séquence

import requests
from PIL import Image
import piexif
from io import BytesIO
from datetime import datetime

def to_deg(value, loc):
    if value < 0:
        loc_value = loc[0]
    else:
        loc_value = loc[1]
    abs_value = abs(value)
    deg = int(abs_value)
    t1 = (abs_value - deg) * 60
    min = int(t1)
    sec = round((t1 - min) * 60, 5)
    return (deg, min, sec, loc_value)

url = "https://graph.mapillary.com/images?access_token=MLY|XXXXXXXXXXXXXXXXXXXXXXXXXXXXX&fields=id,altitude,atomic_scale,camera_parameters,camera_type,captured_at,compass_angle,computed_altitude,computed_compass_angle,computed_geometry,computed_rotation,exif_orientation,height,width,merge_cc,sfm_cluster,sequence,geometry,mesh,detections,thumb_256_url,thumb_1024_url,thumb_2048_url,thumb_original_url&sequence_ids=Pvdg_DcsRrqwdVk8ZrcLeg"

response = requests.get(url)
data = response.json()

for image_data in data['data']:
    img_url = image_data['thumb_original_url']
    coordinates = image_data['computed_geometry']['coordinates']
    captured_at = datetime.fromtimestamp(image_data['captured_at'] / 1000.0).strftime('%Y:%m:%d %H:%M:%S')

    img_response = requests.get(img_url)
    img = Image.open(BytesIO(img_response.content))

    exif_dict = {
        "0th": {
            piexif.ImageIFD.Make: "Google",
            piexif.ImageIFD.Model: "Pixel",
        },
        "Exif": {
            piexif.ExifIFD.DateTimeOriginal: captured_at,
            piexif.ExifIFD.LensMake: "Google",
            piexif.ExifIFD.LensModel: "Pixel Lens",
        },
        "GPS": {}
    }

    lat_deg = to_deg(coordinates[1], ["S", "N"])
    lng_deg = to_deg(coordinates[0], ["W", "E"])

    exif_dict["GPS"][piexif.GPSIFD.GPSLatitudeRef] = lat_deg[3]
    exif_dict["GPS"][piexif.GPSIFD.GPSLatitude] = (int(lat_deg[0]), 1), (int(lat_deg[1]), 1), (int(lat_deg[2]*100000), 100000)
    exif_dict["GPS"][piexif.GPSIFD.GPSLongitudeRef] = lng_deg[3]
    exif_dict["GPS"][piexif.GPSIFD.GPSLongitude] = (int(lng_deg[0]), 1), (int(lng_deg[1]), 1), (int(lng_deg[2]*100000), 100000)

    exif_bytes = piexif.dump(exif_dict)

    file_name = f"{image_data['id']}_{captured_at.replace(':', '-').replace(' ', '_')}.jpg"
    img.save(file_name, exif=exif_bytes)

print("Téléchargement et modification des images terminés.")


EDIT

Pourtant ça ressemble à une séquence : (Anim QGIS) sur le timestamp

output

Data :

Jeu de données JPG : Télécharger les fichiers

Restera à :

  • trouver comment lister toutes les séquences d’une zone
  • Voir pourquoi les séquence mal importées/comprises/bug ?