Przedstawianie i rozwiązywanie labiryntu z obrazem

Jaki jest najlepszy sposób przedstawiania i rozwiązywania labiryntu, biorąc pod uwagę obraz?

Zdjęcie okładki numeru Scope 134

Biorąc pod uwagę obraz JPEG (jak widać powyżej), jaki jest najlepszy sposób, aby go odczytać, przeanalizować go w jakiejś strukturze danych i rozwiązać labirynt? Moim pierwszym instynktem jest odczytanie obrazu piksel po pikselu i zapisanie go w liście (tablicy) wartości logicznych: True dla białego piksela i False Dla Nie białego piksela (kolory można odrzucić). Problem z tą metodą polega na tym, że obraz nie może być " pikselowy perfect". Chodzi mi po prostu o to, że jeśli gdzieś na ścianie znajduje się biały piksel, może to spowodować niezamierzoną ścieżkę.

Inną metodą (która przyszła mi do głowy po namyśle) jest konwersja obrazu do pliku SVG - czyli listy ścieżek narysowanych na płótnie. W ten sposób ścieżki mogą być wczytane do tego samego rodzaju listy (wartości logiczne), gdzie True wskazuje ścieżkę lub ścianę, False wskazuje przestrzeń zdolną do podróży. Problem z tą metodą pojawia się, jeśli konwersja nie jest w 100% dokładna, i nie w pełni połączyć wszystkie ściany, tworząc luki.

Problem z konwersją do SVG polega również na tym, że linie nie są "idealnie" proste. Powoduje to, że ścieżki są sześciennymi krzywymi Beziera. Z listą (tablicą) wartości logicznych indeksowanych przez liczby całkowite, krzywe nie będą łatwo przenoszone, a wszystkie punkty, które linia na krzywej musiałyby być obliczone, ale nie będą dokładnie dopasowane do indeksów list.

Zakładam, że chociaż jedna z tych metod może działać (choć prawdopodobnie nie), że są żałośnie nieefektywne biorąc pod uwagę tak duży obraz i że istnieje lepszy sposób. Jak to najlepiej (najefektywniej i/lub z najmniejszą złożonością) zrobić? Jest w ogóle najlepszy sposób?

Potem przychodzi rozwiązanie labiryntu. Jeśli użyję jednej z dwóch pierwszych metod, w zasadzie skończę z matrycą. Według tej odpowiedzi, dobrym sposobem na reprezentowanie labiryntu jest użycie drzewa, a dobrym sposobem na jego rozwiązanie jest użycie algorytmu a* . Jak można by stworzyć drzewo z obrazu? Jakieś pomysły?

TL; DR
Najlepszy sposób na analizę? W jakiej strukturze danych? W jaki sposób wspomniana struktura pomoże / utrudni rozwiązanie?

UPDATE
Próbowałem swoich sił w implementacji tego, co napisał @Mikhail w Pythonie, używając numpy, jak polecił @ Thomas. Czuję, że algorytm jest poprawny, ale nie działa tak, jak się spodziewaliśmy. (Kod poniżej.) Biblioteka PNG to 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, [])
Author: Community, 2012-10-21

9 answers

Oto rozwiązanie.

  1. Konwertuj obraz na skalę szarości (jeszcze nie binarną), dostosowując wagi kolorów tak, aby ostateczny obraz w skali szarości był w przybliżeniu jednolity. Możesz to zrobić po prostu sterując suwakami w Photoshopie w Image - > Adjustments - > Black & White.
  2. Konwertuj obraz na binarny, ustawiając odpowiedni próg w programie Photoshop w Image - > Adjustments - > Threshold.
  3. Upewnij się, że próg jest wybrany prawidłowo. Użyj narzędzia Magic Wand z tolerancją 0, punkt próbka, przylegająca, bez antyaliasingu. Sprawdź, czy krawędzie, na których podziały zaznaczenia nie są fałszywymi krawędziami wprowadzonymi przez niewłaściwy próg. W rzeczywistości wszystkie wewnętrzne punkty tego labiryntu są dostępne od początku.
  4. Dodaj sztuczne granice na labiryncie, aby wirtualny podróżnik nie chodził po nim:)
  5. zaimplementuj width - pierwsze wyszukiwanie (BFS) w swoim ulubionym języku i uruchom go od początku. Do tego zadania wolę MATLAB. Jak już wspomniał @ Tomasz, tam nie ma potrzeby bałaganu z regularną reprezentacją Wykresów. Możesz pracować bezpośrednio z obrazem binarnym.

