Bir görüntü verilen bir labirenti temsil etme ve çözme


271

Bir görüntü verilen bir labirenti temsil etmenin ve çözmenin en iyi yolu nedir?

The Scope Issue 134 kapak resmi

Bir JPEG görüntüsü verildiğinde (yukarıda görüldüğü gibi), onu okumanın, bazı veri yapısına ayrıştırmanın ve labirenti çözmenin en iyi yolu nedir? İlk içgüdüm görüntüyü piksel piksel okumak ve boole değerlerinin bir listesinde (dizi) saklamaktır: Truebeyaz bir piksel için ve beyaz Falseolmayan bir piksel için (renkler atılabilir). Bu yöntemin sorunu, görüntünün "piksel mükemmel" olmayabilir. Bununla demek istediğim, eğer bir duvarda bir yerde beyaz bir piksel varsa, istenmeyen bir yol oluşturabilir.

Başka bir yöntem (biraz düşünmeden sonra bana geldi), görüntüyü bir tuval üzerine çizilen yolların bir listesi olan bir SVG dosyasına dönüştürmektir. Bu şekilde, yollar aynı yol listesine (boole değerleri) okunabilir, burada Truebir yol veya duvarı gösterir, Falsebu da seyahat edebilen bir alanı gösterir. Dönüşüm% 100 doğru değilse ve tüm duvarları tam olarak bağlamaz ve boşluklar oluşturuyorsa, bu yöntemle ilgili bir sorun ortaya çıkar.

Ayrıca SVG'ye dönüştürmeyle ilgili bir sorun, çizgilerin "mükemmel" düz olmamasıdır. Bu, yolların kübik bezier eğrileri olmasına neden olur. Tamsayılar tarafından endekslenen boole değerlerinin bir listesi (dizi) ile, eğriler kolayca aktarılmaz ve eğri üzerindeki çizginin tüm noktalarının hesaplanması gerekir, ancak liste indeksleriyle tam olarak eşleşmez.

Bu yöntemlerden birinin işe yarayabileceğini (muhtemelen olmasa da) böylesine büyük bir görüntü göz önüne alındığında acımasızca verimsiz olduklarını ve daha iyi bir yol olduğunu varsayıyorum. Bu en iyi nasıl (en verimli ve / veya en az karmaşıklıkla) yapılır? En iyi yol var mı?

Sonra labirent çözme geliyor. İlk iki yöntemden birini kullanırsam, aslında bir matrisle sonuçlanacağım. Bu cevaba göre , bir labirenti temsil etmenin iyi bir yolu bir ağaç kullanmaktır ve onu çözmek için iyi bir yol A * algoritması kullanmaktır . Resimden nasıl bir ağaç oluşturulur? Herhangi bir fikir?

TL; DR
Ayrıştırmanın en iyi yolu? Hangi veri yapısına? Söz konusu yapı çözüme nasıl yardımcı olur / engel olur?

GÜNCELLEME
@Mikhail'in Python'da yazdıklarını uygulamada @ numpyThomas'ın önerdiği gibi kullanarak elimi denedim. Algoritmanın doğru olduğunu hissediyorum, ancak umulduğu gibi çalışmıyor. (Aşağıdaki kod.) PNG kütüphanesi PyPNG'dir .

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, [])

12
Labirenti siyah beyaza dönüştürür ve çözmek için hücresel otomat yöntemini kullanırdım.
Dan D.

Sadece bu görüntüyle mi, yoksa bunun gibi birçok görüntüyle mi ilgilenmeniz gerekiyor? Yani bu belirli görüntü için belirli bir manuel işleme seçeneği var mı?
Mikhail

1
@Whymarrh Python kodlamıyorum, ama eminim visited.append(s)bir altına taşınmalı for.ifve onunla değiştirmelisiniz visited.append(np). Bir tepe noktası kuyruğa eklendiğinde ziyaret edilir. Aslında, bu dizi "sıraya alınmış" olarak adlandırılmalıdır. Bitişe ulaştıktan sonra BFS'yi de sonlandırabilirsiniz.
Mikhail

