Ejemplo n.º 1
0
def main(args):
    # Configure argument parser
    parser = argparse.ArgumentParser(
        description='Compute Normalized Difference Vegetation Index (NDVI)')
    parser.add_argument(
        "source_msi",
        help="List of registered MSI images from which to estimate NDVI",
        nargs="+")
    parser.add_argument("output_ndvi",
                        help="File path to write out the NDVI image")
    args = parser.parse_args(args)

    # If more than one input MSI image it is assumed that they are the
    # same size and registered

    avg_ndvi = None
    first_msi = None
    for msi_file in args.source_msi:
        msi = gdal_open(msi_file)
        # Compute normalized difference vegetation index (NDVI)
        ndvi = compute_ndvi(msi)
        if avg_ndvi is None:
            avg_ndvi = ndvi
            first_msi = msi
        else:
            avg_ndvi += ndvi

    if len(args.source_msi) > 1:
        avg_ndvi /= len(args.source_msi)

    gdal_save(avg_ndvi,
              first_msi,
              args.output_ndvi,
              gdal.GDT_Float32,
              options=['COMPRESS=DEFLATE'])
Ejemplo n.º 2
0
def copy_tif_info(in_fpath, out_fpath):
    """
    Copy the TIF metadata from in_fpath and save it to the image at out_fpath.
    :param in_fpath: Filepath for input image which has metadata to copy.
    :type in_fpath: str

    :param out_fpath: Filepath for image to which we want to save the metadata.
    :type out_fpath: str
    """
    try:
        logging.info("---- Copying TIF information ----")
        gdal_in = gdal_utils.gdal_open(in_fpath)
        gdal_out = gdal_utils.gdal_open(out_fpath, gdal.GA_Update)
        metadata_domains = gdal_in.GetMetadataDomainList()
        for domain in metadata_domains:
            dico = gdal_in.GetMetadata_Dict(domain)
            for key, val in dico.items():
                gdal_out.SetMetadataItem(key, val, domain)
        return True
    except Exception as e:
        logging.exception(e)
        return False
Ejemplo n.º 3
0
def main(args):
    # Configure argument parser
    parser = argparse.ArgumentParser(
        description='Use road and bridge labels from OSM to augment CLS')
    parser.add_argument("source_cls",
                        help="Source Building mask (CLS) image file name")
    parser.add_argument('--road-vector-shapefile-dir',
                        help='Path to road vector shapefile directory')
    parser.add_argument('--road-vector-shapefile-prefix',
                        help='Prefix for road vector shapefile')
    parser.add_argument('--road-vector', help='Path to road vector file')
    # XXX this is not ideal
    parser.add_argument('--road-rasterized',
                        help='Path to save rasterized road image')
    parser.add_argument('--road-rasterized-bridge',
                        help='Path to save rasterized bridge image')
    parser.add_argument("-d",
                        "--debug",
                        action="store_true",
                        help="Enable debug output and visualization")
    parser.add_argument(
        "destination_cls",
        help="Destination Building/Bridge mask (CLS) image file name")
    args = parser.parse_args(args)

    # For now assume the input DSM and DTM are in the same resolution,
    # aligned, and in the same coordinates.  Later we can warp the DTM
    # to the DSM, if needed.

    # open the CLS
    cls_file = gdal_open(args.source_cls)
    cls_band = cls_file.GetRasterBand(1)
    cls = cls_band.ReadAsArray()
    logging.debug("CLS raster shape {}".format(cls.shape))

    # Extract building labels as a binary mask
    mask = (cls == 6)

    use_roads = (args.road_vector or args.road_vector_shapefile_dir
                 or args.road_vector_shapefile_prefix or args.road_rasterized
                 or args.road_rasterized_bridge)
    if use_roads:
        use_shapefile_dir_with_prefix = (args.road_vector_shapefile_dir
                                         and args.road_vector_shapefile_prefix)
        if not ((args.road_vector or use_shapefile_dir_with_prefix)
                and args.road_rasterized and args.road_rasterized_bridge):
            raise RuntimeError(
                "All road path arguments must be provided if any is provided")

        if args.road_vector and use_shapefile_dir_with_prefix:
            raise RuntimeError("Should specify EITHER --road-vector OR \
both --road-vector-shapefile-dir AND --road-vector-shapefile-prefix")

        if use_shapefile_dir_with_prefix:
            input_road_vector = os.path.join(
                args.road_vector_shapefile_dir,
                "{}.shx".format(args.road_vector_shapefile_prefix))
        else:
            input_road_vector = args.road_vector

        # The dilation is intended to create semi-realistic widths
        roads = rasterize_file_dilated_line(
            input_road_vector,
            cls_file,
            args.road_rasterized,
            numpy.ones((3, 3)),
            dilation_iterations=20,
        )
        road_bridges = rasterize_file_dilated_line(
            input_road_vector,
            cls_file,
            args.road_rasterized_bridge,
            numpy.ones((3, 3)),
            dilation_iterations=20,
            query=ELEVATED_ROADS_QUERY,
        )

        # Remove building candidates that overlap with a road
        mask[roads] = False
        road_bridges = morphology.binary_closing(road_bridges,
                                                 numpy.ones((3, 3)),
                                                 iterations=3)

    # label the larger mask image
    label_img = ndm.label(mask)[0]
    # extract the unique labels that match the seeds
    selected, counts = numpy.unique(label_img, return_counts=True)
    # filter out very oblong objects
    subselected = []
    for i, n in zip(selected, counts):
        # skip the background and small components
        if i == 0 or n < 64:
            continue
        dim_large, dim_small = estimate_object_scale(label_img == i)
        if dim_small > 0 and dim_large / dim_small < 6:
            subselected.append(i)

    logging.info("Keeping {} connected components".format(len(subselected)))

    # keep only the mask components selected above
    good_mask = numpy.isin(label_img, subselected)

    # a final mask cleanup
    good_mask = morphology.binary_closing(good_mask,
                                          numpy.ones((3, 3)),
                                          iterations=3)

    # visualize final mask if in debug mode
    if args.debug:
        cv2.imshow('mask', good_mask.astype(numpy.uint8) * 255)
        cv2.waitKey(0)
        cv2.destroyAllWindows()

    # convert the mask to label map with value 2 (background),
    # 6 (building), and 17 (elevated roadway)
    cls = numpy.full(good_mask.shape, 2)
    cls[good_mask] = 6
    if use_roads:
        cls[road_bridges] = 17

    # create the mask image
    logging.info("Create destination mask of size:({}, {}) ...".format(
        cls_file.RasterXSize, cls_file.RasterYSize))
    gdal_save(cls,
              cls_file,
              args.destination_cls,
              gdal.GDT_Byte,
              options=['COMPRESS=DEFLATE'])
