def test_get_windows(self):
        extent = Box(0, 0, 100, 100)
        windows = list(extent.get_windows(10, 10))
        self.assertEqual(len(windows), 100)

        extent = Box(0, 0, 100, 100)
        windows = list(extent.get_windows(10, 5))
        self.assertEqual(len(windows), 400)

        extent = Box(0, 0, 20, 20)
        windows = set(
            [window.tuple_format() for window in extent.get_windows(10, 10)])
        expected_windows = [
            Box.make_square(0, 0, 10),
            Box.make_square(10, 0, 10),
            Box.make_square(0, 10, 10),
            Box.make_square(10, 10, 10)
        ]
        expected_windows = set(
            [window.tuple_format() for window in expected_windows])
        self.assertSetEqual(windows, expected_windows)

        extent = Box(10, 10, 20, 20)
        windows = set(
            [window.tuple_format() for window in extent.get_windows(6, 6)])
        expected_windows = [
            Box.make_square(10, 10, 6),
            Box.make_square(10, 16, 6),
            Box.make_square(16, 10, 6),
            Box.make_square(16, 16, 6)
        ]
        expected_windows = set(
            [window.tuple_format() for window in expected_windows])
        self.assertSetEqual(windows, expected_windows)
Example #2
0
    def test_nonidentical_extents_and_resolutions(self):
        img_path_1 = data_file_path(
            'multi_raster_source/const_100_600x600.tiff')
        img_path_2 = data_file_path('multi_raster_source/const_175_60x60.tiff')
        img_path_3 = data_file_path('multi_raster_source/const_250_6x6.tiff')
        source_1 = RasterioSourceConfig(uris=[img_path_1], channel_order=[0])
        source_2 = RasterioSourceConfig(uris=[img_path_2], channel_order=[0])
        source_3 = RasterioSourceConfig(uris=[img_path_3], channel_order=[0])

        cfg = MultiRasterSourceConfig(raster_sources=[
            SubRasterSourceConfig(raster_source=source_1, target_channels=[0]),
            SubRasterSourceConfig(raster_source=source_2, target_channels=[1]),
            SubRasterSourceConfig(raster_source=source_3, target_channels=[2])
        ])
        rs = cfg.build(tmp_dir=self.tmp_dir)
        with rs.activate():
            for get_chip_fn in [rs._get_chip, rs.get_chip]:
                ch_1_only = get_chip_fn(Box(0, 0, 10, 10))
                self.assertEqual(tuple(ch_1_only.reshape(-1, 3).mean(axis=0)),
                                 (100, 0, 0))
                ch_2_only = get_chip_fn(Box(0, 600, 10, 600 + 10))
                self.assertEqual(tuple(ch_2_only.reshape(-1, 3).mean(axis=0)),
                                 (0, 175, 0))
                ch_3_only = get_chip_fn(Box(600, 0, 600 + 10, 10))
                self.assertEqual(tuple(ch_3_only.reshape(-1, 3).mean(axis=0)),
                                 (0, 0, 250))
                full_img = get_chip_fn(Box(0, 0, 600, 600))
                self.assertEqual(set(np.unique(full_img[..., 0])), {100})
                self.assertEqual(set(np.unique(full_img[..., 1])), {0, 175})
                self.assertEqual(set(np.unique(full_img[..., 2])), {0, 250})
Example #3
0
 def get_extent(self):
     h, w = self.height, self.width
     if self.extent_crop is not None:
         skip_top, skip_left, skip_bottom, skip_right = self.extent_crop
         ymin, xmin = int(h * skip_top), int(w * skip_left)
         ymax, xmax = h - int(h * skip_bottom), w - int(w * skip_right)
         return Box(ymin, xmin, ymax, xmax)
     return Box(0, 0, h, w)
