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
|