forked from mapillary/OpenSfM
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmasking.py
126 lines (101 loc) · 3.48 KB
/
masking.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
import logging
from typing import List, Tuple, Optional
import cv2
import numpy as np
from opensfm import upright
from opensfm.dataset_base import DataSetBase
logger: logging.Logger = logging.getLogger(__name__)
def mask_from_segmentation(
segmentation: np.ndarray, ignore_values: List[int]
) -> np.ndarray:
"""Binary mask that is 0 for pixels with segmentation value to ignore."""
mask = np.ones(segmentation.shape, dtype=np.uint8)
for value in ignore_values:
mask &= segmentation != value
return mask
def combine_masks(
mask1: Optional[np.ndarray], mask2: Optional[np.ndarray]
) -> Optional[np.ndarray]:
"""Combine two masks as mask1 AND mask2.
Ignore any missing mask argument.
"""
if mask1 is None:
if mask2 is None:
return None
else:
return mask2
else:
if mask2 is None:
return mask1
else:
mask1, mask2 = _resize_masks_to_match(mask1, mask2)
return mask1 & mask2
def _resize_masks_to_match(
im1: np.ndarray,
im2: np.ndarray,
) -> Tuple[np.ndarray, np.ndarray]:
h, w = max(im1.shape, im2.shape)
if im1.shape != (h, w):
im1 = cv2.resize(im1, (w, h), interpolation=cv2.INTER_NEAREST)
if im2.shape != (h, w):
im2 = cv2.resize(im2, (w, h), interpolation=cv2.INTER_NEAREST)
return im1, im2
def load_features_mask(
data: DataSetBase,
image: str,
points: np.ndarray,
mask_image: Optional[np.ndarray] = None,
) -> np.ndarray:
"""Load a feature-wise mask.
This is a binary array true for features that lie inside the
combined mask.
The array is all true when there's no mask.
"""
if points is None or len(points) == 0:
return np.array([], dtype=bool)
if mask_image is None:
mask_image = _load_combined_mask(data, image)
if mask_image is None:
logger.debug("No segmentation for {}, no features masked.".format(image))
return np.ones((points.shape[0],), dtype=bool)
exif = data.load_exif(image)
width = exif["width"]
height = exif["height"]
orientation = exif["orientation"]
new_height, new_width = mask_image.shape
ps = upright.opensfm_to_upright(
points[:, :2],
width,
height,
orientation,
new_width=new_width,
new_height=new_height,
).astype(int)
mask = mask_image[ps[:, 1], ps[:, 0]]
n_removed = np.sum(mask == 0)
logger.debug(
"Masking {} / {} ({:.2f}) features for {}".format(
n_removed, len(mask), n_removed / len(mask), image
)
)
return np.array(mask, dtype=bool)
def _load_segmentation_mask(data: DataSetBase, image: str) -> Optional[np.ndarray]:
"""Build a mask from segmentation ignore values.
The mask is non-zero only for pixels with segmentation
labels not in segmentation_ignore_values.
"""
ignore_values = data.segmentation_ignore_values(image)
if not ignore_values:
return None
segmentation = data.load_segmentation(image)
if segmentation is None:
return None
return mask_from_segmentation(segmentation, ignore_values)
def _load_combined_mask(data: DataSetBase, image: str) -> Optional[np.ndarray]:
"""Combine binary mask with segmentation mask.
Return a mask that is non-zero only where the binary
mask and the segmentation mask are non-zero.
"""
mask = data.load_mask(image)
smask = _load_segmentation_mask(data, image)
return combine_masks(mask, smask)