Example #4
0
    def test_make_buffer(self):
        buffer_size = 1
        max_extent = Box.make_square(0, 0, 3)
        buffer_box = Box(0, 0, 3, 3)
        output_buffer_box = self.box.make_buffer(buffer_size, max_extent)
        self.assertEqual(output_buffer_box, buffer_box)

        buffer_size = 0.5
        max_extent = Box.make_square(0, 0, 5)
        buffer_box = Box(0, 0, 3, 5)
        output_buffer_box = self.box.make_buffer(buffer_size, max_extent)
        self.assertEqual(output_buffer_box, buffer_box)
Example #5
0
def read_labels(geojson, extent=None) -> ChipClassificationLabels:
    """Convert GeoJSON to ChipClassificationLabels.

    If the GeoJSON already contains a grid of cells, then it can be constructed
    in a straightforward manner without having to infer the class of cells.

    If extent is given, only labels that intersect with the extent are returned.

    Args:
        geojson: dict in normalized GeoJSON format (see VectorSource)
        extent: Box in pixel coords

    Returns:
       ChipClassificationLabels
    """
    labels = ChipClassificationLabels()

    for f in geojson['features']:
        geom = shape(f['geometry'])
        (xmin, ymin, xmax, ymax) = geom.bounds
        cell = Box(ymin, xmin, ymax, xmax)
        if extent is not None and not cell.to_shapely().intersects(
                extent.to_shapely()):
            continue

        props = f['properties']
        class_id = props['class_id']
        scores = props.get('scores')
        labels.set_cell(cell, class_id, scores)

    return labels
 def test_subscripting(self):
     box = Box(1, 2, 3, 4)
     self.assertEqual(box[0], 1)
     self.assertEqual(box[1], 2)
     self.assertEqual(box[2], 3)
     self.assertEqual(box[3], 4)
     self.assertRaises(IndexError, lambda: box[4])
    def setUp(self):
        self.windows = [
            Box.make_square(0, 0, 10),
            Box.make_square(0, 5, 10),
            Box.make_square(0, 10, 10)
        ]
        self.num_classes = 3
        self.scores_left = make_random_scores(self.num_classes, 10, 10)
        self.scores_mid = make_random_scores(self.num_classes, 10, 10)
        self.scores_right = make_random_scores(self.num_classes, 10, 10)

        self.scores_left = self.scores_left.astype(np.float16)
        self.scores_mid = self.scores_mid.astype(np.float16)
        self.scores_right = self.scores_right.astype(np.float16)

        self.extent = Box(0, 0, 10, 20)
        self.labels = SemanticSegmentationSmoothLabels(
            extent=self.extent, num_classes=self.num_classes)
        self.labels[self.windows[0]] = self.scores_left
        self.labels[self.windows[1]] = self.scores_mid
        self.labels[self.windows[2]] = self.scores_right

        arr = np.zeros((self.num_classes, 10, 20), dtype=np.float16)
        arr[..., :10] += self.scores_left
        arr[..., 5:15] += self.scores_mid
        arr[..., 10:] += self.scores_right
        self.expected_scores = arr

        hits = np.zeros((10, 20), dtype=np.uint8)
        hits[..., :10] += 1
        hits[..., 5:15] += 1
        hits[..., 10:] += 1
        self.expected_hits = hits
