-
Notifications
You must be signed in to change notification settings - Fork 378
/
semantic_segmentation_label_store_config.py
181 lines (156 loc) · 6.92 KB
/
semantic_segmentation_label_store_config.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
from typing import TYPE_CHECKING, Iterator, List, Optional
from os.path import join
from rastervision.pipeline.config import register_config, Config, Field
from rastervision.core.data.label_store import (LabelStoreConfig,
SemanticSegmentationLabelStore)
from rastervision.core.data.utils import (denoise, mask_to_building_polygons,
mask_to_polygons)
if TYPE_CHECKING:
import numpy as np
from shapely.geometry.base import BaseGeometry
from rastervision.core.box import Box
from rastervision.core.data import (ClassConfig, CRSTransformer,
SceneConfig)
from rastervision.core.rv_pipeline import RVPipelineConfig
def vo_config_upgrader(cfg_dict: dict, version: int) -> dict:
if version == 8:
try:
# removed in version 9
del cfg_dict['uri']
except KeyError:
pass
return cfg_dict
@register_config('vector_output', upgrader=vo_config_upgrader)
class VectorOutputConfig(Config):
"""Config for vectorized semantic segmentation predictions."""
class_id: int = Field(
...,
description='The prediction class that is to turned into vectors.')
denoise: int = Field(
8,
description='Diameter of the circular structural element used to '
'remove high-frequency signals from the image. Smaller values will '
'reduce less noise and make vectorization slower and more memory '
'intensive (especially for large images). Larger values will remove '
'more noise and make vectorization faster but might also remove '
'legitimate detections.')
threshold: Optional[float] = Field(
None,
description='Probability threshold for creating the binary mask for '
'the pixels of this class. Pixels will be considered to belong to '
'this class if their probability for this class is >= ``threshold``. '
'Note that Raster Vision treats classes as mutually exclusive so the '
'threshold should vary with the number of total classes. '
'``None`` is equivalent to setting this to (1 / num_classes). '
'Defaults to ``None``.')
def vectorize(self, mask: 'np.ndarray') -> Iterator['BaseGeometry']:
"""Vectorize binary mask representing the target class into polygons.
"""
raise NotImplementedError()
def get_uri(self, root: str,
class_config: Optional['ClassConfig'] = None) -> str:
if class_config is not None:
class_name = class_config.get_name(self.class_id)
uri = join(root, f'class-{self.class_id}-{class_name}.json')
else:
uri = join(root, f'class-{self.class_id}.json')
return uri
@register_config('polygon_vector_output')
class PolygonVectorOutputConfig(VectorOutputConfig):
"""Config for vectorized semantic segmentation predictions."""
def vectorize(self, mask: 'np.ndarray') -> Iterator['BaseGeometry']:
if self.denoise > 0:
mask = denoise(mask, self.denoise)
return mask_to_polygons(mask)
def building_vo_config_upgrader(cfg_dict: dict, version: int) -> dict:
if version == 6:
try:
# removed in version 7
del cfg_dict['min_aspect_ratio']
except KeyError:
pass
return cfg_dict
@register_config(
'building_vector_output', upgrader=building_vo_config_upgrader)
class BuildingVectorOutputConfig(VectorOutputConfig):
"""Config for vectorized semantic segmentation predictions.
Intended to break up clusters of buildings.
"""
min_area: float = Field(
0.0,
description='Minimum area (in pixels^2) of anything that can be '
'considered to be a building or a cluster of buildings. The goal is '
'to distinguish between buildings and artifacts.')
element_width_factor: float = Field(
0.5,
description='Width of the structural element used to break building '
'clusters as a fraction of the width of the cluster.')
element_thickness: float = Field(
0.001,
description='Thickness of the structural element that is used to '
'break building clusters.')
def vectorize(self, mask: 'np.ndarray') -> Iterator['BaseGeometry']:
if self.denoise > 0:
mask = denoise(mask, self.denoise)
polygons = mask_to_building_polygons(
mask=mask,
min_area=self.min_area,
width_factor=self.element_width_factor,
thickness=self.element_thickness)
return polygons
@register_config('semantic_segmentation_label_store')
class SemanticSegmentationLabelStoreConfig(LabelStoreConfig):
"""Configure a :class:`.SemanticSegmentationLabelStore`.
Stores class raster as GeoTIFF, and can optionally vectorizes predictions and stores
them in GeoJSON files.
"""
uri: Optional[str] = Field(
None,
description=(
'URI of file with predictions. If None, and this Config is part of '
'a SceneConfig inside an RVPipelineConfig, this fiend will be '
'auto-generated.'))
vector_output: List[VectorOutputConfig] = []
rgb: bool = Field(
False,
description=
('If True, save prediction class_ids in RGB format using the colors in '
'class_config.'))
smooth_output: bool = Field(
False,
description='If True, expects labels to be continuous values '
'representing class scores and stores both scores and discrete '
'labels.')
smooth_as_uint8: bool = Field(
False,
description='If True, stores smooth scores as uint8, resulting in '
'loss of precision, but reduced file size. Only used if '
'smooth_output=True.')
rasterio_block_size: int = Field(
256,
description='blockxsize and blockysize params in rasterio.open() will '
'be set to this.')
def build(self,
class_config: 'ClassConfig',
crs_transformer: 'CRSTransformer',
bbox: 'Box',
tmp_dir: Optional[str] = None) -> SemanticSegmentationLabelStore:
class_config.ensure_null_class()
label_store = SemanticSegmentationLabelStore(
uri=self.uri,
crs_transformer=crs_transformer,
class_config=class_config,
bbox=bbox,
tmp_dir=tmp_dir,
vector_outputs=self.vector_output,
save_as_rgb=self.rgb,
smooth_output=self.smooth_output,
smooth_as_uint8=self.smooth_as_uint8,
rasterio_block_size=self.rasterio_block_size)
return label_store
def update(self,
pipeline: Optional['RVPipelineConfig'] = None,
scene: Optional['SceneConfig'] = None):
if pipeline is not None and scene is not None:
if self.uri is None:
self.uri = join(pipeline.predict_uri, f'{scene.id}')