Ejemplo n.º 1
0
def load_masks(images):
    """
    Return a list of boolean land masks.

    Images must all be from the same station.

    Arguments:
        images (iterable): Image objects
    """
    # All images must be from the same station (for now)
    station = parse_image_path(images[0].path)['station']
    pattern = re.compile(station + r'_[0-9]{8}_[0-9]{6}[^\/]*$')
    is_station = [pattern.search(img.path) is not None for img in images[1:]]
    assert all(is_station)
    # Find all station svg with 'land' markup
    imgsz = images[0].cam.imgsz
    svg_paths = glob.glob(os.path.join(CG_PATH, 'svg', station + '_*.svg'))
    markups = [glimpse.svg.parse_svg(path, imgsz=imgsz) for path in svg_paths]
    land_index = np.where(['land' in markup for markup in markups])[0]
    if len(land_index) == 0:
        raise ValueError('No land masks found for station')
    svg_paths = np.array(svg_paths)[land_index]
    land_markups = np.array(markups)[land_index]
    # Select svg files nearest to images, with preference within breaks
    svg_datetimes = paths_to_datetimes(svg_paths)
    svg_break_indices = np.array(
        [_station_break_index(path) for path in svg_paths])
    img_datetimes = [img.datetime for img in images]
    distances = glimpse.helpers.pairwise_distance_datetimes(
        img_datetimes, svg_datetimes)
    nearest_index = []
    for i, img in enumerate(images):
        break_index = _station_break_index(img.path)
        same_break = np.where(break_index == svg_break_indices)[0]
        if same_break.size > 0:
            i = same_break[np.argmin(distances[i][same_break])]
        else:
            raise ValueError('No mask found within motion breaks for image', i)
            i = np.argmin(distances[i])
        nearest_index.append(i)
    nearest = np.unique(nearest_index)
    # Make masks and expand per image without copying
    masks = [None] * len(images)
    image_sizes = np.array([img.cam.imgsz for img in images])
    sizes = np.unique(image_sizes, axis=0)
    for i in nearest:
        polygons = land_markups[i]['land'].values()
        is_nearest = nearest_index == i
        for size in sizes:
            scale = size / imgsz
            rpolygons = [polygon * scale for polygon in polygons]
            mask = glimpse.helpers.polygons_to_mask(rpolygons,
                                                    size=size).astype(np.uint8)
            mask = sharedmem.copy(mask)
            for j in np.where(is_nearest
                              & np.all(image_sizes == size, axis=1))[0]:
                masks[j] = mask
    return masks
Ejemplo n.º 2
0
def load_model(camera,
               svgs=None,
               keys=None,
               step=None,
               group_params=dict(),
               station_calib=False,
               camera_calib=False,
               fixed=None):
    # Gather motion control
    motion_images, motion_controls, motion_cam_params = cg.camera_motion_matches(
        camera, station_calib=station_calib, camera_calib=camera_calib)
    # Gather svg control
    svg_images, svg_controls, svg_cam_params = cg.camera_svg_controls(
        camera,
        keys=keys,
        svgs=svgs,
        correction=True,
        station_calib=station_calib,
        camera_calib=camera_calib,
        step=step)
    # Standardize image sizes
    imgszs = np.unique([img.cam.imgsz for img in (motion_images + svg_images)],
                       axis=0)
    if len(imgszs) > 1:
        i_max = np.argmax(imgszs[:, 0])
        print('Resizing images and controls to', imgszs[i_max])
        for control in motion_controls + svg_controls:
            control.resize(size=imgszs[i_max], force=True)
        # Set new imgsz as original camera imgsz
        for img in motion_images + svg_images:
            img.cam.original_vector[6:8] = imgszs[i_max]
    # Determine whether xyz can be optimized
    stations = [cg.parse_image_path(img.path)['station'] for img in svg_images]
    if fixed is None:
        if len(stations) > 0 and (np.array(stations) == stations[0]).all():
            fixed = cg.Stations()[stations[0]]['properties']['fixed']
        else:
            fixed = True
    station = None if fixed else stations[0]
    if station:
        group_params = glimpse.helpers.merge_dicts(group_params,
                                                   dict(xyz=True))
    model = glimpse.optimize.Cameras(
        cams=[img.cam for img in motion_images + svg_images],
        controls=motion_controls + svg_controls,
        cam_params=motion_cam_params + svg_cam_params,
        group_params=group_params)
    return motion_images, svg_images, model, station
Ejemplo n.º 3
0
    # not_small = np.diff(tranges, axis=1) >= min_dt
    # tranges = tranges[not_small.ravel()]
    # ranges = ranges[not_small.ravel()]
    # Save results
    station_iranges[station] = ranges
    station_ranges[station] = tranges
    station_images[station] = images
    station_datetimes[station] = datetimes

# ---- Count dropped images ----

print('--- Image loss (after station filtering) ----')
for station in stations:
    n = len(station_images[station])
    x = np.arange(n)
    nf = np.unique(np.concatenate([x[i:(j + 1)]
        for i, j in station_iranges[station]])).size
    dropped = n - nf
    print(station, dropped, '(' + str(round(100 * dropped / n, 1)) + '%)')

