|
| 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())) |
0 commit comments