Mewakili dan memecahkan labirin yang diberi gambar

271

Apa cara terbaik untuk mewakili dan memecahkan labirin yang diberi gambar?

Gambar sampul The Scope Issue 134

Diberikan gambar JPEG (seperti yang terlihat di atas), apa cara terbaik untuk membacanya, parsing ke dalam beberapa struktur data dan pecahkan maze? Insting pertama saya adalah membaca gambar dalam pixel demi pixel dan menyimpannya dalam daftar (array) nilai boolean: Trueuntuk pixel putih, dan Falseuntuk piksel non-putih (warna dapat dibuang). Masalah dengan metode ini, adalah bahwa gambar mungkin tidak "pixel perfect". Maksud saya, jika ada piksel putih di suatu tempat di dinding, itu mungkin membuat jalur yang tidak diinginkan.

Metode lain (yang datang kepada saya setelah sedikit berpikir) adalah untuk mengkonversi gambar ke file SVG - yang merupakan daftar jalur yang digambar di atas kanvas. Dengan cara ini, jalur dapat dibaca ke dalam daftar yang sama (nilai boolean) di mana Truemenunjukkan jalur atau dinding,False menunjukkan ruang yang dapat bepergian. Masalah dengan metode ini muncul jika konversi tidak 100% akurat, dan tidak sepenuhnya menghubungkan semua dinding, menciptakan celah.

Juga masalah dengan mengkonversi ke SVG adalah bahwa garis tidak lurus "sempurna". Ini menghasilkan lintasan menjadi kurva kubik bezier. Dengan daftar (larik) nilai boolean yang diindeks oleh bilangan bulat, kurva tidak akan mudah ditransfer, dan semua titik yang garis pada kurva harus dihitung, tetapi tidak akan sama persis dengan daftar indeks.

Saya berasumsi bahwa sementara salah satu metode ini dapat bekerja (meskipun mungkin tidak) bahwa mereka sangat tidak efisien diberi gambar sebesar itu, dan bahwa ada cara yang lebih baik. Bagaimana ini terbaik (paling efisien dan / atau dengan kompleksitas paling sedikit) dilakukan? Apakah ada cara terbaik?

Kemudian datang pemecahan labirin. Jika saya menggunakan salah satu dari dua metode pertama, saya pada dasarnya akan berakhir dengan sebuah matriks. Menurut jawaban ini , cara yang baik untuk mewakili labirin menggunakan pohon, dan cara yang baik untuk menyelesaikannya adalah menggunakan algoritma A * . Bagaimana cara membuat pohon dari gambar? Ada ide?

TL; DR
Cara terbaik untuk menguraikan? Ke dalam struktur data apa? Bagaimana mengatakan struktur membantu / menghambat penyelesaian?

PEMBARUAN
Saya sudah mencoba menerapkan @Mikhail dengan Python, menggunakan numpy, seperti yang direkomendasikan oleh @Thomas. Saya merasa bahwa algoritme itu benar, tetapi tidak berfungsi seperti yang diharapkan. (Kode di bawah ini.) Perpustakaan PNG adalah 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, [])
Whymarrh
sumber
12
Saya akan mengubah labirin menjadi hitam dan putih dan menggunakan jalur menemukan metode automata seluler untuk menyelesaikannya.
Dan D.
Apakah Anda hanya perlu berurusan dengan gambar itu, atau dengan banyak gambar seperti itu? Yaitu apakah ada opsi beberapa pemrosesan manual khusus untuk gambar tertentu ini?
Mikhail
1
@ Whymarrh Saya tidak kode python, tapi saya cukup yakin Anda harus bergerak di visited.append(s)bawah for.ifdan menggantinya dengan visited.append(np). Sebuah vertex dikunjungi setelah ditambahkan ke antrian. Bahkan, array ini harus dinamai "antri". Anda juga dapat menghentikan BFS setelah Anda mencapai finish.
Mikhail
2
@ Whymarrh Dan Anda juga tampaknya telah melewatkan mengimplementasikan jalur pengekstrak jalur. Tanpa itu, Anda hanya bisa mengetahui apakah hasil akhirnya dapat dicapai atau tidak, tetapi tidak seberapa.
Mikhail
1
Untuk mengetahui apakah ada adalah solusi, sebuah UnionFind dan Scan Linear adalah algoritma tercepat. Itu tidak memberi Anda jalan, tetapi memberi Anda satu set ubin yang akan memiliki jalan sebagai subset.
st0le