Example #8
0
 def test_make_eroded(self):
     max_extent = Box.make_square(0, 0, 10)
     box = Box(1, 1, 3, 4)
     buffer_size = erosion_size = 1
     eroded_box = box.make_buffer(buffer_size, max_extent) \
                     .make_eroded(erosion_size)
     self.assertEqual(eroded_box, box)
    def test_normalized_to_local(self):
        norm_npboxes = np.array([[0., 0., 0.2, 0.02], [0.2, 0.02, 0.4, 0.04]])
        window = Box(0, 0, 10, 100)
        local_npboxes = ObjectDetectionLabels.normalized_to_local(
            norm_npboxes, window)

        expected_local_npboxes = np.array([[0., 0., 2., 2.], [2., 2., 4., 4.]])
        np.testing.assert_array_equal(local_npboxes, expected_local_npboxes)
    def test_fill_overflow(self):
        extent = Box(10, 10, 90, 90)
        window = Box(0, 0, 100, 100)
        arr = np.ones((100, 100), dtype=np.uint8)
        out = fill_overflow(extent, window, arr)
        mask = np.zeros_like(arr).astype(bool)
        mask[10:90, 10:90] = 1
        self.assertTrue(np.all(out[mask] == 1))
        self.assertTrue(np.all(out[~mask] == 0))

        window = Box(0, 0, 80, 100)
        arr = np.ones((80, 100), dtype=np.uint8)
        out = fill_overflow(extent, window, arr)
        mask = np.zeros((80, 100), dtype=bool)
        mask[10:90, 10:90] = 1
        self.assertTrue(np.all(out[mask] == 1))
        self.assertTrue(np.all(out[~mask] == 0))
    def test_build(self):
        self.assertIsInstance(SemanticSegmentationLabels.build(smooth=False),
                              SemanticSegmentationDiscreteLabels)

        self.assertRaises(
            ValueError, lambda: SemanticSegmentationLabels.build(smooth=True))

        self.assertRaises(
            ValueError,
            lambda: SemanticSegmentationLabels.build(smooth=True,
                                                     extent=Box(0, 0, 10, 10)))

        self.assertIsInstance(
            SemanticSegmentationLabels.build(smooth=True,
                                             extent=Box(0, 0, 10, 10),
                                             num_classes=2),
            SemanticSegmentationSmoothLabels)
Example #12
0
 def test_make_random_square(self):
     window = Box(5, 5, 15, 15)
     size = 5
     nb_tests = 100
     for _ in range(nb_tests):
         box = window.make_random_square(size)
         self.assertEqual(box.get_width(), box.get_height())
         self.assertEqual(box.get_width(), size)
         self.assertTrue(window.to_shapely().contains(box.to_shapely()))
 def get_extent(self) -> Box:
     rs = self.raster_sources[0]
     extent = rs.get_extent()
     if self.extent_crop is not None:
         h, w = extent.get_height(), extent.get_width()
         skip_top, skip_left, skip_bottom, skip_right = self.extent_crop
         ymin, xmin = int(h * skip_top), int(w * skip_left)
         ymax, xmax = h - int(h * skip_bottom), w - int(w * skip_right)
         return Box(ymin, xmin, ymax, xmax)
     return extent
 def test_enough_target_pixels_true(self):
     data = np.zeros((10, 10, 3), dtype=np.uint8)
     data[4:, 4:, :] = [1, 1, 1]
     raster_source = MockRasterSource([0, 1, 2], 3)
     raster_source.set_raster(data)
     rgb_class_map = ClassMap([ClassItem(id=1, color='#010101')])
     label_source = SemanticSegmentationLabelSource(
         source=raster_source, rgb_class_map=rgb_class_map)
     with label_source.activate():
         extent = Box(0, 0, 10, 10)
         self.assertTrue(label_source.enough_target_pixels(extent, 30, [1]))