2
@Whymarrh Ayrıca yol çıkarma bloğunu uygularken de atlamış gibisiniz. Onsuz, sadece finişin ulaşılabilir olup olmadığını öğrenebilir, ancak nasıl yapılacağını öğrenemezsiniz.
Mikhail

1
Orada öğrenmek için olan bir çözüm, bir UnionFind ve Lineer Tarama hızlı algoritmadır. Size yolu vermez, ancak bir alt küme olarak yola sahip olacak bir karo seti verir.
st0le

Yanıtlar:


236

İşte bir çözüm.

  1. Görüntüyü gri tonlamaya dönüştürün (henüz ikili değil), renkler için ağırlıkları ayarlayarak son gri tonlamalı görüntünün yaklaşık olarak eşit olmasını sağlayın. Bunu Photoshop'ta Görüntü -> Ayarlamalar -> Siyah Beyaz öğesindeki kaydırıcıları kontrol ederek yapabilirsiniz.
  2. Görüntü -> Ayarlamalar -> Eşik'te Photoshop'ta uygun eşiği ayarlayarak görüntüyü ikilik biçime dönüştürün.
  3. Eşiğin doğru seçildiğinden emin olun. Sihirli Değnek Aracı'nı 0 tolerans, nokta örneği, bitişik, kenar yumuşatma olmadan kullanın. Seçimin sonlandığı kenarların yanlış eşikle yanlış kenarlar olmadığını kontrol edin. Aslında, bu labirentin tüm iç noktalarına başlangıçtan itibaren erişilebilir.
  4. Sanal gezginin etrafta dolaşmayacağından emin olmak için labirente yapay kenarlıklar ekleyin :)
  5. Uygulamak genişliği birinci arama favori dilinde (BFS) ve baştan çalıştırın. Bu görev için MATLAB'ı tercih ederim . @Thomas'ın daha önce de belirtildiği gibi, grafiklerin düzenli gösterimi ile uğraşmanıza gerek yoktur. Doğrudan ikili görüntü ile çalışabilirsiniz.

İşte BFS için MATLAB kodu:

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

Gerçekten çok basit ve standarttır, bunu Python'da veya başka bir şekilde uygulamada zorluk olmamalıdır .

Ve işte cevap:

Resim açıklamasını buraya girin


1
@Whymarrh "Sadece bu görüntü" için artık bir cevabınız var. Özel sorularınız mı var? Listemdeki 1-4. Öğeler hakkında sorduğum manuel işlem. Öğe 5, grafikler için çok temel bir algoritma olan bir BFS'dir, ancak pikselleri köşelere ve komşuları kenarlara dönüştürmeden doğrudan görüntüye uygulanabilir.
Mikhail

Her şeyi kapsadığını hissediyorum. (Daha önce bir kez kodladığım için, BFS yerine DFS kullanarak) Python'da söylediklerinizi uygulamaya çalışıyorum. Soruyu güncellemeye / cevapları biraz kabul etmeye geri döneceğim.
Whymarrh

2
@Bmarmar DFS sizi en kısa yolla bulamayacakken, BFS bulacaktır. Doğal olarak aynıdırlar, tek fark altta yatan yapıdır. DFS için yığın (FILO) ve BFS için sıra (FIFO).
Mikhail

3
BFS burada doğru seçimdir, çünkü en kısa yol üretir, bu da koridorlar 1 pikselden çok daha geniş olsa bile "mantıklı" bir yol verir. OTOH DFS koridorları ve ödün vermeyen labirent bölgelerini "taşkın dolgu" deseni ile keşfetme eğiliminde olacaktır.
j_random_hacker

1
@JosephKern Path herhangi bir duvarla çakışmaz. Tüm kırmızı pikselleri kaldırmanız yeterlidir.
Mikhail