Jawaban:

236

Ini solusinya.

  1. Konversi gambar ke skala abu-abu (belum biner), sesuaikan bobot untuk warna sehingga gambar skala abu-abu mendekati seragam. Anda dapat melakukannya hanya dengan mengendalikan slider di Photoshop di Image -> Adjustments -> Black & White.
  2. Konversi gambar ke biner dengan menetapkan ambang batas yang sesuai di Photoshop dalam Image -> Adjustments -> Threshold.
  3. Pastikan ambang batas dipilih dengan benar. Gunakan Magic Wand Tool dengan 0 toleransi, titik sampel, berdekatan, tanpa anti-aliasing. Periksa bahwa tepian di mana seleksi tidak merupakan tepi palsu yang diperkenalkan oleh ambang batas yang salah. Bahkan, semua titik interior labirin ini dapat diakses dari awal.
  4. Tambahkan batas buatan pada labirin untuk memastikan pelancong virtual tidak akan berjalan di sekitarnya :)
  5. Terapkan pencarian breadth-first (BFS) dalam bahasa favorit Anda dan jalankan dari awal. Saya lebih suka MATLAB untuk tugas ini. Seperti @Thomas telah sebutkan, tidak perlu dipusingkan dengan representasi grafik yang teratur. Anda dapat langsung bekerja dengan gambar yang sudah dibubarkan.

Berikut adalah kode MATLAB untuk 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

Ini benar-benar sangat sederhana dan standar, tidak boleh ada kesulitan dalam mengimplementasikan ini dengan Python atau apa pun.

Dan inilah jawabannya:

Masukkan deskripsi gambar di sini

Mikhail
sumber
1
@ Wahh Yah, untuk "Hanya gambar ini" Anda sekarang benar-benar memiliki jawaban. Apakah Anda memiliki pertanyaan spesifik? Item 1-4 dari daftar saya adalah pemrosesan manual yang saya tanyakan. Item 5 adalah BFS - algoritma yang sangat mendasar untuk grafik, tetapi dapat diterapkan untuk gambar secara langsung, tanpa mengubah piksel menjadi simpul dan tetangga menjadi ujung.
Mikhail
Saya merasa Anda sudah membahas semuanya. Saya mencoba untuk menerapkan apa yang Anda katakan dengan Python (menggunakan DFS sebagai pengganti BFS, hanya karena saya pernah mengkodekannya sebelumnya). Saya akan kembali untuk memperbarui pertanyaan / menerima jawaban sedikit.
Whymarrh
2
@Whyarrh DFS tidak akan menemukan Anda dengan cara terpendek, sementara BFS akan. Mereka pada dasarnya sama, satu-satunya perbedaan adalah struktur yang mendasarinya. Stack (FILO) untuk DFS dan antrian (FIFO) untuk BFS.
Mikhail
3
BFS adalah pilihan yang tepat di sini, karena menghasilkan jalur terpendek, yang memberikan jalur "masuk akal" bahkan ketika koridor jauh lebih lebar dari 1 piksel. OTOH DFS akan cenderung menjelajahi koridor dan daerah labirin yang tidak menjanjikan dengan pola "banjir".
j_random_hacker
1
@JosephKern Path tidak tumpang tindih dengan dinding apa pun. Hapus saja semua piksel merah dan ini dia.
Mikhail
160

Solusi ini ditulis dalam Python. Terima kasih Mikhail untuk petunjuk tentang persiapan gambar.

