Spaces:
Sleeping
Sleeping
Add dependencies
Browse files- app.py +1 -5
- geo_tools.py +105 -0
- grid_optim.py +113 -0
- requirements.txt +2 -0
app.py
CHANGED
@@ -1,12 +1,8 @@
|
|
1 |
-
import json
|
2 |
import os
|
3 |
import gradio as gr
|
4 |
import plotly.graph_objects as go
|
5 |
|
6 |
-
import
|
7 |
-
|
8 |
-
sys.path.append(os.path.join(os.path.dirname(__file__), "..", "SatSeg"))
|
9 |
-
from satseg.geo_tools import (
|
10 |
shapefile_to_latlong,
|
11 |
shapefile_to_grid_indices,
|
12 |
points_to_shapefile,
|
|
|
|
|
1 |
import os
|
2 |
import gradio as gr
|
3 |
import plotly.graph_objects as go
|
4 |
|
5 |
+
from geo_tools import (
|
|
|
|
|
|
|
6 |
shapefile_to_latlong,
|
7 |
shapefile_to_grid_indices,
|
8 |
points_to_shapefile,
|
geo_tools.py
ADDED
@@ -0,0 +1,105 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import cv2
|
2 |
+
import fiona
|
3 |
+
from pyproj import Transformer
|
4 |
+
|
5 |
+
import numpy as np
|
6 |
+
from tqdm import tqdm
|
7 |
+
|
8 |
+
from grid_optim import get_optimal_grid
|
9 |
+
|
10 |
+
|
11 |
+
def shapefile_to_latlong(file_path: str):
|
12 |
+
c = fiona.open(file_path)
|
13 |
+
contours = []
|
14 |
+
|
15 |
+
transformer = Transformer.from_crs(c.crs, 4326, always_xy=True)
|
16 |
+
|
17 |
+
for poly in c:
|
18 |
+
coords = poly["geometry"].coordinates
|
19 |
+
for coord_set in coords:
|
20 |
+
contours.append(
|
21 |
+
np.array(
|
22 |
+
list(
|
23 |
+
transformer.itransform(
|
24 |
+
coord_set[0] if len(coord_set) == 1 else coord_set
|
25 |
+
)
|
26 |
+
)
|
27 |
+
)
|
28 |
+
)
|
29 |
+
|
30 |
+
return contours
|
31 |
+
|
32 |
+
|
33 |
+
def shapefile_to_grid_indices(
|
34 |
+
file_path: str, side_len_m: float = 100, meters_per_px: float = 10
|
35 |
+
):
|
36 |
+
c = fiona.open(file_path, "r")
|
37 |
+
all_indices = []
|
38 |
+
side_len = side_len_m / meters_per_px
|
39 |
+
|
40 |
+
for poly in c:
|
41 |
+
coords = poly["geometry"].coordinates
|
42 |
+
for coord_set in tqdm(coords):
|
43 |
+
contour = np.array(coord_set[0] if len(coord_set) == 1 else coord_set)
|
44 |
+
|
45 |
+
cmin = contour.min(axis=0)
|
46 |
+
contour -= cmin
|
47 |
+
cmax = int(contour.max() / meters_per_px)
|
48 |
+
contour = contour // meters_per_px
|
49 |
+
|
50 |
+
if cmax < side_len:
|
51 |
+
continue
|
52 |
+
|
53 |
+
mask = np.zeros((cmax, cmax), dtype="uint8")
|
54 |
+
mask = cv2.drawContours(
|
55 |
+
mask,
|
56 |
+
[contour.reshape((-1, 1, 2)).astype(np.int32)],
|
57 |
+
-1,
|
58 |
+
(255),
|
59 |
+
thickness=cv2.FILLED,
|
60 |
+
)
|
61 |
+
|
62 |
+
indices = np.array(get_optimal_grid(mask, side_len=side_len)[0])
|
63 |
+
indices = indices * meters_per_px + cmin
|
64 |
+
|
65 |
+
all_indices += indices.tolist()
|
66 |
+
|
67 |
+
transformer = Transformer.from_crs(c.crs, 4326, always_xy=True)
|
68 |
+
all_indices = list(transformer.itransform(all_indices))
|
69 |
+
|
70 |
+
return np.array(all_indices)
|
71 |
+
|
72 |
+
|
73 |
+
def points_to_shapefile(points: np.ndarray, file_path: str):
|
74 |
+
schema = {"geometry": "Point", "properties": [("ID", "int")]}
|
75 |
+
|
76 |
+
# open a fiona object
|
77 |
+
pointShp = fiona.open(
|
78 |
+
file_path,
|
79 |
+
mode="w",
|
80 |
+
driver="ESRI Shapefile",
|
81 |
+
schema=schema,
|
82 |
+
crs="EPSG:4326",
|
83 |
+
)
|
84 |
+
# iterate over each row in the dataframe and save record
|
85 |
+
for i, (x, y) in enumerate(points):
|
86 |
+
rowDict = {
|
87 |
+
"geometry": {"type": "Point", "coordinates": (x, y)},
|
88 |
+
"properties": {"ID": i},
|
89 |
+
}
|
90 |
+
pointShp.write(rowDict)
|
91 |
+
# close fiona object
|
92 |
+
pointShp.close()
|
93 |
+
|
94 |
+
|
95 |
+
def get_cached_grid_indices(file_path: str):
|
96 |
+
c = fiona.open(file_path, "r")
|
97 |
+
all_indices = []
|
98 |
+
all_labels = []
|
99 |
+
|
100 |
+
for poly in c:
|
101 |
+
all_indices.append(poly["geometry"].coordinates)
|
102 |
+
if "Name" in poly["properties"]:
|
103 |
+
all_labels.append(poly["properties"]["Name"])
|
104 |
+
|
105 |
+
return np.array(all_indices), all_labels
|
grid_optim.py
ADDED
@@ -0,0 +1,113 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import numpy as np
|
2 |
+
import math
|
3 |
+
from tqdm import tqdm
|
4 |
+
|
5 |
+
|
6 |
+
def meters_to_latitude(m_dist: float):
|
7 |
+
return 0.00001 / 1.11 * m_dist
|
8 |
+
|
9 |
+
|
10 |
+
def degrees_to_radians(angle_deg: float):
|
11 |
+
return angle_deg / 180 * math.pi
|
12 |
+
|
13 |
+
|
14 |
+
def radians_to_degrees(angle_deg: float):
|
15 |
+
return angle_deg / math.pi * 180
|
16 |
+
|
17 |
+
|
18 |
+
def plot_line(offset: tuple, start: tuple, indices: list, size: tuple):
|
19 |
+
while (start[0] < 0 or start[1] < 0) and (
|
20 |
+
start[0] < size[0] and start[1] < size[1]
|
21 |
+
):
|
22 |
+
start = (round(start[0] + offset[0]), round(start[1] + offset[1]))
|
23 |
+
cur_h = start
|
24 |
+
|
25 |
+
while cur_h[0] < size[0] and cur_h[1] < size[1]:
|
26 |
+
indices.append(cur_h)
|
27 |
+
cur_h = (round(cur_h[0] + offset[0]), round(cur_h[1] + offset[1]))
|
28 |
+
return start
|
29 |
+
|
30 |
+
|
31 |
+
def get_grid_score(
|
32 |
+
angle: float, side_len: float, seg_result: np.ndarray, threshold: float = 0.5
|
33 |
+
):
|
34 |
+
h, w = seg_result.shape
|
35 |
+
|
36 |
+
offset = (int(side_len * math.sin(angle)), int(side_len * math.cos(angle)))
|
37 |
+
s_offset_1 = (
|
38 |
+
int(side_len * math.sin(math.pi / 3 + angle)),
|
39 |
+
int(side_len * math.cos(math.pi / 3 + angle)),
|
40 |
+
)
|
41 |
+
s_offset_2 = (
|
42 |
+
int(side_len * math.sin(math.pi / 3 - angle)),
|
43 |
+
-int(side_len * math.cos(math.pi / 3 - angle)),
|
44 |
+
)
|
45 |
+
|
46 |
+
cur = (0, 0)
|
47 |
+
i = 0
|
48 |
+
indices = []
|
49 |
+
|
50 |
+
# Lower triangle
|
51 |
+
while cur[0] < h and cur[1] < w:
|
52 |
+
cur = plot_line(offset, cur, indices, (h, w))
|
53 |
+
s_offset = s_offset_1 if i % 2 else s_offset_2
|
54 |
+
cur = (round(cur[0] + s_offset[0]), round(cur[1] + s_offset[1]))
|
55 |
+
i += 1
|
56 |
+
|
57 |
+
# Upper triangle
|
58 |
+
cur = (0, 0)
|
59 |
+
for j in range(i + 3):
|
60 |
+
cur = plot_line(offset, cur, indices, (h, w))
|
61 |
+
s_offset = s_offset_1 if j % 2 else s_offset_2
|
62 |
+
cur = (round(cur[0] - s_offset[0]), round(cur[1] - s_offset[1]))
|
63 |
+
j += 1
|
64 |
+
|
65 |
+
indices = np.array(indices)
|
66 |
+
seg_result = (seg_result > threshold) * seg_result
|
67 |
+
|
68 |
+
max_score = 0
|
69 |
+
best_start = tuple()
|
70 |
+
best_indices = None
|
71 |
+
for s_1 in np.arange(0, side_len, side_len // 5):
|
72 |
+
for s_2 in np.arange(0, side_len, side_len // 5):
|
73 |
+
indices_new = indices + np.array([s_1, s_2])
|
74 |
+
indices_new = indices_new[
|
75 |
+
np.logical_and(indices_new[:, 0] < h, indices_new[:, 1] < w)
|
76 |
+
].astype(int)
|
77 |
+
|
78 |
+
result = np.zeros((h, w))
|
79 |
+
result[indices_new[:, 0], indices_new[:, 1]] = seg_result[
|
80 |
+
indices_new[:, 0], indices_new[:, 1]
|
81 |
+
]
|
82 |
+
|
83 |
+
score = result.sum()
|
84 |
+
if score > max_score:
|
85 |
+
max_score = score
|
86 |
+
best_start = (s_1, s_2)
|
87 |
+
x, y = np.where(result > threshold)
|
88 |
+
best_indices = np.array([x, y]).T
|
89 |
+
|
90 |
+
# plt.imshow(grid)
|
91 |
+
# plt.show()
|
92 |
+
# plt.imshow(result)
|
93 |
+
# plt.show()
|
94 |
+
return max_score, best_start, np.array([best_indices[:, 1], best_indices[:, 0]]).T
|
95 |
+
|
96 |
+
|
97 |
+
def get_optimal_grid(mask: np.ndarray, side_len: float, display_progress: bool = False):
|
98 |
+
angle_range = (0, degrees_to_radians(60))
|
99 |
+
angle_res = degrees_to_radians(10)
|
100 |
+
|
101 |
+
max_score = 0
|
102 |
+
max_config = tuple()
|
103 |
+
best_indices = None
|
104 |
+
for angle in tqdm(
|
105 |
+
np.arange(angle_range[0], angle_range[1], angle_res), disable=True
|
106 |
+
):
|
107 |
+
score, start, indices = get_grid_score(angle, side_len, mask, 0.8)
|
108 |
+
if score > max_score:
|
109 |
+
max_score = score
|
110 |
+
max_config = (angle, side_len, start)
|
111 |
+
best_indices = indices
|
112 |
+
|
113 |
+
return best_indices, max_config
|
requirements.txt
ADDED
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
1 |
+
plotly==5.13.1
|
2 |
+
pyproj==3.5.0
|