Đại diện và giải quyết một mê cung cho một hình ảnh


271

Cách tốt nhất để đại diện và giải quyết một mê cung cho một hình ảnh là gì?

Ảnh bìa của Vấn đề Phạm vi 134

Đưa ra một hình ảnh JPEG (như đã thấy ở trên), cách tốt nhất để đọc nó, phân tích nó thành một số cấu trúc dữ liệu và giải quyết mê cung là gì? Bản năng đầu tiên của tôi là đọc hình ảnh theo pixel theo pixel và lưu trữ nó trong một danh sách (mảng) các giá trị boolean: Truecho pixel trắng và Falsecho pixel không trắng (màu sắc có thể bị loại bỏ). Vấn đề với phương pháp này là hình ảnh có thể không phải là "pixel perfect". Điều đó có nghĩa đơn giản là nếu có một pixel trắng ở đâu đó trên tường thì nó có thể tạo ra một đường dẫn ngoài ý muốn.

Một phương pháp khác (xuất hiện sau một chút suy nghĩ) là chuyển đổi hình ảnh thành tệp SVG - đó là danh sách các đường dẫn được vẽ trên khung vẽ. Theo cách này, các đường dẫn có thể được đọc vào cùng một loại danh sách (giá trị boolean) trong đó Truechỉ ra một đường dẫn hoặc tường, Falsebiểu thị một không gian có thể đi được. Một vấn đề với phương pháp này phát sinh nếu chuyển đổi không chính xác 100% và không kết nối đầy đủ tất cả các bức tường, tạo ra các khoảng trống.

Ngoài ra, một vấn đề với việc chuyển đổi sang SVG là các đường thẳng không "hoàn hảo". Điều này dẫn đến các đường dẫn là các đường cong bezier hình khối. Với một danh sách (mảng) các giá trị boolean được lập chỉ mục bởi các số nguyên, các đường cong sẽ không dễ dàng chuyển và tất cả các điểm trên đường cong sẽ phải được tính toán, nhưng sẽ không khớp chính xác với các chỉ số danh sách.

Tôi giả định rằng trong khi một trong những phương pháp này có thể hoạt động (mặc dù có lẽ là không) thì chúng không hiệu quả khi đưa ra một hình ảnh lớn như vậy, và tồn tại một cách tốt hơn. Làm thế nào là tốt nhất (hiệu quả nhất và / hoặc với độ phức tạp ít nhất) được thực hiện? Thậm chí có một cách tốt nhất?

Rồi đến việc giải quyết mê cung. Nếu tôi sử dụng một trong hai phương thức đầu tiên, về cơ bản tôi sẽ kết thúc bằng một ma trận. Theo câu trả lời này , một cách tốt để thể hiện một mê cung là sử dụng cây và một cách tốt để giải quyết nó là sử dụng thuật toán A * . Làm thế nào một người sẽ tạo ra một cây từ hình ảnh? Có ý kiến ​​gì không?

TL; DR
Cách tốt nhất để phân tích? Vào cấu trúc dữ liệu nào? Làm thế nào sẽ nói cấu trúc giúp / cản trở giải quyết?

CẬP NHẬT
Tôi đã thử thực hiện những gì @Mikhail đã viết bằng Python, sử dụng numpy, như @Thomas khuyến nghị. Tôi cảm thấy rằng thuật toán là chính xác, nhưng nó không hoạt động như mong đợi. (Mã bên dưới.) Thư viện PNG là 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, [])

12
Tôi sẽ chuyển đổi mê cung thành đen trắng và sử dụng đường dẫn tìm phương thức automata di động để giải quyết nó.
Dan D.

Bạn có cần chỉ đối phó với hình ảnh đó, hoặc với nhiều hình ảnh như vậy không? Tức là có một tùy chọn xử lý thủ công cụ thể cho hình ảnh nhất định này?
Mikhail

1
@Whymarrh Tôi không mã python, nhưng tôi khá chắc chắn rằng bạn nên di chuyển visited.append(s)theo for.ifvà thay thế nó bằng visited.append(np). Một đỉnh được truy cập khi nó được thêm vào hàng đợi. Trong thực tế, mảng này nên được đặt tên là "xếp hàng". Bạn cũng có thể chấm dứt BFS sau khi bạn hoàn thành.
Mikhail