Animasi Breadth-First Search:

Versi animasi BFS

Labirin Lengkap:

Labirin Lengkap

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

Catatan: Menandai abu-abu piksel yang dikunjungi putih. Ini menghilangkan kebutuhan untuk daftar yang dikunjungi, tetapi ini membutuhkan pemuatan kedua file gambar dari disk sebelum menggambar jalur (jika Anda tidak ingin gambar komposit dari jalur akhir dan SEMUA jalur diambil).

Versi kosong dari labirin yang saya gunakan.

Joseph Kern
sumber
13
Karena Anda cukup hebat untuk kembali dan mendukung saya bahkan setelah pertanyaan Anda telah dijawab, saya membuat gif animasi BFS, untuk membantu memvisualisasikan proses dengan lebih baik.
Joseph Kern
1
Bagus, terima kasih. Untuk orang lain yang ingin bermain-main dengan ini, seperti yang saya lakukan, saya ingin berbagi tips berdasarkan kesulitan yang saya hadapi. 1) Ubah gambar menjadi murni hitam & putih atau ubah fungsi 'isWhite ()' Anda untuk menerima hampir-putih | hitam. Saya menulis metode 'cleanImage' yang memproses semua piksel yang mengonversinya menjadi putih atau hitam, jika tidak algoritma gagal menemukan jalur. 2) Baca gambar secara eksplisit sebagai RGB [base_img = Image.open (img_in); base_img = base_img.convert ('RGB')]. Untuk mendapatkan gif, hasilkan beberapa gambar lalu jalankan 'convert -delay 5 -loop 1 * .jpg bfs.gif'.
stefano
1
hilang indent pada baris 13
sloewen
81

Saya mencoba menerapkan pencarian A-Star untuk masalah ini. Diikuti dengan cermat implementasi oleh Joseph Kern untuk kerangka kerja dan algoritma pseudocode yang diberikan di sini :

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

Karena A-Star adalah algoritma pencarian heuristik, Anda perlu membuat fungsi yang memperkirakan biaya yang tersisa (di sini: jarak) hingga tujuan tercapai. Kecuali Anda merasa nyaman dengan solusi suboptimal itu tidak boleh melebih-lebihkan biayanya. Pilihan konservatif di sini adalah jarak manhattan (atau taksi) karena ini mewakili jarak garis lurus antara dua titik di grid untuk lingkungan Von Neumann yang digunakan. (Yang, dalam hal ini, tidak akan pernah melebih-lebihkan biayanya.)

Namun ini akan secara signifikan meremehkan biaya sebenarnya untuk labirin yang diberikan. Oleh karena itu saya telah menambahkan dua metrik jarak lain kuadrat jarak euclidean dan jarak manhattan dikalikan empat untuk perbandingan. Namun ini mungkin melebih-lebihkan biaya aktual, dan karenanya mungkin menghasilkan hasil yang kurang optimal.

Ini kodenya:

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

Berikut adalah beberapa gambar untuk visualisasi hasil (terinspirasi oleh yang diposting oleh Joseph Kern ). Animasi menunjukkan bingkai baru masing-masing setelah 10.000 iterasi dari while-loop utama.

Pencarian Breadth-First:

Pencarian Luas Pertama

A-Star Manhattan Jarak:

A-Star Manhattan Jarak

A-Star Squared Euclidean Jarak:

A-Star Squared Euclidean Distance

A-Star Manhattan Distance dikalikan empat:

A-Star Manhattan Distance dikalikan empat

Hasil penelitian menunjukkan bahwa daerah yang dieksplorasi dari labirin sangat berbeda untuk heuristik yang digunakan. Dengan demikian, jarak euclide kuadrat bahkan menghasilkan jalur (suboptimal) yang berbeda dengan metrik lainnya.