# ---- Combine station ranges ----
# coverage, station_coverage

# Cut all ranges at all range endpoints
ranges = np.vstack([ranges for ranges in station_ranges.values()])
cuts = np.unique(ranges.ravel())
cut_ranges = glimpse.helpers.cut_ranges(ranges, cuts)
# Flatten to coverage
unique_cut_ranges = np.array(list({tuple(r) for r in cut_ranges}))
order = np.lexsort((unique_cut_ranges[:, 1], unique_cut_ranges[:, 0]))
coverage = unique_cut_ranges[order]
# Append stubs to coverage
Ejemplo n.º 4
0
 print(station, services)
 images = cg.load_images(station=station,
                         services=services,
                         snap=SNAP,
                         use_exif=False,
                         service_exif=True,
                         anchors=True,
                         viewdir=True,
                         viewdir_as_anchor=True)
 # Keep only oriented images
 images = np.array([img for img in images if img.anchor])
 # Write animation(s)
 # HACK: Split at camera changes to use observer.animate()
 # HACK: Use k1 to quickly differentiate between cameras
 ks = np.array([img.cam.k[0] for img in images])
 for k in np.unique(ks):
     idx = np.where(ks == k)[0]
     sizes = np.row_stack([img.cam.imgsz for img in images[idx]])
     unique_sizes = np.unique(sizes, axis=0)
     if len(unique_sizes) > 1:
         # Standardize image sizes
         size = unique_sizes.min(axis=0)
         not_size = np.any(sizes != size, axis=1)
         f = images[~not_size][0].cam.f
         for img in images[not_size]:
             img.cam.resize(size, force=True)
             # HACK: Fix focal length rounding errors
             if any(img.cam.f - f > 0.1):
                 raise ValueError('Focal lengths cannot be reconciled')
             img.cam.f = f
     observer = glimpse.Observer(images[idx], cache=False).subset(snap=snap)
Ejemplo n.º 5
0
diagonal_neighbors = False  # Whether to include diagonal neighbors
min_observers = 2  # Median ignores neighbors with observers below min if any above min
fill_missing = False  # Whether to fill missing points with neighborhood median
nan_median = True  # Whether to ignore missing values in computing median
exact_median_variance = False  # Whether to use "exact" variance of median calculation (when averaging nearest neighbors)

# ---- Load medatada ----
# basenames: <date>.<interval id>
# template:
# ids: Unique point ids (flat index of points in template raster)

# Load tracks basenames
paths = glob.glob(os.path.join(tracks_path, '*.pkl'))
basepaths = [glimpse.helpers.strip_path(path) for path in paths]
basenames = np.unique([
    re.sub(r"-[^-]+$", '', path) for path in basepaths
    if re.search(r"[0-9]+-.+$", path) is not None
])
track_ids = [int(re.sub(r"^[0-9]+-", '', basename)) for basename in basenames]

# Load template
template = glimpse.Raster.read(os.path.join(points_path, 'template.tif'))

# Load ids of all tracked points
ids = []
for basename in basenames:
    points = glimpse.helpers.read_pickle(
        os.path.join(points_path, basename + '.pkl'))
    ids = np.union1d(ids, points['ids']).astype(int)

# ---- Build arrays ----
# xyi: [ x | y | point_id ]
Ejemplo n.º 6
0
def camera_svg_controls(camera,
                        size=1,
                        force_size=False,
                        keys=None,
                        svgs=None,
                        correction=True,
                        step=None,
                        station_calib=False,
                        camera_calib=False,
                        synth=True):
    """
    Return all SVG control objects available for a camera.

    Arguments:
        camera (str): Camera identifer
        size: Image scale factor (number) or image size in pixels (nx, ny)
        force_size (bool): Whether to force `size` even if different aspect ratio
            than original size.
        keys (iterable): SVG layers to include
        svgs (iterable): SVG basenames to include
        correction: Whether control objects should use elevation correction (bool)
            or arguments to `glimpse.helpers.elevation_corrections()`
        station_calib (bool): Whether to load station calibration. If `False`,
            falls back to the station estimate.
        camera_calib (bool): Whether to load camera calibrations. If `False`,
            falls back to the EXIF estimate.

    Returns:
        list: Image objects
        list: Control objects (Points, Lines)
        list: Per-camera calibration parameters [{'viewdir': True}, ...]
    """
    paths = glob.glob(os.path.join(CG_PATH, 'svg', '*.svg'))
    if synth:
        paths += glob.glob(os.path.join(CG_PATH, 'svg-synth', '*.pkl'))
        paths += glob.glob(os.path.join(CG_PATH, 'svg-synth', '*.svg'))
    basenames = np.unique([glimpse.helpers.strip_path(path) for path in paths])
    images, controls, cam_params = [], [], []
    for basename in basenames:
        ids = parse_image_path(basename, sequence=True)
        if ids['camera'] == camera:
            calibration = load_calibrations(basename,
                                            camera=camera_calib,
                                            station=station_calib,
                                            station_estimate=not station_calib,
                                            merge=True)
            img_path = find_image(basename)
            img = glimpse.Image(img_path, cam=calibration)
            control = []
            if svgs is None or basename in svgs:
                control += svg_controls(img,
                                        keys=keys,
                                        correction=correction,
                                        step=step)
            if synth:
                control += synth_controls(img, step=None, directions=False)
            if control:
                for x in control:
                    x.resize(size, force=force_size)
                images.append(img)
                controls.extend(control)
                cam_params.append(dict(viewdir=True))
    return images, controls, cam_params