2
@Whymarrh Và bạn dường như cũng đã bỏ qua việc thực hiện khối trích xuất đường dẫn. Không có nó, bạn chỉ có thể tìm hiểu xem kết thúc có thể đạt được hay không, nhưng không làm thế nào.
Mikhail

1
Để tìm hiểu xem có một giải pháp, một UnionFind và một Linear Scan là thuật toán nhanh nhất. Nó không cung cấp cho bạn đường dẫn, nhưng cung cấp cho bạn một tập hợp các ô sẽ có đường dẫn dưới dạng tập hợp con.
st0le

Câu trả lời:


236

Đây là một giải pháp.

  1. Chuyển đổi hình ảnh sang thang độ xám (chưa nhị phân), điều chỉnh trọng số cho màu sắc để hình ảnh thang độ xám cuối cùng xấp xỉ nhau. Bạn có thể làm điều đó một cách đơn giản bằng cách điều khiển các thanh trượt trong Photoshop trong Hình ảnh -> Điều chỉnh -> Đen & Trắng.
  2. Chuyển đổi hình ảnh thành nhị phân bằng cách đặt ngưỡng thích hợp trong Photoshop trong Hình ảnh -> Điều chỉnh -> Ngưỡng.
  3. Đảm bảo ngưỡng được chọn đúng. Sử dụng Công cụ Magic Wand với 0 dung sai, mẫu điểm, tiếp giáp, không khử răng cưa. Kiểm tra xem các cạnh mà tại đó ngắt lựa chọn không phải là các cạnh sai được giới thiệu bởi ngưỡng sai. Trong thực tế, tất cả các điểm bên trong của mê cung này có thể truy cập ngay từ đầu.
  4. Thêm đường viền nhân tạo trên mê cung để đảm bảo khách du lịch ảo sẽ không đi bộ xung quanh nó :)
  5. Triển khai tìm kiếm đầu tiên (BFS) bằng ngôn ngữ yêu thích của bạn và chạy nó từ đầu. Tôi thích MATLAB cho nhiệm vụ này. Như @Thomas đã đề cập, không cần phải lộn xộn với biểu diễn thường xuyên của đồ thị. Bạn có thể làm việc với hình ảnh nhị phân trực tiếp.

Đây là mã MATLAB cho 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

Nó thực sự rất đơn giản và chuẩn, không nên gặp khó khăn khi thực hiện điều này trong Python hoặc bất cứ điều gì.

Và đây là câu trả lời:

Nhập mô tả hình ảnh ở đây


1
@Whymarrh Chà, với "Chỉ hình ảnh này" bây giờ bạn thực sự có câu trả lời. Bạn có câu hỏi cụ thể nào không? Các mục 1-4 trong danh sách của tôi là cách xử lý thủ công mà tôi đã hỏi về. Mục 5 là một BFS - thuật toán rất cơ bản cho đồ thị, nhưng nó có thể được áp dụng trực tiếp cho hình ảnh, mà không cần chuyển đổi pixel thành các đỉnh và hàng xóm thành các cạnh.
Mikhail

Tôi cảm thấy rằng bạn đã bao gồm tất cả mọi thứ. Tôi đang cố gắng thực hiện những gì bạn đã nói trong Python (sử dụng DFS thay cho BFS, chỉ vì tôi đã mã hóa điều đó một lần trước đó). Tôi sẽ trở lại để cập nhật câu hỏi / chấp nhận câu trả lời một chút.
Whymarrh

2
@Whymarrh DFS sẽ không tìm thấy bạn con đường ngắn nhất, trong khi BFS sẽ. Chúng vốn giống nhau, sự khác biệt duy nhất là cấu trúc cơ bản. Ngăn xếp (FILO) cho DFS và hàng đợi (FIFO) cho BFS.
Mikhail

3
BFS là lựa chọn phù hợp ở đây, bởi vì nó tạo ra một con đường ngắn nhất, mang lại một con đường "hợp lý" ngay cả khi các hành lang rộng hơn 1 pixel. OTOH DFS sẽ có xu hướng khám phá các hành lang và các khu vực mê cung không hứa hẹn với mô hình "lấp đầy lũ".
j_random_hacker

1
@JosephKern Path không chồng lên bất kỳ bức tường nào. Chỉ cần loại bỏ tất cả các pixel màu đỏ và ở đây bạn đi.
Mikhail