160

Bu çözüm Python ile yazılmıştır. Görüntü hazırlama konusundaki göstergeler için Mikhail'e teşekkür ederiz.

Animasyonlu Genişlik-İlk Arama:

BFS'nin animasyonlu sürümü

Tamamlanan Labirent:

Tamamlanan Labirent

#!/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])

Not: Ziyaret edilen beyaz piksel grisini işaretler. Bu, ziyaret edilen bir listeye olan ihtiyacı ortadan kaldırır, ancak bu, bir yol çizmeden önce görüntü dosyasının diskten ikinci bir yüklenmesini gerektirir (son yolun ve TÜM yolların bileşik bir görüntüsünü istemiyorsanız).

Kullandığım labirentin boş versiyonu.


13
Sorunuz cevaplandıktan sonra bile geri dönüp beni oylayacak kadar harika olduğunuz için, süreci daha iyi görselleştirmek için BFS'nin hareketli bir gifini oluşturdum.
Joseph Kern

1
Güzel olan, teşekkürler. Benim gibi oynamak isteyenler için, yaşadığım zorluklara dayanarak ipuçlarını paylaşmak istiyorum. 1) Ya görüntüyü saf siyah beyaza dönüştürün ya da 'isWhite ()' işlevinizi neredeyse beyaz | siyahı kabul edecek şekilde değiştirin. Onları saf beyaz veya siyaha dönüştüren tüm pikselleri önceden işleyen bir 'cleanImage' yöntemi yazdım, aksi takdirde algoritma bir yol bulamaz. 2) Görüntüyü açıkça RGB olarak okuyun [base_img = Image.open (img_in); base_img = base_img.convert ('RGB')]. Bir gif almak için, birkaç görüntü çıktısı alın ve 'convert -delay 5 -loop 1 * .jpg bfs.gif' komutunu çalıştırın.
stefano

1
satır 13 eksik girinti
sloewen

81

Kendimi bu problem için A-Star araması yapmaya çalıştım. Burada verilen çerçeve ve algoritma sözde kodu için Joseph Kern'in uygulamasını yakından takip etti :

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 []

A-Star sezgisel bir arama algoritması olduğundan, hedefe ulaşılana kadar kalan maliyeti (burada: mesafe) tahmin eden bir işlev bulmanız gerekir. Yetersiz bir çözümden memnun değilseniz, maliyeti abartmamak gerekir. Burada muhafazakar bir seçim manhattan (veya taksikab) mesafesi olacaktır, çünkü bu kullanılan Von Neumann mahallesi için ızgaradaki iki nokta arasındaki düz çizgi mesafesini temsil eder. (Bu durumda, maliyeti hiç abartmaz.)

Ancak bu, eldeki labirent için gerçek maliyeti önemli ölçüde küçümseyecektir. Bu nedenle iki öteki metrik karesini öklid karesine ekledim ve manhattan mesafesinin karşılaştırma için dört ile çarpıldığını. Ancak bunlar gerçek maliyeti fazla tahmin edebilir ve bu nedenle de yetersiz sonuçlar verebilir.

İşte 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])

Sonuçların görselleştirilmesi için bazı görüntüler ( Joseph Kern tarafından yayınlananlardan esinlenilmiştir ). Animasyonlar, ana while döngüsünün 10000 yinelemesinden sonra her biri yeni bir kare gösterir.

Genişlik İlk Arama:

Genişlik İlk Arama

A-Star Manhattan Uzaklık:

A-Star Manhattan Uzaklık

A-Yıldız Kare Öklid Mesafesi:

A-Yıldız Kare Öklid Mesafesi

A-Star Manhattan Mesafesi dört ile çarpılır:

A-Star Manhattan Mesafe dört ile çarpıldı