Example #15
0
    def test_get_chip(self):
        # create a 3-channel raster from a 1-channel raster
        # using a ReclassTransformer to give each channel a different value
        # (100, 175, and 250 respectively)
        path = data_file_path('multi_raster_source/const_100_600x600.tiff')
        source_1 = RasterioSourceConfig(uris=[path], channel_order=[0])
        source_2 = RasterioSourceConfig(
            uris=[path],
            channel_order=[0],
            transformers=[ReclassTransformerConfig(mapping={100: 175})])
        source_3 = RasterioSourceConfig(
            uris=[path],
            channel_order=[0],
            transformers=[ReclassTransformerConfig(mapping={100: 250})])

        cfg = MultiRasterSourceConfig(raster_sources=[
            SubRasterSourceConfig(raster_source=source_1, target_channels=[0]),
            SubRasterSourceConfig(raster_source=source_2, target_channels=[1]),
            SubRasterSourceConfig(raster_source=source_3, target_channels=[2])
        ],
                                      channel_order=[2, 1, 0],
                                      transformers=[
                                          ReclassTransformerConfig(mapping={
                                              100: 10,
                                              175: 17,
                                              250: 25
                                          })
                                      ])
        rs = cfg.build(tmp_dir=self.tmp_dir)

        with rs.activate():
            window = Box(0, 0, 100, 100)

            # sub transformers and channel_order applied
            sub_chips = rs._get_sub_chips(window, raw=False)
            self.assertEqual(tuple(c.mean() for c in sub_chips),
                             (100, 175, 250))
            # sub transformers, channel_order, and transformer applied
            chip = rs.get_chip(window)
            self.assertEqual(tuple(chip.reshape(-1, 3).mean(axis=0)),
                             (25, 17, 10))

            # none of sub transformers, channel_order, and transformer applied
            sub_chips = rs._get_sub_chips(window, raw=True)
            self.assertEqual(tuple(c.mean() for c in sub_chips),
                             (100, 100, 100))
            chip = rs._get_chip(window)
            self.assertEqual(tuple(chip.reshape(-1, 3).mean(axis=0)),
                             (100, 100, 100))
    def test_get_with_aoi(self):
        aoi_polygons = [Box.make_square(5, 15, 2).to_shapely()]
        exp_label_arr = self.label_arr1.copy()
        mask = np.zeros(exp_label_arr.shape)
        mask[5:7, 5:7] = 1
        exp_label_arr = exp_label_arr * mask

        labels = self.labels.filter_by_aoi(aoi_polygons)
        label_arr = labels.get_label_arr(self.windows[1])
        np.testing.assert_array_equal(label_arr, exp_label_arr)

        # Set clip_extent
        clip_extent = Box(0, 0, 10, 18)
        label_arr = labels.get_label_arr(self.windows[1],
                                         clip_extent=clip_extent)
        np.testing.assert_array_equal(label_arr, exp_label_arr[:, 0:8])
Example #17
0
    def _get_shifted_window(self, window):
        do_shift = self.x_shift != 0.0 or self.y_shift != 0.0
        if do_shift:
            ymin, xmin, ymax, xmax = window.tuple_format()
            width = window.get_width()
            height = window.get_height()

            # Transform image coordinates into world coordinates
            transform = self.image_dataset.transform
            xmin2, ymin2 = transform * (xmin, ymin)

            # Transform from world coordinates to WGS84
            if self.crs != wgs84_proj4 and self.proj:
                lon, lat = pyproj.transform(self.proj, wgs84, xmin2, ymin2)
            else:
                lon, lat = xmin2, ymin2

            # Shift.  This is performed by computing the shifts in
            # meters to shifts in degrees.  Those shifts are then
            # applied to the WGS84 coordinate.
            #
            # Courtesy of https://gis.stackexchange.com/questions/2951/algorithm-for-offsetting-a-latitude-longitude-by-some-amount-of-meters  # noqa
            lat_radians = math.pi * lat / 180.0
            dlon = Decimal(self.x_shift) / Decimal(
                meters_per_degree * math.cos(lat_radians))
            dlat = Decimal(self.y_shift) / Decimal(meters_per_degree)
            lon = float(Decimal(lon) + dlon)
            lat = float(Decimal(lat) + dlat)

            # Transform from WGS84 to world coordinates
            if self.crs != wgs84_proj4 and self.proj:
                xmin3, ymin3 = pyproj.transform(wgs84, self.proj, lon, lat)
                xmin3 = int(round(xmin3))
                ymin3 = int(round(ymin3))
            else:
                xmin3, ymin3 = lon, lat

            # Trasnform from world coordinates back into image coordinates
            xmin4, ymin4 = ~transform * (xmin3, ymin3)

            window = Box(ymin4, xmin4, ymin4 + height, xmin4 + width)
        return window