160

Giải pháp này được viết bằng Python. Cảm ơn Mikhail cho con trỏ về việc chuẩn bị hình ảnh.

Một tìm kiếm hoạt hình đầu tiên:

Phiên bản hoạt hình của BFS

Mê cung đã hoàn thành:

Mê cung đã hoàn thành

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

Lưu ý: Đánh dấu màu xám pixel truy cập màu trắng. Điều này loại bỏ sự cần thiết của một danh sách đã truy cập, nhưng điều này đòi hỏi tải tập tin hình ảnh thứ hai từ đĩa trước khi vẽ một đường dẫn (nếu bạn không muốn một hình ảnh tổng hợp của đường dẫn cuối cùng và TẤT CẢ các đường dẫn đã thực hiện).

Một phiên bản trống của mê cung tôi đã sử dụng.


13
Bởi vì bạn đã đủ tuyệt vời để quay lại và nâng cấp tôi ngay cả sau khi câu hỏi của bạn đã được trả lời, tôi đã tạo một gif hoạt hình của BFS, để giúp hình dung rõ hơn về quy trình.
Joseph Kern

1
Đẹp một, cảm ơn. Đối với những người khác muốn chơi xung quanh vấn đề này, như tôi đã làm, tôi muốn chia sẻ những lời khuyên của mình dựa trên những khó khăn tôi gặp phải. 1) Chuyển đổi hình ảnh thành đen trắng thuần túy hoặc sửa đổi hàm 'isWhite ()' của bạn để chấp nhận gần trắng | đen. Tôi đã viết một phương thức 'CleanImage' để xử lý trước tất cả các pixel chuyển đổi chúng thành màu trắng hoặc đen thuần túy, nếu không thuật toán không tìm thấy đường dẫn. 2) Đọc rõ ràng hình ảnh dưới dạng RGB [base_img = Image.open (img_in); base_img = base_img.convert ('RGB')]. Để nhận ảnh gif, hãy xuất một số hình ảnh và sau đó chạy 'convert -delay 5 -loop 1 * .jpg bfs.gif'.
stefano

1
mất tích thụt dòng trong dòng 13
sloewen

81

Tôi đã cố gắng thực hiện tìm kiếm A-Star cho vấn đề này. Theo sát việc triển khai của Joseph Kern cho khung và mã giả thuật toán được đưa ra ở đây :

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

Vì A-Star là một thuật toán tìm kiếm heuristic, bạn cần đưa ra một hàm ước tính chi phí còn lại (ở đây: khoảng cách) cho đến khi đạt được mục tiêu. Trừ khi bạn cảm thấy thoải mái với giải pháp tối ưu, không nên đánh giá quá cao chi phí. Một lựa chọn bảo thủ ở đây sẽ là khoảng cách manhattan (hoặc taxi) vì điều này thể hiện khoảng cách đường thẳng giữa hai điểm trên lưới cho khu phố Von Neumann đã sử dụng. (Trong trường hợp này, sẽ không bao giờ đánh giá quá cao chi phí.)

Tuy nhiên, điều này sẽ đánh giá thấp đáng kể chi phí thực tế cho mê cung đã cho. Do đó, tôi đã thêm hai số liệu khoảng cách khác bình phương khoảng cách euclide và khoảng cách manhattan nhân với bốn để so sánh. Tuy nhiên, những điều này có thể đánh giá quá cao chi phí thực tế và do đó có thể mang lại kết quả tối ưu.

Đây là mã:

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

Dưới đây là một số hình ảnh để trực quan hóa kết quả (lấy cảm hứng từ bức ảnh được đăng bởi Joseph Kern ). Các hình ảnh động hiển thị một khung hình mới sau mỗi 10000 lần lặp của vòng lặp while chính.

Bề rộng-Tìm kiếm đầu tiên:

Bề rộng-Tìm kiếm đầu tiên

Khoảng cách Manhattan A-Star:

Khoảng cách Manhattan A-Star

A-Star Squared Khoảng cách Euclide:

A-Star Squared Khoảng cách Euclide

Khoảng cách Manhattan A-Star nhân với bốn:

Khoảng cách Manhattan A-Star nhân với bốn

