Skip to content

Commit f5ef1fc

Browse files
committed
add example for testing with images, first step towards benchmarking
1 parent d838db8 commit f5ef1fc

File tree

9 files changed

+120
-7
lines changed

9 files changed

+120
-7
lines changed

.gitignore

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,4 +63,6 @@ target/
6363
.ipynb_checkpoints
6464

6565
# python virtual env
66-
venv/
66+
venv/
67+
68+
examples/out.png

README.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,13 @@ see [pathfinding on pypi](https://pypi.org/project/pathfinding/)
3131

3232
## Usage examples
3333

34-
For usage examples with detailed descriptions take a look at the [docs](docs/) folder, also take a look at the [test/](test/) folder for more examples, e.g. how to use pandas
34+
For usage examples with detailed descriptions take a look at the [docs](docs/) folder, also take a look at the [test/](test/) folder for more examples, e.g. how to use pandas.
35+
36+
*image_pathfinding.py* in the `examples/`-folder provides an example how to load an image with a start and goal point. You can all it with an input and output file like this:
37+
```
38+
cd examples/
39+
python3 image_pathfinding.py -i map.png -o foo.png
40+
```
3541

3642
## Rerun the algorithm
3743

docs/03_graphs.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
# Using a graph
22
The default is to plan a path from one node of a grid to another but you can use any graph that has a weight (also known as cost) assigned to its edges.
33

4+
This is based on the animation at [Wikipedia about Dijkstra's algorithm](https://en.wikipedia.org/wiki/Dijkstra%27s_algorithm#/media/File:Dijkstra_Animation.gif)
5+
46
```python
57
from pathfinding.core.diagonal_movement import DiagonalMovement
68
from pathfinding.core.graph import Graph
79
from pathfinding.core.node import Node
810
from pathfinding.finder.dijkstra import DijkstraFinder
911

10-
# based on the animation at [Wikipedia about Dijkstra's algorithm](https://en.wikipedia.org/wiki/Dijkstra%27s_algorithm#/media/File:Dijkstra_Animation.gif)
11-
12-
# source node, target node, distance value
12+
# list values are: source node, target node, distance value
1313
edges = [
1414
[1, 2, 7],
1515
[1, 3, 9],

docs/04_images.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# Working with images as input
2+
Because its easier to visualize maps as pictures and to manipulate you often want to work on images instead of providing the map as big array.

docs/05_benchmarking.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# Benchmarking
2+
Many pathfinding algorithms need more system resources the bigger the input grid is, some are optimized for specific scenarios to (for example Jump-Point Search) for narrow passages, while others like Dijkstra are for a general solution that come at the cost of looking at all cells of the grid in the worst case.
3+
4+
## Creating a benchmark
5+
6+
## Record performance and visualize iterations
7+
8+
http://jiffyclub.github.io/snakeviz/
9+
10+
## Memory

examples/README.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# Example implementations using python-pathfinding
2+
3+
## *image_pathfinding.py*
4+
Create a map from and image and run a path finding algorithm on it (requires PIL/Pillow).
5+
6+
You can all it with an input and output file like this:
7+
```
8+
cd examples/
9+
python3 image_pathfinding.py -i map.png -o foo.png
10+
```

examples/image_pathfinding.py

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
# Create a Map from an image
2+
import os
3+
from pathlib import Path
4+
import argparse
5+
6+
# Pillow
7+
from PIL import Image
8+
9+
# pathfinding
10+
from pathfinding.core.diagonal_movement import DiagonalMovement
11+
from pathfinding.core.grid import Grid
12+
from pathfinding.finder.a_star import AStarFinder
13+
14+
15+
# image file with the map
16+
BASE_PATH = Path(os.path.dirname(__file__))
17+
MAP_FILE = BASE_PATH / "map.png"
18+
OUT_FILE = BASE_PATH / "out.png"
19+
# color channel order, defaults to RGB
20+
RED, GREEN, BLUE = 0, 1, 2
21+
22+
23+
def green(pixel: list) -> bool:
24+
"""Returns True if the pixel is green (the starting point)"""
25+
return pixel[RED] < 10 and pixel[GREEN] > 250 and pixel[BLUE] < 10
26+
27+
28+
def red(pixel: list) -> bool:
29+
"""Returns True if the pixel is red (the goal position)"""
30+
return pixel[RED] > 250 and pixel[GREEN] < 10 and pixel[BLUE] < 10
31+
32+
33+
def pixel_walkable(pixel, x, y):
34+
"""returns True if the pixel is walkable."""
35+
return any([p > 50 for p in pixel]) # darker pixel are not walkable
36+
37+
38+
def main(filename_map: str = MAP_FILE, filename_out: str = OUT_FILE):
39+
nodes = []
40+
if not Path(filename_map).exists():
41+
print(f'File {filename_map} does not exist.')
42+
return
43+
with Image.open(filename_map) as im:
44+
width, height = im.size
45+
for y in range(height):
46+
nodes.append([])
47+
for x in range(width):
48+
pixel = im.getpixel((x, y))
49+
node = pixel_walkable(pixel[:3], x, y)
50+
nodes[y].append(node)
51+
if red(pixel):
52+
_goal = (x, y)
53+
if green(pixel):
54+
_start = (x, y)
55+
grid = Grid(matrix=nodes)
56+
end = grid.node(*_goal)
57+
start = grid.node(*_start)
58+
59+
finder = AStarFinder(diagonal_movement=DiagonalMovement.never)
60+
path, runs = finder.find_path(start, end, grid)
61+
62+
# print(grid.grid_str(path=path, end=end, start=start))
63+
print('iterations:', runs, 'path length:', len(path))
64+
out = im.copy()
65+
for p in path[1:-1]:
66+
out.putpixel((p.x, p.y), (255, 165, 0))
67+
out.save(filename_out)
68+
69+
70+
if __name__ == '__main__':
71+
parser = argparse.ArgumentParser(
72+
prog='image_pathfinding',
73+
description='find a path in an image from a green pixel to a red one')
74+
parser.add_argument(
75+
'-i', '--filename_map',
76+
help='input file',
77+
default=MAP_FILE)
78+
parser.add_argument(
79+
'-o', '--filename_out',
80+
help='output file',
81+
default=OUT_FILE)
82+
83+
main(**vars(parser.parse_args()))

examples/map.png

3.96 KB
Loading

notebooks/performance.ipynb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@
9090
],
9191
"metadata": {
9292
"kernelspec": {
93-
"display_name": "venv",
93+
"display_name": "Python 3",
9494
"language": "python",
9595
"name": "python3"
9696
},
@@ -104,7 +104,7 @@
104104
"name": "python",
105105
"nbconvert_exporter": "python",
106106
"pygments_lexer": "ipython3",
107-
"version": "3.9.6"
107+
"version": "3.13.1"
108108
}
109109
},
110110
"nbformat": 4,

0 commit comments

Comments
 (0)