Mengenai kinerja algoritma A-Star dalam hal runtime sampai penghentian, perhatikan bahwa banyak evaluasi jarak dan fungsi biaya bertambah dibandingkan dengan Breadth-First Search (BFS) yang hanya perlu mengevaluasi "tujuan" dari masing-masing posisi kandidat. Apakah biaya untuk evaluasi fungsi tambahan ini (A-Star) melebihi biaya untuk jumlah node yang lebih besar untuk diperiksa (BFS) dan terutama apakah kinerja merupakan masalah bagi aplikasi Anda atau tidak, itu adalah masalah persepsi individu dan tentu saja tidak bisa dijawab secara umum.

Suatu hal yang dapat dikatakan secara umum tentang apakah atau tidak algoritma pencarian informasi (seperti A-Star) bisa menjadi pilihan yang lebih baik dibandingkan dengan pencarian yang lengkap (misalnya, BFS) adalah sebagai berikut. Dengan jumlah dimensi labirin, yaitu, faktor percabangan dari pohon pencarian, kelemahan dari pencarian lengkap (untuk mencari secara mendalam) tumbuh secara eksponensial. Dengan semakin rumitnya, semakin tidak mungkin untuk melakukannya dan pada titik tertentu Anda cukup senang dengan jalur hasil apa pun , baik itu (kurang-lebih) optimal atau tidak.

moooeeeep
sumber
1
"A-Star Manhattan Distance dikalikan empat"? A-Star bukan A-Star jika heuristik dapat melebih-lebihkan jarak. (Dan dengan demikian tidak menjamin untuk menemukan jalan terpendek juga)
contoh
@contoh Tentu saja, jika seseorang menerapkan fungsi heuristik yang tidak dapat diterima, algoritma tersebut mungkin gagal menemukan solusi optimal (seperti yang saya tunjukkan dalam jawaban saya). Tapi saya tidak akan melangkah lebih jauh untuk mengganti nama algoritma dasar karena alasan itu.
moooeeeep
38

Pencarian pohon terlalu banyak. Labirin secara inheren dapat dipisahkan di sepanjang jalur solusi.

(Terima kasih kepada rainman002 dari Reddit karena menunjukkan ini padaku.)

Karena itu, Anda dapat dengan cepat menggunakan komponen yang terhubung untuk mengidentifikasi bagian dinding labirin yang terhubung. Ini mengulangi piksel dua kali.

Jika Anda ingin mengubahnya menjadi diagram yang bagus dari jalur solusi, Anda kemudian dapat menggunakan operasi biner dengan elemen penataan untuk mengisi jalur "jalan buntu" untuk setiap wilayah yang terhubung.

Kode demo untuk MATLAB berikut. Itu bisa menggunakan tweaking untuk membersihkan hasilnya lebih baik, membuatnya lebih digeneralisasikan, dan membuatnya berjalan lebih cepat. (Kadang saat itu bukan jam 2:30 pagi.)

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

hasil dari kode saat ini

Jim Gray
sumber
24

Menggunakan antrian untuk pengisian terus menerus ambang. Dorong pixel kiri dari pintu masuk ke antrian dan kemudian mulai loop. Jika piksel yang antri cukup gelap, warnanya abu-abu terang (di atas ambang batas), dan semua tetangga didorong ke antrean.

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

Solusi adalah koridor antara dinding abu-abu dan dinding berwarna. Perhatikan bahwa labirin ini memiliki banyak solusi. Juga, ini hanya berfungsi.

Larutan

kylefinn
sumber
1
Resolusi naif yang menarik, berdasarkan metode hand-on-wall. Memang bukan yang terbaik, tapi saya suka.
zessx
23

Ini dia: maze-solver-python (GitHub)

masukkan deskripsi gambar di sini

Saya bersenang-senang bermain-main dengan ini dan memperluas Joseph Kern jawaban . Tidak mengurangi darinya; Saya hanya membuat beberapa tambahan kecil untuk orang lain yang mungkin tertarik bermain-main dengan ini.

