Added data analysis (WORK IN PROGRESS)
@@ -1,4 +1,8 @@
|
|||||||
gdpc==7.1.0
|
gdpc==7.1.0
|
||||||
|
networkx==3.3
|
||||||
numpy==1.26.4
|
numpy==1.26.4
|
||||||
|
Pillow==10.3.0
|
||||||
pygame==2.5.2
|
pygame==2.5.2
|
||||||
scipy==1.13.0
|
scipy==1.13.1
|
||||||
|
skan==0.11.1
|
||||||
|
skimage==0.0
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
from District import District, CustomDistrict, VoronoiDistrict
|
from District import District
|
||||||
from Position import Position
|
from Position import Position
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
import random
|
import random
|
||||||
@@ -33,13 +33,14 @@ class City:
|
|||||||
watermap.close()
|
watermap.close()
|
||||||
heightmap.close()
|
heightmap.close()
|
||||||
|
|
||||||
def add_district(self, center: Position):
|
def add_district(self, center: Position, district_type: str = ""):
|
||||||
"""
|
"""
|
||||||
Add a new district to the city.
|
Add a new district to the city.
|
||||||
|
|
||||||
|
:param district_type:
|
||||||
:param center: The center position of the new district.
|
:param center: The center position of the new district.
|
||||||
"""
|
"""
|
||||||
self.districts.append(CustomDistrict(len(self.districts) + 1, center))
|
self.districts.append(District(len(self.districts) + 1, center, district_type))
|
||||||
self.map_data[center.y][center.x] = len(self.districts)
|
self.map_data[center.y][center.x] = len(self.districts)
|
||||||
|
|
||||||
def is_expend_finished(self):
|
def is_expend_finished(self):
|
||||||
@@ -121,6 +122,6 @@ class City:
|
|||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
city = City()
|
city = City()
|
||||||
for i in range(10):
|
for i in range(10):
|
||||||
city.add_district(Position(random.randint(0, 600), random.randint(0, 600)))
|
city.add_district(Position(random.randint(0, 400), random.randint(0, 400)))
|
||||||
city.loop_expend_district()
|
city.loop_expend_district()
|
||||||
city.custom_district_draw_map()
|
city.custom_district_draw_map()
|
||||||
|
|||||||
@@ -1,31 +1,35 @@
|
|||||||
from Position import Position
|
from Position import Position
|
||||||
|
|
||||||
|
|
||||||
class District:
|
class District:
|
||||||
"""
|
"""
|
||||||
The District class represents a district in the world.
|
The CustomDistrict class represents a district that can be expanded.
|
||||||
A district can be characterized by its type and its unique id.
|
|
||||||
|
|
||||||
Attributes:
|
Attributes:
|
||||||
tile_id (int): The unique id of the district.
|
center_expend (Position): The center position from which the district expands.
|
||||||
type (str): The type of the district. Can be "Forest", "City", "Mountain" or "Villa".
|
area (list): The list of positions that are part of the district.
|
||||||
|
area_expend_from_point (list): The list of positions from which the district can expand.
|
||||||
|
area_expend (list): The list of positions to which the district will maybe expand.
|
||||||
"""
|
"""
|
||||||
|
def __init__(self, tile_id: int, center: Position, type: str = ""):
|
||||||
def __init__(self, tile_id: int):
|
|
||||||
"""
|
"""
|
||||||
The constructor for the District class.
|
The constructor for the District class.
|
||||||
|
|
||||||
:param tile_id: Unique id of the district (Must be greater than 0)
|
:param tile_id: Unique id of the district (Must be greater than 0)
|
||||||
|
:param center: The center position from which the district expands.
|
||||||
|
:param type: The type of the district (Forest, City, Mountain, Villa)
|
||||||
"""
|
"""
|
||||||
if tile_id <= 0:
|
if tile_id <= 0:
|
||||||
raise ValueError("Tile id must be greater than 0")
|
raise ValueError("Tile id must be greater than 0")
|
||||||
self.tile_id = tile_id
|
self.tile_id = tile_id
|
||||||
self.type = "" #Forest, City, Montain, Villa
|
self.type = type
|
||||||
|
self.center_expend = center
|
||||||
|
self.area = [center]
|
||||||
|
self.area_expend_from_point = [center]
|
||||||
|
self.area_expend = []
|
||||||
|
|
||||||
|
def verify_point(self, point: Position, point_new: Position, map_data: list[list[int]], height_map: list[list[int]]):
|
||||||
def verify_point(point: Position, point_new: Position, map_data: list[list[int]], height_map: list[list[int]]):
|
|
||||||
"""
|
"""
|
||||||
Function to verify if a new point can be added to a district extend area list.
|
Verify if a new point can be added to a district extend area list.
|
||||||
|
|
||||||
:param point: The current point.
|
:param point: The current point.
|
||||||
:param point_new: The new point to be verified.
|
:param point_new: The new point to be verified.
|
||||||
@@ -36,31 +40,8 @@ def verify_point(point: Position, point_new: Position, map_data: list[list[int]]
|
|||||||
return (0 <= point_new.x < len(map_data[0]) and
|
return (0 <= point_new.x < len(map_data[0]) and
|
||||||
0 <= point_new.y < len(map_data) and
|
0 <= point_new.y < len(map_data) and
|
||||||
map_data[point_new.y][point_new.x] == 0 and
|
map_data[point_new.y][point_new.x] == 0 and
|
||||||
abs(height_map[point_new.y][point_new.x] - height_map[point.y][point.x]) < 2)
|
(self.type == "Mountain" or
|
||||||
|
abs(height_map[point_new.y][point_new.x] - height_map[point.y][point.x]) < 2))
|
||||||
|
|
||||||
class CustomDistrict(District):
|
|
||||||
"""
|
|
||||||
The CustomDistrict class represents a district that can be expanded.
|
|
||||||
|
|
||||||
Attributes:
|
|
||||||
center_expend (Position): The center position from which the district expands.
|
|
||||||
area (list): The list of positions that are part of the district.
|
|
||||||
area_expend_from_point (list): The list of positions from which the district can expand.
|
|
||||||
area_expend (list): The list of positions to which the district will maybe expand.
|
|
||||||
"""
|
|
||||||
def __init__(self, tile_id: int, center: Position):
|
|
||||||
"""
|
|
||||||
The constructor for the CustomDistrict class.
|
|
||||||
|
|
||||||
:param tile_id: Unique id of the district (Must be greater than 0)
|
|
||||||
:param center: The center position from which the district expands.
|
|
||||||
"""
|
|
||||||
super().__init__(tile_id)
|
|
||||||
self.center_expend = center
|
|
||||||
self.area = [center]
|
|
||||||
self.area_expend_from_point = [center]
|
|
||||||
self.area_expend = []
|
|
||||||
|
|
||||||
def update_expend_points(self, point: Position, map_data: list[list[int]], height_map: list[list[int]]):
|
def update_expend_points(self, point: Position, map_data: list[list[int]], height_map: list[list[int]]):
|
||||||
"""
|
"""
|
||||||
@@ -71,20 +52,7 @@ class CustomDistrict(District):
|
|||||||
:param height_map: The 2D list representing the height map.
|
:param height_map: The 2D list representing the height map.
|
||||||
"""
|
"""
|
||||||
for pos in [Position(1, 0), Position(-1, 0), Position(0, 1), Position(0, -1)]:
|
for pos in [Position(1, 0), Position(-1, 0), Position(0, 1), Position(0, -1)]:
|
||||||
if verify_point(point, point + pos, map_data, height_map):
|
if self.verify_point(point, point + pos, map_data, height_map):
|
||||||
if point + pos not in self.area_expend:
|
if point + pos not in self.area_expend:
|
||||||
self.area_expend.append(point + pos)
|
self.area_expend.append(point + pos)
|
||||||
self.area_expend_from_point.remove(point)
|
self.area_expend_from_point.remove(point)
|
||||||
|
|
||||||
|
|
||||||
class Edge: #I'm Edging rn
|
|
||||||
def __init__(self, point1, point2):
|
|
||||||
self.point1 = point1
|
|
||||||
self.point2 = point2
|
|
||||||
|
|
||||||
|
|
||||||
class VoronoiDistrict(District):
|
|
||||||
def __init__(self, tile_id: int, center: Position):
|
|
||||||
super().__init__(tile_id)
|
|
||||||
self.center = center
|
|
||||||
self.edges = []
|
|
||||||
|
|||||||
205
world_maker/Skeleton.py
Normal file
@@ -0,0 +1,205 @@
|
|||||||
|
import numpy as np
|
||||||
|
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):
|
||||||
|
self.lines = []
|
||||||
|
self.intersections = []
|
||||||
|
self.centers = []
|
||||||
|
self.graph = []
|
||||||
|
self.coordinates = []
|
||||||
|
|
||||||
|
def setSkeleton(self, data):
|
||||||
|
binary_skeleton = skeletonize(data)
|
||||||
|
|
||||||
|
graph, coordinates = skeleton_to_csgraph(binary_skeleton)
|
||||||
|
self.graph = graph.tocoo()
|
||||||
|
|
||||||
|
# List of lists. Inverted coordinates.
|
||||||
|
coordinates = list(coordinates)
|
||||||
|
print(coordinates)
|
||||||
|
for i in range(len(coordinates)):
|
||||||
|
coordinates[i] = list(coordinates[i])
|
||||||
|
|
||||||
|
print(coordinates)
|
||||||
|
coordinates_final = []
|
||||||
|
|
||||||
|
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]))
|
||||||
|
|
||||||
|
self.coordinates = coordinates_final
|
||||||
|
|
||||||
|
def findNextElements(self, key):
|
||||||
|
"""Find the very nearest elements"""
|
||||||
|
|
||||||
|
line = []
|
||||||
|
|
||||||
|
values = np.array(self.graph.row)
|
||||||
|
indices = np.where(values == key)[0]
|
||||||
|
|
||||||
|
for i in range(len(indices)):
|
||||||
|
if self.graph.row[indices[i]] == key:
|
||||||
|
line.append(self.graph.col[indices[i]])
|
||||||
|
return line
|
||||||
|
|
||||||
|
def findLine(self, key):
|
||||||
|
nextKeys = self.findNextElements(key)
|
||||||
|
|
||||||
|
if len(nextKeys) >= 3: # Intersections.
|
||||||
|
return nextKeys
|
||||||
|
|
||||||
|
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])
|
||||||
|
|
||||||
|
nextKeys = line[0], line[-1]
|
||||||
|
|
||||||
|
while len(nextKeys) == 2 or len(nextKeys) == 1:
|
||||||
|
extremity = []
|
||||||
|
for key in nextKeys:
|
||||||
|
nextKeys = self.findNextElements(key)
|
||||||
|
|
||||||
|
if len(nextKeys) <= 2:
|
||||||
|
# Add the neighbors that is not already in the line.
|
||||||
|
for element in nextKeys:
|
||||||
|
if element not in line:
|
||||||
|
extremity.append(element)
|
||||||
|
line.append(element)
|
||||||
|
|
||||||
|
if len(nextKeys) >= 3:
|
||||||
|
# Add the intersection only.
|
||||||
|
extremity.append(key)
|
||||||
|
|
||||||
|
nextKeys = []
|
||||||
|
for key in extremity:
|
||||||
|
ends = self.findNextElements(key)
|
||||||
|
if len(ends) == 2:
|
||||||
|
nextKeys.append(key)
|
||||||
|
return line
|
||||||
|
|
||||||
|
def parseGraph(self):
|
||||||
|
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)
|
||||||
|
|
||||||
|
# We have now all the connected points if it's an
|
||||||
|
# intersection. We need to find the line.
|
||||||
|
|
||||||
|
if value != 1:
|
||||||
|
# It's not an endpoint.
|
||||||
|
self.centers.append(key)
|
||||||
|
self.intersections.append(line)
|
||||||
|
for i in line:
|
||||||
|
line = self.findLine(i)
|
||||||
|
|
||||||
|
if i in line:
|
||||||
|
# The key is inside the result : it's a line.
|
||||||
|
alreadyInside = False
|
||||||
|
for l in self.lines:
|
||||||
|
# Verification if not already inside.
|
||||||
|
if Counter(l) == Counter(line):
|
||||||
|
alreadyInside = True
|
||||||
|
# print(line, "inside", lines)
|
||||||
|
|
||||||
|
if alreadyInside == False:
|
||||||
|
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
|
||||||
|
for l in self.lines:
|
||||||
|
# Verification if not already inside.
|
||||||
|
if Counter(l) == Counter(line):
|
||||||
|
alreadyInside = True
|
||||||
|
# print(line, "inside", lines)
|
||||||
|
|
||||||
|
if alreadyInside == False:
|
||||||
|
self.lines.append(line)
|
||||||
|
|
||||||
|
def map(self):
|
||||||
|
"""
|
||||||
|
|
||||||
|
Generate an image to visualize 2D path of the skeleton.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
image: 2D path of the skeleton on top of the heightmap.
|
||||||
|
"""
|
||||||
|
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]))
|
||||||
|
|
||||||
|
heightmap = Image.open("data/heightmap.png").convert('RGB')
|
||||||
|
roadsArea = Image.new("L", xzDistance, 0)
|
||||||
|
width, height = heightmap.size
|
||||||
|
|
||||||
|
# Lines
|
||||||
|
for i in range(len(self.lines)):
|
||||||
|
r, g, b = (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255))
|
||||||
|
|
||||||
|
for j in range(len(self.lines[i])):
|
||||||
|
z = self.coordinates[self.lines[i][j]][0]
|
||||||
|
y = self.coordinates[self.lines[i][j]][1]
|
||||||
|
x = self.coordinates[self.lines[i][j]][2]
|
||||||
|
|
||||||
|
heightmap.putpixel(
|
||||||
|
(
|
||||||
|
int(z),
|
||||||
|
int(x),
|
||||||
|
),
|
||||||
|
(r + j, g + j, b + j),
|
||||||
|
)
|
||||||
|
|
||||||
|
roadsArea.putpixel(
|
||||||
|
(
|
||||||
|
int(z),
|
||||||
|
int(x),
|
||||||
|
),
|
||||||
|
(255),
|
||||||
|
)
|
||||||
|
|
||||||
|
# Centers
|
||||||
|
for i in range(len(self.centers)):
|
||||||
|
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),
|
||||||
|
)
|
||||||
|
|
||||||
|
# # Intersections
|
||||||
|
# for i in range(len(self.intersections)):
|
||||||
|
# intersection = []
|
||||||
|
# for j in range(len(self.intersections[i])):
|
||||||
|
# intersection.append(self.coordinates[self.intersections[i][j]])
|
||||||
|
|
||||||
|
# for i in range(len(intersection)):
|
||||||
|
# heightmap.putpixel(
|
||||||
|
# (int(self.intersections[i][2]), int(self.intersections[i][0])),
|
||||||
|
# (255, 0, 255),
|
||||||
|
# )
|
||||||
|
|
||||||
|
return heightmap, roadsArea
|
||||||
|
Before Width: | Height: | Size: 9.7 KiB After Width: | Height: | Size: 7.1 KiB |
|
Before Width: | Height: | Size: 74 KiB After Width: | Height: | Size: 24 KiB |
BIN
world_maker/data/highwaymap.png
Normal file
|
After Width: | Height: | Size: 2.7 KiB |
BIN
world_maker/data/sobelmap.png
Normal file
|
After Width: | Height: | Size: 32 KiB |
BIN
world_maker/data/test.png
Normal file
|
After Width: | Height: | Size: 4.4 KiB |
|
Before Width: | Height: | Size: 122 KiB After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 4.9 KiB After Width: | Height: | Size: 3.6 KiB |
182
world_maker/data_analysis.py
Normal file
@@ -0,0 +1,182 @@
|
|||||||
|
import World
|
||||||
|
from PIL import Image
|
||||||
|
from PIL import ImageFilter
|
||||||
|
import numpy as np
|
||||||
|
import networkx as nx
|
||||||
|
from scipy import ndimage
|
||||||
|
from scipy.ndimage import gaussian_gradient_magnitude
|
||||||
|
from scipy.ndimage import label
|
||||||
|
from Skeleton import Skeleton
|
||||||
|
|
||||||
|
def get_data(world: World):
|
||||||
|
heightmap, watermap, treemap = world.getData()
|
||||||
|
heightmap.save('./data/heightmap.png')
|
||||||
|
watermap.save('./data/watermap.png')
|
||||||
|
treemap.save('./data/treemap.png')
|
||||||
|
return heightmap, watermap, treemap
|
||||||
|
|
||||||
|
|
||||||
|
def filter_inverse(image: Image) -> Image:
|
||||||
|
"""
|
||||||
|
Invert the colors of an image.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
image (image): image to filter
|
||||||
|
"""
|
||||||
|
return Image.fromarray(np.invert(np.array(image)))
|
||||||
|
|
||||||
|
|
||||||
|
def filter_sobel(image) -> Image:
|
||||||
|
"""
|
||||||
|
Edge detection algorithms from an image.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
image (image): image to filter
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Open the image
|
||||||
|
if isinstance(image, str):
|
||||||
|
image = Image.open(image).convert('RGB')
|
||||||
|
|
||||||
|
img = np.array(image).astype(np.uint8)
|
||||||
|
|
||||||
|
# Apply gray scale
|
||||||
|
gray_img = np.round(
|
||||||
|
0.299 * img[:, :, 0] + 0.587 * img[:, :, 1] + 0.114 * img[:, :, 2]
|
||||||
|
).astype(np.uint8)
|
||||||
|
|
||||||
|
# Sobel Operator
|
||||||
|
h, w = gray_img.shape
|
||||||
|
# define filters
|
||||||
|
horizontal = np.array([[-1, 0, 1], [-2, 0, 2], [-1, 0, 1]]) # s2
|
||||||
|
vertical = np.array([[-1, -2, -1], [0, 0, 0], [1, 2, 1]]) # s1
|
||||||
|
|
||||||
|
# define images with 0s
|
||||||
|
newhorizontalImage = np.zeros((h, w))
|
||||||
|
newverticalImage = np.zeros((h, w))
|
||||||
|
newgradientImage = np.zeros((h, w))
|
||||||
|
|
||||||
|
# offset by 1
|
||||||
|
for i in range(1, h - 1):
|
||||||
|
for j in range(1, w - 1):
|
||||||
|
horizontalGrad = (
|
||||||
|
(horizontal[0, 0] * gray_img[i - 1, j - 1])
|
||||||
|
+ (horizontal[0, 1] * gray_img[i - 1, j])
|
||||||
|
+ (horizontal[0, 2] * gray_img[i - 1, j + 1])
|
||||||
|
+ (horizontal[1, 0] * gray_img[i, j - 1])
|
||||||
|
+ (horizontal[1, 1] * gray_img[i, j])
|
||||||
|
+ (horizontal[1, 2] * gray_img[i, j + 1])
|
||||||
|
+ (horizontal[2, 0] * gray_img[i + 1, j - 1])
|
||||||
|
+ (horizontal[2, 1] * gray_img[i + 1, j])
|
||||||
|
+ (horizontal[2, 2] * gray_img[i + 1, j + 1])
|
||||||
|
)
|
||||||
|
|
||||||
|
newhorizontalImage[i - 1, j - 1] = abs(horizontalGrad)
|
||||||
|
|
||||||
|
verticalGrad = (
|
||||||
|
(vertical[0, 0] * gray_img[i - 1, j - 1])
|
||||||
|
+ (vertical[0, 1] * gray_img[i - 1, j])
|
||||||
|
+ (vertical[0, 2] * gray_img[i - 1, j + 1])
|
||||||
|
+ (vertical[1, 0] * gray_img[i, j - 1])
|
||||||
|
+ (vertical[1, 1] * gray_img[i, j])
|
||||||
|
+ (vertical[1, 2] * gray_img[i, j + 1])
|
||||||
|
+ (vertical[2, 0] * gray_img[i + 1, j - 1])
|
||||||
|
+ (vertical[2, 1] * gray_img[i + 1, j])
|
||||||
|
+ (vertical[2, 2] * gray_img[i + 1, j + 1])
|
||||||
|
)
|
||||||
|
|
||||||
|
newverticalImage[i - 1, j - 1] = abs(verticalGrad)
|
||||||
|
|
||||||
|
# Edge Magnitude
|
||||||
|
mag = np.sqrt(pow(horizontalGrad, 2.0) + pow(verticalGrad, 2.0))
|
||||||
|
newgradientImage[i - 1, j - 1] = mag
|
||||||
|
|
||||||
|
image = Image.fromarray(newgradientImage)
|
||||||
|
image = image.convert("L")
|
||||||
|
|
||||||
|
return image
|
||||||
|
|
||||||
|
|
||||||
|
def filter_smooth(image, radius: int = 3):
|
||||||
|
"""
|
||||||
|
:param image: white and black image representing the derivative of the terrain (sobel), where black is flat and white is very steep.
|
||||||
|
:param radius: Radius of the Gaussian blur.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
image: black or white image, with black as flat areas to be skeletonized
|
||||||
|
"""
|
||||||
|
|
||||||
|
if isinstance(image, str):
|
||||||
|
image = Image.open(image)
|
||||||
|
|
||||||
|
# image = image.filter(ImageFilter.SMOOTH_MORE)
|
||||||
|
# image = image.filter(ImageFilter.SMOOTH_MORE)
|
||||||
|
# image = image.filter(ImageFilter.SMOOTH_MORE)
|
||||||
|
image = image.convert('L')
|
||||||
|
image = image.filter(ImageFilter.GaussianBlur(radius))
|
||||||
|
array = np.array(image)
|
||||||
|
|
||||||
|
bool_array = array > 7
|
||||||
|
|
||||||
|
# bool_array = ndimage.binary_opening(bool_array, structure=np.ones((3,3)), iterations=1)
|
||||||
|
# bool_array = ndimage.binary_closing(bool_array, structure=np.ones((3,3)), iterations=1)
|
||||||
|
# bool_array = ndimage.binary_opening(bool_array, structure=np.ones((5,5)), iterations=1)
|
||||||
|
# bool_array = ndimage.binary_closing(bool_array, structure=np.ones((5,5)), iterations=1)
|
||||||
|
# bool_array = ndimage.binary_opening(bool_array, structure=np.ones((7,7)), iterations=1)
|
||||||
|
# bool_array = ndimage.binary_closing(bool_array, structure=np.ones((7,7)), iterations=1)
|
||||||
|
|
||||||
|
return Image.fromarray(bool_array)
|
||||||
|
|
||||||
|
|
||||||
|
def remove_water_from_map(image: Image) -> Image:
|
||||||
|
watermap = Image.open('./data/watermap.png').convert('L')
|
||||||
|
|
||||||
|
array_heightmap = np.array(image)
|
||||||
|
array_watermap = np.array(watermap)
|
||||||
|
|
||||||
|
mask = array_watermap == 255
|
||||||
|
array_heightmap[mask] = 0
|
||||||
|
|
||||||
|
result_image = Image.fromarray(array_heightmap)
|
||||||
|
return result_image
|
||||||
|
|
||||||
|
|
||||||
|
def group_map(image1: Image, image2: Image) -> Image:
|
||||||
|
array1 = np.array(image1)
|
||||||
|
array2 = np.array(image2)
|
||||||
|
|
||||||
|
mask = array1 == 255
|
||||||
|
array2[mask] = 255
|
||||||
|
|
||||||
|
result_image = Image.fromarray(array2)
|
||||||
|
return result_image
|
||||||
|
|
||||||
|
|
||||||
|
def highway_map() -> Image:
|
||||||
|
smooth_sobel = filter_smooth("./data/sobelmap.png", 1)
|
||||||
|
inverse_sobel = filter_inverse(smooth_sobel)
|
||||||
|
sobel_no_water = remove_water_from_map(inverse_sobel)
|
||||||
|
sobel_no_water.save("./data/test.png")
|
||||||
|
array = np.array(sobel_no_water)
|
||||||
|
array = ndimage.binary_erosion(array, iterations=10)
|
||||||
|
array = ndimage.binary_dilation(array, iterations=5)
|
||||||
|
image = Image.fromarray(array)
|
||||||
|
smooth_image = filter_smooth(image, 5)
|
||||||
|
array = np.array(smooth_image)
|
||||||
|
array = ndimage.binary_erosion(array, iterations=17)
|
||||||
|
image = Image.fromarray(array)
|
||||||
|
smooth_image = filter_smooth(image, 6)
|
||||||
|
array = np.array(smooth_image)
|
||||||
|
array = ndimage.binary_dilation(array, iterations=3)
|
||||||
|
image = Image.fromarray(array)
|
||||||
|
image.save('./data/highwaymap.png')
|
||||||
|
return image
|
||||||
|
|
||||||
|
def skeletonnize_map(map: Image):
|
||||||
|
skeleton = Skeleton()
|
||||||
|
image_array = np.array(map)
|
||||||
|
skeleton.setSkeleton(image_array)
|
||||||
|
skeleton.parseGraph()
|
||||||
|
heightmap_skeleton, roadsArea = skeleton.map()
|
||||||
|
heightmap_skeleton.save('./data/skeleton.png')
|
||||||
|
roadsArea.save('./data/roads.png')
|
||||||
@@ -1,17 +1,10 @@
|
|||||||
import World
|
import World
|
||||||
|
from PIL import Image
|
||||||
|
from data_analysis import get_data, highway_map, filter_sobel, skeletonnize_map
|
||||||
def get_data(world: World):
|
|
||||||
heightmap, watermap, treemap = world.getData()
|
|
||||||
heightmap.save('./data/heightmap.png')
|
|
||||||
watermap.save('./data/watermap.png')
|
|
||||||
treemap.save('./data/treemap.png')
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
world = World.World()
|
|
||||||
get_data(world)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
main()
|
#world = World.World()
|
||||||
|
#heightmap, watermap, treemap = get_data(world)
|
||||||
|
#filter_sobel("./data/heightmap.png").save('./data/sobelmap.png')
|
||||||
|
highway_map()
|
||||||
|
skeletonnize_map(Image.open('./data/highwaymap.png'))
|
||||||
|
|||||||