Diff raster: comment vérifier si les images ont des valeurs identiques?

10

Existe-t-il un moyen de vérifier si 2 couches raster données ont un contenu identique ?

Nous avons un problème sur le volume de stockage partagé de notre entreprise: il est maintenant si important qu'il faut plus de 3 jours pour effectuer une sauvegarde complète. Une enquête préliminaire révèle que l'un des plus grands coupables consommant de l'espace sont les rasters on / off qui devraient vraiment être stockés sous forme de couches 1 bit avec la compression CCITT.

un raster typique présent / non présent

Cet exemple d'image est actuellement de 2 bits (donc 3 valeurs possibles) et enregistré en tant que tiff compressé LZW, 11 Mo dans le système de fichiers. Après avoir converti en 1 bit (donc 2 valeurs possibles) et appliqué la compression CCITT Group 4, nous le réduisons à 1,3 Mo, soit presque un ordre de grandeur d'économies.

(Il s'agit en fait d'un citoyen très bien élevé, il y en a d'autres stockés sous forme de flotteur 32 bits!)

Ce sont des nouvelles fantastiques! Cependant, il y a près de 7 000 images à appliquer également. Il serait simple d'écrire un script pour les compresser:

for old_img in [list of images]:
    convert_to_1bit_and_compress(old_img)
    remove(old_img)
    replace_with_new(old_img, new_img)

... mais il manque un test essentiel: la nouvelle version compressée est-elle identique au contenu?

  if raster_diff(old_img, new_img) == "Identical":
      remove(old_img)
      rename(new_img, old_img)

Existe-t-il un outil ou une méthode qui peut (dé) prouver automatiquement que le contenu de l'Image-A est identique en valeur au contenu de l'Image-B?

J'ai accès à ArcGIS 10.2 et QGIS, mais je suis également ouvert à presque tout ce qui ne peut pas nécessiter l'inspection manuelle de toutes ces images pour garantir leur exactitude avant de les écraser. Il serait horrible de convertir à tort et écraser une image qui vraiment n'avoir plus on / off valeurs en elle. La plupart coûtent des milliers de dollars à rassembler et à générer.

un très mauvais résultat

mise à jour: Les plus grands contrevenants sont les flotteurs 32 bits qui vont jusqu'à 100 000 pixels sur un côté, donc ~ 30 Go non compressés.

Matt Wilkie
la source
1
Une façon de mettre en œuvre raster_diff(old_img, new_img) == "Identical"serait de vérifier que le zonal max de la valeur absolue de la différence est égal à 0, où la zone est prise sur toute l'étendue de la grille. Est-ce le type de solution que vous recherchez? (Si tel est le cas, il devrait être affiné pour vérifier que toutes les valeurs NoData sont également cohérentes.)
whuber
1
@whuber merci d'avoir veillé à ce que la bonne NoDatagestion reste dans la conversation.
matt wilkie
si vous pouvez le vérifier len(numpy.unique(yourraster)) == 2, vous savez qu'il a 2 valeurs uniques et vous pouvez le faire en toute sécurité.
RemcoGerlich
@Remco L'algorithme sous numpy.unique- jacent va être plus coûteux en termes de calcul (en termes de temps et d'espace) que la plupart des autres moyens de vérifier que la différence est une constante. Lorsqu'il est confronté à une différence entre deux très grands rasters à virgule flottante qui présentent de nombreuses différences (comme la comparaison d'un original à une version compressée avec perte), il risque de s'enliser pour toujours ou d'échouer complètement.
whuber
1
@Aaron, je me suis retiré du projet pour faire autre chose. Cela s'explique en partie par le fait que le temps de développement ne cesse de s'allonger: trop de cas marginaux à gérer automatiquement, de sorte que la décision a été prise de renvoyer le problème aux personnes générant les images plutôt que de les corriger. (par exemple "Votre quota de disque est X. Vous apprenez à travailler à l'intérieur.") Cependant, cela a gdalcompare.pymontré une grande promesse ( voir la réponse )
matt wilkie

Réponses:

8

Essayez de convertir vos rasters en tableaux numpy, puis vérifiez s'ils ont la même forme et les mêmes éléments avec array_equal . S'ils sont identiques, le résultat devrait être True:

ArcGIS:

import arcpy, numpy

raster1 = r'C:\path\to\raster.tif'
raster2 = r'C:\path\to\raster.tif'

r1 = arcpy.RasterToNumPyArray(raster1)
r2 = arcpy.RasterToNumPyArray(raster2)

d = numpy.array_equal(r1,r2)

if d == False:
    print "They differ"

else:
    print "They are the same"

GDAL:

import numpy
from osgeo import gdal        

raster1 = r'C:\path\to\raster.tif'
raster2 = r'C:\path\to\raster.tif'

ds1 = gdal.Open(raster1)
ds2 = gdal.Open(raster2)

r1 = numpy.array(ds1.ReadAsArray())
r2 = numpy.array(ds2.ReadAsArray())

d = numpy.array_equal(r1,r2)

if d == False:
    print "They differ"

else:
    print "They are the same"
Aaron
la source
Cela a l'air doux et simple. Je suis curieux de connaître deux détails (qui, aussi techniques soient-ils, pourraient être cruciaux). Tout d'abord, cette solution gère-t-elle correctement les valeurs NoData? Deuxièmement, comment sa vitesse se compare-t-elle à l'utilisation de fonctions intégrées destinées aux comparaisons de grille, telles que les résumés zonaux?
whuber
1
Bons points @whuber. J'ai fait un ajustement rapide au script qui devrait prendre en compte la forme et les éléments. Je vais vérifier les points que vous avez soulevés et rapporter les résultats.
Aaron
1
@whuber Concernant la NoDatagestion, RasterToNumPyArrayaffecte par défaut la valeur NoData du raster en entrée au tableau. L'utilisateur peut spécifier une valeur différente, bien que cela ne s'applique pas dans le cas de Matt. En ce qui concerne la vitesse, il a fallu 4,5 secondes au script pour comparer 2 rasters 4 bits avec 6210 colonnes et 7650 lignes (étendue DOQQ). Je n'ai comparé la méthode à aucun résumé de zone.
Aaron
1
J'ai plié l'équivalent gdal, adapté de gis.stackexchange.com/questions/32995/…
matt wilkie
4

Vous pouvez essayer avec le script gdalcompare.py http://www.gdal.org/gdalcompare.html . Le code source du script se trouve à http://trac.osgeo.org/gdal/browser/trunk/gdal/swig/python/scripts/gdalcompare.py et parce qu'il s'agit d'un script python, il devrait être facile de supprimer l'inutile tests et en ajouter de nouveaux pour répondre à vos besoins actuels. Le script semble faire une comparaison pixel par pixel en lisant les données d'image des deux images bande par bande et c'est probablement une méthode assez rapide et réutilisable.