Kết quả cho thấy các khu vực khám phá của mê cung khác nhau đáng kể đối với các phương pháp phỏng đoán được sử dụng. Như vậy, khoảng cách euclide bình phương thậm chí tạo ra một đường dẫn khác (dưới mức tối ưu) như các số liệu khác.

Liên quan đến hiệu suất của thuật toán A-Star về thời gian chạy cho đến khi chấm dứt, lưu ý rằng rất nhiều đánh giá về các hàm khoảng cách và chi phí cộng lại so với Tìm kiếm đầu tiên (BFS) chỉ cần đánh giá "tính mục tiêu" của từng vị trí ứng viên. Việc chi phí cho các đánh giá chức năng bổ sung (A-Star) này có cao hơn chi phí cho số lượng nút lớn hơn để kiểm tra (BFS) hay không và đặc biệt là liệu hiệu suất có phải là vấn đề đối với ứng dụng của bạn hay không, là vấn đề về nhận thức cá nhân và tất nhiên có thể không được trả lời chung chung.

Một điều thể nói chung về việc liệu thuật toán tìm kiếm có thông tin (như A-Star) có thể là lựa chọn tốt hơn so với tìm kiếm toàn diện hay không (ví dụ: BFS) là như sau. Với số lượng kích thước của mê cung, tức là hệ số phân nhánh của cây tìm kiếm, nhược điểm của tìm kiếm toàn diện (để tìm kiếm một cách triệt để) tăng theo cấp số nhân. Với sự phức tạp ngày càng tăng, nó trở nên ngày càng ít khả thi hơn để làm điều đó và đến một lúc nào đó bạn khá hài lòng với bất kỳ đường dẫn kết quả nào , có thể là tối ưu hay không.


1
"Khoảng cách Manhattan A-Star nhân với bốn"? A-Star không phải là A-Star nếu heuristic có thể đánh giá quá cao khoảng cách. (Và do đó cũng không đảm bảo tìm được con đường ngắn nhất)
ví dụ

@example Tất nhiên, nếu người ta áp dụng hàm heuristic không được chấp nhận, thuật toán có thể không tìm được giải pháp tối ưu (như tôi đã chỉ ra trong câu trả lời của mình). Nhưng tôi sẽ không đi xa đến mức đổi tên thuật toán cơ bản vì lý do đó.
moooeeeep

38

Cây tìm kiếm là quá nhiều. Mê cung vốn có thể phân tách dọc theo (các) đường dẫn giải pháp.

(Cảm ơn rainman002 từ Reddit đã chỉ ra điều này cho tôi.)

Do đó, bạn có thể nhanh chóng sử dụng các thành phần được kết nối để xác định các phần được kết nối của tường mê cung. Điều này lặp đi lặp lại trên các pixel hai lần.

Nếu bạn muốn biến nó thành một sơ đồ đẹp của (các) đường dẫn giải pháp, thì bạn có thể sử dụng các phép toán nhị phân với các thành phần cấu trúc để điền vào các đường dẫn "ngõ cụt" cho từng vùng được kết nối.

Mã demo cho MATLAB sau. Nó có thể sử dụng tinh chỉnh để làm sạch kết quả tốt hơn, làm cho nó khái quát hơn và làm cho nó chạy nhanh hơn. (Đôi khi khi đó không phải là 2:30 sáng.)

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

kết quả của mã hiện tại


24

Sử dụng một hàng đợi cho một ngưỡng điền liên tục. Đẩy pixel bên trái của lối vào hàng đợi và sau đó bắt đầu vòng lặp. Nếu một pixel được xếp hàng đủ tối, nó có màu xám nhạt (trên ngưỡng) và tất cả các hàng xóm được đẩy lên hàng đợi.

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

Giải pháp là hành lang giữa tường màu xám và tường màu. Lưu ý mê cung này có nhiều giải pháp. Ngoài ra, điều này chỉ xuất hiện để làm việc.

Giải pháp


1
Giải pháp ngây thơ thú vị, dựa trên phương pháp tường tay. Thật vậy, không phải là tốt nhất, nhưng tôi thích nó.
zessx

23

Ở đây bạn đi: mê cung-solver-python (GitHub)

nhập mô tả hình ảnh ở đây

Tôi đã rất vui khi chơi xung quanh vấn đề này và mở rộng câu trả lời của Joseph Kern . Không làm mất giá trị của nó; Tôi chỉ thực hiện một số bổ sung nhỏ cho bất kỳ ai khác có thể quan tâm đến việc chơi xung quanh này.