Ejemplo n.º 4
0
def main(args):
    # Configure argument parser
    parser = argparse.ArgumentParser(
        description='Segment buildings by comparing a DSM to a DTM')
    parser.add_argument("source_dsm",
                        help="Digital surface model (DSM) image file name")
    parser.add_argument("source_dtm",
                        help="Digital terrain model (DTM) image file name")
    parser.add_argument("--input-ndvi",
                        help="Optional Normalized Difference Vegetation "
                        "Index image file")
    parser.add_argument("--ndsm", help="Write out the normalized DSM image")
    parser.add_argument('--road-vector-shapefile-dir',
                        help='Path to road vector shapefile directory')
    parser.add_argument('--road-vector-shapefile-prefix',
                        help='Prefix for road vector shapefile')
    parser.add_argument('--road-vector', help='Path to road vector file')
    # XXX this is not ideal
    parser.add_argument('--road-rasterized',
                        help='Path to save rasterized road image')
    parser.add_argument('--road-rasterized-bridge',
                        help='Path to save rasterized bridge image')
    parser.add_argument("-d",
                        "--debug",
                        action="store_true",
                        help="Enable debug output and visualization")
    parser.add_argument("destination_mask", help="Building mask output image")
    args = parser.parse_args(args)

    # For now assume the input DSM and DTM are in the same resolution,
    # aligned, and in the same coordinates.  Later we can warp the DTM
    # to the DSM, if needed.

    # open the DSM
    dsm_file = gdal_open(args.source_dsm)
    dsm_band = dsm_file.GetRasterBand(1)
    dsm = dsm_band.ReadAsArray()
    dsm_nodata_value = dsm_band.GetNoDataValue()
    print("DSM raster shape {}".format(dsm.shape))

    # open the DTM
    dtm_file = gdal_open(args.source_dtm)
    dtm_band = dtm_file.GetRasterBand(1)
    dtm = dtm_band.ReadAsArray()
    print("DTM raster shape {}".format(dtm.shape))

    # Compute the normalized DSM by subtracting the terrain
    ndsm = dsm - dtm

    # consider any point above 2m as possible buildings
    mask = ndsm > 2
    # Use any point above 4m as a high confidence seed point
    seeds = ndsm > 4

    # if requested, write out the normalized DSM
    if args.ndsm:
        ndsm[dsm == dsm_nodata_value] = dsm_nodata_value
        save_ndsm(ndsm, dsm_file, args.ndsm)

    # if an NDVI image was specified, us it to filter
    if args.input_ndvi:
        # Load normalized difference vegetation index (NDVI) file
        ndvi_file = gdal_open(args.input_ndvi)
        ndvi_band = ndvi_file.GetRasterBand(1)
        ndvi = ndvi_band.ReadAsArray()

        # remove building candidates with high vegetation likelihood
        mask[ndvi > 0.2] = False
        # reduce seeds to areas with high confidence non-vegetation
        seeds[ndvi > 0.1] = False

    use_roads = (args.road_vector or args.road_vector_shapefile_dir
                 or args.road_vector_shapefile_prefix or args.road_rasterized
                 or args.road_rasterized_bridge)
    if use_roads:
        use_shapefile_dir_with_prefix = (args.road_vector_shapefile_dir
                                         and args.road_vector_shapefile_prefix)
        if not ((args.road_vector or use_shapefile_dir_with_prefix)
                and args.road_rasterized and args.road_rasterized_bridge):
            raise RuntimeError(
                "All road path arguments must be provided if any is provided")

        if args.road_vector and use_shapefile_dir_with_prefix:
            raise RuntimeError("Should specify EITHER --road-vector OR \
both --road-vector-shapefile-dir AND --road-vector-shapefile-prefix")

        if use_shapefile_dir_with_prefix:
            input_road_vector = os.path.join(
                args.road_vector_shapefile_dir,
                "{}.shx".format(args.road_vector_shapefile_prefix))
        else:
            input_road_vector = args.road_vector

        # The dilation is intended to create semi-realistic widths
        roads = rasterize_file_dilated_line(
            input_road_vector,
            dsm_file,
            args.road_rasterized,
            numpy.ones((3, 3)),
            dilation_iterations=20,
        )
        road_bridges = rasterize_file_dilated_line(
            input_road_vector,
            dsm_file,
            args.road_rasterized_bridge,
            numpy.ones((3, 3)),
            dilation_iterations=20,
            query=ELEVATED_ROADS_QUERY,
        )

        # Remove building candidates that overlap with a road
        mask[roads] = False
        seeds[roads] = False

    # use morphology to clean up the mask
    mask = morphology.binary_opening(mask, numpy.ones((3, 3)), iterations=1)
    mask = morphology.binary_closing(mask, numpy.ones((3, 3)), iterations=1)
    # use morphology to clean up the seeds
    seeds = morphology.binary_opening(seeds, numpy.ones((3, 3)), iterations=1)
    seeds = morphology.binary_closing(seeds, numpy.ones((3, 3)), iterations=1)

    # compute connected components on the seeds
    label_img = ndm.label(seeds)[0]
    # compute the size of each connected component
    labels, counts = numpy.unique(label_img, return_counts=True)
    # filter seed connected components to keep only large areas
    to_remove = numpy.extract(counts < 500, labels)
    print("Removing {} small connected components".format(len(to_remove)))
    seeds[numpy.isin(label_img, to_remove)] = False

    # visualize initial seeds if in debug mode
    if args.debug:
        cv2.imshow(
            'seeds',
            mask.astype(numpy.uint8) * 127 + seeds.astype(numpy.uint8) * 127)
        cv2.waitKey(0)
        cv2.destroyAllWindows()

    # label the larger mask image
    label_img = ndm.label(mask)[0]
    # extract the unique labels that match the seeds
    selected = numpy.unique(numpy.extract(seeds, label_img))
    # filter out very oblong objects
    subselected = []
    for i in selected:
        dim_large, dim_small = estimate_object_scale(label_img == i)
        if dim_large / dim_small < 6:
            subselected.append(i)

    print("Keeping {} connected components".format(len(subselected)))

    # keep only the mask components selected above
    good_mask = numpy.isin(label_img, subselected)

    # a final mask cleanup
    good_mask = morphology.binary_closing(good_mask,
                                          numpy.ones((3, 3)),
                                          iterations=3)

    # visualize final mask if in debug mode
    if args.debug:
        cv2.imshow('mask', good_mask.astype(numpy.uint8) * 255)
        cv2.waitKey(0)
        cv2.destroyAllWindows()

    # convert the mask to label map with value 2 (background),
    # 6 (building), and 17 (elevated roadway)
    cls = numpy.full(good_mask.shape, 2)
    cls[good_mask] = 6
    if use_roads:
        cls[road_bridges] = 17

    # create the mask image
    print("Create destination mask of size:({}, {}) ...".format(
        dsm_file.RasterXSize, dsm_file.RasterYSize))
    gdal_save(cls,
              dsm_file,
              args.destination_mask,
              gdal.GDT_Byte,
              options=['COMPRESS=DEFLATE'])