Oto kod MATLAB dla BFS:

function path = solve_maze(img_file)
  %% Init data
  img = imread(img_file);
  img = rgb2gray(img);
  maze = img > 0;
  start = [985 398];
  finish = [26 399];

  %% Init BFS
  n = numel(maze);
  Q = zeros(n, 2);
  M = zeros([size(maze) 2]);
  front = 0;
  back = 1;

  function push(p, d)
    q = p + d;
    if maze(q(1), q(2)) && M(q(1), q(2), 1) == 0
      front = front + 1;
      Q(front, :) = q;
      M(q(1), q(2), :) = reshape(p, [1 1 2]);
    end
  end

  push(start, [0 0]);

  d = [0 1; 0 -1; 1 0; -1 0];

  %% Run BFS
  while back <= front
    p = Q(back, :);
    back = back + 1;
    for i = 1:4
      push(p, d(i, :));
    end
  end

  %% Extracting path
  path = finish;
  while true
    q = path(end, :);
    p = reshape(M(q(1), q(2), :), 1, 2);
    path(end + 1, :) = p;
    if isequal(p, start) 
      break;
    end
  end
end

Jest to naprawdę bardzo proste i standardowe, nie powinno być trudności z implementacją tego w Python czy cokolwiek innego.

A oto odpowiedź:

Tutaj wpisz opis obrazka

 219
Author: Mikhail,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-11-11 11:43:03

To rozwiązanie jest napisane w Pythonie. Dzięki Mikhail za wskazówki dotyczące przygotowania obrazu.

Animowana Szerokość - pierwsze wyszukiwanie:

Animowana wersja BFS

Ukończony Labirynt:

Ukończony Labirynt

#!/usr/bin/env python

import sys

from Queue import Queue
from PIL import Image

start = (400,984)
end = (398,25)

def iswhite(value):
    if value == (255,255,255):
        return True

def getadjacent(n):
    x,y = n
    return [(x-1,y),(x,y-1),(x+1,y),(x,y+1)]

def BFS(start, end, pixels):

    queue = Queue()
    queue.put([start]) # Wrapping the start tuple in a list

    while not queue.empty():

        path = queue.get() 
        pixel = path[-1]

        if pixel == end:
            return path

        for adjacent in getadjacent(pixel):
            x,y = adjacent
            if iswhite(pixels[x,y]):
                pixels[x,y] = (127,127,127) # see note
                new_path = list(path)
                new_path.append(adjacent)
                queue.put(new_path)

    print "Queue has been exhausted. No answer was found."


if __name__ == '__main__':

    # invoke: python mazesolver.py <mazefile> <outputfile>[.jpg|.png|etc.]
    base_img = Image.open(sys.argv[1])
    base_pixels = base_img.load()

    path = BFS(start, end, base_pixels)

    path_img = Image.open(sys.argv[1])
    path_pixels = path_img.load()

    for position in path:
        x,y = position
        path_pixels[x,y] = (255,0,0) # red

    path_img.save(sys.argv[2])

Uwaga: oznacza biały piksel szary. Usuwa to potrzebę odwiedzanej listy, ale wymaga to drugiego załadowania pliku obrazu z dysku przed narysowaniem ścieżki (jeśli nie chcesz mieć obrazu kompozytowego ścieżki końcowej i wszystkich ścieżek taken).

Pusta wersja Labiryntu, którego użyłem.

 148
Author: Joseph Kern,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-07-27 08:23:19

Próbowałem sobie zaimplementować A-Star search dla tego problemu. Po implementacji przez Josepha Kerna dla frameworka i pseudokodu algorytmu podanego TUTAJ :