Sonuçlar, labirentin keşfedilen bölgelerinin kullanılan buluşsal yöntemler için önemli ölçüde farklı olduğunu göstermektedir. Böylece, kareli öklid mesafesi diğer metriklerden farklı bir (yetersiz) yol üretir.

A-Star algoritmasının sonlandırmaya kadar olan çalışma süresi açısından performansıyla ilgili olarak, yalnızca mesafe ve maliyet işlevlerinin değerlendirilmesinin, yalnızca her aday pozisyonu. Bu ek işlev değerlendirmelerinin (A-Star) maliyetinin, daha fazla sayıda düğümün (BFS) kontrol etme maliyetinden daha ağır olup olmadığı ve özellikle performansın uygulamanız için bir sorun olup olmadığı bireysel bir meseledir. ve elbette genel olarak cevaplanamaz.

Bir şey olabilir olsun veya olmasın (örneğin A-Star gibi) bilinçli bir arama algoritması ayrıntılı bir ara (örneğin BFS) kıyasla daha iyi bir seçim olabilir hakkında genel olarak söylenebilir şudur. Labirentin boyut sayısı, yani, arama ağacının dallanma faktörü ile, kapsamlı bir aramanın (kapsamlı olarak arama yapmak) dezavantajı katlanarak büyür. Artan karmaşıklık ile bunu yapmak gittikçe daha az uygulanabilir hale gelir ve bir noktada (yaklaşık) optimal olsun veya olmasın, herhangi bir sonuç yolundan oldukça mutlu olursunuz .


1
"A-Star Manhattan Mesafe dört ile çarpıldı"? Buluşsal yöntem mesafeyi abartırsa A-Star A-Star değildir. (Ve böylece en kısa yolu da bulmayı garanti etmiyoruz)
örnek

@example Elbette, eğer kabul edilemez bir sezgisel işlev uygularsa, algoritma en uygun çözümü bulamayabilir (cevabımda belirttiğim gibi). Ama bu nedenle temel algoritmayı yeniden adlandıracak kadar ileri gitmem.
moooeeeep

38

Ağaç araması çok fazla. Labirent doğal olarak çözüm yolları boyunca ayrılabilir.

( Bunu bana işaret ettiği için Reddit'ten rainman002'ye teşekkürler .)

Bu nedenle, labirent duvarının bağlı bölümlerini tanımlamak için bağlı bileşenleri hızlı bir şekilde kullanabilirsiniz . Bu piksel üzerinde iki kez yinelenir.

Bunu çözüm yollarının güzel bir diyagramına dönüştürmek istiyorsanız, bağlı her bölge için "çıkmaz sokak" yollarını doldurmak için yapılandırma elemanları ile ikili işlemleri kullanabilirsiniz.

MATLAB için demo kodu aşağıdadır. Sonucu daha iyi temizlemek, daha genelleştirilebilir hale getirmek ve daha hızlı çalışmasını sağlamak için ince ayar yapabilir. (Bazen 02:30 değil.)

% 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);

mevcut kodun sonucu


24

Bir eşik sürekli dolgusu için kuyruk kullanır. Girişin solundaki pikseli kuyruğa iter ve sonra döngüyü başlatır. Kuyruğa alınmış bir piksel yeterince karanlıksa, açık gri renktedir (eşiğin üstünde) ve tüm komşular sıraya itilir.

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")

Çözüm gri duvar ve renkli duvar arasındaki koridordur. Bu labirentin birden fazla çözümü olduğunu unutmayın. Ayrıca, bu sadece işe yarıyor gibi görünüyor.

Çözüm


1
Elde duvar yöntemine dayanan ilginç saf çözünürlük. Gerçekten, en iyisi değil, ama hoşuma gitti.
zessx

23

Buyrun: Labirent-Çözücü-Python (GitHub)

resim açıklamasını buraya girin

Bununla oynamaktan çok eğlendim ve Joseph Kern'in cevabını uzattım . Ondan uzaklaşmamak; Ben sadece bununla oynamak isteyen herkes için küçük eklemeler yaptım.