Ejemplo n.º 5
0
def main(args):
    global VECTOR_TYPES
    parser = argparse.ArgumentParser(
        description=
        "Generate building mask aligned with image. To do that we shift input "
        "vector to match edges generated from image.")
    parser.add_argument(
        'output_mask',
        help="Output image mask base name. <output_mask>_buildings.shp, "
        "<output_mask>_buildings.tif are generated. Optionally "
        "<output_mask>_roads.tif and <output_mask>_roads.shp are "
        "also generated. See --input_vectors parameter.")
    parser.add_argument('input_image', help='Orthorectified 8-bit image file')
    parser.add_argument(
        'input_vectors',
        nargs='+',
        help='Buildings and optionally road vector files with OSM or '
        'US Cities data. A polygon layer is chosen for buildings and a '
        'line string layer is chosen for roads. '
        'If both building and road layers are in the same vector file just '
        'pass the file twice. Only elevated bridges are rendered '
        'by default. If all roads need to be rendered pass --render_roads')
    parser.add_argument('--render_cls',
                        action="store_true",
                        help='Output a CLS image')
    parser.add_argument('--render_roads',
                        action="store_true",
                        help='Render all roads, not only elevated bridges')
    parser.add_argument(
        '--scale',
        type=float,
        default=0.2,
        help='Scale factor. '
        'We cannot deal with the images with original resolution')
    parser.add_argument('--move_thres',
                        type=float,
                        default=5,
                        help='Distance for edge matching')
    parser.add_argument(
        "--offset",
        type=float,
        nargs=2,
        help="Shift the mask using the offset specified "
        "(using the SRS of the input_image) instead of the computed offset.")
    parser.add_argument("--debug",
                        action="store_true",
                        help="Print debugging information")
    args = parser.parse_args(args)

    scale = args.scale

    inputImage = gdal_utils.gdal_open(args.input_image, gdal.GA_ReadOnly)
    band = inputImage.GetRasterBand(1)
    if (not band.DataType == gdal.GDT_Byte):
        raise RuntimeError(
            "Input image {} does not have Byte type. Use msi-to-rgb.py to-8bit.py "
            "to convert it.".format(args.input_image))

    projection = inputImage.GetProjection()
    inputImageSrs = osr.SpatialReference(projection)
    gt = inputImage.GetGeoTransform()  # captures origin and pixel size

    left, top = gdal.ApplyGeoTransform(gt, 0, 0)
    right, bottom = gdal.ApplyGeoTransform(gt, inputImage.RasterXSize,
                                           inputImage.RasterYSize)
    band = None

    print("Resize and edge detection: {}".format(
        os.path.basename(args.input_image)))
    color_image = cv2.imread(args.input_image)
    small_color_image = numpy.zeros((int(
        color_image.shape[0] * scale), int(color_image.shape[1] * scale), 3),
                                    dtype=numpy.uint8)
    if scale != 1.0:
        small_color_image = cv2.resize(color_image, None, fx=scale, fy=scale)
        color_image = small_color_image
    grayimg = cv2.cvtColor(color_image, cv2.COLOR_BGR2GRAY)
    edge_img = cv2.Canny(grayimg, 100, 200)
    if args.debug:
        cv2.imwrite(
            os.path.splitext(args.output_mask)[0] + '_edge.tif', edge_img)

    model = {}
    model['corners'] = [left, top, right, bottom]
    model['project_model'] = gt
    model['scale'] = scale

    inputImageCorners = [left, right, bottom, top]
    features = spat_vectors(args.input_vectors, inputImageCorners,
                            inputImageSrs, args.output_mask)
    print("Aligning {} buildings ...".format(len(features[0])))
    tmp_img = numpy.zeros(
        [int(color_image.shape[0]),
         int(color_image.shape[1])],
        dtype=numpy.uint8)
    for feature in features[0]:
        poly = feature.GetGeometryRef()
        for ring_idx in range(poly.GetGeometryCount()):
            ring = poly.GetGeometryRef(ring_idx)
            rp = []
            for i in range(0, ring.GetPointCount()):
                pt = ring.GetPoint(i)
                rp.append(ProjectPoint(model, pt))
            ring_points = numpy.array(rp)
            ring_points = ring_points.reshape((-1, 1, 2))

            # edge mask of the building cluster
            cv2.polylines(tmp_img, [ring_points], True, (255), thickness=2)

    check_point_list = []
    # build a sparse set to fast process
    for y in range(0, tmp_img.shape[0]):
        for x in range(0, tmp_img.shape[1]):
            if tmp_img[y, x] > 200:
                check_point_list.append([x, y])
    print("Checking {} points ...".format(len(check_point_list)))

    max_value = 0
    index_max_value = 0
    offsetGeo = [0.0, 0.0]
    current = [0, 0]
    if not args.offset:
        offset = [0, 0]
        # shift moves possible from [0, 0]
        moves = [
            [1, 0],  # 0
            [1, 1],  # 1
            [0, 1],  # 2
            [-1, 1],  # 3
            [-1, 0],  # 4
            [-1, -1],  # 5
            [0, -1],  # 6
            [1, -1]
        ]  # 7
        initial_cases = range(8)
        # cases[i] shows shift moves possible after the previous move was cases[i][0]
        # we change direction with at most 45 degrees.
        next_cases = [[0, 7, 1], [1, 0, 2], [2, 1, 3], [3, 2, 4], [4, 3, 5],
                      [5, 4, 6], [6, 5, 7], [7, 6, 0]]

        # move the mask to match
        cases = initial_cases
        old_max_value = 0
        total_value = computeMatchingPoints(check_point_list, edge_img, 0, 0)
        max_value = total_value
        if args.debug:
            print("Total value for ({}, {}) is: {} (max value: {})".format(
                0, 0, total_value, max_value))
        for i in range(args.move_thres):
            if args.debug:
                print("===== {} =====".format(i))
            while (max_value > old_max_value):
                old_max_value = max_value
                for i in cases:
                    [dx, dy] = moves[i]
                    total_value = computeMatchingPoints(
                        check_point_list, edge_img, current[0] + dx,
                        current[1] + dy)
                    if args.debug:
                        print(
                            "Total value for ({}, {}) is: {} (max value: {})".
                            format(dx, dy, total_value, max_value))
                    if total_value > max_value:
                        max_value = total_value
                        index_max_value = i
                if (max_value > old_max_value):
                    [dx, dy] = moves[index_max_value]
                    current = [current[0] + dx, current[1] + dy]
                    if args.debug:
                        print("Current: {}".format(current))
                    offset = current
                    cases = next_cases[index_max_value]
                    break

        offsetGeo = gdal.ApplyGeoTransform(gt, offset[0] / scale,
                                           offset[1] / scale)
        offsetGeo[0] = offsetGeo[0] - left
        offsetGeo[1] = top - offsetGeo[1]
        print("Using offset: {} ({})".format(offsetGeo, offset))
        if max_value / float(len(check_point_list)) < 0.05:
            print(
                "Fewer than 5% of points match {} / {}. This may happen because of "
                "missing areas in the orthorectified image. "
                "Increasing scale may increase the number of points that match."
                .format(max_value, len(check_point_list)))
    else:
        print("Using offset: {}".format(offsetGeo))
        offsetGeo = args.offset

    for i in range(len(features)):
        outputNoExt = os.path.splitext(args.output_mask)[0]
        outputVectorFile = outputNoExt + "_" + VECTOR_TYPES[i] + "_spat.shp"
        if not (offsetGeo[0] == 0.0 and offsetGeo[1] == 0.0):
            shift_vector(features[i], outputVectorFile, outputNoExt,
                         projection, offsetGeo)
        else:
            inputVectorFileName = outputNoExt + "_" + VECTOR_TYPES[
                i] + "_spat_not_aligned.shp"
            print("Copy vector -> {}".format(
                os.path.basename(outputVectorFile)))
            copy_shapefile(inputVectorFileName, outputVectorFile)
        if not args.debug:
            remove_shapefile(outputNoExt + "_" + VECTOR_TYPES[i] +
                             "_spat_not_aligned.shp")

        ogr2ogr_args = [
            "ogr2ogr", "-clipsrc",
            str(inputImageCorners[0]),
            str(inputImageCorners[2]),
            str(inputImageCorners[1]),
            str(inputImageCorners[3])
        ]
        outputNoExt = os.path.splitext(args.output_mask)[0]
        ogr2ogr_args.extend([
            outputNoExt + "_" + VECTOR_TYPES[i] + ".shp",
            outputNoExt + "_" + VECTOR_TYPES[i] + "_spat.shp"
        ])
        print("Clipping vector file {} -> {}".format(
            os.path.basename(outputNoExt + "_" + VECTOR_TYPES[i] +
                             "_spat.shp"),
            os.path.basename(outputNoExt + "_" + VECTOR_TYPES[i] + ".shp")))
        response = subprocess.run(ogr2ogr_args,
                                  stdout=subprocess.PIPE,
                                  stderr=subprocess.PIPE)
        if args.debug:
            print(*ogr2ogr_args)
            print("{}\n{}".format(response.stdout, response.stderr))
        remove_shapefile(outputNoExt + "_" + VECTOR_TYPES[i] + "_spat.shp")

        if i == 0:
            print("Rasterizing buildings ...")
            if args.render_cls:
                rasterize_args = [
                    "gdal_rasterize", "-ot", "Byte", "-init", "2", "-burn",
                    "6", "-ts",
                    str(inputImage.RasterXSize),
                    str(inputImage.RasterYSize), "-te",
                    str(inputImageCorners[0]),
                    str(inputImageCorners[2]),
                    str(inputImageCorners[1]),
                    str(inputImageCorners[3])
                ]
            else:
                # make buildings red
                rasterize_args = [
                    "gdal_rasterize", "-ot", "Byte", "-burn", "255", "-burn",
                    "0", "-burn", "0", "-burn", "255", "-ts",
                    str(inputImage.RasterXSize),
                    str(inputImage.RasterYSize), "-te",
                    str(inputImageCorners[0]),
                    str(inputImageCorners[2]),
                    str(inputImageCorners[1]),
                    str(inputImageCorners[3])
                ]

            outputNoExt = os.path.splitext(args.output_mask)[0]
            rasterize_args.extend([
                outputNoExt + "_" + VECTOR_TYPES[i] + ".shp",
                outputNoExt + "_" + VECTOR_TYPES[i] + ".tif"
            ])
            print("Rasterizing {} -> {}".format(
                os.path.basename(outputNoExt + "_" + VECTOR_TYPES[i] + ".shp"),
                os.path.basename(outputNoExt + "_" + VECTOR_TYPES[i] +
                                 ".tif")))
            response = subprocess.run(rasterize_args,
                                      stdout=subprocess.PIPE,
                                      stderr=subprocess.PIPE)
            if args.debug:
                print(*rasterize_args)
                print("{}\n{}".format(response.stdout, response.stderr))
        else:
            print("Rasterizing bridges ...")
            outputNoExt = os.path.splitext(args.output_mask)[0]
            input = os.path.basename(outputNoExt + "_" + VECTOR_TYPES[i] +
                                     ".shp")
            output = os.path.basename(outputNoExt + "_" + VECTOR_TYPES[i] +
                                      "_bridges.tif")
            bridges = rasterize.rasterize_file_dilated_line(
                input,
                inputImage,
                output,
                numpy.ones((3, 3)),
                dilation_iterations=20,
                query=rasterize.ELEVATED_ROADS_QUERY,
            )
            if not args.debug:
                os.remove(output)
            if args.render_roads:
                output = os.path.basename(outputNoExt + "_" + VECTOR_TYPES[i] +
                                          "_roads.tif")
                roads = rasterize.rasterize_file_dilated_line(
                    input,
                    inputImage,
                    output,
                    numpy.ones((3, 3)),
                    dilation_iterations=20,
                    query=rasterize.ROADS_QUERY)
                if not args.debug:
                    os.remove(output)
            buildingsData = gdal_utils.gdal_open(
                os.path.basename(outputNoExt + "_" + VECTOR_TYPES[0] + ".tif"),
                gdal.GA_ReadOnly)
            if args.render_cls:
                cls = buildingsData.GetRasterBand(1).ReadAsArray()
                if args.render_roads:
                    cls[roads] = 11
                cls[bridges] = 17
                gdal_utils.gdal_save(cls,
                                     inputImage,
                                     os.path.basename(outputNoExt + ".tif"),
                                     gdal.GDT_Byte,
                                     options=['COMPRESS=DEFLATE'])
            else:
                red = buildingsData.GetRasterBand(1).ReadAsArray()
                green = buildingsData.GetRasterBand(2).ReadAsArray()
                blue = buildingsData.GetRasterBand(3).ReadAsArray()
                opacity = buildingsData.GetRasterBand(4).ReadAsArray()
                if args.render_roads:
                    red[roads] = 0
                    green[roads] = 255
                    blue[roads] = 0
                    opacity[roads] = 255
                red[bridges] = 0
                green[bridges] = 0
                blue[bridges] = 255
                opacity[bridges] = 255
                gdal_utils.gdal_save([red, green, blue, opacity],
                                     inputImage,
                                     os.path.basename(outputNoExt + ".tif"),
                                     gdal.GDT_Byte,
                                     options=['COMPRESS=DEFLATE'])
            if not args.debug:
                os.remove(
                    os.path.basename(outputNoExt + "_" + VECTOR_TYPES[0] +
                                     ".tif"))
