Ejemplo n.º 1
0
def warp(h, w, t, affine_matrices_inv, src):
    # (Inverse) Warp each pixel
    mid = np.zeros((h, w, 3))
    for i in range(h):
        for j in range(w):  # added here to see if i still get index errors
            tri_vertices_indeces_index = tsearch(t, [j, i])
            iw = inverse_warp(affine_matrices_inv[tri_vertices_indeces_index],
                              j, i)
            x = int(iw[0])
            y = int(iw[1])
            if x > w - 1:
                print("Inverse Warp returned an x greater than width.")
                print(x)
                x = w - 1
            if y > h - 1:
                print("Inverse Warp returned a y greater than height.")
                print(y)
                y = h - 1
            if x < 0:
                print("Inverse Warp returned a x less than 0.")
                print(x)
                x = 0
            if y < 0:
                print("Inverse Warp returend a  y less than 0.")
                print(y)
                y = 0

            mid[i, j, :] = src[y, x, :]
    return mid
Ejemplo n.º 2
0
def inversewarp(source, result, totalTrans):
    for specH in range(0, 480):
        for specW in range(0, 640):
            triIndex = tsearch(trAvrShape, (specW, specH))
            x, y = findingCoords(specH, specW, totalTrans[:, :, triIndex])
            if insideTheLimits(x, y, 480, 640):
                result[specH, specW, 0] = source[int(x), int(y), 0]
                result[specH, specW, 1] = source[int(x), int(y), 1]
                result[specH, specW, 2] = source[int(x), int(y), 2]
    return result
Ejemplo n.º 3
0
def findMidWayFace(imA, imB, imA_points, imB_points, imMid_points, triMid,
                   triA, triB, weight):
    affineAMatrices = findAffine(triMid, imA_points, imMid_points)
    affineBMatrices = findAffine(triMid, imB_points, imMid_points)

    for y in range(imA.shape[0]):
        for x in range(imA.shape[1]):
            tri_index = tsearch(triMid, (x, y))
            affined_pointA = np.dot(np.linalg.inv(affineAMatrices[tri_index]),
                                    [x, y, 1])
            affined_pointB = np.dot(np.linalg.inv(affineBMatrices[tri_index]),
                                    [x, y, 1])

            affineAx = np.int(affined_pointA[0, 0])
            affineAy = np.int(affined_pointA[0, 1])
            affineBx = np.int(affined_pointB[0, 0])
            affineBy = np.int(affined_pointB[0, 1])

            morphedIm[y, x, :] = imA[affineAy, affineAx, :] * weight + imB[
                affineBy, affineBx, :] * (1 - weight)

    return morphedIm
Ejemplo n.º 4
0
def warpMeanFace(im_names, im_points, average_points, saved_name, t=1):
    im_points = np.array(im_points)
    triAverage = Delaunay(average_points)
    image = plt.imread(im_names[0])
    height = image.shape[0]
    width = image.shape[1]

    morphedIm = np.zeros((height, width, 3), dtype="float32")
    for i in range(len(im_names)):
        image = plt.imread(im_names[i])
        affineMatrices = findAffine(triAverage, im_points[i], average_points)
        for y in range(height):
            for x in range(width):
                tri_index = tsearch(triAverage, (x, y))
                affined_point = np.dot(
                    np.linalg.inv(affineMatrices[tri_index]), [x, y, 1])

                affineX = affined_point[0, 0]
                affineY = affined_point[0, 1]

                morphedIm[y, x, :] += image[np.int(affineY),
                                            np.int(affineX), :]
    morphedIm = morphedIm / len(im_names)
    misc.imsave(saved_name, morphedIm)
Ejemplo n.º 5
0
# -*- coding: utf-8 -*-
"""
Created on Mon Jun  8 16:00:31 2020

@author: wegia
"""
import numpy as np

points = np.array([[0, 0], [0, 1], [0, 2], [1, 0], [1, 1], [1, 2], [2, 0],
                   [2, 1], [2, 2]])
from scipy.spatial import Voronoi, voronoi_plot_2d
vor = Voronoi(points)

import matplotlib.pyplot as plt
fig = voronoi_plot_2d(vor)
plt.show()

import numpy as np
import matplotlib.pyplot as plt
from scipy.spatial import Delaunay, delaunay_plot_2d, tsearch

pts = np.random.rand(20, 2)
tri = Delaunay(pts)
_ = delaunay_plot_2d(tri)