Ini adalah pemecah berbasis python yang menggunakan BFS untuk menemukan jalur terpendek. Tambahan utama saya, pada saat itu, adalah:

  1. Gambar dibersihkan sebelum pencarian (mis. Dikonversi menjadi hitam & putih murni)
  2. Secara otomatis menghasilkan GIF.
  3. Secara otomatis menghasilkan AVI.

Seperti berdiri, titik awal / akhir adalah kode keras untuk labirin sampel ini, tapi saya berencana untuk memperluasnya sehingga Anda dapat memilih piksel yang sesuai.

stefano
sumber
1
Hebat, terima kasih, itu tidak berjalan pada BSD / Darwin / Mac, beberapa dependensi dan skrip shell memerlukan perubahan kecil, bagi mereka yang ingin mencoba di Mac: [maze-solver-python]: github.com/holg/maze- solver-python
HolgT
@HggT: Senang Anda menemukannya bermanfaat. Saya menyambut setiap permintaan penarikan untuk ini. :)
stefano
5

Saya akan memilih opsi matrix-of-bools. Jika Anda menemukan bahwa daftar Python standar terlalu tidak efisien untuk ini, Anda bisa menggunakan anumpy.bool array. Penyimpanan untuk labirin 1000x1000 piksel kemudian hanya 1 MB.

Jangan repot-repot membuat struktur data pohon atau grafik. Itu hanya cara berpikir tentang hal itu, tetapi belum tentu cara yang baik untuk merepresentasikannya dalam memori; matriks boolean lebih mudah dikodekan dan lebih efisien.

Kemudian gunakan algoritma A * untuk menyelesaikannya. Untuk jarak heuristik, gunakan jarak Manhattan ( distance_x + distance_y).

Mewakili node dengan tupel (row, column)koordinat. Setiap kali algoritme ( pseudocode Wikipedia ) memanggil "tetangga", ini adalah masalah sederhana untuk mengulangi empat tetangga yang mungkin (pikirkan ujung-ujung gambar!).

Jika ternyata masih terlalu lambat, Anda dapat mencoba menurunkan skala gambar sebelum memuatnya. Berhati-hatilah agar tidak kehilangan jalur sempit dalam proses.

Mungkin mungkin untuk melakukan downscaling 1: 2 dengan Python juga, memeriksa bahwa Anda tidak benar-benar kehilangan jalur yang mungkin. Pilihan yang menarik, tetapi perlu sedikit lebih banyak pemikiran.

Thomas
sumber
Posting blog yang luar biasa ini menunjukkan cara memecahkan labirin dalam Mathematica. Menerjemahkan metode ke python seharusnya tidak menjadi masalah
Boris Gorelik
Saya telah memperbarui pertanyaan. Jika saya memilih untuk menggunakan RGB tiga kali lipat sebagai pengganti booleannilai, apakah penyimpanan masih akan dibandingkan? Matriksnya kemudian 2400 * 1200. Dan apakah A * lebih dari BFS akan berdampak signifikan pada waktu berjalan nyata?
Whymarrh
@ Woaharrh, kedalaman bit bisa menyusut untuk mengimbangi. 2 bit per piksel harus cukup untuk siapa pun.
Brian Cain
5

Berikut ini beberapa ide.

(1. Pemrosesan Gambar :)

1.1 Muat gambar sebagai peta piksel RGB . Dalam C # itu adalah penggunaan sepele system.drawing.bitmap. Dalam bahasa yang tidak memiliki dukungan sederhana untuk pencitraan, cukup konversikan gambar ke format pixmap portabel (PPM) (representasi teks Unix, menghasilkan file besar) atau beberapa format file biner sederhana yang dapat Anda baca dengan mudah, seperti BMP atau TGA . ImageMagick di Unix atau IrfanView di Windows.