user30184
la source
1
intrigant, j'adore gdal, je ne connaissais pas ce script. Les documents pour interpréter les résultats sont cependant rares à inexistants ;-). Lors de mes premiers tests, il signale des différences d'interprétation des couleurs et des palettes, ce qui signifie qu'il pourrait être trop spécifique pour mes besoins actuels. Je suis toujours en train de l'explorer. (note: cette réponse trop courte pour être un bon ajustement ici, seules les réponses de lien sont déconseillées, veuillez envisager de l'étoffer).
matt wilkie
1

Je vous suggère de créer votre table d'attributs raster pour chaque image, puis vous pouvez comparer les tables. Ce n'est pas une vérification complète (comme calculer la différence entre les deux), mais la probabilité que vos images soient différentes avec les mêmes valeurs d'histogramme est très très faible. Il vous donne également le nombre de valeurs uniques sans NoData (à partir du nombre de lignes dans le tableau). Si votre nombre total est inférieur à la taille de l'image, vous savez que vous disposez de pixels NoData.

radouxju
la source
Est-ce que cela fonctionnerait avec des flottants 32 bits? La construction et la comparaison de deux tables seraient-elles en fait plus rapides (ou plus faciles) que l'examen des valeurs de la différence des deux rasters (qui devraient en principe être juste zéro et NoData)?
whuber
Vous avez raison, cela ne fonctionnerait pas avec un flotteur 32 bits et je n'ai pas vérifié la vitesse. Cependant, la création de la table attributaire ne doit lire les données qu'une seule fois et peut aider à éviter la compression 1 bit lorsque vous savez qu'elle échouera. Je ne connais pas non plus la taille des images, mais parfois vous ne pouvez pas les stocker en mémoire.
radouxju
@radouxju les images vont jusqu'à 100 000 pixels sur un côté, donc ~ 30 Go non compressés. Nous n'avons pas de machine avec autant de RAM (mais peut-être avec du virtuel ...)
Matt Wilkie
Il semble probable que la RAM ne serait pas un problème à condition de rester fidèle aux opérations natives d'ArcGIS. C'est assez bon avec l'utilisation de la RAM lors du traitement des grilles: en interne, il peut effectuer le traitement ligne par ligne, par groupes de lignes et par fenêtres rectangulaires. Les opérations locales telles que la soustraction d'une grille d'une autre peuvent fonctionner essentiellement à la vitesse d'entrée et de sortie, ne nécessitant qu'un seul (relativement petit) tampon pour chaque ensemble de données d'entrée. La construction d'une table attributaire nécessite une table de hachage supplémentaire - qui serait minuscule lorsque seulement une ou deux valeurs apparaissent, mais pourrait être énorme pour les grilles arbitraires.
whuber
numpy fera beaucoup d'échanges avec des baies 2 * 30Go, ce n'est plus ArcGIS. J'ai supposé sur la base de l'écran d'impression que les images sont des images classées (la plupart avec seulement quelques valeurs), donc vous ne vous attendez pas à autant de classes.
radouxju
0

La solution la plus simple que j'ai trouvée consiste à calculer des statistiques récapitulatives sur les rasters et à les comparer. J'utilise généralement l'écart-type et la moyenne, qui sont robustes à la plupart des changements, bien qu'il soit possible de les tromper en manipulant intentionnellement les données.

mean_obj = arcpy.GetRasterProperties(input_raster, 'MEAN')
mean = float(mean_obj.getOutput(0))
if round(mean, 4) != 0.2010:
    print("raster differs from expected mean.")

std_obj = arcpy.GetRasterProperties(input_raster, 'STD')
std = float(std_obj.getOutput(0))
if round(std, 4) != 0.0161:
    print("raster differs from expected standard deviation.")
scw
la source
2
Un énorme moyen de tromper ces statistiques serait de permuter le contenu des cellules (ce qui peut se produire, et se produit, lorsque les dimensions de l'image ne sont pas tout à fait correctes). Sur les très grands rasters, ni la SD ni la moyenne ne détectent de manière fiable quelques petits changements dispersés (surtout si quelques pixels sont simplement supprimés). En théorie, ils ne détecteraient pas non plus un rééchantillonnage en gros du réseau, à condition que la convolution cubique soit utilisée (qui est destinée à préserver la moyenne et l'écart-type). Il semblerait plutôt prudent de comparer le SD de la différence des grilles à zéro.
whuber
0

Le moyen le plus simple consiste à soustraire un raster de l'autre, si le résultat est 0, les deux images sont identiques. Vous pouvez également voir l'histogramme ou tracer par couleur le résultat.

Pau
la source
La soustraction semble être un bon moyen de faire une comparaison. Cependant, je pense que l'histogramme ne serait pas très utile pour détecter les problèmes avec les valeurs NoData. Supposons, par exemple, que la procédure de compression élimine une bordure d'un pixel autour de la grille (cela peut arriver!), Mais sinon, elle était précise: toutes les différences seraient toujours nulles. De plus, avez-vous remarqué que l'OP doit le faire avec 7000 jeux de données raster? Je ne suis pas sûr qu'il aimerait examiner 7 000 parcelles.
whuber