En kısa yolu bulmak için BFS kullanan python tabanlı bir çözücüdür. O zaman ana eklemelerim:

  1. Görüntü aramadan önce temizlenir (yani saf siyah beyaza dönüştür)
  2. Otomatik olarak bir GIF oluşturun.
  3. Otomatik olarak bir AVI oluşturun.

Durduğu gibi, başlangıç ​​/ bitiş noktaları bu örnek labirent için sabit kodlanmıştır, ancak uygun pikselleri seçebileceğiniz şekilde genişletmeyi planlıyorum.


1
Harika, teşekkürler, BSD / Darwin / Mac üzerinde çalışmadı, Mac'te denemek isteyenler için bazı bağımlılıklar ve kabuk komut dosyası küçük değişikliklere ihtiyaç duydu: [maze-solver-python]: github.com/holg/maze- çözücü-python
HolgT

@HolgT: Yararlı bulduğunuza sevindim. Bunun için herhangi bir çekme isteği hoş geldiniz. :)
stefano

5

Matris-of-bools seçeneğine giderdim. Standart Python listelerinin bunun için çok verimsiz olduğunu görürseniz, numpy.boolbunun yerine bir dizi kullanabilirsiniz . 1000x1000 piksel labirent için depolama alanı sadece 1 MB'dir.

Herhangi bir ağaç veya grafik veri yapısı oluşturmakla uğraşmayın. Bu sadece bunu düşünmenin bir yolu, ama onu hafızada temsil etmenin iyi bir yolu değil; bir boole matrisinin kodlanması daha kolay ve daha verimlidir.

Ardından çözmek için A * algoritmasını kullanın. Sezgisel mesafe için Manhattan mesafesini ( distance_x + distance_y) kullanın.

Düğümleri bir dizi (row, column)koordinatla temsil edin. Algoritma ( Wikipedia sözde kodu ) ne zaman "komşular" ı çağırdığında, bu dört olası komşu üzerinde döngü yapmak için basit bir konudur (görüntünün kenarlarına dikkat edin!).

Hala çok yavaş olduğunu fark ederseniz, yüklemeden önce görüntüyü küçültmeyi deneyebilirsiniz. Süreçteki herhangi bir dar yolu kaybetmemeye dikkat edin.

Belki de Python'da 1: 2 ölçek küçültme yapmak ve olası yolları kaybetmediğinizi kontrol etmek mümkündür. İlginç bir seçenek, ama biraz daha düşünülmesi gerekiyor.


Bu mükemmel blog yazısı , mathematica'da bir labirentin nasıl çözüleceğini gösterir. Yöntemi
python'a çevirmek

Soruyu güncelledim. RGB üçlü booleandeğerlerini değerler yerine kullanmayı seçersem , depolama alanı hala karşılaştırılabilir mi? Matris daha sonra 2400 * 1200 olur. Ve BFS üzerindeki A * 'nın gerçek çalışma süresi üzerinde önemli bir etkisi olur mu?
Whymarrh

@Niçin, bit derinliği telafi etmek için küçülebilir. Herkes için piksel başına 2 bit yeterli olmalıdır.
Brian Cain

5

İşte bazı fikirler.

(1. Görüntü İşleme :)

1.1 Görüntüyü RGB piksel haritası olarak yükleyin . In C # bunu kullanarak önemsiz system.drawing.bitmap. Görüntüleme için basit bir desteği olmayan dillerde, görüntüyü taşınabilir pixmap biçimine (PPM) (Unix metin gösterimi, büyük dosyalar üretir) veya BMP veya TGA gibi kolayca okuyabileceğiniz basit bir ikili dosya biçimine dönüştürün . Unix'te ImageMagick veya Windows'ta IrfanView .