loc = np.random.uniform(0.2, 0.8, (5, 2))
s = tsearch(tri, loc)
plt.triplot(pts[:, 0], pts[:, 1], tri.simplices[s], 'b-', mask=s == -1)
plt.scatter(loc[:, 0], loc[:, 1], c='r', marker='x')
plt.show()
Ejemplo n.º 6
0
def run(in_jsons: List[params.preprocessing_content_type],
        out_dir: str,
        resolution: float = 0.5,
        min_elevation_offset: float = None,
        max_elevation_offset: float = None,
        epsg: int = None,
        sigma: float = None,
        dsm_radius: int = 1,
        dsm_no_data: int = -32768,
        color_no_data: int = 0,
        corr_config: Dict = None,
        output_stats: bool = False,
        mode: str = "local_dask",
        nb_workers: int = 4,
        walltime: str = "00:59:00",
        roi: Tuple[List[int], int] = None,
        use_geoid_alt: bool = False,
        use_sec_disp: bool = False,
        snap_to_img1: bool = False,
        align: bool = False,
        cloud_small_components_filter: bool = True,
        cloud_statistical_outliers_filter: bool = True,
        epi_tile_size: int = None):
    """
    Main function for the compute_dsm subcommand

    This function will compute independent tiles of the final DSM, with the following steps:

    1. Epipolar resampling (including mask)
    2. Disparity map estimation
    3. Triangulation of disparity map
    4. Rasterization to DSM

    :param in_jsons: dictionaries describing the input pair (as produced by cars_preproc tool)
    :param out_dir: directory where output raster and color images will be written
    :param resolution: resolution of DSM to produce
    :param min_elevation_offset: Override minimum disparity from prepare step with this offset in meters
    :param max_elevation_offset: Override maximum disparity from prepare step with this offset in meters
    :param epsg: epsg code for the CRS of the output DSM
    :param sigma: width of gaussian weight for rasterization
    :param dsm_radius: Radius around a cell for gathering points for rasterization
    :param dsm_no_data: No data value to use in the final DSM file
    :param color_no_data: No data value to use in the final colored image
    :param corr_config: Correlator configuration
    :param output_stats: flag, if true, outputs dsm as a geotiff file with quality statistics.
    :param mode: Parallelization mode
    :param nb_workers: Number of dask workers to use for the sift matching step
    :param walltime: Walltime of the dask workers
    :param roi: DSM ROI in final projection with the corresponding epsg code ([xmin, ymin, xmax, ymax], roi_epsg))
    (roi_epsg can be set to None if the ROI is in final projection)
    :param use_geoid_alt: Wheter altitude should be computed wrt geoid height or not.
    :param use_sec_disp: Boolean activating the use of the secondary disparity map
    :param snap_to_img1: If this is True, Lines of Sight of img2 are moved so as to cross those of img1
    :param align: If this is True, use the correction estimated during prepare to align to lowres DEM (if available)
    :param cloud_small_components_filter: Boolean activating the points cloud small components filtering. The filter's
    parameters are set in the static configuration json.
    :param cloud_statistical_outliers_filter: Boolean activating the points cloud statistical outliers filtering.
    The filter's parameters are set in the static configuration json.
    :param epi_tile_size: Force the size of epipolar tiles (None by default)
    """
    out_dir = os.path.abspath(out_dir)
    # Ensure that outdir exists
    try:
        os.makedirs(out_dir)
    except OSError as exc:
        if exc.errno == errno.EEXIST and os.path.isdir(out_dir):
            pass
        else:
            raise
    tmp_dir = os.path.join(out_dir, 'tmp')

    utils.add_log_file(out_dir, 'compute_dsm')
    logging.info("Received {} stereo pairs configurations".format(
        len(in_jsons)))

    # Retrieve static parameters (rasterization and cloud filtering)
    static_params = static_cfg.get_cfg()

    # Initiate ouptut json dictionary
    out_json = {
        params.stereo_inputs_section_tag: [],
        params.stereo_section_tag: {
            params.stereo_version_tag: utils.get_version(),
            params.stereo_parameters_section_tag: {
                params.resolution_tag: resolution,
                params.sigma_tag: sigma,
                params.dsm_radius_tag: dsm_radius
            },
            params.static_params_tag:
            static_params[static_cfg.compute_dsm_tag],
            params.stereo_output_section_tag: {}
        }
    }

    if use_geoid_alt:
        geoid_data = utils.read_geoid_file()
        out_json[params.stereo_section_tag][params.stereo_output_section_tag][
            params.alt_reference_tag] = 'geoid'
    else:
        geoid_data = None
        out_json[params.stereo_section_tag][params.stereo_output_section_tag][
            params.alt_reference_tag] = 'ellipsoid'

    if epsg is not None:
        out_json[params.stereo_section_tag][
            params.stereo_parameters_section_tag][params.epsg_tag] = epsg

    roi_epsg = None
    if roi is not None:
        (roi_xmin, roi_ymin, roi_xmax, roi_ymax), roi_epsg = roi
        roi_poly = Polygon([(roi_xmin, roi_ymin), (roi_xmax, roi_ymin),
                            (roi_xmax, roi_ymax), (roi_xmin, roi_ymax),
                            (roi_xmin, roi_ymin)])

    # set the timeout for each job in multiprocessing mode (in seconds)
    perJobTimeout = 600

    configurations_data = {}

    config_idx = 1

    ref_left_image = None

    for in_json in in_jsons:
        # Build config id
        config_id = "config_{}".format(config_idx)

        # Check configuration with respect to schema
        configuration = utils.check_json(in_json,
                                         params.preprocessing_content_schema)

        preprocessing_output_config = configuration[
            params.preprocessing_section_tag][
                params.preprocessing_output_section_tag]

        # Append input configuration to output json
        out_json[params.stereo_inputs_section_tag].append(configuration)

        configurations_data[config_id] = {}

        configurations_data[config_id]['configuration'] = configuration

        # Check left image and raise a warning if different left images are used along with snap_to_img1 mpode
        if ref_left_image is None:
            ref_left_image = configuration[params.input_section_tag][
                params.img1_tag]
        else:
            if snap_to_img1 and ref_left_image != configuration[
                    params.input_section_tag][params.img1_tag]:
                logging.warning(
                    "--snap_to_left_image mode is used but input configurations have different images as their left image in pair. This may result in increasing registration discrepencies between pairs"
                )

        # Get largest epipolar regions from configuration file
        largest_epipolar_region = [
            0, 0, preprocessing_output_config[params.epipolar_size_x_tag],
            preprocessing_output_config[params.epipolar_size_y_tag]
        ]

        configurations_data[config_id][
            'largest_epipolar_region'] = largest_epipolar_region

        disp_min = preprocessing_output_config[params.minimum_disparity_tag]
        disp_max = preprocessing_output_config[params.maximum_disparity_tag]
        disp_to_alt_ratio = preprocessing_output_config[
            params.disp_to_alt_ratio_tag]

        # Check if we need to override disp_min
        if min_elevation_offset is not None:
            user_disp_min = min_elevation_offset / disp_to_alt_ratio
            if user_disp_min > disp_min:
                logging.warning((
                    'Overriden disparity minimum = {:.3f} pix. (or {:.3f} m.) is greater '
                    'than disparity minimum estimated in prepare step = {:.3f} pix. (or '
                    '{:.3f} m.) for configuration {}').format(
                        user_disp_min, min_elevation_offset, disp_min,
                        disp_min * disp_to_alt_ratio, config_id))
                disp_min = user_disp_min

        # Check if we need to override disp_max
        if max_elevation_offset is not None:
            user_disp_max = max_elevation_offset / disp_to_alt_ratio
            if user_disp_max < disp_max:
                logging.warning((
                    'Overriden disparity maximum = {:.3f} pix. (or {:.3f} m.) is lower '
                    'than disparity maximum estimated in prepare step = {:.3f} pix. (or '
                    '{:.3f} m.) for configuration {}').format(
                        user_disp_max, max_elevation_offset, disp_max,
                        disp_max * disp_to_alt_ratio, config_id))
            disp_max = user_disp_max

        logging.info(
            'Disparity range for config {}: [{:.3f} pix., {:.3f} pix.] (or [{:.3f} m., {:.3f} m.])'
            .format(config_id, disp_min, disp_max,
                    disp_min * disp_to_alt_ratio,
                    disp_max * disp_to_alt_ratio))

        configurations_data[config_id]['disp_min'] = disp_min
        configurations_data[config_id]['disp_max'] = disp_max

        origin = [
            preprocessing_output_config[params.epipolar_origin_x_tag],
            preprocessing_output_config[params.epipolar_origin_y_tag]
        ]
        spacing = [
            preprocessing_output_config[params.epipolar_spacing_x_tag],
            preprocessing_output_config[params.epipolar_spacing_y_tag]
        ]

        configurations_data[config_id]['origin'] = origin
        configurations_data[config_id]['spacing'] = spacing

        logging.info(
            "Size of epipolar image: {}".format(largest_epipolar_region))
        logging.debug("Origin of epipolar grid: {}".format(origin))
        logging.debug("Spacing of epipolar grid: {}".format(spacing))

        # Warning if align is set but correction is missing
        if align and params.lowres_dem_splines_fit_tag not in preprocessing_output_config:
            logging.warning((
                'Align with low resolution DSM option is set but splines correction file '
                'is not available for configuration {}. Correction '
                'will not be applied for this configuration'
            ).format(config_id))

        # Numpy array with corners of largest epipolar region. Order
        # does not matter here, since it will be passed to stereo.compute_epipolar_grid_min_max
        corners = np.array(
            [[[largest_epipolar_region[0], largest_epipolar_region[1]],
              [largest_epipolar_region[0], largest_epipolar_region[3]]],
             [[largest_epipolar_region[2], largest_epipolar_region[3]],
              [largest_epipolar_region[2], largest_epipolar_region[1]]]],
            dtype=np.float64)

        # get utm zone with the middle point of terrain_min if epsg is
        # None
        if epsg is None:
            # Compute terrain position of epipolar image corners for min and max disparity
            terrain_dispmin, terrain_dispmax = stereo.compute_epipolar_grid_min_max(
                corners, 4326, configuration, disp_min, disp_max)
            epsg = rasterization.get_utm_zone_as_epsg_code(
                *np.mean(terrain_dispmin, axis=0))
            logging.info("EPSG code: {}".format(epsg))

        # Compute terrain min and max again, this time using estimated epsg code
        terrain_dispmin, terrain_dispmax = stereo.compute_epipolar_grid_min_max(
            corners, epsg, configuration, disp_min, disp_max)

        if roi_epsg is not None:
            if roi_epsg != epsg:
                roi_poly = projection.polygon_projection(
                    roi_poly, roi_epsg, epsg)

        # Compute bounds from epipolar image corners and dispmin/dispmax
        terrain_bounds = np.stack((terrain_dispmin, terrain_dispmax), axis=0)
        terrain_min = np.amin(terrain_bounds, axis=(0, 1))
        terrain_max = np.amax(terrain_bounds, axis=(0, 1))

        terrain_area = (terrain_max[0] - terrain_min[0]) * (terrain_max[1] -
                                                            terrain_min[1])

        configurations_data[config_id]['terrain_area'] = terrain_area

        logging.info(
            "Terrain area covered: {} square meters (or square degrees)".
            format(terrain_area))

        # Retrieve bounding box of the ground intersection of the envelopes
        inter_poly, inter_epsg = utils.read_vector(
            preprocessing_output_config[params.envelopes_intersection_tag])

        if epsg != inter_epsg:
            inter_poly = projection.polygon_projection(inter_poly, inter_epsg,
                                                       epsg)

        (inter_xmin, inter_ymin, inter_xmax, inter_ymax) = inter_poly.bounds

        # Align bounding box to integer resolution steps
        xmin, ymin, xmax, ymax = tiling.snap_to_grid(inter_xmin, inter_ymin,
                                                     inter_xmax, inter_ymax,
                                                     resolution)

        logging.info("Terrain bounding box : [{}, {}] x [{}, {}]".format(
            xmin, xmax, ymin, ymax))

        configurations_data[config_id]['terrain_bounding_box'] = [
            xmin, ymin, xmax, ymax
        ]

        if roi is not None:
            if not roi_poly.intersects(inter_poly):
                logging.warning(
                    "The pair composed of {} and {} does not intersect the requested ROI"
                    .format(
                        configuration[params.input_section_tag][
                            params.img1_tag], configuration[
                                params.input_section_tag][params.img2_tag]))

        # Get optimal tile size
        if epi_tile_size is not None:
            opt_epipolar_tile_size = epi_tile_size
        else:
            opt_epipolar_tile_size = stereo.optimal_tile_size(
                disp_min, disp_max)
        logging.info(
            "Optimal tile size for epipolar regions: {}x{} pixels".format(
                opt_epipolar_tile_size, opt_epipolar_tile_size))

        configurations_data[config_id][
            'opt_epipolar_tile_size'] = opt_epipolar_tile_size

        # Split epipolar image in pieces
        epipolar_regions = tiling.split(
            0, 0, preprocessing_output_config[params.epipolar_size_x_tag],
            preprocessing_output_config[params.epipolar_size_y_tag],
            opt_epipolar_tile_size, opt_epipolar_tile_size)
        epipolar_regions_grid = tiling.grid(
            0, 0, preprocessing_output_config[params.epipolar_size_x_tag],
            preprocessing_output_config[params.epipolar_size_y_tag],
            opt_epipolar_tile_size, opt_epipolar_tile_size)

        configurations_data[config_id]['epipolar_regions'] = epipolar_regions
        configurations_data[config_id][
            'epipolar_regions_grid'] = epipolar_regions_grid

        logging.info("Epipolar image will be processed in {} splits".format(
            len(epipolar_regions)))

        # Increment config index
        config_idx += 1

    xmin, ymin, xmax, ymax = tiling.union([
        conf['terrain_bounding_box']
        for config_id, conf in configurations_data.items()
    ])

    if roi is not None:
        # terrain bounding box polygon
        terrain_poly = Polygon([(xmin, ymin), (xmax, ymin), (xmax, ymax),
                                (xmin, ymax), (xmin, ymin)])

        if not roi_poly.intersects(terrain_poly):
            raise Exception(
                'None of the input pairs intersect the requested ROI')
        else:
            logging.info('Setting terrain bounding box to the requested ROI')
            xmin, ymin, xmax, ymax = roi_poly.bounds
            xmin, ymin, xmax, ymax = tiling.snap_to_grid(
                xmin, ymin, xmax, ymax, resolution)

    logging.info("Total terrain bounding box : [{}, {}] x [{}, {}]".format(
        xmin, xmax, ymin, ymax))

    # Compute optimal terrain tile size
    optimal_terrain_tile_widths = []

    for config_id, conf in configurations_data.items():
        # Compute terrain area covered by a single epipolar tile
        terrain_area_covered_by_epipolar_tile = conf["terrain_area"] / len(
            epipolar_regions)

        # Compute tile width in pixels
        optimal_terrain_tile_widths.append(
            math.sqrt(terrain_area_covered_by_epipolar_tile))

    # In case of multiple json configuration, take the average optimal size,
    # and align to multiple of resolution
    optimal_terrain_tile_width = int(
        math.ceil(
            np.mean(optimal_terrain_tile_widths) / resolution)) * resolution

    logging.info("Optimal terrain tile size: {}x{} pixels".format(
        int(optimal_terrain_tile_width / resolution),
        int(optimal_terrain_tile_width / resolution)))

    # Split terrain bounding box in pieces
    terrain_grid = tiling.grid(xmin, ymin, xmax, ymax,
                               optimal_terrain_tile_width,
                               optimal_terrain_tile_width)
    number_of_terrain_splits = (terrain_grid.shape[0] -
                                1) * (terrain_grid.shape[1] - 1)

    logging.info("Terrain bounding box will be processed in {} splits".format(
        number_of_terrain_splits))

    # Start dask cluster
    cluster = None
    client = None

    # Use dask
    use_dask = {"local_dask": True, "pbs_dask": True, "mp": False}
    if mode not in use_dask.keys():
        raise NotImplementedError('{} mode is not implemented'.format(mode))

    if use_dask[mode]:
        if mode == "local_dask":
            cluster, client = start_local_cluster(nb_workers)
        else:
            cluster, client = start_cluster(nb_workers, walltime, out_dir)

        # Add plugin to monitor memory of workers
        plugin = ComputeDSMMemoryLogger(out_dir)
        client.register_worker_plugin(plugin)

        geoid_data_futures = None
        if geoid_data is not None:
            # Broadcast geoid data to all dask workers
            geoid_data_futures = client.scatter(geoid_data, broadcast=True)

    # Retrieve the epsg code which will be used for the triangulation's output points clouds
    # (ecef if filters are activated)
    if cloud_small_components_filter or cloud_statistical_outliers_filter:
        stereo_out_epsg = 4978
    else:
        stereo_out_epsg = epsg

    # Submit all epipolar regions to be processed as delayed tasks, and
    # project terrain grid to epipolar
    for config_id, conf in configurations_data.items():
        # This list will hold the different epipolar tiles to be
        # processed as points cloud
        delayed_point_clouds = []

        if use_dask[mode]:
            # Use Dask delayed
            for region in conf['epipolar_regions']:
                delayed_point_clouds.append(
                    dask.delayed(stereo.images_pair_to_3d_points)(
                        conf['configuration'],
                        region,
                        corr_config,
                        disp_min=conf['disp_min'],
                        disp_max=conf['disp_max'],
                        geoid_data=geoid_data_futures,
                        out_epsg=stereo_out_epsg,
                        use_sec_disp=use_sec_disp,
                        snap_to_img1=snap_to_img1,
                        align=align))
            logging.info(
                "Submitted {} epipolar delayed tasks to dask for stereo configuration {}"
                .format(len(delayed_point_clouds), config_id))
        else:
            # Use multiprocessing module

            # create progress bar with an update callback
            pbar = tqdm(total=len(conf['epipolar_regions']))

            def update(args):
                pbar.update()

            # create a thread pool
            pool = multiprocessing.Pool(nb_workers)

            # launch several 'write_3d_points()' to process each epipolar region
            for region in conf['epipolar_regions']:
                delayed_point_clouds.append(
                    pool.apply_async(write_3d_points,
                                     args=(conf['configuration'], region,
                                           corr_config, tmp_dir, config_id),
                                     kwds={
                                         'disp_min': conf['disp_min'],
                                         'disp_max': conf['disp_max'],
                                         'geoid_data': geoid_data,
                                         'out_epsg': stereo_out_epsg,
                                         'use_sec_disp': use_sec_disp
                                     },
                                     callback=update))

            # Wait computation results (timeout in seconds) and replace the
            # async objects by the actual output of write_3d_points(), meaning
            # the paths to cloud files
            delayed_point_clouds = [
                delayed_pc.get(timeout=perJobTimeout)
                for delayed_pc in delayed_point_clouds
            ]

            # closing thread pool when computation is done
            pool.close()
            pool.join()

        configurations_data[config_id][
            'delayed_point_clouds'] = delayed_point_clouds

        # build list of epipolar region hashes
        configurations_data[config_id]['epipolar_regions_hash'] = [
            region_hash_string(k) for k in conf['epipolar_regions']
        ]

        # Compute disp_min and disp_max location for epipolar grid
        epipolar_grid_min, epipolar_grid_max = stereo.compute_epipolar_grid_min_max(
            conf['epipolar_regions_grid'], epsg, conf["configuration"],
            conf['disp_min'], conf['disp_max'])

        epipolar_regions_grid_flat = conf['epipolar_regions_grid'].reshape(
            -1, conf['epipolar_regions_grid'].shape[-1])

        # in the following code a factor is used to increase the precision
        spatial_ref = osr.SpatialReference()
        spatial_ref.ImportFromEPSG(epsg)
        if spatial_ref.IsGeographic():
            precision_factor = 1000.0
        else:
            precision_factor = 1.0

        # Build delaunay_triangulation
        delaunay_min = Delaunay(epipolar_grid_min * precision_factor)
        delaunay_max = Delaunay(epipolar_grid_max * precision_factor)

        # Build kdtrees
        tree_min = cKDTree(epipolar_grid_min * precision_factor)
        tree_max = cKDTree(epipolar_grid_max * precision_factor)

        # Look-up terrain_grid with Delaunay
        s_min = tsearch(delaunay_min, terrain_grid * precision_factor)
        s_max = tsearch(delaunay_max, terrain_grid * precision_factor)

        points_disp_min = epipolar_regions_grid_flat[
            delaunay_min.simplices[s_min]]
        points_disp_max = epipolar_regions_grid_flat[
            delaunay_max.simplices[s_max]]
        nn_disp_min = epipolar_regions_grid_flat[tree_min.query(
            terrain_grid * precision_factor)[1]]
        nn_disp_max = epipolar_regions_grid_flat[tree_max.query(
            terrain_grid * precision_factor)[1]]

        points_disp_min_min = np.min(points_disp_min, axis=2)
        points_disp_min_max = np.max(points_disp_min, axis=2)
        points_disp_max_min = np.min(points_disp_max, axis=2)
        points_disp_max_max = np.max(points_disp_max, axis=2)

        # Use either Delaunay search or NN search if delaunay search fails (point outside triangles)
        points_disp_min_min = np.where(
            np.stack((s_min, s_min), axis=-1) != -1, points_disp_min_min,
            nn_disp_min)
        points_disp_min_max = np.where(
            np.stack((s_min, s_min), axis=-1) != -1, points_disp_min_max,
            nn_disp_min)
        points_disp_max_min = np.where(
            np.stack((s_max, s_max), axis=-1) != -1, points_disp_max_min,
            nn_disp_max)
        points_disp_max_max = np.where(
            np.stack((s_max, s_max), axis=-1) != -1, points_disp_max_max,
            nn_disp_max)

        points = np.stack((points_disp_min_min, points_disp_min_max,
                           points_disp_max_min, points_disp_max_max),
                          axis=0)

        points_min = np.min(points, axis=0)
        points_max = np.max(points, axis=0)

        configurations_data[config_id]['epipolar_points_min'] = points_min
        configurations_data[config_id]['epipolar_points_max'] = points_max

    # Retrieve number of bands
    if params.color1_tag in configuration[params.input_section_tag]:
        nb_bands = utils.rasterio_get_nb_bands(
            configuration[params.input_section_tag][params.color1_tag])
    else:
        logging.info(
            'No color image has been given in input, {} will be used as the color image'
            .format(configuration[params.input_section_tag][params.img1_tag]))
        nb_bands = utils.rasterio_get_nb_bands(
            configuration[params.input_section_tag][params.img1_tag])
    logging.info("Number of bands in color image: {}".format(nb_bands))

    rank = []

    # This list will contained the different raster tiles to be written by cars
    delayed_dsm_tiles = []
    number_of_epipolar_tiles_per_terrain_tiles = []

    if not use_dask[mode]:
        # create progress bar with update callback
        pbar = tqdm(
            total=number_of_terrain_splits,
            desc="Finding correspondences between terrain and epipolar tiles")

        def update(args):
            pbar.update()

        # initialize a thread pool for multiprocessing mode
        pool = multiprocessing.Pool(nb_workers)

    # Loop on terrain regions and derive dependency to epipolar regions
    for terrain_region_dix in tqdm(range(number_of_terrain_splits),
                                   total=number_of_terrain_splits,
                                   desc="Delaunay look-up"):

        j = int(terrain_region_dix / (terrain_grid.shape[1] - 1))
        i = terrain_region_dix % (terrain_grid.shape[1] - 1)

        logging.debug("Processing tile located at {},{} in tile grid".format(
            i, j))

        terrain_region = [
            terrain_grid[j, i, 0], terrain_grid[j, i, 1],
            terrain_grid[j + 1, i + 1, 0], terrain_grid[j + 1, i + 1, 1]
        ]

        logging.debug(
            "Corresponding terrain region: {}".format(terrain_region))

        # This list will hold the required points clouds for this terrain tile
        required_point_clouds = []

        # For each stereo configuration
        for config_id, conf in configurations_data.items():

            epipolar_points_min = conf['epipolar_points_min']
            epipolar_points_max = conf['epipolar_points_max']

            tile_min = np.minimum(
                np.minimum(
                    np.minimum(epipolar_points_min[j, i],
                               epipolar_points_min[j + 1, i]),
                    np.minimum(epipolar_points_min[j + 1, i + 1],
                               epipolar_points_min[j, i + 1])),
                np.minimum(
                    np.minimum(epipolar_points_max[j, i],
                               epipolar_points_max[j + 1, i]),
                    np.minimum(epipolar_points_max[j + 1, i + 1],
                               epipolar_points_max[j, i + 1])))

            tile_max = np.maximum(
                np.maximum(
                    np.maximum(epipolar_points_min[j, i],
                               epipolar_points_min[j + 1, i]),
                    np.maximum(epipolar_points_min[j + 1, i + 1],
                               epipolar_points_min[j, i + 1])),
                np.maximum(
                    np.maximum(epipolar_points_max[j, i],
                               epipolar_points_max[j + 1, i]),
                    np.maximum(epipolar_points_max[j + 1, i + 1],
                               epipolar_points_max[j, i + 1])))

            # Bouding region of corresponding cell
            epipolar_region_minx = tile_min[0]
            epipolar_region_miny = tile_min[1]
            epipolar_region_maxx = tile_max[0]
            epipolar_region_maxy = tile_max[1]

            # This mimics the previous code that was using
            # transform_terrain_region_to_epipolar
            epipolar_region = [
                epipolar_region_minx, epipolar_region_miny,
                epipolar_region_maxx, epipolar_region_maxy
            ]

            # Crop epipolar region to largest region
            epipolar_region = tiling.crop(epipolar_region,
                                          conf['largest_epipolar_region'])

            logging.debug(
                "Corresponding epipolar region: {}".format(epipolar_region))

            # Check if the epipolar region contains any pixels to process
            if tiling.empty(epipolar_region):
                logging.debug(
                    "Skipping terrain region because corresponding epipolar region is empty"
                )
            else:

                # Loop on all epipolar tiles covered by epipolar region
                for epipolar_tile in tiling.list_tiles(
                        epipolar_region, conf['largest_epipolar_region'],
                        conf['opt_epipolar_tile_size']):

                    cur_hash = region_hash_string(epipolar_tile)

                    # Look for corresponding hash in delayed point clouds
                    # dictionnary
                    if cur_hash in conf['epipolar_regions_hash']:

                        # If hash can be found, append it to the required
                        # clouds to compute for this terrain tile
                        pos = conf['epipolar_regions_hash'].index(cur_hash)
                        required_point_clouds.append(
                            conf['delayed_point_clouds'][pos])

        # start and size parameters for the rasterization function
        xstart, ystart, xsize, ysize = tiling.roi_to_start_and_size(
            terrain_region, resolution)

        # cloud filtering params
        if cloud_small_components_filter:
            small_cpn_filter_params = static_cfg.get_small_components_filter_params(
            )
        else:
            small_cpn_filter_params = None

        if cloud_statistical_outliers_filter:
            statistical_filter_params = static_cfg.get_statistical_outliers_filter_params(
            )
        else:
            statistical_filter_params = None

        # rasterization grid division factor
        rasterization_params = static_cfg.get_rasterization_params()
        grid_points_division_factor = getattr(
            rasterization_params, static_cfg.grid_points_division_factor_tag)

        if len(required_point_clouds) > 0:
            logging.debug(
                "Number of clouds to process for this terrain tile: {}".format(
                    len(required_point_clouds)))

            if use_dask[mode]:
                # Delayed call to rasterization operations using all required
                # point clouds
                rasterized = dask.delayed(rasterization_wrapper)(
                    required_point_clouds,
                    resolution,
                    epsg,
                    xstart=xstart,
                    ystart=ystart,
                    xsize=xsize,
                    ysize=ysize,
                    radius=dsm_radius,
                    sigma=sigma,
                    dsm_no_data=dsm_no_data,
                    color_no_data=color_no_data,
                    small_cpn_filter_params=small_cpn_filter_params,
                    statistical_filter_params=statistical_filter_params,
                    grid_points_division_factor=grid_points_division_factor)

                # Keep track of delayed raster tiles
                delayed_dsm_tiles.append(rasterized)
                rank.append(i * i + j * j)

            else:
                # Launch asynchrone job for write_dsm_by_tile()
                delayed_dsm_tiles.append(
                    pool.apply_async(
                        write_dsm_by_tile,
                        args=(required_point_clouds, resolution, epsg, tmp_dir,
                              nb_bands, static_cfg.get_color_image_encoding(),
                              output_stats),
                        kwds={
                            'xstart':
                            xstart,
                            'ystart':
                            ystart,
                            'xsize':
                            xsize,
                            'ysize':
                            ysize,
                            'radius':
                            dsm_radius,
                            'sigma':
                            sigma,
                            'dsm_no_data':
                            dsm_no_data,
                            'color_no_data':
                            color_no_data,
                            'small_cpn_filter_params':
                            small_cpn_filter_params,
                            'statistical_filter_params':
                            statistical_filter_params,
                            'grid_points_division_factor':
                            grid_points_division_factor
                        },
                        callback=update))

            number_of_epipolar_tiles_per_terrain_tiles.append(
                len(required_point_clouds))

    logging.info(
        "Average number of epipolar tiles for each terrain tile: {}".format(
            int(np.round(
                np.mean(number_of_epipolar_tiles_per_terrain_tiles)))))
    logging.info(
        "Max number of epipolar tiles for each terrain tile: {}".format(
            np.max(number_of_epipolar_tiles_per_terrain_tiles)))

    bounds = (xmin, ymin, xmax, ymax)
    # Derive output image files parameters to pass to rasterio
    xsize, ysize = tiling.roi_to_start_and_size([xmin, ymin, xmax, ymax],
                                                resolution)[2:]

    out_dsm = os.path.join(out_dir, "dsm.tif")
    out_clr = os.path.join(out_dir, "clr.tif")
    out_dsm_mean = os.path.join(out_dir, "dsm_mean.tif")
    out_dsm_std = os.path.join(out_dir, "dsm_std.tif")
    out_dsm_n_pts = os.path.join(out_dir, "dsm_n_pts.tif")
    out_dsm_points_in_cell = os.path.join(out_dir, "dsm_pts_in_cell.tif")

    if use_dask[mode]:
        # Sort tiles according to rank
        delayed_dsm_tiles = [
            delayed for _, delayed in sorted(zip(rank, delayed_dsm_tiles),
                                             key=lambda pair: pair[0])
        ]

        logging.info("Submitting {} tasks to dask".format(
            len(delayed_dsm_tiles)))
        # Transform all delayed raster tiles to futures (computation starts
        # immediatly on workers, assynchronously)
        future_dsm_tiles = client.compute(delayed_dsm_tiles)

        logging.info("DSM output image size: {}x{} pixels".format(
            xsize, ysize))

        readwrite.write_geotiff_dsm(
            future_dsm_tiles,
            out_dir,
            xsize,
            ysize,
            bounds,
            resolution,
            epsg,
            nb_bands,
            dsm_no_data,
            color_no_data,
            color_dtype=static_cfg.get_color_image_encoding(),
            write_color=True,
            write_stats=output_stats)

        # stop cluster
        stop_cluster(cluster, client)

    else:
        logging.info("Computing DSM tiles ...")
        # Wait for asynchrone jobs (timeout in seconds) and replace them by
        # write_dsm_by_tile() output
        delayed_dsm_tiles = [
            delayed_tile.get(timeout=perJobTimeout)
            for delayed_tile in delayed_dsm_tiles
        ]

        # closing the tread pool after computation
        pool.close()
        pool.join()

        # vrt to tif
        logging.info("Building VRT")
        vrt_options = gdal.BuildVRTOptions(resampleAlg='nearest')

        def vrt_mosaic(tiles_glob, vrt_name, vrt_options, output):
            vrt_file = os.path.join(out_dir, vrt_name)
            tiles_list = glob(os.path.join(out_dir, 'tmp', tiles_glob))
            vrt = gdal.BuildVRT(vrt_file, tiles_list, options=vrt_options)
            vrt = None
            ds = gdal.Open(vrt_file)
            ds = gdal.Translate(output, ds)
            ds = None

        vrt_mosaic('*_dsm.tif', 'dsm.vrt', vrt_options, out_dsm)
        vrt_mosaic('*_clr.tif', 'clr.vrt', vrt_options, out_clr)

        if output_stats:
            vrt_mosaic('*_dsm_mean.tif', 'dsm_mean.vrt', vrt_options,
                       out_dsm_mean)
            vrt_mosaic('*_dsm_std.tif', 'dsm_std.vrt', vrt_options,
                       out_dsm_std)
            vrt_mosaic('*_dsm_n_pts.tif', 'dsm_n_pts.vrt', vrt_options,
                       out_dsm_n_pts)
            vrt_mosaic('*_pts_in_cell.tif', 'dsm_pts_in_cell.vrt', vrt_options,
                       out_dsm_points_in_cell)

    # Fill output json file
    out_json[params.stereo_section_tag][params.stereo_output_section_tag][
        params.epsg_tag] = epsg
    out_json[params.stereo_section_tag][params.stereo_output_section_tag][
        params.dsm_tag] = out_dsm
    out_json[params.stereo_section_tag][params.stereo_output_section_tag][
        params.dsm_no_data_tag] = float(dsm_no_data)
    out_json[params.stereo_section_tag][params.stereo_output_section_tag][
        params.color_no_data_tag] = float(color_no_data)
    out_json[params.stereo_section_tag][params.stereo_output_section_tag][
        params.color_tag] = out_clr

    if output_stats:
        out_json[params.stereo_section_tag][params.stereo_output_section_tag][
            params.dsm_mean_tag] = out_dsm_mean
        out_json[params.stereo_section_tag][params.stereo_output_section_tag][
            params.dsm_std_tag] = out_dsm_std
        out_json[params.stereo_section_tag][params.stereo_output_section_tag][
            params.dsm_n_pts_tag] = out_dsm_n_pts
        out_json[params.stereo_section_tag][params.stereo_output_section_tag][
            params.dsm_points_in_cell_tag] = out_dsm_points_in_cell

    # Write the output json
    out_json_path = os.path.join(out_dir, "content.json")

    try:
        utils.check_json(out_json, params.stereo_content_schema)
    except CheckerError as e:
        logging.warning(
            "content.json does not comply with schema: {}".format(e))

    params.write_stereo_content_file(out_json, out_json_path)