1.2 Anda dapat, seperti disebutkan sebelumnya, menyederhanakan data dengan mengambil (R + G + B) / 3 untuk setiap piksel sebagai indikator nada abu-abu dan kemudian memberikan nilai ambang batas untuk menghasilkan tabel hitam putih. Sesuatu yang mendekati 200 dengan asumsi 0 = hitam dan 255 = putih akan mengeluarkan artefak JPEG.

(2. Solusi :)

2.1 Pencarian Kedalaman-Pertama: Masukkan tumpukan kosong dengan lokasi awal, kumpulkan gerakan tindak lanjut yang tersedia, pilih satu secara acak dan dorong ke tumpukan, lanjutkan sampai akhir tercapai atau deadend. Pada backend deadend dengan memunculkan stack, Anda perlu melacak posisi mana yang dikunjungi di peta sehingga ketika Anda mengumpulkan gerakan yang tersedia, Anda tidak akan pernah mengambil jalur yang sama dua kali. Sangat menarik untuk menghidupkan.

2.2 Pencarian Breadth-First: Disebutkan sebelumnya, mirip seperti di atas tetapi hanya menggunakan antrian. Juga menarik untuk dianimasikan. Ini berfungsi seperti mengisi-banjir dalam perangkat lunak pengedit gambar. Saya pikir Anda mungkin dapat memecahkan labirin di Photoshop menggunakan trik ini.

2.3 Wall Follower: Secara geometris, labirin adalah tabung yang terlipat / berbelit-belit. Jika Anda meletakkan tangan di dinding, Anda pada akhirnya akan menemukan pintu keluar;) Ini tidak selalu berhasil. Ada asumsi tertentu tentang: labirin sempurna, dll., Misalnya, labirin tertentu mengandung pulau. Lihat itu; itu menarik.

(3. Komentar :)

Ini yang sulit. Mudah untuk memecahkan labirin jika direpresentasikan dalam beberapa formal array sederhana dengan setiap elemen menjadi tipe sel dengan dinding utara, timur, selatan dan barat dan bidang bendera yang dikunjungi. Namun mengingat bahwa Anda mencoba melakukan ini mengingat sketsa yang digambar tangan itu menjadi berantakan. Jujur saya berpikir bahwa mencoba merasionalisasi sketsa akan membuat Anda gila. Ini mirip dengan masalah penglihatan komputer yang cukup terlibat. Mungkin langsung ke peta gambar mungkin lebih mudah namun lebih boros.

lino
sumber
2

Inilah solusi menggunakan 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 ke skala abu-abu, lihat: 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!

solusi yang dengan benar menemukan jalur terpendek

Inilah yang terjadi jika Anda tidak mengisi beberapa piksel perbatasan (Ha!) ...

versi solusi di mana pemecah mengelilingi labirin

Pengungkapan penuh: Saya bertanya dan menjawab pertanyaan yang sangat mirip sendiri sebelum saya menemukan yang ini. Kemudian melalui keajaiban SO, ditemukan ini sebagai salah satu dari "Pertanyaan Terkait" teratas. Saya pikir saya akan menggunakan labirin ini sebagai test case tambahan ... Saya sangat senang menemukan bahwa jawaban saya di sana juga berfungsi untuk aplikasi ini dengan sedikit modifikasi.

Brian D
sumber
0

solusi yang baik adalah bahwa alih-alih menemukan tetangga dengan pixel, itu akan dilakukan oleh sel, karena sebuah koridor dapat memiliki 15px sehingga dalam koridor yang sama dapat mengambil tindakan seperti kiri atau kanan, sementara jika itu dilakukan seolah-olah perpindahan adalah sebuah kubus itu akan menjadi tindakan sederhana seperti ATAS, BAWAH, KIRI ATAU KANAN

black_john
sumber
Bisakah Anda menambahkan grafik solusi dan algoritme seperti sisa jawaban untuk memvalidasi poin Anda? Akan lebih baik jika Anda dapat menambahkannya untuk menambah bobot jawaban Anda sehingga orang lain benar-benar dapat lebih memahami jawaban Anda.
Himanshu Bansal