Skeleton highway

This commit is contained in:
NichiHachi
2024-06-13 00:36:19 +02:00
parent 2b2d5ba869
commit 03e1e441e5
16 changed files with 151 additions and 119 deletions

View File

@@ -1,44 +1,40 @@
import numpy as np
import skan
#import skan
from skimage.morphology import skeletonize
from skan.csr import skeleton_to_csgraph
from collections import Counter
from PIL import Image
import random
from gdpc import Editor
class Skeleton:
def __init__(self):
def __init__(self, data: np.ndarray = None):
self.lines = []
self.intersections = []
self.centers = []
self.graph = []
self.coordinates = []
self.graph = None
if data is not None:
self.set_skeleton(data)
def setSkeleton(self, data):
binary_skeleton = skeletonize(data)
def set_skeleton(self, data: np.ndarray):
binary_skeleton = skeletonize(data, method="lee")
graph, coordinates = skeleton_to_csgraph(binary_skeleton)
self.graph = graph.tocoo()
# List of lists. Inverted coordinates.
coordinates = list(coordinates)
print(coordinates)
# print(coordinates)
for i in range(len(coordinates)):
coordinates[i] = list(coordinates[i])
print(coordinates)
coordinates_final = []
# print(coordinates)
for i in range(len(coordinates[0])):
print((coordinates[0][i], coordinates[1][i], coordinates[2][i]))
coordinates_final.append((coordinates[0][i], coordinates[1][i], coordinates[2][i]))
# print((coordinates[0][i], coordinates[1][i], coordinates[2][i]))
self.coordinates.append((coordinates[0][i], coordinates[1][i], coordinates[2][i]))
self.coordinates = coordinates_final
def findNextElements(self, key):
def find_next_elements(self, key: str) -> list:
"""Find the very nearest elements"""
line = []
@@ -51,51 +47,50 @@ class Skeleton:
line.append(self.graph.col[indices[i]])
return line
def findLine(self, key):
nextKeys = self.findNextElements(key)
def find_line(self, key: str):
next_keys = self.find_next_elements(key)
if len(nextKeys) >= 3: # Intersections.
return nextKeys
if len(next_keys) >= 3: # Intersections.
return next_keys
if len(nextKeys) == 2 or len(nextKeys) == 1: # In line or endpoints.
line = []
line.append(key)
line.insert(0, nextKeys[0])
if len(nextKeys) == 2:
line.insert(len(line), nextKeys[1])
if len(next_keys) == 2 or len(next_keys) == 1: # In line or endpoints.
line = [key]
line.insert(0, next_keys[0])
if len(next_keys) == 2:
line.insert(len(line), next_keys[1])
nextKeys = line[0], line[-1]
next_keys = line[0], line[-1]
while len(nextKeys) == 2 or len(nextKeys) == 1:
while len(next_keys) == 2 or len(next_keys) == 1:
extremity = []
for key in nextKeys:
nextKeys = self.findNextElements(key)
for key in next_keys:
next_keys = self.find_next_elements(key)
if len(nextKeys) <= 2:
if len(next_keys) <= 2:
# Add the neighbors that is not already in the line.
for element in nextKeys:
for element in next_keys:
if element not in line:
extremity.append(element)
line.append(element)
if len(nextKeys) >= 3:
if len(next_keys) >= 3:
# Add the intersection only.
extremity.append(key)
nextKeys = []
next_keys = []
for key in extremity:
ends = self.findNextElements(key)
ends = self.find_next_elements(key)
if len(ends) == 2:
nextKeys.append(key)
next_keys.append(key)
return line
def parseGraph(self):
def parse_graph(self, parse_orphan: bool = False):
for key, value in sorted(
Counter(self.graph.row).items(), key=lambda kv: kv[1], reverse=True
):
# Start from the biggest intersections.
if value != 2: # We don't want to be in the middle of a line.
line = self.findLine(key)
line = self.find_line(key)
# We have now all the connected points if it's an
# intersection. We need to find the line.
@@ -105,34 +100,44 @@ class Skeleton:
self.centers.append(key)
self.intersections.append(line)
for i in line:
line = self.findLine(i)
line = self.find_line(i)
if i in line:
# The key is inside the result : it's a line.
alreadyInside = False
already_inside = False
for l in self.lines:
# Verification if not already inside.
if Counter(l) == Counter(line):
alreadyInside = True
already_inside = True
# print(line, "inside", lines)
if alreadyInside == False:
if not already_inside:
self.lines.append(line)
else:
# The key is not inside the result, it's an
# intersection directly connected to the key.
line = [key, i]
alreadyInside = False
already_inside = False
for l in self.lines:
# Verification if not already inside.
if Counter(l) == Counter(line):
alreadyInside = True
already_inside = True
# print(line, "inside", lines)
if alreadyInside == False:
if not already_inside:
self.lines.append(line)
elif value == 2 and parse_orphan:
line = self.find_line(key)
already_inside = False
for l in self.lines:
# Verification if not already inside.
if Counter(l) == Counter(line):
already_inside = True
def map(self):
if not already_inside:
self.lines.append(line)
def map(self) -> Image:
"""
Generate an image to visualize 2D path of the skeleton.
@@ -140,17 +145,17 @@ class Skeleton:
Returns:
image: 2D path of the skeleton on top of the heightmap.
"""
editor = Editor()
# editor = Editor()
buildArea = editor.getBuildArea()
buildRect = buildArea.toRect()
xzStart = buildRect.begin
xzDistance = (max(buildRect.end[0], buildRect.begin[0]) - min(buildRect.end[0], buildRect.begin[0]),
max(buildRect.end[1], buildRect.begin[1]) - min(buildRect.end[1], buildRect.begin[1]))
# buildArea = editor.getBuildArea()
# buildRect = buildArea.toRect()
# xzStart = buildRect.begin
# xzDistance = (max(buildRect.end[0], buildRect.begin[0]) - min(buildRect.end[0], buildRect.begin[0]),
# max(buildRect.end[1], buildRect.begin[1]) - min(buildRect.end[1], buildRect.begin[1]))
heightmap = Image.open("data/heightmap.png").convert('RGB')
roadsArea = Image.new("L", xzDistance, 0)
width, height = heightmap.size
# roadsArea = Image.new("L", xzDistance, 0)
# width, height = heightmap.size
# Lines
for i in range(len(self.lines)):
@@ -158,7 +163,7 @@ class Skeleton:
for j in range(len(self.lines[i])):
z = self.coordinates[self.lines[i][j]][0]
y = self.coordinates[self.lines[i][j]][1]
# y = self.coordinates[self.lines[i][j]][1]
x = self.coordinates[self.lines[i][j]][2]
heightmap.putpixel(
@@ -169,26 +174,26 @@ class Skeleton:
(r + j, g + j, b + j),
)
roadsArea.putpixel(
(
int(z),
int(x),
),
(255),
)
# roadsArea.putpixel(
# (
# int(z),
# int(x),
# ),
# (255),
# )
# Centers
for i in range(len(self.centers)):
print(self.coordinates[self.centers[i]])
# print(self.coordinates[self.centers[i]])
heightmap.putpixel(
(int(self.coordinates[self.centers[i]][0]), int(self.coordinates[self.centers[i]][2])),
(255, 255, 0),
)
roadsArea.putpixel(
(int(self.coordinates[self.centers[i]][0]), int(self.coordinates[self.centers[i]][2])),
(255),
)
# roadsArea.putpixel(
# (int(self.coordinates[self.centers[i]][0]), int(self.coordinates[self.centers[i]][2])),
# (255),
# )
# # Intersections
# for i in range(len(self.intersections)):
@@ -202,4 +207,4 @@ class Skeleton:
# (255, 0, 255),
# )
return heightmap, roadsArea
return heightmap # , roadsArea