1.2 Daha önce de belirtildiği gibi, gri tonunun bir göstergesi olarak her piksel için (R + G + B) / 3 alarak verileri basitleştirebilir ve ardından siyah beyaz bir tablo oluşturmak için değeri eşleştirebilirsiniz. 0 = siyah ve 255 = beyaz olduğu varsayılarak 200'e yakın bir şey JPEG eserlerini çıkaracaktır.

(2. Çözümler :)

2.1 Derinlik-İlk Arama: Başlangıç ​​konumuna sahip boş bir yığın başlatın, mevcut takip hareketlerini toplayın, rastgele birini seçin ve yığın üzerine itin, sona ulaşılana veya bir ölümcül olana kadar devam edin. Deadend backtrack'ta yığını patlatarak, haritada hangi pozisyonların ziyaret edildiğini izlemeniz gerekir, böylece mevcut hareketleri topladığınızda aynı yolu asla iki kez izlemezsiniz. Animasyon yapmak çok ilginç.

2.2 En Büyük İlk Arama: Daha önce bahsedilmişti, yukarıdaki gibi benzer ancak sadece kuyruklar kullanıyor. Ayrıca canlandırmak ilginç. Bu, görüntü düzenleme yazılımında taşkın dolgu gibi çalışır. Bu hileyi kullanarak Photoshop'ta bir labirenti çözebileceğinizi düşünüyorum.

2.3 Duvar Takipçisi: Geometrik olarak labirent katlanmış / kıvrık bir tüptür. Elinizi duvarda tutarsanız, sonunda çıkışı bulacaksınız;) Bu her zaman işe yaramaz. Bazı varsayımlar vardır: mükemmel labirentler, vs., örneğin, bazı labirentler adalar içerir. Yukarı bak; büyüleyici.

(3. Yorumlar :)

Bu zor olanı. Her elementin kuzey, doğu, güney ve batı duvarları ve ziyaret edilen bir bayrak alanı olan bir hücre tipi olmasıyla, basit bir dizi biçiminde temsil edilirse labirentleri çözmek kolaydır. Bununla birlikte, elle çizilmiş bir kroki verildiğinde bunu yapmaya çalıştığınız için dağınık hale gelir. Dürüst olmak gerekirse, taslağı rasyonelleştirmeye çalışmanın sizi delirteceğini düşünüyorum. Bu, oldukça ilgili olan bilgisayar görme sorunlarına benzer. Belki de doğrudan görüntü haritasına gitmek daha kolay ve daha israflı olabilir.


2

İşte R'yi kullanan bir çözüm.

### 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'den gri tonlamaya, bkz. 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")

İşte bu kadar!

en kısa yolu doğru bulan çözüm

Bazı kenarlık piksellerini (Ha!) Doldurmazsanız bu olur ...

çözücü labirent dolaşıyor çözüm sürümü

Tam açıklama: Bunu bulmadan önce kendime çok benzer bir soru sordum ve cevapladım . Sonra SO büyüsü aracılığıyla, bu bir üst "İlgili Sorular" biri olarak bulundu. Bu labirenti ek bir test örneği olarak kullanacağımı düşündüm ... Oradaki cevabımın çok az değişiklikle bu uygulama için de işe yaradığını bulmaktan çok memnun oldum.


0

iyi çözüm, komşuları pikselle bulmak yerine, hücre tarafından yapılacaktır, çünkü bir koridor 15 piksele sahip olabilir, böylece aynı koridorda sol veya sağ gibi eylemler gerçekleştirebilir, eğer yer değiştirme gibi yapılmışsa bir küptü YUKARI, AŞAĞI, SOL VEYA SAĞ gibi basit bir eylem olurdu


Noktanızı doğrulamak için cevabın geri kalanı gibi çözüm grafiği ve algoritma ekleyebilir misiniz? Yanıtınıza daha fazla ağırlık eklemek için bunları ekleyebilmeniz daha iyi olacaktır, böylece diğerleri cevabınızı daha iyi anlayabilir.
Himanshu Bansal
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.