Example #18
0
    def get_transformed_window(self,
                               window: Box,
                               inverse: bool = False) -> Box:
        """Apply the CRS transform to the window.

        Args:
            window (Box): A window in pixel coordinates.

        Returns:
            Box: A window in world coordinates.
        """
        if inverse:
            tf = self.crs_transformer.map_to_pixel
        else:
            tf = self.crs_transformer.pixel_to_map
        ymin, xmin, ymax, xmax = window
        xmin, ymin = tf((xmin, ymin))
        xmax, ymax = tf((xmax, ymax))
        window = Box(ymin, xmin, ymax, xmax)
        return window
Example #19
0
    def from_geojson(geojson, extent=None):
        """Convert GeoJSON to ObjectDetectionLabels object.

        If extent is provided, filter out the boxes that lie "more than a little
        bit" outside the extent.

        Args:
            geojson: (dict) normalized GeoJSON (see VectorSource)
            extent: (Box) in pixel coords

        Returns:
            ObjectDetectionLabels
        """
        boxes = []
        class_ids = []
        scores = []

        for f in geojson['features']:
            geom = shape(f['geometry'])
            (xmin, ymin, xmax, ymax) = geom.bounds
            boxes.append(Box(ymin, xmin, ymax, xmax))

            props = f['properties']
            class_ids.append(props['class_id'])
            scores.append(props.get('score', 1.0))

        if len(boxes):
            boxes = np.array([box.npbox_format() for box in boxes],
                             dtype=float)
            class_ids = np.array(class_ids)
            scores = np.array(scores)
            labels = ObjectDetectionLabels(boxes, class_ids, scores=scores)
        else:
            labels = ObjectDetectionLabels.make_empty()

        if extent is not None:
            labels = ObjectDetectionLabels.get_overlapping(labels,
                                                           extent,
                                                           ioa_thresh=0.8,
                                                           clip=True)
        return labels
    def test_extent_crop_overflow(self):
        f = 1 / 10
        arr = np.ones((100, 100), dtype=np.uint8)
        mask = np.zeros_like(arr).astype(bool)
        mask[10:90, 10:90] = 1
        with NamedTemporaryFile('wb') as fp:
            uri = fp.name
            with rasterio.open(uri,
                               'w',
                               driver='GTiff',
                               height=100,
                               width=100,
                               count=1,
                               dtype=np.uint8) as ds:
                ds.write_band(1, arr)
            cfg = RasterioSourceConfig(uris=[uri], extent_crop=(f, f, f, f))
            rs = cfg.build(tmp_dir=self.tmp_dir)
            with rs.activate():
                out = rs.get_chip(Box(0, 0, 100, 100))[..., 0]

        self.assertTrue(np.all(out[mask] == 1))
        self.assertTrue(np.all(out[~mask] == 0))
    def make_labels(self, class_ids):
        """Make 2x2 grid label store.

        Args:
            class_ids: 2x2 array of class_ids to use
        """
        cell_size = 200
        y_cells = 2
        x_cells = 2
        labels = ChipClassificationLabels()

        for yind in range(y_cells):
            for xind in range(x_cells):
                ymin = yind * cell_size
                xmin = xind * cell_size
                ymax = ymin + cell_size
                xmax = xmin + cell_size
                window = Box(ymin, xmin, ymax, xmax)
                class_id = class_ids[yind][xind]
                new_labels = ChipClassificationLabels()
                new_labels.set_cell(window, class_id)
                labels.extend(new_labels)

        return labels
Example #22
0
 def test_neq(self):
     other = Box(self.ymin + 1, self.xmin, self.ymax, self.xmax)
     self.assertTrue(self.box != other)
Example #23
0
 def test_int(self):
     other = Box(
         float(self.ymin) + 0.01, float(self.xmin), float(self.ymax),
         float(self.xmax))
     self.assertTrue(other.to_int() == self.box)
 def test_repr(self):
     box = Box(1, 2, 3, 4)
     self.assertEqual(box.__repr__(), 'Box(1, 2, 3, 4)')
 def test_unpacking(self):
     box = Box(1, 2, 3, 4)
     ymin, xmin, ymax, xmax = box
     self.assertEqual((ymin, xmin, ymax, xmax), box.tuple_format())
