Fix : Detect Mountain

This commit is contained in:
NichiHachi
2024-06-22 19:33:12 +02:00
parent 5836902614
commit 5a4ca0a3f8
3 changed files with 128 additions and 63 deletions

View File

@@ -170,14 +170,15 @@ class City:
def generate_district(self): def generate_district(self):
image = handle_import_image('./world_maker/data/smooth_sobel_watermap.png').convert('L') image = handle_import_image('./world_maker/data/smooth_sobel_watermap.png').convert('L')
array = np.array(image) array = np.array(image)
mountain_coo = detect_mountain() mountain = detect_mountain()
self.add_district(Position(mountain_coo[0], mountain_coo[1]), "mountain") for mountain_coo in mountain:
print("[City] District added.") self.add_district(mountain_coo, "mountain")
remove_circle_data(array, mountain_coo) print("[City] Mountain district added.")
remove_circle_data(array, (mountain_coo.x, mountain_coo.y))
area = get_area_array(array) area = get_area_array(array)
sizeX, sizeY = len(array[0]), len(array) size_x, size_y = len(array[0]), len(array)
while area > sizeX * sizeY * 0.1: while area > size_x * size_y * 0.1:
x, y = randint(0, sizeX - 1), randint(0, sizeY - 1) x, y = randint(0, size_x - 1), randint(0, size_y - 1)
if array[y][x]: if array[y][x]:
self.add_district(Position(x, y)) self.add_district(Position(x, y))
remove_circle_data(array, (x, y)) remove_circle_data(array, (x, y))

View File

