Was ist der beste Weg, um ein Labyrinth bei einem bestimmten Bild darzustellen und zu lösen?
Wie kann man ein JPEG-Bild (wie oben gezeigt) am besten einlesen, in eine Datenstruktur analysieren und das Labyrinth lösen? Mein erster Instinkt ist, das Bild Pixel für Pixel zu lesen und in einer Liste (Array) von Booleschen Werten zu speichern: True
für ein weißes Pixel und False
für ein nicht weißes Pixel (die Farben können verworfen werden). Das Problem bei dieser Methode ist, dass das Bild möglicherweise nicht "pixelgenau" ist. Damit meine ich einfach, dass ein weißes Pixel irgendwo an einer Wand einen unbeabsichtigten Pfad erzeugen kann.
Eine andere Methode (die mir nach einigem Nachdenken einfiel) besteht darin, das Bild in eine SVG-Datei zu konvertieren - eine Liste von Pfaden, die auf einer Leinwand gezeichnet sind. Auf diese Weise könnten die Pfade in dieselbe Art von Liste (Boolesche Werte) eingelesen werden, in der True
ein Pfad oder eine Wand angegeben ist, False
die einen befahrbaren Raum angibt. Ein Problem bei dieser Methode tritt auf, wenn die Konvertierung nicht 100% genau ist und nicht alle Wände vollständig verbunden werden, wodurch Lücken entstehen.
Ein Problem bei der Konvertierung in SVG ist auch, dass die Linien nicht "perfekt" gerade sind. Dies führt dazu, dass die Pfade kubische Bezierkurven sind. Mit einer Liste (Array) von Booleschen Werten, die durch Ganzzahlen indiziert sind, würden die Kurven nicht einfach übertragen, und alle Punkte, die auf der Kurve liegen, müssten berechnet werden, stimmen jedoch nicht genau mit den Listenindizes überein.
Ich gehe davon aus, dass eine dieser Methoden zwar funktioniert (wenn auch wahrscheinlich nicht), dass sie bei einem so großen Bild jedoch äußerst ineffizient sind und dass es einen besseren Weg gibt. Wie wird dies am besten (am effizientesten und / oder am wenigsten komplex) durchgeführt? Gibt es überhaupt einen besten Weg?
Dann kommt das Lösen des Labyrinths. Wenn ich eine der ersten beiden Methoden verwende, erhalte ich im Wesentlichen eine Matrix. Nach dieser Antwort ist die Verwendung eines Baums eine gute Möglichkeit, ein Labyrinth darzustellen, und eine gute Möglichkeit, es zu lösen, die Verwendung des A * -Algorithmus . Wie würde man aus dem Bild einen Baum erstellen? Irgendwelche Ideen?
TL; DR
Beste Art zu analysieren? In welche Datenstruktur? Wie würde diese Struktur beim Lösen helfen / behindern?
UPDATE
Ich habe versucht, das zu implementieren, was @Mikhail in Python geschrieben hat, numpy
und zwar wie von @Thomas empfohlen. Ich denke, dass der Algorithmus korrekt ist, aber er funktioniert nicht wie erhofft. (Code unten.) Die PNG-Bibliothek ist 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)
unter a wechselnfor.if
und es durch ersetzen solltenvisited.append(np)
. Ein Scheitelpunkt wird besucht, sobald er der Warteschlange hinzugefügt wurde. Tatsächlich sollte dieses Array den Namen "Warteschlange" tragen. Sie können BFS auch beenden, sobald Sie das Ziel erreicht haben.Antworten:
Hier ist eine Lösung.
Hier ist der MATLAB-Code für BFS:
Es ist wirklich sehr einfach und Standard, es sollte keine Schwierigkeiten geben, dies in Python oder was auch immer zu implementieren .
Und hier ist die Antwort:
quelle
Diese Lösung ist in Python geschrieben. Vielen Dank an Mikhail für die Hinweise zur Bildvorbereitung.
Eine animierte Breitensuche:
Das fertige Labyrinth:
Hinweis: Markiert ein weißes besuchtes Pixelgrau. Dadurch entfällt die Notwendigkeit einer besuchten Liste, dies erfordert jedoch ein zweites Laden der Bilddatei von der Festplatte, bevor ein Pfad gezeichnet wird (wenn Sie nicht möchten, dass ein zusammengesetztes Bild des endgültigen Pfads und aller Pfade erstellt wird).
Eine leere Version des Labyrinths, das ich benutzt habe.
quelle
Ich habe versucht, die A-Star-Suche für dieses Problem zu implementieren. Dicht gefolgt die Umsetzung von Joseph Kern für den Rahmen und der Algorithmus Pseudo - Code angegeben hier :
Da A-Star ein heuristischer Suchalgorithmus ist, müssen Sie eine Funktion entwickeln, die die verbleibenden Kosten (hier: Entfernung) schätzt, bis das Ziel erreicht ist. Wenn Sie mit einer suboptimalen Lösung nicht vertraut sind, sollten die Kosten nicht überschätzt werden. Eine konservative Wahl wäre hier die Entfernung nach Manhattan (oder Taxi), da dies die geradlinige Entfernung zwischen zwei Punkten im Raster für das verwendete Viertel Von Neumann darstellt. (Was in diesem Fall die Kosten niemals überschätzen würde.)
Dies würde jedoch die tatsächlichen Kosten für das jeweilige Labyrinth erheblich unterschätzen. Daher habe ich zum Vergleich zwei weitere Entfernungsmetriken im Quadrat der euklidischen Entfernung und die Manhattan-Entfernung multipliziert mit vier hinzugefügt. Diese können jedoch die tatsächlichen Kosten überschätzen und daher zu suboptimalen Ergebnissen führen.
Hier ist der Code:
Hier sind einige Bilder zur Visualisierung der Ergebnisse (inspiriert von dem von Joseph Kern ). Die Animationen zeigen nach jeweils 10000 Iterationen der Haupt-while-Schleife jeweils einen neuen Frame.
Breitensuche:
A-Star Manhattan Entfernung:
A-Stern im Quadrat euklidischer Abstand:
A-Star Manhattan Entfernung multipliziert mit vier:
Die Ergebnisse zeigen, dass sich die untersuchten Regionen des Labyrinths hinsichtlich der verwendeten Heuristik erheblich unterscheiden. Daher erzeugt der euklidische Quadratabstand sogar einen anderen (suboptimalen) Pfad als die anderen Metriken.
In Bezug auf die Leistung des A-Star-Algorithmus in Bezug auf die Laufzeit bis zur Beendigung ist zu beachten, dass sich im Vergleich zur Breadth-First Search (BFS), bei der nur die "Zielgenauigkeit" von bewertet werden muss, viele Bewertungen der Entfernungs- und Kostenfunktionen ergeben jede Kandidatenposition. Ob die Kosten für diese zusätzlichen Funktionsbewertungen (A-Star) die Kosten für die größere Anzahl zu überprüfender Knoten (BFS) überwiegen und insbesondere, ob die Leistung für Ihre Anwendung überhaupt ein Problem darstellt oder nicht, ist eine Frage der individuellen Wahrnehmung und kann natürlich nicht allgemein beantwortet werden.
Eine Sache , die kann im allgemeinen gesagt werden , ob oder nicht ein informierter Suchalgorithmus (wie A-Star) im Vergleich die bessere Wahl sein könnte zu einer erschöpfenden Suche (zB BFS) ist die folgende. Mit der Anzahl der Dimensionen des Labyrinths, dh dem Verzweigungsfaktor des Suchbaums, wächst der Nachteil einer erschöpfenden Suche (erschöpfende Suche) exponentiell. Mit zunehmender Komplexität wird es immer weniger machbar, und irgendwann sind Sie mit jedem Ergebnispfad ziemlich zufrieden , ob (ungefähr) optimal oder nicht.
quelle
Baumsuche ist zu viel. Das Labyrinth ist entlang der Lösungswege von Natur aus trennbar.
(Vielen Dank an rainman002 von Reddit für den Hinweis.)
Aus diesem Grund können Sie schnell verbundene Komponenten verwenden , um die verbundenen Abschnitte der Labyrinthwand zu identifizieren. Dies iteriert zweimal über die Pixel.
Wenn Sie daraus ein schönes Diagramm der Lösungspfade machen möchten, können Sie binäre Operationen mit Strukturierungselementen verwenden, um die "Sackgasse" -Pfade für jede verbundene Region auszufüllen.
Demo-Code für MATLAB folgt. Es könnte Optimierungen verwenden, um das Ergebnis besser zu bereinigen, verallgemeinerbar zu machen und es schneller laufen zu lassen. (Irgendwann, wenn es nicht 2:30 Uhr ist.)
quelle
Verwendet eine Warteschlange für eine kontinuierliche Füllung mit Schwellenwert. Schiebt das Pixel links vom Eingang in die Warteschlange und startet dann die Schleife. Wenn ein Pixel in der Warteschlange dunkel genug ist, ist es hellgrau (über dem Schwellenwert) und alle Nachbarn werden in die Warteschlange verschoben.
Lösung ist der Korridor zwischen grauer Wand und farbiger Wand. Beachten Sie, dass dieses Labyrinth mehrere Lösungen hat. Auch dies scheint nur zu funktionieren.
quelle
Los geht's: Labyrinth-Löser-Python (GitHub)
Ich hatte Spaß daran herumzuspielen und erweiterte Joseph Kerns Antwort. Nicht davon abzulenken; Ich habe nur ein paar kleinere Ergänzungen für alle anderen gemacht, die daran interessiert sein könnten, damit herumzuspielen.
Es ist ein Python-basierter Solver, der BFS verwendet, um den kürzesten Weg zu finden. Meine wichtigsten Ergänzungen zu der Zeit sind:
Derzeit sind die Start- / Endpunkte für dieses Beispiellabyrinth fest codiert, aber ich plane, es so zu erweitern, dass Sie die entsprechenden Pixel auswählen können.
quelle
Ich würde mich für die Matrix-of-Bools-Option entscheiden. Wenn Sie feststellen, dass Standard-Python-Listen dafür zu ineffizient sind, können Sie
numpy.bool
stattdessen ein Array verwenden. Der Speicher für ein Labyrinth mit 1000 x 1000 Pixeln beträgt dann nur 1 MB.Erstellen Sie keine Baum- oder Diagrammdatenstrukturen. Das ist nur eine Art, darüber nachzudenken, aber nicht unbedingt eine gute Art, es im Gedächtnis darzustellen. Eine boolesche Matrix ist sowohl einfacher zu codieren als auch effizienter.
Verwenden Sie dann den A * -Algorithmus, um das Problem zu lösen. Verwenden Sie für die Entfernungsheuristik die Manhattan-Entfernung (
distance_x + distance_y
).Stellen Sie Knoten durch ein Tupel von
(row, column)
Koordinaten dar. Immer wenn der Algorithmus ( Wikipedia-Pseudocode ) "Nachbarn" fordert, ist es eine einfache Sache, die vier möglichen Nachbarn zu durchlaufen (beachten Sie die Bildränder!).Wenn Sie feststellen, dass es immer noch zu langsam ist, können Sie versuchen, das Bild vor dem Laden zu verkleinern. Achten Sie darauf, dabei keine engen Pfade zu verlieren.
Möglicherweise ist es auch in Python möglich, eine 1: 2-Verkleinerung durchzuführen, um sicherzustellen, dass Sie keine möglichen Pfade verlieren. Eine interessante Option, aber es bedarf etwas mehr Überlegungen.
quelle
boolean
Werten verwende, wird der Speicher dann immer noch verglichen? Die Matrix ist dann 2400 * 1200. Und würde A * über BFS einen signifikanten Einfluss auf die tatsächliche Laufzeit haben?Hier sind ein paar Ideen.
(1. Bildverarbeitung :)
1.1 Laden Sie das Bild als RGB- Pixelkarte. In C # ist die Verwendung trivial
system.drawing.bitmap
. In Sprachen ohne einfache Unterstützung für das Imaging konvertieren Sie das Bild einfach in das tragbare Pixmap-Format (PPM) (eine Unix-Textdarstellung, erzeugt große Dateien) oder ein einfaches Binärdateiformat, das Sie leicht lesen können, wie z. B. BMP oder TGA . ImageMagick unter Unix oder IrfanView unter Windows.1.2 Sie können, wie bereits erwähnt, die Daten vereinfachen, indem Sie (R + G + B) / 3 für jedes Pixel als Indikator für den Grauton verwenden und dann den Wert für die Erstellung einer Schwarzweißtabelle als Schwellenwert festlegen. Etwas nahe 200 unter der Annahme von 0 = Schwarz und 255 = Weiß entfernt die JPEG-Artefakte.
(2. Lösungen :)
2.1 Tiefensuche: Initiieren Sie einen leeren Stapel mit Startposition, sammeln Sie verfügbare Folgebewegungen, wählen Sie eine nach dem Zufallsprinzip aus und schieben Sie sie auf den Stapel, fahren Sie fort, bis das Ende oder eine Sackgasse erreicht ist. Wenn Sie beim Stoppen des Stacks den Stapel öffnen, müssen Sie nachverfolgen, welche Positionen auf der Karte besucht wurden. Wenn Sie also verfügbare Züge sammeln, nehmen Sie nie zweimal denselben Weg. Sehr interessant zu animieren.
2.2 Breitensuche: Zuvor erwähnt, ähnlich wie oben, jedoch nur unter Verwendung von Warteschlangen. Auch interessant zu animieren. Dies funktioniert wie das Ausfüllen von Bildbearbeitungssoftware. Ich denke, Sie können möglicherweise ein Labyrinth in Photoshop mit diesem Trick lösen.
2.3 Wandfolger: Geometrisch gesehen ist ein Labyrinth eine gefaltete / gewundene Röhre. Wenn Sie Ihre Hand an der Wand halten, finden Sie schließlich den Ausgang;) Dies funktioniert nicht immer. Es gibt bestimmte Annahmen bezüglich perfekter Labyrinthe usw., zum Beispiel enthalten bestimmte Labyrinthe Inseln. Schauen Sie doch mal nach; es ist faszinierend.
(3. Kommentare :)
Dies ist die schwierige Frage. Es ist leicht, Labyrinthe zu lösen, wenn sie in einem einfachen formalen Array dargestellt werden, wobei jedes Element ein Zelltyp mit Nord-, Ost-, Süd- und Westwänden und einem besuchten Flaggenfeld ist. Wenn Sie dies jedoch anhand einer handgezeichneten Skizze versuchen, wird es chaotisch. Ich denke ehrlich, dass der Versuch, die Skizze zu rationalisieren, Sie verrückt machen wird. Dies ist vergleichbar mit Computer-Vision-Problemen, die ziemlich kompliziert sind. Vielleicht ist es einfacher und doch verschwenderischer, direkt auf die Imagemap zuzugreifen.
quelle
Hier ist eine Lösung mit R.
RGB zu Graustufen, siehe: https://stackoverflow.com/a/27491947/2371031
Voila!
Dies passiert, wenn Sie einige Randpixel nicht ausfüllen (Ha!) ...
Vollständige Offenlegung: Ich habe selbst eine sehr ähnliche Frage gestellt und beantwortet, bevor ich diese gefunden habe. Dann durch die Magie von SO, fand diese als eine der Top "Related Questions". Ich dachte, ich würde dieses Labyrinth als zusätzlichen Testfall verwenden ... Ich war sehr erfreut festzustellen, dass meine Antwort dort auch für diese Anwendung mit sehr geringen Änderungen funktioniert.
quelle
Die gute Lösung wäre, dass anstatt die Nachbarn nach Pixeln zu finden, dies nach Zellen erfolgt, da ein Korridor 15 Pixel haben kann, sodass er im selben Korridor Aktionen wie links oder rechts ausführen kann, während dies so geschieht, als ob die Verschiebung erfolgt Wäre ein Würfel, wäre es eine einfache Aktion wie AUF, AB, LINKS ODER RECHTS
quelle