Skip to content

Commit b01ec9a

Browse files
authored
Merge pull request #72 from superstes/feat-example-image-weighted
Example for weighted image-map
2 parents 24f59a2 + fb6dd7d commit b01ec9a

File tree

6 files changed

+147
-10
lines changed

6 files changed

+147
-10
lines changed

.gitignore

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,5 +65,5 @@ target/
6565
# python virtual env
6666
venv/
6767

68-
examples/out.png
69-
examples/*.perf
68+
examples/*/out.png
69+
examples/*/*.perf

examples/README.md

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,30 @@
11
# Example implementations using python-pathfinding
22

3-
## *image_pathfinding.py*
4-
Create a map from and image and run a path finding algorithm on it (requires PIL/Pillow).
3+
## Image Simple
54

6-
You can all it with an input and output file like this:
5+
Create a map from an image and run a path finding algorithm on it. Requires: `pip install pillow`.
6+
7+
You can run it with an input and output file like this:
78
```
8-
cd examples/
9+
cd examples/image-simple/
910
python3 image_pathfinding.py -i map.png -o foo.png
10-
```
11+
```
12+
13+
----
14+
15+
## Image Weighted
16+
17+
Create a map from and image and run a path finding algorithm on it. Requires: `pip install pillow`.
18+
19+
It maps specific colors to their weights. Make sure to update the value-mapping for your custom input-maps!
20+
21+
You can run it with an input and output file like this:
22+
23+
```
24+
cd examples/image-weighted/
25+
python3 image_pathfinding.py -i map.png -o foo.png
26+
```
27+
28+
To add some randomization to the weights - you can use the `-r` flag: `python3 image_pathfinding.py -i map.png -o foo.png -r 0.5`
29+
30+
Note: The terrain-map was generated with OpenSimplex-noise via [this Python script](https://github.com/O-X-L/opensimplex).

examples/image_pathfinding.py renamed to examples/image-simple/image_pathfinding.py

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,11 +35,13 @@ def pixel_walkable(pixel, x, y):
3535
return any([p > 50 for p in pixel]) # darker pixel are not walkable
3636

3737

38-
def main(filename_map: str = MAP_FILE, filename_out: str = OUT_FILE):
38+
def main(filename_map: str = MAP_FILE, filename_out: str = OUT_FILE, diagonal_movement: bool = False):
3939
nodes = []
4040
if not Path(filename_map).exists():
4141
print(f'File {filename_map} does not exist.')
4242
return
43+
44+
print('Parsing map..')
4345
with Image.open(filename_map) as im:
4446
width, height = im.size
4547
for y in range(height):
@@ -56,11 +58,14 @@ def main(filename_map: str = MAP_FILE, filename_out: str = OUT_FILE):
5658
end = grid.node(*_goal)
5759
start = grid.node(*_start)
5860

59-
finder = AStarFinder(diagonal_movement=DiagonalMovement.never)
61+
print('Finding optimal path..')
62+
finder = AStarFinder(diagonal_movement=DiagonalMovement.always if diagonal_movement else DiagonalMovement.never)
6063
path, runs = finder.find_path(start, end, grid)
6164

6265
# print(grid.grid_str(path=path, end=end, start=start))
63-
print('iterations:', runs, 'path length:', len(path))
66+
print(f'iterations: {runs:_} path length: {len(path):_}')
67+
68+
print('Saving image..')
6469
out = im.copy()
6570
for p in path[1:-1]:
6671
out.putpixel((p.x, p.y), (255, 165, 0))
@@ -79,5 +84,9 @@ def main(filename_map: str = MAP_FILE, filename_out: str = OUT_FILE):
7984
'-o', '--filename_out',
8085
help='output file',
8186
default=OUT_FILE)
87+
parser.add_argument(
88+
'-d', '--diagonal-movement',
89+
help='allow for diagonal movement',
90+
action='store_true')
8291

8392
main(**vars(parser.parse_args()))
File renamed without changes.
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
# Create a Map from an image
2+
import os
3+
from pathlib import Path
4+
import argparse
5+
from random import uniform as random_float
6+
7+
# Pillow
8+
from PIL import Image
9+
10+
# pathfinding
11+
from pathfinding.core.diagonal_movement import DiagonalMovement
12+
from pathfinding.core.grid import Grid
13+
from pathfinding.finder.a_star import AStarFinder
14+
15+
16+
# image file with the map
17+
BASE_PATH = Path(os.path.dirname(__file__))
18+
MAP_FILE = BASE_PATH / "map.png"
19+
OUT_FILE = BASE_PATH / "out.png"
20+
21+
# this script searches for specific colors in RGB-format
22+
COLOR_START = (255, 255, 0) # yellow
23+
COLOR_END = (255, 0, 0) # red
24+
COLOR_PATH = (255, 165, 0) # orange
25+
COLOR_WEIGHT_MAPPING = {
26+
(0, 62, 178): 10, # deep water
27+
(9, 82, 198): 3, # water
28+
(254, 224, 179): 1, # sand
29+
(9, 120, 93): 2, # grass
30+
(10, 107, 72): 3, # bushes
31+
(11, 94, 51): 4, # forest
32+
(140, 142, 123): 5, # hills
33+
(160, 162, 143): 10, # alpine
34+
(53, 54, 68): 15, # steep cliff
35+
(255, 255, 255): 10, # snow
36+
}
37+
38+
39+
def main(
40+
filename_map: str = MAP_FILE, filename_out: str = OUT_FILE,
41+
weight_randomization: float = 0, diagonal_movement: bool = False,
42+
):
43+
nodes = []
44+
if not Path(filename_map).exists():
45+
print(f'File {filename_map} does not exist.')
46+
return
47+
48+
print('Parsing map..')
49+
with Image.open(filename_map) as im:
50+
width, height = im.size
51+
for y in range(height):
52+
nodes.append([])
53+
for x in range(width):
54+
pixel = im.getpixel((x, y))
55+
weight = COLOR_WEIGHT_MAPPING.get(pixel, 1)
56+
57+
if weight_randomization != 0:
58+
weight += random_float(0, weight_randomization)
59+
60+
nodes[y].append(weight)
61+
62+
if pixel == COLOR_END:
63+
_goal = (x, y)
64+
elif pixel == COLOR_START:
65+
_start = (x, y)
66+
67+
grid = Grid(matrix=nodes)
68+
end = grid.node(*_goal)
69+
start = grid.node(*_start)
70+
71+
print('Finding optimal path..')
72+
finder = AStarFinder(diagonal_movement=DiagonalMovement.always if diagonal_movement else DiagonalMovement.never)
73+
path, runs = finder.find_path(start, end, grid)
74+
75+
# print(grid.grid_str(path=path, end=end, start=start))
76+
print(f'iterations: {runs:_} path length: {len(path):_}')
77+
78+
print('Saving image..')
79+
out = im.copy()
80+
for p in path[1:-1]:
81+
out.putpixel((p.x, p.y), COLOR_PATH)
82+
out.save(filename_out)
83+
84+
85+
if __name__ == '__main__':
86+
parser = argparse.ArgumentParser(
87+
prog='image_pathfinding',
88+
description='find a path in an image from a yellow pixel (rgb: 255,255,0) to a red one (rgb: 255,0,0) '
89+
'with weighted-tiles')
90+
parser.add_argument(
91+
'-i', '--filename_map',
92+
help='input file',
93+
default=MAP_FILE)
94+
parser.add_argument(
95+
'-o', '--filename_out',
96+
help='output file',
97+
default=OUT_FILE)
98+
parser.add_argument(
99+
'-r', '--weight-randomization',
100+
help='how much randomization should be added to the tile-weights (disabled by default)',
101+
type=float,
102+
default=0)
103+
parser.add_argument(
104+
'-d', '--diagonal-movement',
105+
help='allow for diagonal movement',
106+
action='store_true')
107+
108+
main(**vars(parser.parse_args()))

examples/image-weighted/map.png

117 KB
Loading

0 commit comments

Comments
 (0)