Ejemplo n.º 7
0
def load_images(station,
                services,
                use_exif=False,
                service_exif=False,
                anchors=False,
                viewdir=True,
                viewdir_as_anchor=False,
                file_errors=True,
                **kwargs):
    """
    Return list of calibrated Image objects.

    Any available station, camera, image, and viewdir calibrations are loaded
    and images with image calibrations are marked as anchors.

    Arguments:
        station (str): Station identifier
        services (iterable): Service identifiers
        use_exif (bool): Whether to parse image datetimes from EXIF (slower)
            rather than parsed from paths (faster)
        service_exif (bool): Whether to extract EXIF from first image (faster)
            or all images (slower) in service.
            If `True`, `Image.datetime` is parsed from path.
            Always `False` if `use_exif=True`.
        anchors (bool): Whether to include anchor images even if
            filtered out by `kwargs['snap']`
        **kwargs: Arguments to `glimpse.helpers.select_datetimes()`
    """
    if use_exif:
        service_exif = False
    # Sort services in time
    if isinstance(services, str):
        services = services,
    services = np.sort(services)
    # Parse datetimes of all candidate images
    paths_service = [
        glob.glob(
            os.path.join(IMAGE_PATH, station, station + '_' + service,
                         '*.JPG')) for service in services
    ]
    paths = np.hstack(paths_service)
    basenames = [glimpse.helpers.strip_path(path) for path in paths]
    if use_exif:
        exifs = [glimpse.Exif(path) for path in paths]
        datetimes = np.array([exif.datetime for exif in exifs])
    else:
        datetimes = paths_to_datetimes(basenames)
    # Select images based on datetimes
    indices = glimpse.helpers.select_datetimes(datetimes, **kwargs)
    if anchors:
        # Add anchors
        # HACK: Ignore any <image>-<suffix>.json files
        anchor_paths = glob.glob(
            os.path.join(CG_PATH, 'images', station + '_*[0-9].json'))
        anchor_basenames = [
            glimpse.helpers.strip_path(path) for path in anchor_paths
        ]
        if 'start' in kwargs or 'end' in kwargs:
            # Filter by start, end
            anchor_datetimes = np.asarray(paths_to_datetimes(anchor_basenames))
            inrange = glimpse.helpers.select_datetimes(
                anchor_datetimes,
                **glimpse.helpers.merge_dicts(kwargs, dict(snap=None)))
            anchor_basenames = np.asarray(anchor_basenames)[inrange]
        anchor_indices = np.where(np.isin(basenames, anchor_basenames))[0]
        indices = np.unique(np.hstack((indices, anchor_indices)))
    service_breaks = np.hstack((0, np.cumsum([len(x) for x in paths_service])))
    station_calibration = load_calibrations(station_estimate=station,
                                            station=station,
                                            merge=True,
                                            file_errors=False)
    images = []
    for i, service in enumerate(services):
        index = indices[(indices >= service_breaks[i])
                        & (indices < service_breaks[i + 1])]
        if not index.size:
            continue
        service_calibration = glimpse.helpers.merge_dicts(
            station_calibration,
            load_calibrations(path=paths[index[0]],
                              camera=True,
                              merge=True,
                              file_errors=file_errors))
        if service_exif:
            exif = glimpse.Exif(paths[index[0]])
        for j in index:
            basename = basenames[j]
            calibrations = load_calibrations(
                image=basename,
                viewdir=basename if viewdir else False,
                station_estimate=station,
                merge=False,
                file_errors=False)
            if calibrations['image']:
                calibration = glimpse.helpers.merge_dicts(
                    service_calibration, calibrations['image'])
                anchor = True
            else:
                calibration = glimpse.helpers.merge_dicts(
                    service_calibration,
                    dict(viewdir=calibrations['station_estimate']['viewdir']))
                anchor = False
            if viewdir and calibrations['viewdir']:
                calibration = glimpse.helpers.merge_dicts(
                    calibration, calibrations['viewdir'])
                if viewdir_as_anchor:
                    anchor = True
            if use_exif:
                exif = exifs[j]
            elif not service_exif:
                exif = None
            if KEYPOINT_PATH:
                keypoint_path = os.path.join(KEYPOINT_PATH, basename + '.pkl')
            else:
                keypoint_path = None
            image = glimpse.Image(path=paths[j],
                                  cam=calibration,
                                  anchor=anchor,
                                  exif=exif,
                                  datetime=None if use_exif else datetimes[j],
                                  keypoints_path=keypoint_path)
            images.append(image)
    return images