def AStar(start, goal, neighbor_nodes, distance, cost_estimate):
    def reconstruct_path(came_from, current_node):
        path = []
        while current_node is not None:
            path.append(current_node)
            current_node = came_from[current_node]
        return list(reversed(path))

    g_score = {start: 0}
    f_score = {start: g_score[start] + cost_estimate(start, goal)}
    openset = {start}
    closedset = set()
    came_from = {start: None}

    while openset:
        current = min(openset, key=lambda x: f_score[x])
        if current == goal:
            return reconstruct_path(came_from, goal)
        openset.remove(current)
        closedset.add(current)
        for neighbor in neighbor_nodes(current):
            if neighbor in closedset:
                continue
            if neighbor not in openset:
                openset.add(neighbor)
            tentative_g_score = g_score[current] + distance(current, neighbor)
            if tentative_g_score >= g_score.get(neighbor, float('inf')):
                continue
            came_from[neighbor] = current
            g_score[neighbor] = tentative_g_score
            f_score[neighbor] = tentative_g_score + cost_estimate(neighbor, goal)
    return []

Ponieważ gwiazda jest heurystycznym algorytmem wyszukiwania, musisz wymyślić funkcję, która oszacowuje pozostały koszt (tutaj: odległość), aż do osiągnięcia celu. Jeśli nie czujesz się komfortowo z nieoptymalnym rozwiązaniem, nie powinieneś przeceniać kosztów. Konserwatywnym wyborem byłoby odległość Manhattanu (lub taksówki) , ponieważ reprezentuje to odległość w linii prostej między dwoma punktami na siatce dla używanego sąsiedztwa Von Neumanna. (Co w tym przypadku nigdy nie przeceniałoby kosztów.)

To jednak znacznie zaniżyłoby rzeczywisty koszt danego labiryntu. Dlatego dodałem dwie inne metryki odległości do kwadratu odległość euklidesową i odległość Manhattanu pomnożoną przez cztery dla porównania. Mogą one jednak przecenić rzeczywiste koszty, a zatem może przynieść nieoptymalne wyniki.

Oto kod:

import sys
from PIL import Image

def is_blocked(p):
    x,y = p
    pixel = path_pixels[x,y]
    if any(c < 225 for c in pixel):
        return True
def von_neumann_neighbors(p):
    x, y = p
    neighbors = [(x-1, y), (x, y-1), (x+1, y), (x, y+1)]
    return [p for p in neighbors if not is_blocked(p)]
def manhattan(p1, p2):
    return abs(p1[0]-p2[0]) + abs(p1[1]-p2[1])
def squared_euclidean(p1, p2):
    return (p1[0]-p2[0])**2 + (p1[1]-p2[1])**2

start = (400, 984)
goal = (398, 25)

# invoke: python mazesolver.py <mazefile> <outputfile>[.jpg|.png|etc.]

path_img = Image.open(sys.argv[1])
path_pixels = path_img.load()

distance = manhattan
heuristic = manhattan

path = AStar(start, goal, von_neumann_neighbors, distance, heuristic)

for position in path:
    x,y = position
    path_pixels[x,y] = (255,0,0) # red

path_img.save(sys.argv[2])

Oto kilka zdjęć do wizualizacji wyników (zainspirowanych tym opublikowanym przez Josepha Kerna ). Animacje pokazują nową klatkę po 10000 iteracji głównej pętli while.

Szerokość-Pierwsze Wyszukiwanie:

Szerokość-Pierwsze Wyszukiwanie

A-Gwiazdka Odległość:

A-Star Manhattan Distance

A-Gwiazda Kwadratowa Odległości Euklidesowej:

A-Gwiazda Kwadratowa Odległości Euklidesowej

A-Star Manhattan Odległość pomnożona przez cztery:

A-odległość gwiazdowa pomnożona przez cztery

Wyniki pokazują, że badane regiony labiryntu różnią się znacznie pod względem heurystyki. Jako taka kwadratowa odległość euklidesowa tworzy nawet inną (nieoptymalną) ścieżkę, jak inne metryki.

Jeśli chodzi o wydajność algorytmu A-Star pod względem czasu trwania do zakończenia, zauważ, że wiele funkcji oceny odległości i kosztów sumuje się w porównaniu z wyszukiwaniem Szerokościowym (BFS), które wymaga tylko aby ocenić "celowość" każdego stanowiska kandydata. To, czy koszt tych dodatkowych ocen funkcji (a-Star) przewyższa koszt większej liczby węzłów do sprawdzenia (BFS), a zwłaszcza to, czy wydajność jest problemem dla Twojej aplikacji, jest kwestią indywidualnego postrzegania i oczywiście nie można ogólnie odpowiedzieć.

