Package osgeo :: Package utils :: Module gdalcompare |
[hide private]
[frames] | no frames]

# Source Code for Module osgeo.utils.gdalcompare

      1  #!/usr/bin/env python3
      2  # -*- coding: utf-8 -*-
      3  # ******************************************************************************
      4  #
      5  #  Project:  GDAL
      6  #  Purpose:  Compare two files for differences and report.
      7  #  Author:   Frank Warmerdam, warmerdam@pobox.com
      8  #
      9  # ******************************************************************************
     10  #  Copyright (c) 2012, Frank Warmerdam <warmerdam@pobox.com>
     11  #
     12  #  Permission is hereby granted, free of charge, to any person obtaining a
     13  #  copy of this software and associated documentation files (the "Software"),
     14  #  to deal in the Software without restriction, including without limitation
     15  #  the rights to use, copy, modify, merge, publish, distribute, sublicense,
     16  #  and/or sell copies of the Software, and to permit persons to whom the
     17  #  Software is furnished to do so, subject to the following conditions:
     18  #
     19  #  The above copyright notice and this permission notice shall be included
     20  #  in all copies or substantial portions of the Software.
     21  #
     22  #  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
     23  #  OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     24  #  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
     25  #  THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     26  #  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
     27  #  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
     28  #  DEALINGS IN THE SOFTWARE.
     29  # ******************************************************************************
     30
     31  import os
     32  import sys
     33  import filecmp
     34
     35  from osgeo import gdal
     36  from osgeo import osr
     37
     38  #######################################################
     39
     40
     41 -def compare_metadata(golden_md, new_md, ident, options=None):
     42      # pylint: disable=unused-argument
     43
     44      if golden_md is None and new_md is None:
     45          return 0
     46
     47      found_diff = 0
     48
     49      if len(list(golden_md.keys())) != len(list(new_md.keys())):
     50          print('Difference in %s metadata key count' % ident)
     51          print('  Golden Keys: ' + str(list(golden_md.keys())))
     52          print('  New Keys: ' + str(list(new_md.keys())))
     53          found_diff += 1
     54
     55      for key in list(golden_md.keys()):
     56          if key not in new_md:
     57              print('New %s metadata lacks key \"%s\"' % (ident, key))
     58              found_diff += 1
     59          elif new_md[key] != golden_md[key]:
     60              print('Metadata value difference for key "' + key + '"')
     61              print('  Golden: "' + golden_md[key] + '"')
     62              print('  New:    "' + new_md[key] + '"')
     63              found_diff += 1
     64
     65      return found_diff
     66
     67
     68  #######################################################
     69  # Review and report on the actual image pixels that differ.
     70 -def compare_image_pixels(golden_band, new_band, ident, options=None):
     71      # pylint: disable=unused-argument
     72
     73      diff_count = 0
     74      max_diff = 0
     75
     76      for line in range(golden_band.YSize):
     77          golden_line = golden_band.ReadAsArray(0, line, golden_band.XSize, 1)[0]
     78          new_line = new_band.ReadAsArray(0, line, golden_band.XSize, 1)[0]
     79          diff_line = golden_line.astype(float) - new_line.astype(float)
     80          max_diff = max(max_diff, abs(diff_line).max())
     81          diff_count += len(diff_line.nonzero()[0])
     82
     83      print('  Pixels Differing: ' + str(diff_count))
     84      print('  Maximum Pixel Difference: ' + str(max_diff))
     85
     86  #######################################################
     87
     88
     89 -def compare_band(golden_band, new_band, ident, options=None):
     90      found_diff = 0
     91
     92      options = [] if options is None else options
     93
     94      if golden_band.DataType != new_band.DataType:
     95          print('Band %s pixel types differ.' % ident)
     96          print('  Golden: ' + gdal.GetDataTypeName(golden_band.DataType))
     97          print('  New:    ' + gdal.GetDataTypeName(new_band.DataType))
     98          found_diff += 1
     99
    100      if golden_band.GetNoDataValue() != new_band.GetNoDataValue():
    101          print('Band %s nodata values differ.' % ident)
    102          print('  Golden: ' + str(golden_band.GetNoDataValue()))
    103          print('  New:    ' + str(new_band.GetNoDataValue()))
    104          found_diff += 1
    105
    106      if golden_band.GetColorInterpretation() != new_band.GetColorInterpretation():
    107          print('Band %s color interpretation values differ.' % ident)
    108          print('  Golden: ' + gdal.GetColorInterpretationName(golden_band.GetColorInterpretation()))
    109          print('  New:    ' + gdal.GetColorInterpretationName(new_band.GetColorInterpretation()))
    110          found_diff += 1
    111
    112      if golden_band.Checksum() != new_band.Checksum():
    113          print('Band %s checksum difference:' % ident)
    114          print('  Golden: ' + str(golden_band.Checksum()))
    115          print('  New:    ' + str(new_band.Checksum()))
    116          found_diff += 1
    117          compare_image_pixels(golden_band, new_band, ident, options)
    118
    119      # Check overviews
    120      if golden_band.GetOverviewCount() != new_band.GetOverviewCount():
    121          print('Band %s overview count difference:' % ident)
    122          print('  Golden: ' + str(golden_band.GetOverviewCount()))
    123          print('  New:    ' + str(new_band.GetOverviewCount()))
    124          found_diff += 1
    125      else:
    126          for i in range(golden_band.GetOverviewCount()):
    127              found_diff += compare_band(golden_band.GetOverview(i),
    128                                         new_band.GetOverview(i),
    129                                         ident + ' overview ' + str(i),
    130                                         options)
    131
    132      # Metadata
    133      if 'SKIP_METADATA' not in options:
    134          found_diff += compare_metadata(golden_band.GetMetadata(),
    135                                         new_band.GetMetadata(),
    136                                         'Band ' + ident, options)
    137
    138      # Mask band
    139      if golden_band.GetMaskFlags() != new_band.GetMaskFlags():
    140          print('Band %s mask flags difference:' % ident)
    141          print('  Golden: ' + str(golden_band.GetMaskFlags()))
    142          print('  New:    ' + str(new_band.GetMaskFlags()))
    143          found_diff += 1
    144      elif golden_band.GetMaskFlags() in (gdal.GMF_PER_DATASET, 0):
    145          found_diff += compare_band(golden_band.GetMaskBand(),
    146                                     new_band.GetMaskBand(),
    147                                     ident + ' mask band',
    148                                     options)
    149
    150      # TODO: Color Table, gain/bias, units, blocksize, min/max
    151
    152      return found_diff
    153
    154  #######################################################
    155
    156
    157 -def compare_srs(golden_wkt, new_wkt):
    158      if golden_wkt == new_wkt:
    159          return 0
    160
    161      print('Difference in SRS!')
    162
    163      golden_srs = osr.SpatialReference(golden_wkt)
    164      new_srs = osr.SpatialReference(new_wkt)
    165
    166      if golden_srs.IsSame(new_srs):
    167          print('  * IsSame() reports them as equivalent.')
    168      else:
    169          print('  * IsSame() reports them as different.')
    170
    171      print('  Golden:')
    172      print('  ' + golden_srs.ExportToPrettyWkt())
    173      print('  New:')
    174      print('  ' + new_srs.ExportToPrettyWkt())
    175
    176      return 1
    177
    178  #######################################################
    179
    180
    181 -def compare_db(golden_db, new_db, options=None):
    182      found_diff = 0
    183
    184      options = [] if options is None else options
    185
    186      # SRS
    187      if 'SKIP_SRS' not in options:
    188          found_diff += compare_srs(golden_db.GetProjection(),
    189                                    new_db.GetProjection())
    190
    191      # GeoTransform
    192      if 'SKIP_GEOTRANSFORM' not in options:
    193          golden_gt = golden_db.GetGeoTransform()
    194          new_gt = new_db.GetGeoTransform()
    195          if golden_gt != new_gt:
    196              print('GeoTransforms Differ:')
    197              print('  Golden: ' + str(golden_gt))
    198              print('  New:    ' + str(new_gt))
    199              found_diff += 1
    200
    201      # Metadata
    202      if 'SKIP_METADATA' not in options:
    203          found_diff += compare_metadata(golden_db.GetMetadata(),
    204                                         new_db.GetMetadata(),
    205                                         'Dataset', options)
    206
    207      # Bands
    208      if golden_db.RasterCount != new_db.RasterCount:
    209          print('Band count mismatch (golden=%d, new=%d)'
    210                % (golden_db.RasterCount, new_db.RasterCount))
    211          found_diff += 1
    212
    213      # Dimensions
    214      for i in range(golden_db.RasterCount):
    215          gSzX = golden_db.GetRasterBand(i + 1).XSize
    216          nSzX = new_db.GetRasterBand(i + 1).XSize
    217          gSzY = golden_db.GetRasterBand(i + 1).YSize
    218          nSzY = new_db.GetRasterBand(i + 1).YSize
    219
    220          if gSzX != nSzX or gSzY != nSzY:
    221              print('Band size mismatch (band=%d golden=[%d,%d], new=[%d,%d])' %
    222                    (i, gSzX, gSzY, nSzX, nSzY))
    223              found_diff += 1
    224
    225      # If so-far-so-good, then compare pixels
    226      if found_diff == 0:
    227          for i in range(golden_db.RasterCount):
    228              found_diff += compare_band(golden_db.GetRasterBand(i + 1),
    229                                         new_db.GetRasterBand(i + 1),
    230                                         str(i + 1),
    231                                         options)
    232
    233      return found_diff
    234
    235  #######################################################
    236
    237
    238 -def compare_sds(golden_db, new_db, options=None):
    239      found_diff = 0
    240
    241      options = [] if options is None else options
    242
    243      golden_sds = golden_db.GetMetadata('SUBDATASETS')
    244      new_sds = new_db.GetMetadata('SUBDATASETS')
    245
    246      count = len(list(golden_sds.keys())) // 2
    247      for i in range(count):
    248          key = 'SUBDATASET_%d_NAME' % (i + 1)
    249
    250          sub_golden_db = gdal.Open(golden_sds[key])
    251          sub_new_db = gdal.Open(new_sds[key])
    252
    253          sds_diff = compare_db(sub_golden_db, sub_new_db, options)
    254          found_diff += sds_diff
    255          if sds_diff > 0:
    256              print('%d differences found between:\n  %s\n  %s'
    257                    % (sds_diff, golden_sds[key], new_sds[key]))
    258
    259      return found_diff
    260
    261  #######################################################
    262
    263
    264 -def find_diff(golden_file, new_file, check_sds=False):
    265      # Compare Files
    266      found_diff = 0
    267
    268      # compare raw binary files.
    269      try:
    270          os.stat(golden_file)
    271
    272          if not filecmp.cmp(golden_file, new_file):
    273              print('Files differ at the binary level.')
    274              found_diff += 1
    275      except OSError:
    276          print('Skipped binary file comparison, golden file not in filesystem.')
    277
    278      # compare as GDAL Datasets.
    279      golden_db = gdal.Open(golden_file)
    280      new_db = gdal.Open(new_file)
    281      found_diff += compare_db(golden_db, new_db)
    282
    283      if check_sds:
    284          found_diff += compare_sds(golden_db, new_db)
    285
    286      return found_diff
    287
    288  #######################################################
    289
    290
    291 -def Usage():
    292      print('Usage: gdalcompare.py [-sds] <golden_file> <new_file>')
    293      sys.exit(1)
    294
    295  #######################################################
    296  #
    297  # Mainline
    298  #
    299
    300
    301 -def main(argv):
    302      # Default GDAL argument parsing.
    303      argv = gdal.GeneralCmdLineProcessor(argv)
    304      if argv is None:
    305          sys.exit(0)
    306
    307      if len(argv) == 1:
    308          Usage()
    309
    310      # Script argument parsing.
    311      golden_file = None
    312      new_file = None
    313      check_sds = 0
    314
    315      i = 1
    316      while i < len(argv):
    317
    318          if argv[i] == '-sds':
    319              check_sds = 1
    320
    321          elif golden_file is None:
    322              golden_file = argv[i]
    323
    324          elif new_file is None:
    325              new_file = argv[i]
    326
    327          else:
    328              print('Unrecognised argument: ' + argv[i])
    329              Usage()
    330
    331          i = i + 1
    332          # next argument
    333
    334      found_diff = find_diff(golden_file, new_file, check_sds)
    335      print('Differences Found: ' + str(found_diff))
    336      sys.exit(found_diff)
    337
    338
    339  if __name__ == '__main__':
    340      sys.exit(main(sys.argv))
    341