Example #26
0
 def test_make_square(self):
     square = Box(0, 0, 10, 10)
     output_square = Box.make_square(0, 0, 10)
     self.assertEqual(output_square, square)
     self.assertEqual(output_square.get_width(), output_square.get_height())
Example #27
0
 def setUp(self):
     self.ymin = 0
     self.xmin = 0
     self.ymax = 2
     self.xmax = 3
     self.box = Box(self.ymin, self.xmin, self.ymax, self.xmax)
Example #28
0
 def get_extent(self):
     return Box(0, 0, self.height, self.width)
Example #29
0
def generate_scene(task, tiff_path, labels_path, chip_size,
                   chips_per_dimension):
    """Generate a synthetic object detection scene.

    Randomly generates a GeoTIFF with red and greed boxes denoting two
    classes and a corresponding label file. This is useful for generating
    synthetic scenes for testing purposes.
    """
    class_map = ClassMap([ClassItem(1, 'car'), ClassItem(2, 'building')])

    # make extent that's divisible by chip_size
    chip_size = chip_size
    ymax = chip_size * chips_per_dimension
    xmax = chip_size * chips_per_dimension
    extent = Box(0, 0, ymax, xmax)

    # make windows along grid
    windows = extent.get_windows(chip_size, chip_size)

    # for each window, make some random boxes within it and render to image
    nb_channels = 3
    image = np.zeros((ymax, xmax, nb_channels)).astype(np.uint8)
    boxes = []
    class_ids = []
    for window in windows:
        # leave some windows blank
        if random.uniform(0, 1) > 0.3:
            # pick a random class
            class_id = random.randint(1, 2)
            box = window.make_random_square(50).to_int()

            boxes.append(box)
            class_ids.append(class_id)

            image[box.ymin:box.ymax, box.xmin:box.xmax, class_id - 1] = 255

    # save image as geotiff centered in philly
    transform = from_origin(-75.163506, 39.952536, 0.000001, 0.000001)

    print('Generated {} boxes with {} different classes.'.format(
        len(boxes), len(set(class_ids))))

    with rasterio.open(tiff_path,
                       'w',
                       driver='GTiff',
                       height=ymax,
                       transform=transform,
                       crs='EPSG:4326',
                       compression=rasterio.enums.Compression.none,
                       width=xmax,
                       count=nb_channels,
                       dtype='uint8') as dst:
        for channel_ind in range(0, nb_channels):
            dst.write(image[:, :, channel_ind], channel_ind + 1)

    if task == 'object_detection':
        # make OD labels and make boxes
        npboxes = Box.to_npboxes(boxes)
        class_ids = np.array(class_ids)
        labels = ObjectDetectionLabels(npboxes, class_ids)

        # save labels to geojson
        with rasterio.open(tiff_path) as image_dataset:
            crs_transformer = RasterioCRSTransformer(image_dataset)
            od_file = ObjectDetectionGeoJSONStore(labels_path, crs_transformer,
                                                  class_map)
            od_file.save(labels)
    elif task == 'semantic_segmentation':
        label_image = np.zeros((ymax, xmax, 1)).astype(np.uint8)

        for box, class_id in zip(boxes, class_ids):
            label_image[box.ymin:box.ymax, box.xmin:box.xmax, 0] = class_id

        # save labels to raster
        with rasterio.open(labels_path,
                           'w',
                           driver='GTiff',
                           height=ymax,
                           transform=transform,
                           crs='EPSG:4326',
                           compression=rasterio.enums.Compression.none,
                           width=xmax,
                           count=1,
                           dtype='uint8') as dst:
            dst.write(label_image[:, :, 0], 1)
Example #30
0
 def _sample_window(self) -> Box:
     """Randomly sample a window with random size and location."""
     h, w = self.sample_window_size()
     x, y = self.sample_window_loc(h, w)
     window = Box(y, x, y + h, x + w)
     return window