Rzecz, którą można powiedzieć ogólnie o tym, czy świadomy algorytm wyszukiwania (taki jak A-Star) może być lepszy wybór w porównaniu do wyczerpującego wyszukiwania (NP BFS) jest następujący. Wraz z liczbą wymiarów labiryntu, tj. czynnikiem rozgałęzienia drzewa wyszukiwania, wada wyczerpującego wyszukiwania (wyszukiwania wyczerpującego) rośnie wykładniczo. Wraz z rosnącą złożonością staje się to coraz mniej wykonalne i w pewnym momencie jesteś prawie zadowolony z dowolnej ścieżki wyniku, czy to (w przybliżeniu) optymalnej, czy nie.

 73
Author: moooeeeep,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-12-08 20:06:00

/ Align = "left" / Labirynt jest z natury rozdzielny wzdłuż ścieżki (ścieżek) rozwiązania.

(podziękowania dla rainman002 od Reddita za wskazanie mi tego.)

Dzięki temu możesz szybko użyć połączonych komponentów do identyfikacji połączonych odcinków ściany labiryntu. To iteruje na pikselach dwa razy.

Jeśli chcesz przekształcić to w ładny diagram ścieżki (ścieżek) rozwiązania, możesz użyć operacji binarnych z elementami strukturyzującymi do wypełnienia "ślepy zaułek" ścieżki dla każdego połączonego regionu.

Kod Demo dla MATLAB następuje. Może użyć ulepszenia, aby lepiej wyczyścić wynik, uczynić go bardziej uogólnialnym i przyspieszyć jego działanie. (Czasami, gdy nie jest 2: 30 rano.)

% read in and invert the image
im = 255 - imread('maze.jpg');

% sharpen it to address small fuzzy channels
% threshold to binary 15%
% run connected components
result = bwlabel(im2bw(imfilter(im,fspecial('unsharp')),0.15));

% purge small components (e.g. letters)
for i = 1:max(reshape(result,1,1002*800))
    [count,~] = size(find(result==i));
    if count < 500
        result(result==i) = 0;
    end
end

% close dead-end channels
closed = zeros(1002,800);
for i = 1:max(reshape(result,1,1002*800))
    k = zeros(1002,800);
    k(result==i) = 1; k = imclose(k,strel('square',8));
    closed(k==1) = i;
end

% do output
out = 255 - im;
for x = 1:1002
    for y = 1:800
        if closed(x,y) == 0
            out(x,y,:) = 0;
        end
    end
end
imshow(out);

wynik bieżącego kodu

 32
Author: Jim Gray,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-10-24 09:31:58

Używa kolejki do ciągłego wypełnienia progu. Przesuwa piksel po lewej stronie wejścia na kolejkę, a następnie uruchamia pętlę. Jeśli piksel w kolejce jest wystarczająco ciemny, ma kolor jasnoszary (powyżej progu), a wszyscy sąsiedzi są popychani do kolejki.

from PIL import Image
img = Image.open("/tmp/in.jpg")
(w,h) = img.size
scan = [(394,23)]
while(len(scan) > 0):
    (i,j) = scan.pop()
    (r,g,b) = img.getpixel((i,j))
    if(r*g*b < 9000000):
        img.putpixel((i,j),(210,210,210))
        for x in [i-1,i,i+1]:
            for y in [j-1,j,j+1]:
                scan.append((x,y))
img.save("/tmp/out.png")

Rozwiązaniem jest korytarz pomiędzy szarą ścianą a kolorową ścianą. Uwaga ten labirynt ma wiele rozwiązań. Ponadto, to tylko wydaje się działać.

Rozwiązanie

 21
Author: kylefinn,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-10-24 02:41:16

Proszę bardzo: maze-solver-python (GitHub)

Tutaj wpisz opis obrazka

Bawiłem się tym i rozszerzałem naodpowiedź Josepha Kerna . Nie umniejszając tego; zrobiłem tylko kilka drobnych dodatków dla każdego, kto może być zainteresowany pobawieniem się tym.

