def test_read_with_extent(self): # Extent only includes the first box. extent = Box.make_square(0, 0, 3) config = ObjectDetectionLabelSourceConfig( vector_source=GeoJSONVectorSourceConfig( uri=self.file_path, default_class_id=None)) source = config.build( self.class_config, self.crs_transformer, extent, self.tmp_dir.name) labels = source.get_labels() npboxes = np.array([[0., 0., 2., 2.]]) class_ids = np.array([0]) scores = np.array([0.9]) expected_labels = ObjectDetectionLabels( npboxes, class_ids, scores=scores) labels.assert_equal(expected_labels) # Extent includes both boxes, but clips the second. extent = Box.make_square(0, 0, 3.9) config = ObjectDetectionLabelSourceConfig( vector_source=GeoJSONVectorSourceConfig( uri=self.file_path, default_class_id=None)) source = config.build( self.class_config, self.crs_transformer, extent, self.tmp_dir.name) labels = source.get_labels() npboxes = np.array([[0., 0., 2., 2.], [2., 2., 3.9, 3.9]]) class_ids = np.array([0, 1]) scores = np.array([0.9, 0.9]) expected_labels = ObjectDetectionLabels( npboxes, class_ids, scores=scores) labels.assert_equal(expected_labels)
def read_labels(geojson, extent=None): """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 make_ground_truth_labels(self): size = 100 nw = Box.make_square(0, 0, size) ne = Box.make_square(0, 200, size) se = Box.make_square(200, 200, size) sw = Box.make_square(200, 0, size) npboxes = Box.to_npboxes([nw, ne, se, sw]) class_ids = np.array([0, 0, 1, 1]) return ObjectDetectionLabels(npboxes, class_ids)
def make_predicted_labels(self): size = 100 # Predicted labels are only there for three of the ground truth boxes, # and are offset by 10 pixels. nw = Box.make_square(10, 0, size) ne = Box.make_square(10, 200, size) se = Box.make_square(210, 200, size) npboxes = Box.to_npboxes([nw, ne, se]) class_ids = np.array([0, 0, 1]) scores = np.ones(class_ids.shape) return ObjectDetectionLabels(npboxes, class_ids, scores=scores)
def set_return_vals(self, raster=None): self.mock.get_extent.return_value = Box.make_square(0, 0, 2) self.mock.get_dtype.return_value = np.uint8 self.mock.get_crs_transformer.return_value = IdentityCRSTransformer() self.mock._get_chip.return_value = np.random.rand(1, 2, 2, 3) if raster is not None: self.mock.get_extent.return_value = Box(0, 0, raster.shape[0], raster.shape[1]) self.mock.get_dtype.return_value = raster.dtype def get_chip(window): return raster[window.ymin:window.ymax, window.xmin:window.xmax, :] self.mock._get_chip.side_effect = get_chip
def test_enough_target_pixels_true(self): data = np.zeros((10, 10, 1), dtype=np.uint8) data[4:, 4:, :] = 1 raster_source = MockRasterSource([0], 1) raster_source.set_raster(data) label_source = SemanticSegmentationLabelSource( raster_source=raster_source) with label_source.activate(): extent = Box(0, 0, 10, 10) self.assertTrue(label_source.enough_target_pixels(extent, 30, [1]))
def setUp(self): self.crs_transformer = IdentityCRSTransformer() self.extent = Box.make_square(0, 0, 10) self.tmp_dir_obj = rv_config.get_tmp_dir() self.tmp_dir = self.tmp_dir_obj.name self.class_id = 0 self.background_class_id = 1 self.line_buffer = 1 self.class_config = ClassConfig(names=['a']) self.uri = join(self.tmp_dir, 'tmp.json')
def get_train_windows(scene, chip_size): train_windows = [] extent = scene.raster_source.get_extent() stride = chip_size windows = extent.get_windows(chip_size, stride) if scene.aoi_polygons: windows = Box.filter_by_aoi(windows, scene.aoi_polygons) for window in windows: chip = scene.raster_source.get_chip(window) if np.sum(chip.ravel()) > 0: train_windows.append(window) return train_windows
def test_get_labels(self): data = np.zeros((10, 10, 1), dtype=np.uint8) data[7:, 7:, 0] = 1 raster_source = MockRasterSource([0], 1) raster_source.set_raster(data) label_source = SemanticSegmentationLabelSource( raster_source=raster_source) with label_source.activate(): window = Box.make_square(7, 7, 3) labels = label_source.get_labels(window=window) label_arr = labels.get_label_arr(window) expected_label_arr = np.ones((3, 3)) np.testing.assert_array_equal(label_arr, expected_label_arr)
def filter_windows(windows): if scene.aoi_polygons: windows = Box.filter_by_aoi(windows, scene.aoi_polygons) filt_windows = [] for w in windows: label_arr = label_source.get_labels(w).get_label_arr(w) null_inds = ( label_arr.ravel() == class_config.get_null_class_id()) if not np.all(null_inds): filt_windows.append(w) windows = filt_windows return windows
def test_get_labels_rgb(self): data = np.zeros((10, 10, 3), dtype=np.uint8) data[7:, 7:, :] = [1, 1, 1] raster_source = MockRasterSource([0, 1, 2], 3) raster_source.set_raster(data) rgb_class_config = ClassConfig(names=['a'], colors=['#010101']) rgb_class_config.ensure_null_class() label_source = SemanticSegmentationLabelSource( raster_source=raster_source, rgb_class_config=rgb_class_config) with label_source.activate(): window = Box.make_square(7, 7, 3) labels = label_source.get_labels(window=window) label_arr = labels.get_label_arr(window) expected_label_arr = np.zeros((3, 3)) np.testing.assert_array_equal(label_arr, expected_label_arr)
def test_get_chip_no_polygons(self): geojson = {'type': 'FeatureCollection', 'features': []} source = self.build_source(geojson) with source.activate(): # Get chip that partially overlaps extent. Expect that chip has zeros # outside of extent, and background_class_id otherwise. self.assertEqual(source.get_extent(), self.extent) chip = source.get_chip(Box.make_square(5, 5, 10)) self.assertEqual(chip.shape, (10, 10, 1)) expected_chip = np.zeros((10, 10, 1)) expected_chip[0:5, 0:5, :] = self.background_class_id np.testing.assert_array_equal(chip, expected_chip)
def geoms_to_raster(str_tree, rasterizer_config, window, extent): background_class_id = rasterizer_config.background_class_id all_touched = rasterizer_config.all_touched log.debug('Cropping shapes to window...') # Crop shapes against window, remove empty shapes, and put in window frame of # reference. window_geom = window.to_shapely() shapes = str_tree.query(window_geom) shapes = [(s, s.class_id) for s in shapes] shapes = [(s.intersection(window_geom), c) for s, c in shapes] shapes = [(s, c) for s, c in shapes if not s.is_empty] def to_window_frame(x, y, z=None): return (x - window.xmin, y - window.ymin) shapes = [(shapely.ops.transform(to_window_frame, s), c) for s, c in shapes] log.debug('# of shapes in window: {}'.format(len(shapes))) out_shape = (window.get_height(), window.get_width()) # rasterize needs to be passed >= 1 shapes. if shapes: log.debug('rasterio.rasterize()...') raster = rasterize( shapes, out_shape=out_shape, fill=background_class_id, dtype=np.uint8, all_touched=all_touched) else: raster = np.full(out_shape, background_class_id, dtype=np.uint8) # Ensure that parts of window outside of extent have zero values which are counted as # the don't-care class for segmentation. valid_window = window_geom.intersection(extent.to_shapely()) if valid_window.is_empty: raster[:, :] = 0 else: vw = shapely.ops.transform(to_window_frame, valid_window) vw = Box.from_shapely(vw).to_int() new_raster = np.zeros(out_shape) new_raster[vw.ymin:vw.ymax, vw.xmin:vw.xmax] = \ raster[vw.ymin:vw.ymax, vw.xmin:vw.xmax] raster = new_raster return raster
def setUp(self): self.file_name = 'labels.json' self.tmp_dir = rv_config.get_tmp_dir() self.file_path = os.path.join(self.tmp_dir.name, self.file_name) self.crs_transformer = DoubleCRSTransformer() self.geojson = { 'type': 'FeatureCollection', 'features': [{ 'type': 'Feature', 'geometry': { 'type': 'Polygon', 'coordinates': [[[0., 0.], [0., 1.], [1., 1.], [1., 0.], [0., 0.]]] }, 'properties': { 'class_id': 0, 'score': 0.9 } }, { 'type': 'Feature', 'geometry': { 'type': 'Polygon', 'coordinates': [[[1., 1.], [1., 2.], [2., 2.], [2., 1.], [1., 1.]]] }, 'properties': { 'score': 0.9, 'class_id': 1 } }] } self.extent = Box.make_square(0, 0, 10) self.class_config = ClassConfig(names=['car', 'house']) json_to_file(self.geojson, self.file_path)
def get_vector_scene(self, class_id, use_aoi=False): gt_uri = data_file_path('{}-gt-polygons.geojson'.format(class_id)) pred_uri = data_file_path('{}-pred-polygons.geojson'.format(class_id)) scene_id = str(class_id) rs = MockRasterSource(channel_order=[0, 1, 3], num_channels=3) rs.set_raster(np.zeros((10, 10, 3))) crs_transformer = IdentityCRSTransformer() extent = Box.make_square(0, 0, 360) config = RasterizedSourceConfig( vector_source=GeoJSONVectorSourceConfig(uri=gt_uri, default_class_id=0), rasterizer_config=RasterizerConfig(background_class_id=1)) gt_rs = config.build(self.class_config, crs_transformer, extent) gt_ls = SemanticSegmentationLabelSource(raster_source=gt_rs) config = RasterizedSourceConfig( vector_source=GeoJSONVectorSourceConfig(uri=pred_uri, default_class_id=0), rasterizer_config=RasterizerConfig(background_class_id=1)) pred_rs = config.build(self.class_config, crs_transformer, extent) pred_ls = SemanticSegmentationLabelSource(raster_source=pred_rs) pred_ls.vector_output = [ PolygonVectorOutputConfig(uri=pred_uri, denoise=0, class_id=class_id) ] if use_aoi: aoi_uri = data_file_path('{}-aoi.geojson'.format(class_id)) aoi_geojson = file_to_json(aoi_uri) aoi_polygons = [shape(aoi_geojson['features'][0]['geometry'])] return Scene(scene_id, rs, gt_ls, pred_ls, aoi_polygons) return Scene(scene_id, rs, gt_ls, pred_ls)
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
def filter_windows(windows): if scene.aoi_polygons: windows = Box.filter_by_aoi(windows, scene.aoi_polygons) return windows
def save_image_crop(image_uri, image_crop_uri, label_uri=None, label_crop_uri=None, size=600, min_features=10, vector_labels=True, class_config=None): """Save a crop of an image to use for testing. If label_uri is set, the crop needs to cover >= min_features. Args: image_uri: URI of original image image_crop_uri: URI of cropped image to save label_uri: optional URI of label file label_crop_uri: optional URI of cropped labels to save size: height and width of crop Raises: ValueError if cannot find a crop satisfying min_features constraint. """ if not file_exists(image_crop_uri): print('Saving test crop to {}...'.format(image_crop_uri)) old_environ = os.environ.copy() try: request_payer = S3FileSystem.get_request_payer() if request_payer == 'requester': os.environ['AWS_REQUEST_PAYER'] = request_payer im_dataset = rasterio.open(image_uri) h, w = im_dataset.height, im_dataset.width extent = Box(0, 0, h, w) windows = extent.get_windows(size, size) if label_uri and vector_labels: crs_transformer = RasterioCRSTransformer.from_dataset( im_dataset) geojson_vs_config = GeoJSONVectorSourceConfig(uri=label_uri) vs = geojson_vs_config.build(class_config, crs_transformer) geojson = vs.get_geojson() geoms = [] for f in geojson['features']: g = shape(f['geometry']) geoms.append(g) tree = STRtree(geoms) def p2m(x, y, z=None): return crs_transformer.pixel_to_map((x, y)) for w in windows: use_window = True if label_uri and vector_labels: w_polys = tree.query(w.to_shapely()) use_window = len(w_polys) >= min_features if use_window and label_crop_uri is not None: print('Saving test crop labels to {}...'.format( label_crop_uri)) label_crop_features = [ mapping(transform(p2m, wp)) for wp in w_polys ] label_crop_json = { 'type': 'FeatureCollection', 'features': [{ 'geometry': f } for f in label_crop_features] } json_to_file(label_crop_json, label_crop_uri) if use_window: crop_image(image_uri, w, image_crop_uri) if not vector_labels and label_uri and label_crop_uri: crop_image(label_uri, w, label_crop_uri) break if not use_window: raise ValueError('Could not find a good crop.') finally: os.environ.clear() os.environ.update(old_environ)