Đó là một bộ giải dựa trên python sử dụng BFS để tìm đường đi ngắn nhất. Bổ sung chính của tôi, tại thời điểm đó, là:

  1. Hình ảnh được làm sạch trước khi tìm kiếm (nghĩa là chuyển đổi sang đen trắng thuần túy)
  2. Tự động tạo GIF.
  3. Tự động tạo AVI.

Như hiện tại, điểm bắt đầu / điểm cuối được mã hóa cứng cho mê cung mẫu này, nhưng tôi dự định mở rộng nó để bạn có thể chọn các pixel thích hợp.


1
Tuyệt vời, cảm ơn, nó không chạy trên BSD / Darwin / Mac, một số phụ thuộc và tập lệnh shell cần thay đổi nhỏ, cho những ai muốn thử trên Mac: [maze-solver-python]: github.com/holg/maze- solver-python
HolgT

@HolgT: Rất vui vì bạn thấy nó hữu ích. Tôi hoan nghênh bất kỳ yêu cầu kéo cho việc này. :)
stefano

5

Tôi sẽ chọn tùy chọn ma trận bools. Nếu bạn thấy rằng danh sách Python tiêu chuẩn quá kém hiệu quả cho việc này, bạn có thể sử dụng một numpy.boolmảng thay thế. Dung lượng cho mê cung 1000x1000 pixel sau đó chỉ là 1 MB.

Đừng bận tâm với việc tạo bất kỳ cấu trúc dữ liệu cây hoặc đồ thị. Đó chỉ là một cách nghĩ về nó, nhưng không nhất thiết là một cách tốt để thể hiện nó trong bộ nhớ; một ma trận boolean vừa dễ viết mã vừa hiệu quả hơn.

Sau đó sử dụng thuật toán A * để giải quyết nó. Đối với khoảng cách heuristic, sử dụng khoảng cách Manhattan ( distance_x + distance_y).

Biểu diễn các nút bằng một bộ (row, column)tọa độ. Bất cứ khi nào thuật toán ( mã giả Wikipedia ) gọi cho "hàng xóm", đó là một vấn đề đơn giản để lặp qua bốn hàng xóm có thể (chú ý các cạnh của hình ảnh!).

Nếu bạn thấy rằng nó vẫn còn quá chậm, bạn có thể thử thu nhỏ hình ảnh trước khi tải. Hãy cẩn thận để không mất bất kỳ con đường hẹp trong quá trình.

Có lẽ bạn cũng có thể thực hiện hạ thấp tỷ lệ 1: 2 trong Python, kiểm tra xem bạn có thực sự không bị mất bất kỳ đường dẫn nào không. Một lựa chọn thú vị, nhưng nó cần thêm một chút suy nghĩ.


Bài đăng blog tuyệt vời này cho thấy làm thế nào để giải quyết một mê cung trong toán học. Dịch phương pháp sang python không phải là một vấn đề
Boris Gorelik

Tôi đã cập nhật câu hỏi. Nếu tôi chọn sử dụng bộ ba RGB thay cho các booleangiá trị, liệu bộ lưu trữ có còn so sánh không? Ma trận sau đó là 2400 * 1200. Và liệu A * trên BFS có ảnh hưởng đáng kể đến thời gian chạy thực không?
Whymarrh

@Whymarrh, độ sâu bit có thể co lại để bù lại. 2 bit cho mỗi pixel là đủ cho bất cứ ai.
Brian Cain

5

Đây là một số ý tưởng.

(1. Xử lý ảnh :)

1.1 Tải hình ảnh dưới dạng bản đồ pixel RGB . Trong C #, nó là tầm thường sử dụng system.drawing.bitmap. Trong các ngôn ngữ không hỗ trợ đơn giản cho hình ảnh, chỉ cần chuyển đổi hình ảnh sang định dạng pixmap di động (PPM) (biểu diễn văn bản Unix, tạo tệp lớn) hoặc một số định dạng tệp nhị phân đơn giản mà bạn có thể dễ dàng đọc, chẳng hạn như BMP hoặc TGA . ImageMagick trong Unix hoặc IrfanView trong Windows.