Jest to rozwiązywacz Pythona, który używa BFS, aby znaleźć najkrótszą ścieżkę. Moje główne dodatki, w tym czasie, to:

  1. obraz jest czyszczony przed wyszukiwaniem (np. Konwertuj na czystą czerń & biały)
  2. automatycznie generuje GIF.
  3. automatycznie generuje AVI.

W obecnym stanie punkty początkowe/końcowe są mocno zakodowane dla tego przykładowego labiryntu, ale planuję go rozszerzyć tak, aby można było wybrać odpowiednie piksele.

 20
Author: stefano,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:47:22

Wybrałbym opcję matrix-of-bools. Jeśli okaże się, że standardowe listy Pythona są na to zbyt nieefektywne, możesz użyć tablicy numpy.bool. Miejsce na labirynt 1000x1000 pikseli to wtedy tylko 1 MB.

Nie trudź się tworzeniem żadnych struktur danych drzewa lub wykresu. To tylko sposób myślenia o tym, ale niekoniecznie dobry sposób na reprezentowanie go w pamięci; macierz logiczna jest zarówno łatwiejsza w kodowaniu, jak i bardziej wydajna.

Następnie użyj algorytmu a*, aby go rozwiązać. Na heurystyka odległości, użyj odległości Manhattanu (distance_x + distance_y).

Reprezentuj węzły przez krotkę współrzędnych (row, column). Ilekroć algorytm (Pseudokod Wikipedii ) wzywa do "sąsiadów", jest to prosta sprawa zapętlenia czterech możliwych sąsiadów (uwaga na krawędzie obrazu!).

Jeśli okaże się, że nadal jest zbyt wolny, Możesz spróbować zmniejszyć skalowanie obrazu przed załadowaniem go. Uważaj, aby nie stracić żadnych wąskich ścieżek w procesie.

Może da się zrobić 1: 2 downscaling w Pythonie, jak również, sprawdzanie, czy nie rzeczywiście stracić żadnych możliwych ścieżek. Ciekawa opcja, ale wymaga trochę więcej przemyśleń.

 5
Author: Thomas,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-10-21 07:17:42

Oto kilka pomysłów.

(1. Obróbka Zdjęć:)

1.1 Załaduj obraz jako RGB Mapa pikseli. W C # jest trywialne użycie system.drawing.bitmap. W językach bez prostego wsparcia dla obrazowania, wystarczy przekonwertować obraz do portable pixmap format (PPM) (Unix tekst reprezentacji, tworzy duże pliki) lub jakiś prosty binarny format pliku można łatwo odczytać, takie jak BMP lub TGA. ImageMagick in Unix or IrfanView in Okna.

1.2 Możesz, jak wspomniano wcześniej, uprościć dane, przyjmując (R + G + B) / 3 dla każdego piksela jako wskaźnik odcienia szarości, a następnie próg wartości, aby utworzyć czarno-białą tabelę. Coś blisko 200 zakładając, że 0 = czarny i 255=biały usunie artefakty JPEG.

(2. Rozwiązania:)

2.1 głębia - pierwsze wyszukiwanie: Init pusty stos z miejscem startu, Zbierz dostępne kolejne ruchy, wybierz jeden losowo i wciśnij na stos, Kontynuuj do końca / align = "left" / W deadend backtrack klikając stos, musisz śledzić, które pozycje były odwiedzane na mapie, więc kiedy zbierasz dostępne ruchy, nigdy nie obierasz tej samej ścieżki dwa razy. Bardzo interesujące animować.

2.2 Szerokość - pierwsze wyszukiwanie: wspomniane wcześniej, podobne jak wyżej, ale tylko za pomocą kolejek. Również interesujące do animacji. Działa to jak flood-fill w oprogramowaniu do edycji obrazów. Myślę, że możesz być w stanie rozwiązać labirynt w Photoshopie za pomocą tej sztuczki.

2.3 Wall Follower: geometrycznie rzecz biorąc, labirynt jest złożoną / zwiniętą rurą. Jeśli trzymasz rękę na ścianie, w końcu znajdziesz wyjście ;) to nie zawsze działa. Istnieją pewne założenia re: labirynty doskonałe, itp. na przykład niektóre labirynty zawierają Wyspy. To fascynujące.

(3. Komentarze:)