@@ -3,7 +3,7 @@ from PIL import Image, ImageFilter
import numpy as np import numpy as np
from scipy import ndimage from scipy import ndimage
from world_maker.Skeleton import Skeleton from world_maker.Skeleton import Skeleton
from typing import Union from world_maker.Position import Position
from random import randint from random import randint
import cv2 import cv2
@@ -18,13 +18,13 @@ def get_data(world: World):
return heightmap, watermap, treemap return heightmap, watermap, treemap
def handle_import_image(image: Union[str, Image]) -> Image: def handle_import_image(image: str | Image.Image) -> Image.Image:
if isinstance(image, str): if isinstance(image, str):
return Image.open(image) return Image.open(image)
return image return image
def filter_negative(image: Union[str, Image]) -> Image: def filter_negative(image: str | Image.Image) -> Image.Image:
""" """
Invert the colors of an image. Invert the colors of an image.
@@ -35,7 +35,7 @@ def filter_negative(image: Union[str, Image]) -> Image:
return Image.fromarray(np.invert(np.array(image))) return Image.fromarray(np.invert(np.array(image)))
def filter_sobel(image: Union[str, Image]) -> Image: def filter_sobel(image: str | Image.Image) -> Image.Image:
""" """
Edge detection algorithms from an image. Edge detection algorithms from an image.
@@ -105,7 +105,7 @@ def filter_sobel(image: Union[str, Image]) -> Image:
return image return image
def filter_smooth_theshold(image: Union[str, Image], radius: int = 3): def filter_smooth_theshold(image: str | Image.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 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. :param radius: Radius of the Gaussian blur.
@@ -135,15 +135,14 @@ def filter_smooth_theshold(image: Union[str, Image], radius: int = 3):
return Image.fromarray(bool_array) return Image.fromarray(bool_array)
def filter_smooth(image: Union[str, Image], radius: int = 3): def filter_smooth(image: str | Image.Image, radius: int = 3):
image = handle_import_image(image) image = handle_import_image(image)
image = image.convert('L') image = image.convert('L')
image = image.filter(ImageFilter.GaussianBlur(radius)) image = image.filter(ImageFilter.GaussianBlur(radius))
return image return image
def subtract_map(image: Union[str, Image], substractImage: Union[str, Image]) -> Image: def subtract_map(image: str | Image.Image, substractImage: str | Image.Image) -> Image.Image:
image = handle_import_image(image) image = handle_import_image(image)
substractImage = handle_import_image(substractImage).convert('L') substractImage = handle_import_image(substractImage).convert('L')
@@ -156,7 +155,7 @@ def subtract_map(image: Union[str, Image], substractImage: Union[str, Image]) ->
return Image.fromarray(array_heightmap) return Image.fromarray(array_heightmap)
def overide_map(base: Image, top: Image) -> Image: def overide_map(base: Image, top: Image) -> Image.Image:
base = handle_import_image(base).convert('L') base = handle_import_image(base).convert('L')
top = handle_import_image(top).convert('L') top = handle_import_image(top).convert('L')
@@ -180,7 +179,7 @@ def overide_map(base: Image, top: Image) -> Image:
return result_image return result_image
def group_map(image1: Union[str, Image], image2: Union[str, Image]) -> Image: def group_map(image1: str | Image.Image, image2: str | Image.Image) -> Image.Image:
image1 = handle_import_image(image1).convert('L') image1 = handle_import_image(image1).convert('L')
image2 = handle_import_image(image2).convert('L') image2 = handle_import_image(image2).convert('L')
@@ -200,7 +199,7 @@ def filter_smooth_array(array: np.ndarray, radius: int = 3) -> np.ndarray:
return array return array
def filter_remove_details(image: Union[str, Image], n: int = 20) -> Image: def filter_remove_details(image: str | Image.Image, n: int = 20) -> Image.Image:
image = handle_import_image(image) image = handle_import_image(image)
array = np.array(image) array = np.array(image)
for _ in range(n): for _ in range(n):
@@ -212,7 +211,7 @@ def filter_remove_details(image: Union[str, Image], n: int = 20) -> Image:
return image return image
def highway_map() -> Image: def highway_map() -> Image.Image:
print("[Data Analysis] Generating highway map...") print("[Data Analysis] Generating highway map...")
smooth_sobel = filter_smooth_theshold("./world_maker/data/sobelmap.png", 1) smooth_sobel = filter_smooth_theshold("./world_maker/data/sobelmap.png", 1)
negative_smooth_sobel = filter_negative(smooth_sobel) negative_smooth_sobel = filter_negative(smooth_sobel)
@@ -245,7 +244,7 @@ def create_volume(surface: np.ndarray, heightmap: np.ndarray, make_it_flat: bool
return volume return volume
def convert_2D_to_3D(image: Union[str, Image], make_it_flat: bool = False) -> np.ndarray: def convert_2D_to_3D(image: str | Image.Image, make_it_flat: bool = False) -> np.ndarray:
image = handle_import_image(image) image = handle_import_image(image)
heightmap = Image.open( heightmap = Image.open(
'./world_maker/data/heightmap_smooth.png').convert('L') './world_maker/data/heightmap_smooth.png').convert('L')
@@ -255,7 +254,7 @@ def convert_2D_to_3D(image: Union[str, Image], make_it_flat: bool = False) -> np
return volume return volume
def skeleton_highway_map(image: Union[str, Image] = './world_maker/data/highwaymap.png') -> Skeleton: def skeleton_highway_map(image: str | Image.Image = './world_maker/data/highwaymap.png') -> Skeleton:
image_array = convert_2D_to_3D(image, True) image_array = convert_2D_to_3D(image, True)
skeleton = Skeleton(image_array) skeleton = Skeleton(image_array)
skeleton.parse_graph(True) skeleton.parse_graph(True)
@@ -265,7 +264,7 @@ def skeleton_highway_map(image: Union[str, Image] = './world_maker/data/highwaym
return skeleton return skeleton
def skeleton_mountain_map(image: Union[str, Image] = './world_maker/data/mountain_map.png') -> Skeleton: def skeleton_mountain_map(image: str | Image.Image = './world_maker/data/mountain_map.png') -> Skeleton:
image_array = convert_2D_to_3D(image, True) image_array = convert_2D_to_3D(image, True)
skeleton = Skeleton(image_array) skeleton = Skeleton(image_array)
skeleton.parse_graph() skeleton.parse_graph()
@@ -275,7 +274,7 @@ def skeleton_mountain_map(image: Union[str, Image] = './world_maker/data/mountai
return skeleton return skeleton
def smooth_sobel_water() -> Image: def smooth_sobel_water() -> Image.Image:
watermap = handle_import_image("./world_maker/data/watermap.png") watermap = handle_import_image("./world_maker/data/watermap.png")
watermap = filter_negative( watermap = filter_negative(
filter_remove_details(filter_negative(watermap), 5)) filter_remove_details(filter_negative(watermap), 5))
@@ -287,32 +286,94 @@ def smooth_sobel_water() -> Image:
return group return group
def detect_mountain(image: Union[str, Image] = './world_maker/data/sobelmap.png') -> Image: def mountain_map_expend(mountain_map: list[list[int]], starting_point: tuple[int, int], value: int):
image = handle_import_image(image) explore_points = [starting_point]
sobel = np.array(image) while len(explore_points) > 0:
pixels = sobel.reshape((-1, 1)) x, y = explore_points.pop(0)
pixels = np.float32(pixels) mountain_map[y][x] = value
for i in range(-1, 2):
for j in range(-1, 2):
if (0 <= x + i < len(mountain_map[0]) and 0 <= y + j < len(mountain_map) and
mountain_map[y + j][x + i] == 0):
if (x + i, y + j) not in explore_points:
explore_points.append((x + i, y + j))
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 100, 0.2)
k = 3
_, labels, centers = cv2.kmeans(
pixels, k, None, criteria, 10, cv2.KMEANS_RANDOM_CENTERS)
centers = np.uint8(centers) def set_values_of_building_mountain(mountain_map: list[list[int]], area_mountain: list[int],
segmented_image = centers[labels.flatten()] building_map: str | Image.Image = "./world_maker/data/smooth_sobel_watermap.png"):
segmented_image = segmented_image.reshape(sobel.shape) building_map = handle_import_image(building_map).convert('L')
mountain = segmented_image == segmented_image.max() for y in range(building_map.size[1]):
for x in range(building_map.size[0]):
if building_map.getpixel((x, y)) > 144:
if mountain_map[y][x] != -1:
area_mountain[mountain_map[y][x] - 1] += 1
contours, _ = cv2.findContours(mountain.astype(
np.uint8), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
max_contour = max(contours, key=cv2.contourArea) def get_index_of_biggest_area_mountain(area_mountain: list[int], exception: list[int]) -> int:
M = cv2.moments(max_contour) max_value = -1
cX = int(M["m10"] / M["m00"]) index = -1
cY = int(M["m01"] / M["m00"]) for i in range(len(area_mountain)):
if i not in exception and area_mountain[i] > max_value:
max_value = area_mountain[i]
index = i
return index
print(f"[Data Analysis] The center of the mountain is at ({cX}, {cY})")
return (cX, cY) def get_center_of_area_mountain(mountain_map: list[list[int]], index: int) -> Position:
sum_x = 0
sum_y = 0
count = 0
for y in range(len(mountain_map)):
for x in range(len(mountain_map[0])):
if mountain_map[y][x] == index + 1:
sum_x += x
sum_y += y
count += 1
return Position(sum_x // count, sum_y // count)
def detect_mountain(number_of_mountain: int = 2, height_threshold: int = 10,
image_heightmap: str | Image.Image = './world_maker/data/heightmap.png') -> list[Position]:
print("[Data Analysis] Detecting mountains...")
image_heightmap = handle_import_image(image_heightmap).convert('L')
avg_height = 0
for y in range(image_heightmap.size[1]):
for x in range(image_heightmap.size[0]):
avg_height += image_heightmap.getpixel((x, y))
avg_height = int(avg_height / (image_heightmap.size[0] * image_heightmap.size[1]))
print("[Data Analysis] Average height:", avg_height)
mountain_map = [[-1 if image_heightmap.getpixel((x, y)) < (avg_height + height_threshold) else 0 for x in
range(image_heightmap.size[0])] for y in
range(image_heightmap.size[1])]
area_mountain = []
for y in range(image_heightmap.size[1]):
print(y)
for x in range(image_heightmap.size[0]):
if mountain_map[y][x] == 0:
area_mountain.append(0)
print("Enter the mountain")
mountain_map_expend(mountain_map, (x, y), len(area_mountain))
if not area_mountain:
print("[Data Analysis] No mountain detected.")
return []
set_values_of_building_mountain(mountain_map, area_mountain)
if number_of_mountain < len(area_mountain):
index_mountain = []
for n in range(number_of_mountain):
index_mountain.append(get_index_of_biggest_area_mountain(area_mountain, index_mountain))
else:
index_mountain = [i for i in range(len(area_mountain))]
position_mountain = []
for i in range(len(index_mountain)):
position_mountain.append(get_center_of_area_mountain(mountain_map, index_mountain[i]))
return position_mountain
def rectangle_2D_to_3D(rectangle: list[tuple[tuple[int, int], tuple[int, int]]], def rectangle_2D_to_3D(rectangle: list[tuple[tuple[int, int], tuple[int, int]]],
@@ -334,7 +395,8 @@ def rectangle_2D_to_3D(rectangle: list[tuple[tuple[int, int], tuple[int, int]]],
return new_rectangle return new_rectangle
def transpose_form_heightmap(heightmap: Union[str, Image], coordinates, origin: tuple[int, int]) -> tuple[int, int, int]: def transpose_form_heightmap(heightmap: str | Image.Image, coordinates, origin: tuple[int, int]) -> tuple[
int, int, int]:
heightmap = handle_import_image(heightmap).convert('L') heightmap = handle_import_image(heightmap).convert('L')
xMin, zMin = origin xMin, zMin = origin

View File

@@ -17,10 +17,12 @@ def world_maker():
smooth_sobel_water_map = smooth_sobel_water() smooth_sobel_water_map = smooth_sobel_water()
skeleton_highway = skeleton_highway_map(highway_map()) skeleton_highway = skeleton_highway_map(highway_map())
city = City() city = City()
city.generate_district() city.generate_district()
city.loop_expend_district() city.loop_expend_district()
city.district_draw_map() city.district_draw_map()
road_grid = city.district_generate_road() road_grid = city.district_generate_road()
image_mountain_map = city.get_district_mountain_map() image_mountain_map = city.get_district_mountain_map()
road = city.draw_roads(4) road = city.draw_roads(4)