Ejemplo n.º 7
0
 vor = Voronoi(cent)
 voronoi_plot_2d(vor)
 plt.xlim(0, 94)
 plt.ylim(0, 50)
 colors = cm.rainbow(np.linspace(0, 1, nclusters))
 for i, c in zip(range(nclusters), colors):
     plt.scatter(coord[code == i][['x']], coord[code == i][['y']], color=c)
     plt.scatter(cent[1], cent[0], c='r')
     #print(coord[code == i][['x','y']].shape)
 #for simplex in vor.ridge_vertices:
 #simplex = np.asarray(simplex)
 #plt.plot(vor.vertices[simplex,0], vor.vertices[simplex,1], 'k-')
 plt.show()
 ### recover region given point
 from scipy.spatial import tsearch
 tsearch(vor, [0, 0])
 tsearch(vor, [40, 90])
 tsearch(vor, [20, 42])
 from scipy.spatial import cKDTree
 test_points = [[0, 0], [40, 90], [20, 42]]
 voronoi_kdtree = cKDTree(cent)
 test_point_dist, test_point_regions = voronoi_kdtree.query(test_points,
                                                            k=1)
 ### function for finding centroids from ball positions
 ###     draw ball points from db
 ###     draw all ball points from db
 ###     draw all ball points from db below 7 feet
 ###     build clusters
 ### plot points (and voronoi ridges)
 ### save points
 ### function for loading centroids