To jest ten podstępny. Łatwo jest rozwiązać labirynty, jeśli są reprezentowane w jakiejś prostej tablicy formalnej, a każdy element jest typem komórki z północą, Wschodem, południem i ściany zachodnie oraz pole flagowe. Jednak biorąc pod uwagę, że próbujesz to zrobić, biorąc pod uwagę ręcznie narysowany szkic, staje się bałagan. Szczerze myślę, że próba racjonalizacji skeczu doprowadzi cię do szału. Jest to podobne do problemów z widzeniem komputerowym, które są dość zaangażowane. Być może przejście bezpośrednio na mapę obrazu może być łatwiejsze, ale bardziej marnotrawne.
 5
Author: lino,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-11-22 05:00:26

Oto rozwiązanie za pomocą R.

### download the image, read it into R, converting to something we can play with...
library(jpeg)
url <- "https://i.stack.imgur.com/TqKCM.jpg"
download.file(url, "./maze.jpg", mode = "wb")
jpg <- readJPEG("./maze.jpg")

### reshape array into data.frame
library(reshape2)
img3 <- melt(jpg, varnames = c("y","x","rgb"))
img3$rgb <- as.character(factor(img3$rgb, levels = c(1,2,3), labels=c("r","g","b")))

## split out rgb values into separate columns
img3 <- dcast(img3, x + y ~ rgb)

RGB do skali szarości, patrz: https://stackoverflow.com/a/27491947/2371031

# convert rgb to greyscale (0, 1)
img3$v <- img3$r*.21 + img3$g*.72 + img3$b*.07
# v: values closer to 1 are white, closer to 0 are black

## strategically fill in some border pixels so the solver doesn't "go around":
img3$v2 <- img3$v
img3[(img3$x == 300 | img3$x == 500) & (img3$y %in% c(0:23,988:1002)),"v2"]  = 0

# define some start/end point coordinates
pts_df <- data.frame(x = c(398, 399),
                     y = c(985, 26))

# set a reference value as the mean of the start and end point greyscale "v"s
ref_val <- mean(c(subset(img3, x==pts_df[1,1] & y==pts_df[1,2])$v,
                  subset(img3, x==pts_df[2,1] & y==pts_df[2,2])$v))

library(sp)
library(gdistance)
spdf3 <- SpatialPixelsDataFrame(points = img3[c("x","y")], data = img3["v2"])
r3 <- rasterFromXYZ(spdf3)

# transition layer defines a "conductance" function between any two points, and the number of connections (4 = Manhatten distances)
# x in the function represents the greyscale values ("v2") of two adjacent points (pixels), i.e., = (x1$v2, x2$v2)
# make function(x) encourages transitions between cells with small changes in greyscale compared to the reference values, such that: 
# when v2 is closer to 0 (black) = poor conductance
# when v2 is closer to 1 (white) = good conductance
tl3 <- transition(r3, function(x) (1/max( abs( (x/ref_val)-1 ) )^2)-1, 4) 

## get the shortest path between start, end points
sPath3 <- shortestPath(tl3, as.numeric(pts_df[1,]), as.numeric(pts_df[2,]), output = "SpatialLines")

## fortify for ggplot
sldf3 <- fortify(SpatialLinesDataFrame(sPath3, data = data.frame(ID = 1)))

# plot the image greyscale with start/end points (red) and shortest path (green)
ggplot(img3) +
  geom_raster(aes(x, y, fill=v2)) +
  scale_fill_continuous(high="white", low="black") +
  scale_y_reverse() +
  geom_point(data=pts_df, aes(x, y), color="red") +
  geom_path(data=sldf3, aes(x=long, y=lat), color="green")
Voila!

rozwiązanie, które poprawnie odnajduje najkrótszą ścieżkę

Tak się dzieje, jeśli nie wypełnisz niektórych pikseli ramek (Ha!)...

wersja rozwiązania, w której rozwiązujący porusza się po labiryncie

Pełne ujawnienie: zadałem i odpowiedziałem na bardzo {23]} podobne pytanie sam, zanim znalazłem to. Następnie dzięki magii tak, znalazłem ten jako jeden z najlepszych "powiązanych pytań". Pomyślałem, że wykorzystam ten labirynt jako dodatkowy test... Byłem bardzo zadowolony, że moja odpowiedź działa również w tej aplikacji z bardzo małą modyfikacją.

 0
Author: Brian D,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-10-04 21:06:00