Ejemplo n.º 6
0
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)
Ejemplo n.º 7
0
def main(args):
    parser = argparse.ArgumentParser(description="Download Openstreetmap data \
    for a region and convert to GeoJSON")
    parser.add_argument(
        '--left',
        type=str,
        help='Longitude of left / westernmost side of bounding box')
    parser.add_argument(
        '--bottom',
        type=str,
        help='Latitude of bottom / southernmost side of bounding box')
    parser.add_argument(
        '--right',
        type=str,
        help='Longitude of right / easternmost side of bounding box')
    parser.add_argument(
        '--top',
        type=str,
        help='Latitude of top / northernmost side of bounding box')
    parser.add_argument(
        '--bounding-img',
        type=str,
        help='Get region of interest from image file instead of \
        explicitly setting the bounds')
    parser.add_argument('--api-endpoint',
                        type=str,
                        default="https://api.openstreetmap.org/api/0.6/map")
    parser.add_argument('--output-dir', type=str, required=True)
    parser.add_argument('--output-prefix', type=str, default="road_vector")

    args = parser.parse_args(args)

    if args.bounding_img is not None:
        if os.path.isfile(args.bounding_img):
            img = gdal_utils.gdal_open(args.bounding_img)
            outProj = pyproj.Proj('+proj=longlat +datum=WGS84')
            left, bottom, right, top = gdal_utils.gdal_bounding_box(
                img, outProj)
        else:
            logging.error(
                "Couldn't find bounding_img file: [{}].  Aborting!".format(
                    args.bounding_img))
            exit(1)
    elif all((args.left, args.bottom, args.right, args.top)):
        left, bottom, right, top = (args.left, args.bottom, args.right,
                                    args.top)
    else:
        logging.error("Must specify either '--bounding-img' or all of "
                      "'--left', '--bottom', '--right', '--top'.  Aborting!")
        exit(1)

    request_params = {"bbox": ",".join(map(str, [left, bottom, right, top]))}

    response = requests.get(args.api_endpoint, request_params)

    output_osm_filepath = os.path.join(args.output_dir,
                                       "{}.osm".format(args.output_prefix))
    try:
        os.mkdir(args.output_dir)
    except FileExistsError as e:
        pass

    with open(output_osm_filepath, 'wb') as fd:
        for chunk in response.iter_content(chunk_size=128):
            fd.write(chunk)

    # Make the initial conversion from OSM to GeoJSON
    geojson_preform_output_filepath = os.path.join(
        args.output_dir, "{}.preformat.geojson".format(args.output_prefix))
    osm_sql_query = 'SELECT * FROM lines'
    subprocess.run([
        'ogr2ogr', '-f', 'GeoJSON', geojson_preform_output_filepath,
        output_osm_filepath, '-sql', osm_sql_query
    ],
                   check=True)

    geojson_output_filepath = os.path.join(
        args.output_dir, "{}.geojson".format(args.output_prefix))
    # Manually tweak GeoJSON to fit expected format
    with open(geojson_preform_output_filepath, 'r') as f:
        json_data = json.load(f)

    json_data["features"] = [feature_map(f) for f in json_data["features"]]

    with open(geojson_output_filepath, 'w') as f:
        f.write(json.dumps(json_data))

    return 0