1.2 Bạn có thể, như đã đề cập trước đó, đơn giản hóa dữ liệu bằng cách lấy (R + G + B) / 3 cho mỗi pixel làm chỉ báo cho tông màu xám và sau đó ngưỡng giá trị để tạo bảng đen trắng. Một cái gì đó gần 200 giả sử 0 = đen và 255 = trắng sẽ lấy ra các tạo tác JPEG.

(2. Giải pháp :)

2.1 Tìm kiếm theo chiều sâu: Bắt đầu một ngăn xếp trống với vị trí bắt đầu, thu thập các bước tiếp theo có sẵn, chọn ngẫu nhiên và đẩy lên ngăn xếp, tiến hành cho đến khi kết thúc hoặc hết hạn. Khi quay lại thời hạn cuối bằng cách bật ngăn xếp, bạn cần theo dõi vị trí nào đã được truy cập trên bản đồ để khi bạn thu thập các di chuyển có sẵn, bạn không bao giờ đi cùng một con đường hai lần. Rất thú vị để hoạt hình.

2.2 Tìm kiếm theo chiều rộng: Được đề cập trước đó, tương tự như trên nhưng chỉ sử dụng hàng đợi. Cũng thú vị để animate. Điều này hoạt động như tràn ngập trong phần mềm chỉnh sửa hình ảnh. Tôi nghĩ rằng bạn có thể giải quyết một mê cung trong Photoshop bằng thủ thuật này.

2.3 Người theo dõi tường: Nói một cách hình học, mê cung là một ống gấp / chập. Nếu bạn giữ tay trên tường, cuối cùng bạn sẽ tìm thấy lối ra;) Điều này không phải lúc nào cũng hoạt động. Có một số giả định nhất định: mê cung hoàn hảo, v.v., ví dụ, mê cung nhất định có chứa đảo. Đừng tìm nó; nó thật hấp dẫn

(3. Nhận xét :)

Đây là một trong những khó khăn. Thật dễ dàng để giải quyết mê cung nếu được thể hiện trong một số mảng đơn giản chính thức với mỗi yếu tố là một loại tế bào với các bức tường phía bắc, đông, nam và tây và một trường cờ được truy cập. Tuy nhiên, cho rằng bạn đang cố gắng để làm điều này với một bản phác thảo vẽ tay nó trở nên lộn xộn. Tôi thành thật nghĩ rằng cố gắng hợp lý hóa bản phác thảo sẽ khiến bạn phát điên. Điều này giống như các vấn đề về thị giác máy tính khá liên quan. Có lẽ đi trực tiếp lên bản đồ hình ảnh có thể dễ dàng hơn nhưng lãng phí hơn.


2

Đây là một giải pháp sử dụng 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 đến thang độ xám, xem: 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!

giải pháp tìm đúng đường dẫn ngắn nhất

Đây là những gì xảy ra nếu bạn không điền vào một số pixel viền (Ha!) ...

phiên bản giải pháp nơi người giải quyết đi vòng quanh mê cung

Tiết lộ đầy đủ: Tôi đã hỏi và tự trả lời một câu hỏi tương tự trước khi tôi tìm thấy câu hỏi này. Sau đó thông qua sự kỳ diệu của SO, tìm thấy câu hỏi này là một trong những "Câu hỏi liên quan" hàng đầu. Tôi nghĩ rằng tôi sẽ sử dụng mê cung này như một trường hợp thử nghiệm bổ sung ... Tôi rất hài lòng khi thấy rằng câu trả lời của tôi cũng hoạt động cho ứng dụng này với rất ít sửa đổi.


0

giải pháp tốt là thay vì tìm hàng xóm theo pixel, nó sẽ được thực hiện bằng ô, bởi vì một hành lang có thể có 15px nên trong cùng hành lang, nó có thể thực hiện các hành động như trái hoặc phải, trong khi nếu nó được thực hiện như thể dịch chuyển là một khối lập phương, nó sẽ là một hành động đơn giản như LÊN, XUỐNG, TRÁI HOẶC QUYỀN


Bạn có thể thêm biểu đồ giải pháp và thuật toán như phần còn lại của câu trả lời để xác thực quan điểm của bạn không? Sẽ tốt hơn nếu bạn có thể thêm những câu đó để tăng thêm trọng số cho câu trả lời của mình để những người khác thực sự có thể hiểu hơn về câu trả lời của bạn.
Himanshu Bansal
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.