Как лучше всего представить и решить лабиринт с заданным изображением?
Учитывая изображение в формате JPEG (как показано выше), как лучше всего его прочитать, разобрать в некоторую структуру данных и решить лабиринт? Мой первый инстинкт - читать изображение попиксельно и сохранять его в списке (массиве) логических значений: True
для белого пикселя и False
для небелого пикселя (цвета можно отбрасывать). Проблема этого метода заключается в том, что изображение может быть не «идеальным по пикселям». Под этим я просто подразумеваю, что если где-то на стене есть белый пиксель, он может создать непреднамеренный путь.
Другой метод (который пришёл ко мне после недолгого размышления) - преобразовать изображение в файл SVG, представляющий собой список путей, нарисованных на холсте. Таким образом, пути могут быть прочитаны в один и тот же список (логические значения), где True
указывается путь или стена, False
указывающая пространство, которое можно перемещать. Проблема с этим методом возникает, если преобразование не является точным на 100% и не полностью соединяет все стены, создавая промежутки.
Также проблема с преобразованием в SVG заключается в том, что линии не являются «идеально» прямыми. Это приводит к тому, что пути являются кубическими кривыми Безье. Со списком (массивом) логических значений, индексированных целыми числами, кривые не будут легко переноситься, и все точки, которые находятся на кривой, должны быть рассчитаны, но не будут точно соответствовать индексам списка.
Я предполагаю, что хотя один из этих методов может сработать (хотя, вероятно, нет), он ужасно неэффективен, учитывая такое большое изображение, и что существует лучший способ. Как это лучше всего (наиболее эффективно и / или с наименьшей сложностью) сделать? Есть ли даже лучший способ?
Затем идет решение лабиринта. Если я использую любой из первых двух методов, я по сути получу матрицу. Согласно этому ответу , хорошим способом представления лабиринта является использование дерева, а хорошим способом его решения является использование алгоритма A * . Как можно создать дерево из изображения? Любые идеи?
TL; DR
Лучший способ разобрать? В какую структуру данных? Как бы указанная структура помогла / помешала решению?
ОБНОВЛЕНИЕ
Я попробовал свои силы в реализации того, что @Mikhail написал на Python, используя numpy
, как рекомендовано @Thomas. Я чувствую, что алгоритм правильный, но он работает не так, как хотелось бы. (Код ниже.) Библиотека PNG - это PyPNG .
import png, numpy, Queue, operator, itertools
def is_white(coord, image):
""" Returns whether (x, y) is approx. a white pixel."""
a = True
for i in xrange(3):
if not a: break
a = image[coord[1]][coord[0] * 3 + i] > 240
return a
def bfs(s, e, i, visited):
""" Perform a breadth-first search. """
frontier = Queue.Queue()
while s != e:
for d in [(-1, 0), (0, -1), (1, 0), (0, 1)]:
np = tuple(map(operator.add, s, d))
if is_white(np, i) and np not in visited:
frontier.put(np)
visited.append(s)
s = frontier.get()
return visited
def main():
r = png.Reader(filename = "thescope-134.png")
rows, cols, pixels, meta = r.asDirect()
assert meta['planes'] == 3 # ensure the file is RGB
image2d = numpy.vstack(itertools.imap(numpy.uint8, pixels))
start, end = (402, 985), (398, 27)
print bfs(start, end, image2d, [])
visited.append(s)
под afor.if
и заменить его наvisited.append(np)
. Вершина посещается, как только она добавляется в очередь. На самом деле, этот массив должен быть назван «в очереди». Вы также можете прекратить BFS, как только вы достигли финиша.Ответы:
Вот решение.
Вот код MATLAB для BFS:
Это действительно очень просто и стандартно, не должно быть трудностей при реализации этого в Python или чем-то еще.
И вот ответ:
источник
Это решение написано на Python. Спасибо Михаилу за указатели на подготовку изображения.
Анимированный поиск в ширину:
Завершенный лабиринт:
Примечание. Отмечает посещаемый пиксель серым цветом. Это устраняет необходимость в посещенном списке, но для этого требуется вторая загрузка файла изображения с диска перед рисованием пути (если вы не хотите составное изображение окончательного пути и всех взятых путей).
Пустой вариант лабиринта, который я использовал.
источник
Я попытался реализовать поиск A-Star для этой проблемы. Внимательно следил за реализацией Джозефом Керном для фреймворка и псевдокода алгоритма, приведенного здесь :
Поскольку A-Star - это эвристический алгоритм поиска, вам нужно придумать функцию, которая оценивает оставшуюся стоимость (здесь: расстояние) до достижения цели. Если вам не подходит неоптимальное решение, оно не должно переоценивать стоимость. Консервативный выбор в данном случае - это расстояние в Манхэттене (или такси), поскольку оно представляет прямолинейное расстояние между двумя точками на сетке для используемой окрестности фон Неймана. (Который, в этом случае, никогда не будет переоценивать стоимость.)
Это, однако, значительно недооценило бы фактическую стоимость данного лабиринта под рукой. Поэтому я добавил две другие метрики расстояния в квадрате евклидово расстояние и манхэттенское расстояние, умноженное на четыре для сравнения. Это, однако, может переоценить фактическую стоимость и, следовательно, может привести к неоптимальным результатам.
Вот код:
Вот несколько изображений для визуализации результатов (навеянное Джозефом Керном ). Анимации показывают новый кадр каждый раз после 10000 итераций основного цикла while.
Поиск в ширину:
A-Star Манхэттен Расстояние:
Квадрат евклидова расстояния A-Star:
Расстояние A-Star Manhattan, умноженное на четыре:
Результаты показывают, что исследуемые области лабиринта значительно различаются в зависимости от используемой эвристики. Таким образом, квадрат евклидова расстояния даже дает другой (субоптимальный) путь, как и другие метрики.
Что касается производительности алгоритма A-Star с точки зрения времени выполнения до завершения, обратите внимание, что по сравнению с Breadth-First Search (BFS) требуется много оценки функций расстояния и стоимости, которым нужно только оценить «целеустремленность» каждая кандидатская должность. Вопрос о том, перевешивает ли стоимость этих дополнительных оценок функций (A-Star) стоимость проверки большего количества узлов (BFS), и особенно вопрос о том, является ли производительность проблемой для вашего приложения, зависит от индивидуального восприятия. и, конечно, вообще нельзя ответить.
В общем , можно сказать, что осознанный алгоритм поиска (например, A-Star) может быть лучшим выбором по сравнению с исчерпывающим поиском (например, BFS), заключается в следующем. С количеством измерений лабиринта, т. Е. Коэффициентом ветвления дерева поиска, недостаток полного поиска (для полного поиска) возрастает в геометрической прогрессии. С ростом сложности это становится все менее и менее осуществимым, и в какой-то момент вы в значительной степени удовлетворены любым результатом, будь он (приблизительно) оптимальным или нет.
источник
Дерево поиска слишком много. Лабиринт неотделим по пути решения пути.
(Благодаря rainman002 от Reddit за указание на это мне.)
Благодаря этому вы можете быстро использовать подключенные компоненты для идентификации подключенных участков стены лабиринта. Это перебирает пиксели дважды.
Если вы хотите превратить это в красивую диаграмму пути (путей) решения, вы можете использовать двоичные операции со структурирующими элементами, чтобы заполнить пути «тупика» для каждой связанной области.
Демо-код для MATLAB следует. Он может использовать настройку, чтобы лучше очистить результат, сделать его более обобщенным и ускорить его выполнение. (Иногда, когда не 2:30 утра.)
источник
Использует очередь для непрерывного заполнения порога. Вставляет пиксель слева от входа в очередь, а затем запускает цикл. Если пиксель в очереди достаточно темный, он окрашен в светло-серый цвет (выше порога), и все соседи помещаются в очередь.
Решением является коридор между серой стеной и цветной стеной. Обратите внимание, что этот лабиринт имеет несколько решений. Кроме того, это просто похоже на работу.
источник
Вот и все: лабиринт-решатель-питон (GitHub)
Я с удовольствием поиграл с этим и продолжил на Джозефе Керне ответ . Не отвлекать от этого; Я просто сделал несколько небольших дополнений для тех, кто может поиграть с этим.
Это решатель на основе Python, который использует BFS, чтобы найти кратчайший путь. Мои главные дополнения, в то время, являются:
В его нынешнем виде начальная / конечная точки жестко заданы для этого образца лабиринта, но я планирую расширить его так, чтобы вы могли выбрать соответствующие пиксели.
источник
Я бы пошел на вариант матрицы Bools. Если вы обнаружите, что стандартные списки Python слишком неэффективны для этого, вы можете использовать
numpy.bool
вместо этого массив. Память для лабиринта размером 1000x1000 пикселей составляет всего 1 МБ.Не беспокойтесь о создании каких-либо структур данных дерева или графика. Это просто способ думать об этом, но не обязательно хороший способ представить это в памяти; Булева матрица проще в написании кода и более эффективна.
Затем используйте алгоритм A * для его решения. Для эвристики расстояния используйте Манхэттенское расстояние (
distance_x + distance_y
).Представлять узлы набором
(row, column)
координат. Всякий раз, когда алгоритм ( псевдокод Wikipedia ) вызывает «соседей», это просто зацикливание на четырех возможных соседях (обратите внимание на края изображения!).Если вы обнаружите, что это все еще слишком медленно, вы можете попробовать уменьшить масштаб изображения, прежде чем загружать его. Будьте осторожны, чтобы не потерять узкие пути в этом процессе.
Возможно, в Python можно также выполнить масштабирование 1: 2, убедившись, что вы не потеряете возможные пути. Интересный вариант, но нужно немного подумать.
источник
boolean
значений, будет ли хранилище сравниваться? Тогда матрица будет 2400 * 1200. И окажет ли A * over BFS значительное влияние на реальное время работы?Вот несколько идей.
(1. Обработка изображений :)
1.1 Загрузите изображение в виде карты пикселей RGB . В C # это тривиальное использование
system.drawing.bitmap
. На языках без простой поддержки обработки изображений просто преобразуйте изображение в формат переносимого растрового изображения (PPM) (текстовое представление Unix, создает большие файлы) или в какой-либо простой двоичный формат файла, который вы можете легко прочитать, например, BMP или TGA . ImageMagick в Unix или IrfanView в Windows.1.2 Вы можете, как упоминалось ранее, упростить данные, взяв (R + G + B) / 3 для каждого пикселя в качестве индикатора серого тона, а затем пороговое значение для создания черно-белой таблицы. Нечто близкое к 200 при условии, что 0 = черный, а 255 = белый, устранит артефакты JPEG.
(2. Решения :)
2.1 Поиск в глубину: Инициируйте пустой стек с начальным местоположением, соберите доступные последующие ходы, выберите один случайным образом и протолкните в стек, продолжайте до достижения конца или до конца. При возврате в обратном направлении, выталкивая стопку, вы должны отслеживать, какие позиции были посещены на карте, поэтому, когда вы собираете доступные ходы, вы никогда не идете по одному и тому же пути дважды. Очень интересно оживить.
2.2 Поиск в ширину: упоминалось ранее, аналогично описанному выше, но только с использованием очередей. Также интересно оживить. Это работает как заливка в программном обеспечении для редактирования изображений. Я думаю, что вы можете решить лабиринт в Photoshop, используя этот трюк.
2.3 Стеновой элемент: Геометрически говоря, лабиринт представляет собой сложенную / извитую трубу. Если вы будете держать руку на стене, то в конце концов найдете выход;) Это не всегда работает. Есть определенное предположение: идеальные лабиринты и т. Д., Например, некоторые лабиринты содержат острова. Ищи это; это увлекательно
(3. Комментарии :)
Это хитрый. Легко решить лабиринты, если они представлены в некотором простом формальном массиве, где каждый элемент представляет собой тип ячейки с северной, восточной, южной и западной стенами и полем посещенного флага. Однако, учитывая, что вы пытаетесь сделать это с помощью нарисованного от руки эскиза, он становится грязным. Я искренне думаю, что попытка рационализировать эскиз сведет вас с ума. Это похоже на проблемы с компьютерным зрением, которые довольно сложны. Возможно, переход непосредственно на карту изображения может быть проще, но более расточительным.
источник
Вот решение с использованием R.
От RGB до оттенков серого, см .: https://stackoverflow.com/a/27491947/2371031
Вуаля!
Это то, что происходит, если вы не заполните граничные пиксели (Ха!) ...
Полное раскрытие: я задавал и отвечал на очень похожий вопрос сам, прежде чем нашел этот. Затем, благодаря магии SO, нашел этот вопрос одним из лучших «Связанных вопросов». Я думал, что буду использовать этот лабиринт как дополнительный тестовый пример ... Мне было очень приятно узнать, что мой ответ там также работает для этого приложения с очень небольшими изменениями.
источник
хорошим решением было бы то, что вместо того, чтобы находить соседей по пикселям, это делалось бы по ячейке, потому что коридор может иметь 15 пикселей, поэтому в том же коридоре он может выполнять действия, как влево или вправо, тогда как если бы это было сделано, как если бы перемещение был куб, это было бы простое действие, как ВВЕРХ, ВНИЗ, ВЛЕВО ИЛИ ВПРАВО
источник