Ejemplo n.º 8
0
def main(args):
    parser = argparse.ArgumentParser(
        description=
        'Convert polygons into WAMI-Viewer kw18 format. A polygon is '
        'a list of points pixel value coordinates. ')
    parser.add_argument(
        'input_vector',
        help='Buildings vector file with OSM or US Cities data.')
    parser.add_argument('input_image',
                        help='Image from which to read the resolution '
                        'and the geo position of the data. '
                        'Assume image has square pixels aligned with XY axes. '
                        'The vector and image cover the same area '
                        '(as generated by align-buildings.py)')
    parser.add_argument(
        'output_noext',
        help='Output with no extension for files in WAMI-Viewer kw18 format')
    parser.add_argument(
        '--scale_half',
        action="store_true",
        help=
        'The kw18 polygon coordinates have half the resolution of input_image')
    parser.add_argument('--label_img_path',
                        help='Path to labeled version of input image. (.tif)')
    parser.add_argument('--debug',
                        action='store_true',
                        help='Print debugging information')
    args = parser.parse_args(args)

    inputImage = gdal_utils.gdal_open(args.input_image, gdal.GA_ReadOnly)
    # get imageProj
    projection = inputImage.GetProjection()
    if not projection:
        projection = inputImage.GetGCPProjection()
    imageSrs = osr.SpatialReference(wkt=projection)
    imageProj = pyproj.Proj(imageSrs.ExportToProj4())

    # BB in image geo coordinates
    [minX, minY, maxX, maxY] = gdal_utils.gdal_bounding_box(inputImage)
    pixelSizeX = (maxX - minX) / inputImage.RasterXSize
    pixelSizeY = (maxY - minY) / inputImage.RasterYSize
    print("Image size ({},{}), BB({}, {}, {}, {}), pixel size ({}, {})".format(
        inputImage.RasterXSize, inputImage.RasterYSize, minX, maxX, minY, maxY,
        pixelSizeX, pixelSizeY))

    inputVector = gdal_utils.ogr_open(args.input_vector)
    inputLayer = gdal_utils.ogr_get_layer(inputVector, ogr.wkbPolygon)
    vectorSrs = inputLayer.GetSpatialRef()
    vectorProj = pyproj.Proj(vectorSrs.ExportToProj4())
    inputFeatures = list(inputLayer)

    polygonId = 0
    polygons = {}
    for feature in inputFeatures:
        points = []
        poly = feature.GetGeometryRef()
        for ringIdx in range(poly.GetGeometryCount()):
            ring = poly.GetGeometryRef(ringIdx)
            for pointId in range(0, ring.GetPointCount()):
                p = ring.GetPoint(pointId)
                # transform to image geo coords
                pImage = [0.0, 0.0]
                pImage[0], pImage[1] = pyproj.transform(
                    vectorProj, imageProj, p[0], p[1])
                # transform to pixels
                scale = 1.0
                if args.scale_half:
                    scale = 0.5
                pPixels = [
                    int((pImage[0] - minX) * scale / pixelSizeX + 0.5),
                    int((maxY - pImage[1]) * scale / pixelSizeY + 0.5)
                ]
                rasterSize = [inputImage.RasterXSize, inputImage.RasterYSize]
                for i in [0, 1]:
                    pPixels[i] = pPixels[i] - \
                        1 if pPixels[i] >= rasterSize[i] else pPixels[i]
                    pPixels[i] = 0 if pPixels[i] < 0 else pPixels[i]
                points.append(pPixels)
            if len(points) > 0:
                polygons[polygonId] = points
                polygonId += 1
    if args.label_img_path:
        polygon_labels = mtl_polygon.assign_mtl_polygon_label(
            polygons, inputImage, args.label_img_path)
    else:
        polygon_labels = None
    gen_kw18.gen_kw18(polygons, polygon_labels, args.output_noext)
    if args.debug:
        import vtk
        numberOfPixels = inputImage.RasterXSize * inputImage.RasterYSize
        scalars = vtk.vtkUnsignedCharArray()
        scalars.SetNumberOfComponents(4)
        scalars.SetNumberOfTuples(numberOfPixels)
        # white transparent pixels
        for i in range(3):
            scalars.FillComponent(i, 255)
        scalars.FillComponent(3, 0)
        image = vtk.vtkImageData()
        image.SetDimensions(inputImage.RasterXSize, inputImage.RasterYSize, 1)
        image.SetSpacing(1, 1, 1)
        image.SetOrigin(0, 0, 0)
        image.GetPointData().SetScalars(scalars)
        for i, polygon in polygons.items():
            for point in polygon:
                point = [point[0], inputImage.RasterYSize - point[1] - 1]
                for x in range(-3, 4, 1):
                    for y in range(-3, 4, 1):
                        p = [point[0] + x, point[1] + y]
                        if p[0] >= 0 and p[0] < inputImage.RasterXSize and \
                           p[1] >= 0 and p[1] < inputImage.RasterYSize:
                            # black opaque 7x7 blocks
                            image.SetScalarComponentFromFloat(
                                p[0], p[1], 0, 0, 0)
                            image.SetScalarComponentFromFloat(
                                p[0], p[1], 0, 1, 0)
                            image.SetScalarComponentFromFloat(
                                p[0], p[1], 0, 2, 0)
                            image.SetScalarComponentFromFloat(
                                p[0], p[1], 0, 3, 255)
        writer = vtk.vtkPNGWriter()
        writer.SetFileName(args.output_noext + '.png')
        writer.SetInputDataObject(image)
        writer.Write()