def testCreateGdalGeoTiffDatasetGeoreferenced(self): destPath = os.path.join(self.dir, 'test-create-gdal-dataset-georeferenced.tif') destPath2 = os.path.join(self.dir, 'test-create-gdal-dataset-georeferenced2.tif') image = Image.open(self.file) ndarray = maskImage(image, self.testData) offset = getOffsetValues(self.testData, image) driver = gdal.GetDriverByName("GTiff") if driver is None: raise ValueError("Can't find GeoTiff Driver") response = createGdalDataset(image, ndarray, driver, None, destPath) response1 = createGdalDataset(image, ndarray, driver, offset, destPath2) print '=====================' print 'Test if testCreateGdalGeoTiffDataset ...' print 'Response: %s'%response print '=====================' geoTransform = gdal.GCPsToGeoTransform(self.testGcp) geoTransform1 = gdal.GCPsToGeoTransform(correctGCPOffset(self.testGcp, offset)) geoProj = SRC_DICT_WKT[4314] response.SetProjection(geoProj) response.SetGeoTransform(geoTransform) response1.SetProjection(geoProj) response1.SetGeoTransform(geoTransform1) response.FlushCache() response1.FlushCache() # clear up del image del ndarray del response del response1 # create vrt dst_path = os.path.join(self.dir, 'test_createVrt.vrt') dataset = gdal.Open(self.file, GA_ReadOnly) vrt = createVrt(dataset, dst_path) vrt.SetProjection(geoProj) vrt.SetGeoTransform(geoTransform) vrt.FlushCache() del vrt # create georef classic dst_path2 = os.path.join(self.dir, 'test-mtb-georef.tif') logger = createLogger('GeoreferenceTest', logging.DEBUG) georeference(self.file, dst_path2, self.dir, geoTransform, 4314, 'polynom', logger) # if os.path.exists(destPath): # os.remove(destPath) # create georef clipped dst_path3 = os.path.join(self.dir, 'test-mtb-georef-clipped.tif') clipPath = createClipShapefile(self.bbox, os.path.join(self.dir, 'test_shp'), 4314) response = georeference(self.file, dst_path3, self.dir, geoTransform, 4314, 'polynom', logger, clipPath)
def testGeoreferenceMtbWithClip(self): dstPath = os.path.join(self.dir, 'mtb-georef-clipped.tif') geoTransform = gdal.GCPsToGeoTransform(self.testData1['mtb']['gcps']) clipPath = createClipShapefile(self.testData1['mtb']['bbox'], os.path.join(self.dir, 'test_shp'), self.testData1['mtb']['srs']) response = georeference(self.testData1['mtb']['srcFile'], dstPath, self.dir, geoTransform, self.testData1['mtb']['srs'], 'polynom', self.logger, clipPath) print '=====================' print 'Test if testGeoreferenceMtb ...' print 'Response: %s'%response print '=====================' self.assertTrue(os.path.exists(dstPath), 'Could not find created vrt file ...') self.assertEqual(response, dstPath, 'Response is not like expected ...') responseDataset = gdal.Open(response, GA_ReadOnly) self.assertTrue(responseDataset.GetProjection() == 'GEOGCS["DHDN",DATUM["Deutsches_Hauptdreiecksnetz",SPHEROID["Bessel 1841",6377397.155,299.1528128000008,AUTHORITY["EPSG","7004"]],TOWGS84[598.1,73.7,418.2,0.202,0.045,-2.455,6.7],AUTHORITY["EPSG","6314"]],PRIMEM["Greenwich",0],UNIT["degree",0.0174532925199433],AUTHORITY["EPSG","4314"]]', 'Response has not expected coordinate system ...') # clear up del responseDataset if os.path.exists(dstPath): os.remove(dstPath) if os.path.exists(clipPath): os.remove('%s.shp'%os.path.join(self.dir, 'test_shp')) os.remove('%s.dbf'%os.path.join(self.dir, 'test_shp')) os.remove('%s.prj'%os.path.join(self.dir, 'test_shp')) os.remove('%s.shx'%os.path.join(self.dir, 'test_shp'))
def testCreateGdalGeoTiffDatasetGeoreferencedSimple(self): destPath = os.path.join(self.dir, 'test-create-gdal-dataset-georeferenced-simple.tif') image = Image.open(self.file) ndarray = maskImage(image, self.testData) offset = getOffsetValues(self.testData, image) driver = gdal.GetDriverByName("GTiff") if driver is None: raise ValueError("Can't find GeoTiff Driver") response = createGdalDataset(image, ndarray, driver, offset, destPath) print '=====================' print 'Test if testCreateGdalGeoTiffDatasetGeoreferencedSimple ...' print 'Response: %s'%response print '=====================' geoTransform = gdal.GCPsToGeoTransform(correctGCPOffset(self.testGcp, offset)) geoProj = SRC_DICT_WKT[4314] response.SetProjection(geoProj) response.SetGeoTransform(geoTransform) response.SetProjection(geoProj) response.FlushCache() # clear up del image del ndarray del response if os.path.exists(destPath): os.remove(destPath)
def settinGeotransformation(self, file): file = file.split('.')[0] + '.tiff' fn = self.TIFF_PATH + file ds = gdal.Open(fn, gdal.GA_Update) gcps = ds.GetGCPs() gt = gdal.GCPsToGeoTransform(gcps) return gt
def gdal_bounding_box(raster, outProj=None): """ Computes the bounding box for an open GDAL raster file. The format is [minX, minY, maxX, maxY] in outProj coordinates. For instance outProj for lat/lon is pyproj.Proj('+proj=longlat +datum=WGS84') Returns None in case of an error. """ projection = raster.GetProjection() if (projection): transform = raster.GetGeoTransform() else: projection = raster.GetGCPProjection() gcps = raster.GetGCPs() transform = gdal.GCPsToGeoTransform(gcps) if transform is None: print("Unable to extract a geotransform from GCPs") return None lines = numpy.array([0, 0, raster.RasterYSize, raster.RasterYSize]) pixels = numpy.array([0, raster.RasterXSize, raster.RasterXSize, 0]) arrayX = transform[0] + pixels * transform[1] + lines * transform[2] arrayY = transform[3] + pixels * transform[4] + lines * transform[5] if outProj: srs = osr.SpatialReference(wkt=projection) proj_srs = srs.ExportToProj4() inProj = pyproj.Proj(proj_srs) arrayX, arrayY = pyproj.transform(inProj, outProj, arrayX, arrayY) minX = numpy.amin(arrayX) minY = numpy.amin(arrayY) maxX = numpy.amax(arrayX) maxY = numpy.amax(arrayY) return [minX, minY, maxX, maxY]
def testTransformClipPolygon(self): geoTransform = gdal.GCPsToGeoTransform(self.testData1['mtb']['gcps']) response = transformClipPolygon(self.testData1['mtb']['clip'], geoTransform) print '=====================' print 'Test if testTransformClipPolygon ...' print 'Response: %s'%response print '=====================' self.assertTrue(isinstance(response, list), 'Response is not of type list ...')
def rectifyImageAffine(srcFile, dstFile, clipPolygon, gcps, srs, logger): """ Functions generates clips an image and adds a geotransformation matrix to it :type srcFile: str :type dstFile: str :type clipPolygon: List.<Tuple.<int>> :type gcps: List.<gdal.GCP> :type srs: int Right now the function only supports 4314 as spatial reference system :type logger: logging.logger :return: str :raise: ValueError """ logger.debug('Open image ...') srcImage = Image.open(srcFile) logger.debug('Mask image ...') ndarray = maskImage(srcImage, clipPolygon) logger.debug('Calculate offset ...') offset = {'left': 0, 'top': 0, 'right': 0, 'bottom': 0} if len(clipPolygon) > 0: offset = getOffsetValues(clipPolygon, srcImage) # create an output geotiff newDataset = createGeotiff(srcImage, ndarray, offset, dstFile, logger) logger.debug('Do georeferencing based on an affine transformation ...') geoTransform = gdal.GCPsToGeoTransform(correctGCPOffset(gcps, offset)) geoProj = SRC_DICT_WKT[srs] if geoProj is None: raise ValueError("SRS with id - %s - is not supported" % srs) newDataset.SetProjection(geoProj) newDataset.SetGeoTransform(geoTransform) newDataset.FlushCache() # clear up del srcImage del newDataset del ndarray return dstFile
def testResampleGeoreferencedImageSimple(self): destPath = os.path.join(self.dir, 'test-create-gdal-dataset-resample-simple.tif') image = Image.open(self.file) ndarray = maskImage(image, self.testData) offset = getOffsetValues(self.testData, image) driver = gdal.GetDriverByName("MEM") if driver is None: raise ValueError("Can't find Mem Driver") srcDataset = createGdalDataset(image, ndarray, driver, offset) outputDriver = gdal.GetDriverByName('GTiff') if outputDriver is None: raise ValueError("Can't find GTiff Driver") geoTransform = gdal.GCPsToGeoTransform(correctGCPOffset(self.testGcp, offset)) geoProj = SRC_DICT_WKT[4314] srcDataset.SetProjection(geoProj) srcDataset.SetGeoTransform(geoTransform) response = resampleGeoreferencedImage(srcDataset, geoTransform, geoProj, outputDriver, destPath) print '=====================' print 'Test if testResampleGeoreferencedImageSimple ...' print 'Response: %s'%response print '=====================' response.FlushCache() self.assertTrue(isinstance(response, gdal.Dataset), "Response is not a gdal.Dataset.") # clear up del image del ndarray del response if os.path.exists(destPath): os.remove(destPath)
def main(args): parser = argparse.ArgumentParser( description= 'Render a DSM from a DTM and polygons representing buildings.') parser.add_argument("--input_vtp_path", type=str, help="Input buildings polygonal file (.vtp)") parser.add_argument( "--input_obj_paths", nargs="*", help="List of input building (.obj) file paths. " "Building object files start " "with a digit, road object files start with \"Road\". " "All obj files start with comments specifying the offsets " "that are added the coordinats. There are three comment lines, " "one for each coordinate: \"#c offset: value\" where c is x, y and z.") parser.add_argument("input_dtm", help="Input digital terain model (DTM)") parser.add_argument("output_dsm", help="Output digital surface model (DSM)") parser.add_argument("--render_png", action="store_true", help="Do not save the DSM, render into a PNG instead.") parser.add_argument( "--render_cls", action="store_true", help="Render a buildings mask: render buildings label (6), " "background (2) and no DTM.") parser.add_argument("--buildings_only", action="store_true", help="Do not use the DTM, use only the buildings.") parser.add_argument("--debug", action="store_true", help="Save intermediate results") args = parser.parse_args(args) # open the DTM dtm = gdal.Open(args.input_dtm, gdal.GA_ReadOnly) if not dtm: raise RuntimeError("Error: Failed to open DTM {}".format( args.input_dtm)) dtmDriver = dtm.GetDriver() dtmDriverMetadata = dtmDriver.GetMetadata() dsm = None dtmBounds = [0.0, 0.0, 0.0, 0.0] if dtmDriverMetadata.get(gdal.DCAP_CREATE) == "YES": print("Create destination image " "size:({}, {}) ...".format(dtm.RasterXSize, dtm.RasterYSize)) # georeference information projection = dtm.GetProjection() transform = dtm.GetGeoTransform() gcpProjection = dtm.GetGCPProjection() gcps = dtm.GetGCPs() options = ["COMPRESS=DEFLATE"] # ensure that space will be reserved for geographic corner coordinates # (in DMS) to be set later if (dtmDriver.ShortName == "NITF" and not projection): options.append("ICORDS=G") if args.render_cls: eType = gdal.GDT_Byte else: eType = gdal.GDT_Float32 dsm = dtmDriver.Create(args.output_dsm, xsize=dtm.RasterXSize, ysize=dtm.RasterYSize, bands=1, eType=eType, options=options) if (projection): # georeference through affine geotransform dsm.SetProjection(projection) dsm.SetGeoTransform(transform) else: # georeference through GCPs dsm.SetGCPs(gcps, gcpProjection) gdal.GCPsToGeoTransform(gcps, transform) corners = [[0, 0], [0, dtm.RasterYSize], [dtm.RasterXSize, dtm.RasterYSize], [dtm.RasterXSize, 0]] geoCorners = numpy.zeros((4, 2)) for i, corner in enumerate(corners): geoCorners[i] = [ transform[0] + corner[0] * transform[1] + corner[1] * transform[2], transform[3] + corner[0] * transform[4] + corner[1] * transform[5] ] dtmBounds[0] = numpy.min(geoCorners[:, 0]) dtmBounds[1] = numpy.max(geoCorners[:, 0]) dtmBounds[2] = numpy.min(geoCorners[:, 1]) dtmBounds[3] = numpy.max(geoCorners[:, 1]) if args.render_cls: # label for no building dtmRaster = numpy.full([dtm.RasterYSize, dtm.RasterXSize], 2) nodata = 0 else: print("Reading the DTM {} size: ({}, {})\n" "\tbounds: ({}, {}), ({}, {})...".format( args.input_dtm, dtm.RasterXSize, dtm.RasterYSize, dtmBounds[0], dtmBounds[1], dtmBounds[2], dtmBounds[3])) dtmRaster = dtm.GetRasterBand(1).ReadAsArray() nodata = dtm.GetRasterBand(1).GetNoDataValue() print("Nodata: {}".format(nodata)) else: raise RuntimeError( "Driver {} does not supports Create().".format(dtmDriver)) # read the buildings polydata, set Z as a scalar and project to XY plane print("Reading the buildings ...") # labels for buildings and elevated roads labels = [6, 17] if (args.input_vtp_path and os.path.isfile(args.input_vtp_path)): polyReader = vtk.vtkXMLPolyDataReader() polyReader.SetFileName(args.input_vtp_path) polyReader.Update() polyVtkList = [polyReader.GetOutput()] elif (args.input_obj_paths): # buildings start with numbers # optional elevated roads start with Road*.obj bldg_re = re.compile(".*/?[0-9][^/]*\\.obj") bldg_files = [f for f in args.input_obj_paths if bldg_re.match(f)] print(bldg_files) road_re = re.compile(".*/?Road[^/]*\\.obj") road_files = [f for f in args.input_obj_paths if road_re.match(f)] files = [bldg_files, road_files] files = [x for x in files if x] print(road_files) if len(files) >= 2: print("Found {} buildings and {} roads".format( len(files[0]), len(files[1]))) elif len(files) == 1: print("Found {} buildings".format(len(files[0]))) else: raise RuntimeError("No OBJ files found in {}".format( args.input_obj_paths)) polyVtkList = [] for category in range(len(files)): append = vtk.vtkAppendPolyData() for i, fileName in enumerate(files[category]): offset = [0.0, 0.0, 0.0] gdal_utils.read_offset(fileName, offset) print("Offset: {}".format(offset)) transform = vtk.vtkTransform() transform.Translate(offset[0], offset[1], offset[2]) objReader = vtk.vtkOBJReader() objReader.SetFileName(fileName) transformFilter = vtk.vtkTransformFilter() transformFilter.SetTransform(transform) transformFilter.SetInputConnection(objReader.GetOutputPort()) append.AddInputConnection(transformFilter.GetOutputPort()) append.Update() polyVtkList.append(append.GetOutput()) else: raise RuntimeError( "Must provide either --input_vtp_path, or --input_obj_paths") arrayName = "Elevation" append = vtk.vtkAppendPolyData() for category in range(len(polyVtkList)): poly = dsa.WrapDataObject(polyVtkList[category]) polyElevation = poly.Points[:, 2] if args.render_cls: # label for buildings polyElevation[:] = labels[category] polyElevationVtk = numpy_support.numpy_to_vtk(polyElevation) polyElevationVtk.SetName(arrayName) poly.PointData.SetScalars(polyElevationVtk) append.AddInputDataObject(polyVtkList[category]) append.Update() # Create the RenderWindow, Renderer ren = vtk.vtkRenderer() renWin = vtk.vtkRenderWindow() renWin.OffScreenRenderingOn() renWin.SetSize(dtm.RasterXSize, dtm.RasterYSize) renWin.SetMultiSamples(0) renWin.AddRenderer(ren) # show the buildings trisBuildingsFilter = vtk.vtkTriangleFilter() trisBuildingsFilter.SetInputDataObject(append.GetOutput()) trisBuildingsFilter.Update() p2cBuildings = vtk.vtkPointDataToCellData() p2cBuildings.SetInputConnection(trisBuildingsFilter.GetOutputPort()) p2cBuildings.PassPointDataOn() p2cBuildings.Update() buildingsScalarRange = p2cBuildings.GetOutput().GetCellData().GetScalars( ).GetRange() if (args.debug): polyWriter = vtk.vtkXMLPolyDataWriter() polyWriter.SetFileName("p2c.vtp") polyWriter.SetInputConnection(p2cBuildings.GetOutputPort()) polyWriter.Write() buildingsMapper = vtk.vtkPolyDataMapper() buildingsMapper.SetInputDataObject(p2cBuildings.GetOutput()) buildingsActor = vtk.vtkActor() buildingsActor.SetMapper(buildingsMapper) ren.AddActor(buildingsActor) if (args.render_png): print("Render into a PNG ...") # Show the terrain. print("Converting the DTM into a surface ...") # read the DTM as a VTK object dtmReader = vtk.vtkGDALRasterReader() dtmReader.SetFileName(args.input_dtm) dtmReader.Update() dtmVtk = dtmReader.GetOutput() # Convert the terrain into a polydata. surface = vtk.vtkImageDataGeometryFilter() surface.SetInputDataObject(dtmVtk) # Make sure the polygons are planar, so need to use triangles. tris = vtk.vtkTriangleFilter() tris.SetInputConnection(surface.GetOutputPort()) # Warp the surface by scalar values warp = vtk.vtkWarpScalar() warp.SetInputConnection(tris.GetOutputPort()) warp.SetScaleFactor(1) warp.UseNormalOn() warp.SetNormal(0, 0, 1) warp.Update() dsmScalarRange = warp.GetOutput().GetPointData().GetScalars().GetRange( ) dtmMapper = vtk.vtkPolyDataMapper() dtmMapper.SetInputConnection(warp.GetOutputPort()) dtmActor = vtk.vtkActor() dtmActor.SetMapper(dtmMapper) ren.AddActor(dtmActor) ren.ResetCamera() camera = ren.GetActiveCamera() camera.ParallelProjectionOn() camera.SetParallelScale((dtmBounds[3] - dtmBounds[2]) / 2) if (args.buildings_only): scalarRange = buildingsScalarRange else: scalarRange = [ min(dsmScalarRange[0], buildingsScalarRange[0]), max(dsmScalarRange[1], buildingsScalarRange[1]) ] lut = vtk.vtkColorTransferFunction() lut.AddRGBPoint(scalarRange[0], 0.23, 0.30, 0.75) lut.AddRGBPoint((scalarRange[0] + scalarRange[1]) / 2, 0.86, 0.86, 0.86) lut.AddRGBPoint(scalarRange[1], 0.70, 0.02, 0.15) dtmMapper.SetLookupTable(lut) dtmMapper.SetColorModeToMapScalars() buildingsMapper.SetLookupTable(lut) if (args.buildings_only): ren.RemoveActor(dtmActor) renWin.Render() windowToImageFilter = vtk.vtkWindowToImageFilter() windowToImageFilter.SetInput(renWin) windowToImageFilter.SetInputBufferTypeToRGBA() windowToImageFilter.ReadFrontBufferOff() windowToImageFilter.Update() writerPng = vtk.vtkPNGWriter() writerPng.SetFileName(args.output_dsm + ".png") writerPng.SetInputConnection(windowToImageFilter.GetOutputPort()) writerPng.Write() else: print("Render into a floating point buffer ...") ren.ResetCamera() camera = ren.GetActiveCamera() camera.ParallelProjectionOn() camera.SetParallelScale((dtmBounds[3] - dtmBounds[2]) / 2) distance = camera.GetDistance() focalPoint = [(dtmBounds[0] + dtmBounds[1]) * 0.5, (dtmBounds[3] + dtmBounds[2]) * 0.5, (buildingsScalarRange[0] + buildingsScalarRange[1]) * 0.5 ] position = [focalPoint[0], focalPoint[1], focalPoint[2] + distance] camera.SetFocalPoint(focalPoint) camera.SetPosition(position) valuePass = vtk.vtkValuePass() valuePass.SetRenderingMode(vtk.vtkValuePass.FLOATING_POINT) # use the default scalar for point data valuePass.SetInputComponentToProcess(0) valuePass.SetInputArrayToProcess( vtk.VTK_SCALAR_MODE_USE_POINT_FIELD_DATA, arrayName) passes = vtk.vtkRenderPassCollection() passes.AddItem(valuePass) sequence = vtk.vtkSequencePass() sequence.SetPasses(passes) cameraPass = vtk.vtkCameraPass() cameraPass.SetDelegatePass(sequence) ren.SetPass(cameraPass) # We have to render the points first, otherwise we get a segfault. renWin.Render() valuePass.SetInputArrayToProcess( vtk.VTK_SCALAR_MODE_USE_CELL_FIELD_DATA, arrayName) renWin.Render() elevationFlatVtk = valuePass.GetFloatImageDataArray(ren) valuePass.ReleaseGraphicsResources(renWin) print("Writing the DSM ...") elevationFlat = numpy_support.vtk_to_numpy(elevationFlatVtk) # VTK X,Y corresponds to numpy cols,rows. VTK stores arrays # in Fortran order. elevationTranspose = numpy.reshape(elevationFlat, [dtm.RasterXSize, dtm.RasterYSize], "F") # changes from cols, rows to rows,cols. elevation = numpy.transpose(elevationTranspose) # numpy rows increase as you go down, Y for VTK images increases as you go up elevation = numpy.flip(elevation, 0) if args.buildings_only: dsmElevation = elevation else: # elevation has nans in places other than buildings dsmElevation = numpy.fmax(dtmRaster, elevation) dsm.GetRasterBand(1).WriteArray(dsmElevation) if nodata: dsm.GetRasterBand(1).SetNoDataValue(nodata)
import os.path if len(sys.argv) < 2: print "Usage: gcps2wld.py source_file" sys.exit(1) filename = sys.argv[1] dataset = gdal.Open(filename) if dataset is None: print 'Unable to open ', filename sys.exit(1) gcps = dataset.GetGCPs() if gcps is None or len(gcps) == 0: print 'No GCPs found on file ' + filename sys.exit(1) geotransform = gdal.GCPsToGeoTransform(gcps) if geotransform is None: print 'Unable to extract a geotransform.' sys.exit(1) print geotransform[1] print geotransform[4] print geotransform[2] print geotransform[5] print geotransform[0] + 0.5 * geotransform[1] + 0.5 * geotransform[2] print geotransform[3] + 0.5 * geotransform[4] + 0.5 * geotransform[5]
def get_spatial_extent(raster_path, target_EPSG=4326, tol=0.1): """ Get the spatial extent of a raster file. If the file is not georeferenced (e.g. for raw radarsat 2), this function attempts to use the GCPs in the image . Howver, this doesn't always produce exact results, so it is advisable to use an extra buffer tolerance in your spatial extent (maybe ~0.1 decimal degrees) *Parameters* raster_path : str Path to raster for which a spatial extent is desired target_EPSG : int EPSG code specifying coordinate system for output file tol : float By how many decimal degrees to buffer spatial extent *Returns* dict Dictionary with the following keys: {xmin, xmax, ymin, ymax} corresponding to the spatial extent in WGS84 decimal degrees """ # open file src = gdal.Open(raster_path) prj = src.GetProjection() # check if georeferencing information is available if (sum(src.GetGeoTransform()) == 2) and src.GetGCPCount() > 3: gt = gdal.GCPsToGeoTransform(src.GetGCPs()) prj = src.GetGCPProjection() else: gt = src.GetGeoTransform() # get untransformed upper left and lower right corner coordinates ulx, xres, xskew, uly, yskew, yres = gt lrx = ulx + (src.RasterXSize * xres) lry = uly + (src.RasterYSize * yres) # Setup the source projection - you can also import from epsg, proj4... source = osr.SpatialReference() source.ImportFromWkt(prj) # The target projection target = osr.SpatialReference() target.ImportFromEPSG(target_EPSG) # set up transform transform = osr.CoordinateTransformation(source, target) # transform coordinates upper = transform.TransformPoint(ulx, uly) lower = transform.TransformPoint(lrx, lry) ext = { 'xmin': min(upper[0], lower[0]) - tol, 'xmax': max(upper[0], lower[0]) + tol, 'ymin': min(upper[1], lower[1]) - tol, 'ymax': max(upper[1], lower[1]) + tol } return (ext)
# 创建坐标系 srs = osr.SpatialReference() srs.SetWellKnownGeogCS('WGS84') # 相关信息 rows = ds.RasterYSize # 行数 cols = ds.RasterXSize # 列数 # 创建地面控制点:经度、纬度、z,照片列数,照片行数 gcps = [ gdal.GCP(y1, x2, 0, 0, 0), # 左上 gdal.GCP(y2, x2, 0, cols - 1, 0), # 右上 gdal.GCP(y1, x1, 0, 0, rows - 1), # 左下 gdal.GCP(y2, x1, 0, cols - 1, rows - 1) # 右下 ] ds.SetGCPs(gcps, srs.ExportToWkt()) # 确保在数据集上设置了地理变化和投影信息 ds.SetProjection(srs.ExportToWkt() ) ds.SetGeoTransform(gdal.GCPsToGeoTransform(gcps) ) subimgs.append(tif_fp) #加入到subimgs,准备拼接 del ds # 对registration_dir内的数据进行拼接 out_fn = os.path.join(mosaic_dir, "out.tif") params = { "-o": out_fn, "input_files": subimgs } from gdal_merge import main_by_params main_by_params(params) pass
def projectImage(inputPatch, x0, y0, x1, y1, cornerPoints, rpcInformation): invCornerPoints = np.zeros(cornerPoints.shape) invCornerPoints[0, :] = inverseLookup(cornerPoints[0, :], (x0, y0) + (-1, 1), rpcInformation) invCornerPoints[1, :] = inverseLookup(cornerPoints[1, :], (x0, y1) + (1, 1), rpcInformation) invCornerPoints[2, :] = inverseLookup(cornerPoints[2, :], (x1, y1) + (1, -1), rpcInformation) invCornerPoints[3, :] = inverseLookup(cornerPoints[3, :], (x1, y0) + (-1, -1), rpcInformation) xySource = np.mgrid[y0:y1:1, x0:x1:1] inputPatch = inputPatch.transpose(1, 0, 2) plotCoordinates = xySource[:, 0:inputPatch.shape[0], 0:inputPatch.shape[1]] pixelPoints = np.ones((4, 2)) pixelPoints[0, 0] = y0 pixelPoints[0, 1] = x0 pixelPoints[1, 0] = y1 pixelPoints[1, 1] = x0 pixelPoints[2, 0] = y1 pixelPoints[2, 1] = x1 pixelPoints[3, 0] = y0 pixelPoints[3, 1] = x1 gcp_list = [] #List of Ground Control Points for i in range(0, 4): pixel = pixelPoints[i, 0].item() line = pixelPoints[i, 1].item() x = invCornerPoints[i, 0].item() y = invCornerPoints[i, 1].item() z = 0 gcp = gdal.GCP(x, y, z, pixel, line) gcp_list.append(gcp) #polygon = Polygon(cornerPoints) #for i in range(0,25): a = 1 #np.random.uniform(.8,1,1)[0] b = 1 #np.random.uniform(.8,1,1)[0] x = (a * invCornerPoints[0, 0] + (2 - a) * invCornerPoints[2, 0]) / 2 y = (b * invCornerPoints[0, 1] + (2 - b) * invCornerPoints[2, 1]) / 2 #point = Point(x,y) #if(polygon.contains(point)): line, pixel = getCorrespondingPixel((y, x), rpcInformation) z = 0 gcp = gdal.GCP(x, y, z, pixel, line) gcp_list.append(gcp) # Generating the Geo Transform: gt2 = gdal.GCPsToGeoTransform(gcp_list) fwd = Affine.from_gdal(*gt2) long, lat = fwd * plotCoordinates cornerPoints3 = np.zeros((4, 2)) cornerPoints3[0, :] = [long[0, 0], lat[0, 0]] cornerPoints3[3, :] = [long[0, -1], lat[0, -1]] cornerPoints3[2, :] = [long[-1, -1], lat[-1, -1]] cornerPoints3[1, :] = [long[-1, 0], lat[-1, 0]] transformMatrix = solve_affine(cornerPoints3, invCornerPoints) input = np.vstack( (long.flatten(), lat.flatten(), np.ones((1, np.prod(long.shape))))) updatedPerspective = transformMatrix * input long = updatedPerspective[0].reshape(long.shape) lat = updatedPerspective[1].reshape(lat.shape) print("error", np.sum(pow(cornerPoints3 - invCornerPoints, 2))) return long, lat, inputPatch
def main(args): parser = argparse.ArgumentParser( description="Crop out images for each of the CORE3D AOIs") parser.add_argument("aoi", help="dataset AOI: D1 (WPAFB), D2 (WPAFB), " "D3 (USCD), D4 (Jacksonville) or " "DSM used to get the cropping bounds and elevation") parser.add_argument( "dest_dir", help="Destination directory for writing crops. Crop files have " "the same name as source images + an optional postifx.") parser.add_argument("src_root", help="Source imagery root directory or list of images", nargs="+") parser.add_argument( "--dest_file_postfix", help="Postfix added to destination files, before the extension") parser.add_argument("--rpc_dir", help="Source directory for RPCs or list of RPC files", nargs="+") args = parser.parse_args(args) useDSM = False if os.path.isfile(args.aoi): useDSM = True if os.path.isfile(args.src_root[0]): src_root = args.src_root print('Cropping a list of {} images'.format(len(args.src_root))) else: src_root = os.path.join(args.src_root[0], '') print('Cropping all images from directory: {}'.format(args.src_root)) dest_dir = os.path.join(args.dest_dir, '') if (not args.dest_file_postfix): dest_file_postfix = "_crop" else: dest_file_postfix = args.dest_file_postfix print('Writing crops in directory: ' + dest_dir) print('Cropping to AOI: ' + args.aoi) rpc_dir = None if args.rpc_dir: if os.path.isfile(args.rpc_dir[0]): rpc_dir = args.rpc_dir print('Using a list of {} RPCs.'.format(len(args.rpc_dir))) else: rpc_dir = os.path.join(args.rpc_dir[0], '') print("Using all RPCs from directory: {}".format(rpc_dir)) else: print('Using RPCs from image metadata.') if useDSM: data_dsm = gdal_utils.gdal_open(args.aoi) # Elevation dsm = data_dsm.GetRasterBand(1).ReadAsArray(0, 0, data_dsm.RasterXSize, data_dsm.RasterYSize, buf_type=gdal.GDT_Float32) no_data_value = data_dsm.GetRasterBand(1).GetNoDataValue() dsm_without_no_data = dsm[dsm != no_data_value] elevations = np.array([[dsm_without_no_data.min()], [dsm_without_no_data.max()]]) # Cropping bounds from DSM [minX, minY, maxX, maxY] = gdal_utils.gdal_bounding_box( data_dsm, pyproj.Proj('+proj=longlat +datum=WGS84')) latlong_corners = np.array([[minX, minY, elevations[0]], [maxX, minY, elevations[0]], [maxX, maxY, elevations[0]], [minX, maxY, elevations[0]], [minX, minY, elevations[1]], [maxX, minY, elevations[1]], [maxX, maxY, elevations[1]], [minX, maxY, elevations[1]]]) print("Cropping bounds extracted from DSM") else: elevation_range = 100 # WPAFB AOI D1 if args.aoi == 'D1': elevation = 240 ul_lon = -84.11236693243779 ul_lat = 39.77747025512961 ur_lon = -84.10530109439955 ur_lat = 39.77749705975315 lr_lon = -84.10511182729961 lr_lat = 39.78290042788092 ll_lon = -84.11236485416471 ll_lat = 39.78287156225952 # WPAFB AOI D2 if args.aoi == 'D2': elevation = 300 ul_lon = -84.08847226672408 ul_lat = 39.77650841377968 ur_lon = -84.07992142333644 ur_lat = 39.77652166058358 lr_lon = -84.07959205694203 lr_lat = 39.78413758747398 ll_lon = -84.0882028871317 ll_lat = 39.78430009793551 # UCSD AOI D3 if args.aoi == 'D3': elevation = 120 ul_lon = -117.24298768132505 ul_lat = 32.882791370856857 ur_lon = -117.24296375496185 ur_lat = 32.874021450913411 lr_lon = -117.2323749640905 lr_lat = 32.874041569804469 ll_lon = -117.23239784772379 ll_lat = 32.882811496466012 # Jacksonville AOI D4 if args.aoi == 'D4': elevation = 2 ul_lon = -81.67078466333165 ul_lat = 30.31698808384777 ur_lon = -81.65616946309449 ur_lat = 30.31729872444624 lr_lon = -81.65620275072482 lr_lat = 30.329923847788603 ll_lon = -81.67062242425624 ll_lat = 30.32997669492018 for src_img_file, dst_img_file in filesFromArgs(src_root, dest_dir, dest_file_postfix): dst_file_no_ext = os.path.splitext(dst_img_file)[0] dst_img_file = dst_file_no_ext + ".tif" print('Converting img: ' + src_img_file) src_image = gdal.Open(src_img_file, gdalconst.GA_ReadOnly) nodata_values = [] nodata = 0 for i in range(src_image.RasterCount): nodata_value = src_image.GetRasterBand(i + 1).GetNoDataValue() if not nodata_value: nodata_value = nodata nodata_values.append(nodata_value) if useDSM: poly = latlong_corners.copy() elevation = np.median(dsm) else: poly = np.array([[ul_lon, ul_lat, elevation + elevation_range], [ur_lon, ur_lat, elevation + elevation_range], [lr_lon, lr_lat, elevation + elevation_range], [ll_lon, ll_lat, elevation + elevation_range], [ul_lon, ul_lat, elevation + elevation_range], [ul_lon, ul_lat, elevation - elevation_range], [ur_lon, ur_lat, elevation - elevation_range], [lr_lon, lr_lat, elevation - elevation_range], [ll_lon, ll_lat, elevation - elevation_range], [ul_lon, ul_lat, elevation - elevation_range]]) if rpc_dir: print("Using file RPC: {}".format(rpc_dir)) model = read_raytheon_RPC(rpc_dir, src_img_file) if model is None: print('No RPC file exists using image metadata RPC: ' + src_img_file + '\n') rpc_md = src_image.GetMetadata('RPC') model = rpc.rpc_from_gdal_dict(rpc_md) else: rpc_md = rpc.rpc_to_gdal_dict(model) else: print("Using image RPC.") rpc_md = src_image.GetMetadata('RPC') model = rpc.rpc_from_gdal_dict(rpc_md) # Project the world point locations into the image pixel_poly = model.project(poly) ul_x, ul_y = map(int, pixel_poly.min(0)) lr_x, lr_y = map(int, pixel_poly.max(0)) min_x, min_y, z = poly.min(0) max_x, max_y, z = poly.max(0) ul_x = max(0, ul_x) ul_y = max(0, ul_y) lr_x = min(src_image.RasterXSize - 1, lr_x) lr_y = min(src_image.RasterYSize - 1, lr_y) samp_off = rpc_md['SAMP_OFF'] samp_off = float(samp_off) - ul_x rpc_md['SAMP_OFF'] = str(samp_off) line_off = rpc_md['LINE_OFF'] line_off = float(line_off) - ul_y rpc_md['LINE_OFF'] = str(line_off) model.image_offset[0] -= ul_x model.image_offset[1] -= ul_y # Calculate the pixel size of the new image # Constrain the width and height to the bounds of the image px_width = int(lr_x - ul_x + 1) if px_width + ul_x > src_image.RasterXSize - 1: px_width = int(src_image.RasterXSize - ul_x - 1) px_height = int(lr_y - ul_y + 1) if px_height + ul_y > src_image.RasterYSize - 1: px_height = int(src_image.RasterYSize - ul_y - 1) # We've constrained x & y so they are within the image. # If the width or height ends up negative at this point, # the AOI is completely outside the image if px_width < 0 or px_height < 0: print('AOI out of range, skipping\n') continue corners = [[0, 0], [px_width, 0], [px_width, px_height], [0, px_height]] corner_names = ['UpperLeft', 'UpperRight', 'LowerRight', 'LowerLeft'] world_corners = model.back_project(corners, elevation) corner_gcps = [] for (p, l), (x, y, h), n in zip(corners, world_corners, corner_names): corner_gcps.append(gdal.GCP(x, y, h, p, l, "", n)) # Load the source data as a gdalnumeric array clip = src_image.ReadAsArray(ul_x, ul_y, px_width, px_height) # create output raster raster_band = src_image.GetRasterBand(1) output_driver = gdal.GetDriverByName('MEM') # In the event we have multispectral images, # shift the shape dimesions we are after, # since position 0 will be the number of bands try: clip_shp_0 = clip.shape[0] clip_shp_1 = clip.shape[1] if clip.ndim > 2: clip_shp_0 = clip.shape[1] clip_shp_1 = clip.shape[2] except (AttributeError): print('Error decoding image, skipping\n') continue output_dataset = output_driver.Create('', clip_shp_1, clip_shp_0, src_image.RasterCount, raster_band.DataType) # Copy All metadata data from src to dst domains = src_image.GetMetadataDomainList() for tag in domains: md = src_image.GetMetadata(tag) if md: output_dataset.SetMetadata(md, tag) # Rewrite the rpc_md that we modified above. output_dataset.SetMetadata(rpc_md, 'RPC') output_dataset.SetGeoTransform(gdal.GCPsToGeoTransform(corner_gcps)) output_dataset.SetProjection(gdal_get_projection(src_image)) # End logging, print blank line for clarity print('') bands = src_image.RasterCount if bands > 1: for i in range(bands): outBand = output_dataset.GetRasterBand(i + 1) outBand.SetNoDataValue(nodata_values[i]) outBand.WriteArray(clip[i]) else: outBand = output_dataset.GetRasterBand(1) outBand.SetNoDataValue(nodata_values[0]) outBand.WriteArray(clip) if dst_img_file: output_driver = gdal.GetDriverByName('GTiff') output_driver.CreateCopy(dst_img_file, output_dataset, False)
def gcps_to_geotransform(self): ''' Transforms ground control points into geotransform using gdal. ''' return gdal.GCPsToGeoTransform(self.gcps())