이미지가 주어진 미로를 표현하고 해결하기


271

이미지가 주어진 미로를 표현하고 해결하는 가장 좋은 방법은 무엇입니까?

범위 문제 134의 표지 이미지

JPEG 이미지 (위에서 볼 수 있듯이)를 읽으면 그것을 읽는 가장 좋은 방법은 무엇입니까? 데이터 구조로 구문 분석하고 미로를 해결합니까? 첫 번째 본능은 이미지를 픽셀 단위로 읽고 이미지를 부울 값의 목록 (배열)에 저장하는 것입니다 : True흰색 픽셀 및 False흰색이 아닌 픽셀 (색상을 버릴 수 있음). 이 방법의 문제점은 이미지가 "픽셀 완벽"하지 않을 수 있다는 것입니다. 즉, 벽 어딘가에 흰색 픽셀이 있으면 의도하지 않은 경로가 만들어 질 수 있습니다.

또 다른 방법 (약간의 생각 후에 나에게 온)은 이미지를 캔버스에 그려진 경로 목록 인 SVG 파일로 변환하는 것입니다. 이런 식으로 경로 True는 경로 또는 벽을 False나타내는 이동 가능한 공간을 나타내는 동일한 종류의 목록 (부울 값)으로 읽을 수 있습니다 . 이 방법의 문제는 변환이 100 % 정확하지 않고 모든 벽을 완전히 연결하지 않아 간격이 생기는 경우에 발생합니다.

SVG로 변환 할 때의 문제는 선이 "완벽하게"직선적이지 않다는 것입니다. 그 결과 경로는 3 차 베 지어 곡선이됩니다. 정수로 인덱싱 된 부울 값의 목록 (배열)을 사용하면 곡선이 쉽게 전달되지 않고 곡선의 해당 선이 계산되어야하지만 목록 색인과 정확히 일치하지는 않습니다.

나는이 방법 중 하나가 효과적 일 수 있지만 그렇게 큰 이미지가 주어지면 비효율적이며 더 좋은 방법이 있다고 가정합니다. 이것이 가장 효율적이고 (또는 가장 복잡하지 않은) 어떻게 이루어 집니까? 가장 좋은 방법이 있습니까?

그런 다음 미로를 해결합니다. 처음 두 방법 중 하나를 사용하면 본질적으로 행렬로 끝납니다. 이 답변 에 따르면 , 미로를 표현하는 좋은 방법은 나무를 사용하는 것이고, 그것을 해결하는 좋은 방법은 A * 알고리즘을 사용하는 것입니다 . 이미지에서 나무를 어떻게 만들 수 있습니까? 어떤 아이디어?

TL; DR
파싱하는 가장 좋은 방법은? 어떤 데이터 구조로? 상기 구조는 어떻게 도움이되고 / 후진 해결에 도움이됩니까?

업데이트
@Thomas가 numpy권장하는 것처럼 @Mikhail이 Python으로 작성한 것을 구현하여 내 손을 사용해 보았습니다. 알고리즘이 정확하다고 생각하지만 원하는대로 작동하지 않습니다. PNG 코드는 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
미로를 흑백으로 변환하고 셀룰러 오토마타 방법을 찾는 경로를 사용하여 해결했습니다.
Dan D.

해당 이미지 만 처리해야합니까, 아니면 그와 같은 많은 이미지를 처리해야합니까? 즉이 특정 이미지에 특정한 수동 처리 옵션이 있습니까?
Mikhail

1
@Whymarrh 나는 파이썬을 코딩하지 않지만 visited.append(s)a 아래로 이동 하여로 for.if대체 해야한다고 확신합니다 visited.append(np). 큐에 추가되면 버텍스가 방문됩니다. 실제로이 배열의 이름은 "queued"여야합니다. 완료되면 BFS를 종료 할 수도 있습니다.
Mikhail

2
@Whymarrh 그리고 당신은 또한 경로 추출 블록을 구현하는 것을 건너 뛴 것 같습니다. 그것이 없으면 마무리에 도달 할 수 있는지 여부 만 알 수 있지만 방법은 없습니다.
Mikhail

1
해결책 있는지 알아 보려면 UnionFind와 Linear Scan이 가장 빠른 알고리즘입니다. 경로는 제공하지 않지만 경로를 하위 집합으로 사용할 타일 세트를 제공합니다.
st0le

답변:


236

해결책은 다음과 같습니다.

  1. 이미지를 그레이 스케일 (아직 이진 아님)로 변환하고 최종 그레이 스케일 이미지가 거의 균일하도록 색상의 가중치를 조정합니다. 이미지-> 조정-> 흑백에서 Photoshop의 슬라이더를 제어하여 간단하게 수행 할 수 있습니다.
  2. 이미지-> 조정-> 임계 값에서 Photoshop의 적절한 임계 값을 설정하여 이미지를 이진으로 변환하십시오.
  3. 임계 값이 올바르게 선택되어 있는지 확인하십시오. 공차, 포인트 샘플, 연속, 앤티 앨리어싱이없는 Magic Wand Tool을 사용하십시오. 선택이 끊어진 가장자리가 잘못된 임계 값으로 인해 잘못된 가장자리가 아닌지 확인하십시오. 실제로,이 미로의 모든 내부 지점은 처음부터 접근 할 수 있습니다.
  4. 미로에 인공 테두리를 추가하여 가상 여행자가 주변을 걷지 않도록하십시오 :)
  5. 원하는 언어로 너비 우선 검색 (BFS)을 구현 하고 처음부터 실행하십시오. 이 작업에 MATLAB 을 선호합니다 . @Thomas가 이미 언급했듯이 정기적 인 그래프 표현을 망칠 필요가 없습니다. 이진화 된 이미지로 직접 작업 할 수 있습니다.

다음은 BFS 용 MATLAB 코드입니다.

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

실제로 매우 간단하고 표준 적이므로 Python 또는 기타로 구현하는 데 어려움이 없어야합니다 .

그리고 여기에 답이 있습니다 :

여기에 이미지 설명을 입력하십시오


1
@Whymarrh 글쎄, "이 이미지 만"에 대한 답이 실제로 있습니다. 구체적인 질문이 있습니까? 내 목록의 항목 1-4는 내가 요청한 수동 처리입니다. 항목 5는 그래프의 기본 알고리즘 인 BFS이지만 픽셀을 꼭짓점으로, 이웃을 가장자리로 변환하지 않고 이미지에 직접 적용 할 수 있습니다.
Mikhail

네가 모든 것을 다룬 것 같아 파이썬에서 말한 것을 구현하려고 노력하고 있습니다 (BFS 대신 DFS 사용, 이전에 한 번 코딩했기 때문에). 질문 / 답변을 조금 업데이트하기 위해 다시 돌아올 것입니다.
Whymarrh

2
@Whymarrh DFS는 가장 짧은 길을 찾지 못하지만 BFS는 그 길을 찾을 것입니다. 그것들은 본질적으로 동일하지만 유일한 차이점은 기본 구조입니다. DFS 용 스택 (FILO) 및 BFS 용 대기열 (FIFO)
Mikhail

3
BFS는 가장 짧은 경로를 생성하기 때문에 올바른 선택입니다. 복도가 1 픽셀보다 훨씬 더 넓은 경우에도 "가상적인"경로를 제공합니다. OTOH DFS는 "홍수 채우기"패턴으로 복도와 유망한 미로 지역을 탐험하는 경향이 있습니다.
j_random_hacker

1
@JosephKern Path는 벽과 겹치지 않습니다. 빨간색 픽셀을 모두 제거하면됩니다.
Mikhail

160

이 솔루션은 Python으로 작성되었습니다. 이미지 준비에 대한 조언을 준 Mikhail에게 감사합니다.

애니메이션 폭 우선 검색 :

BFS의 애니메이션 버전

완성 된 미로 :

완성 된 미로

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

참고 : 방문한 흰색 픽셀을 회색으로 표시합니다. 이렇게하면 방문한 목록이 필요하지 않지만 경로를 그리기 전에 디스크에서 이미지 파일을 다시로드해야합니다 (최종 경로와 전체 경로의 합성 이미지를 원하지 않는 경우).

내가 사용한 미로의 빈 버전.


13
귀하의 질문에 대한 답변이 있더라도 돌아와서 나를 찬양 할만큼 굉장히 훌륭했기 때문에 BFS의 애니메이션 GIF를 만들어 프로세스를 더 잘 시각화했습니다.
Joseph Kern

1
고마워요 내가했던 것처럼 이것으로 놀고 싶은 다른 사람들을 위해, 나는 직면 한 어려움에 따라 내 팁을 공유하고 싶습니다. 1) 이미지를 순수한 흑백으로 변환하거나 'isWhite ()'함수를 수정하여 근백을 받도록하십시오. 모든 픽셀을 사전 처리하여 순수한 흰색 또는 검은 색으로 변환하는 'cleanImage'메서드를 작성했습니다. 그렇지 않으면 알고리즘이 경로를 찾지 못합니다. 2) 이미지를 RGB로 명시 적으로 읽습니다. [base_img = Image.open (img_in); base_img = base_img.convert ( 'RGB')]입니다. gif를 얻으려면 여러 이미지를 출력 한 다음 'convert -delay 5 -loop 1 * .jpg bfs.gif'를 실행하십시오.
stefano

1
13 행에 들여 쓰기 누락
sloewen

81

이 문제에 대해 A-Star 검색을 구현하려고했습니다. 프레임 워크와 여기에 주어진 알고리즘 의사 코드에 대한 Joseph Kern 의 구현을 면밀히 따랐 습니다 .

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는 휴리스틱 검색 알고리즘이므로 목표에 도달 할 때까지 남은 비용 (여기 : 거리)을 추정하는 기능이 필요합니다. 차선책 솔루션에 익숙하지 않으면 비용을 과대 평가해서는 안됩니다. 보수적 인 선택은 여기서 맨하탄 (또는 택시) 거리 입니다. 이는 사용 된 폰 노이만 (Von Neumann) 이웃에 대한 그리드상의 두 점 사이의 직선 거리 를 나타냅니다. (이 경우 비용을 과대 평가하지는 않습니다.)

그러나 이것은 주어진 미로에 대한 실제 비용을 상당히 과소 평가합니다. 따라서 비교를 위해 두 개의 다른 거리 메트릭에 제곱 유클리드 거리와 맨해튼 거리에 4를 곱한 값을 추가했습니다. 그러나 이는 실제 비용을 과대 평가하여 차선책의 결과를 산출 할 수 있습니다.

코드는 다음과 같습니다.

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

다음은 결과를 시각화하기위한 일부 이미지입니다 ( Joseph Kern이 게시 한 이미지에서 영감을 얻음 ). 애니메이션은 기본 while 루프를 10000 회 반복 한 후 각각 새로운 프레임을 보여줍니다.

너비 우선 검색 :

너비 우선 검색

스타 맨하탄 거리 :

스타 맨하탄 거리

별 제곱 유클리드 거리 :

별 제곱 유클리드 거리

A-Star Manhattan Distance에 4를 곱한 값 :

A-Star Manhattan Distance에 4를 곱한 값

결과는 미로의 탐색 된 영역이 사용되는 휴리스틱에 대해 상당히 다르다는 것을 보여줍니다. 따라서, 제곱 유클리드 거리는 다른 메트릭과는 다른 (차선이 아닌) 경로를 생성합니다.

종료까지의 런타임 측면에서 A-Star 알고리즘의 성능과 관련하여, 거리 및 비용 함수에 대한 많은 평가는 BFS (Breadth-First Search)와 비교하여 "goaliness"만 평가하면됩니다. 각 후보자 위치. 이러한 추가 기능 평가 (A-Star)에 대한 비용이 많은 수의 노드 (BFS)에 대한 비용보다 큰지 여부, 특히 성능이 애플리케이션에 전혀 문제가되지 않는지는 개별 인식의 문제입니다. 물론 일반적으로 대답 할 수 없습니다.

정보 검색 알고리즘 (예 : A-Star)이 전체 검색 (예 : BFS)에 비해 더 나은 선택이 될 수 있는지에 대해 일반적으로 말할 있는 것은 다음과 같습니다. 미로의 차원의 수, 즉 검색 트리의 분기 인자에 따라, 철저한 검색 (완전히 검색)의 단점이 기하 급수적으로 증가한다. 복잡성 증가로 당신이 꽤 많이 행복과 그래서 어떤 시점에서 할 적게 가능하게 어떤 결과로, 그것은 (약) 최적의 여부를합니다.


1
"A-Star Manhattan Distance에 4를 곱한 것"? 휴리스틱이 거리를 과대 평가할 수있는 경우 A-Star는 A-Star가 아닙니다. (따라서 최단 경로를 찾도록 보장하지는 않습니다)

@example 물론, 허용되지 않는 휴리스틱 함수를 적용하면 알고리즘이 최적의 솔루션을 찾지 못할 수 있습니다 (내 대답에서 지적한 것처럼). 그러나 그런 이유로 기본 알고리즘의 이름을 바꾸는 데는 도움이되지 않습니다.
moooeeeep

38

나무 검색이 너무 많습니다. 미로는 본질적으로 용액 경로 ​​(들)를 따라 분리 가능하다.

( Redit의 rainman002 에게 감사의 말을 합니다.)

이로 인해 연결된 구성 요소 를 빠르게 사용 하여 미로 벽의 연결된 섹션을 식별 할 수 있습니다 . 픽셀을 두 번 반복합니다.

이를 솔루션 경로의 멋진 다이어그램으로 바꾸려면, 구조화 요소와 함께 이진 연산을 사용하여 연결된 각 영역의 "데드 엔드"경로를 채울 수 있습니다.

MATLAB의 데모 코드는 다음과 같습니다. 조정을 사용하여 결과를 더 잘 정리하고 더 일반화 할 수 있으며 더 빠르게 실행할 수 있습니다. (오전 2:30이 아닌 경우도 있습니다.)

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

현재 코드의 결과


24

임계 값 연속 채우기에 큐를 사용합니다. 입구의 왼쪽 픽셀을 큐로 밀고 루프를 시작합니다. 대기중인 픽셀이 충분히 어두우면 밝은 회색 (임계 값 위)으로 표시되고 모든 인접 항목이 대기열로 푸시됩니다.

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

해결책은 회색 벽과 컬러 벽 사이의 복도입니다. 이 미로는 여러 가지 해결책이 있습니다. 또한 이것은 단지 작동하는 것처럼 보입니다.

해결책


1
벽면 방식을 기반으로 한 흥미로운 순진한 해상도. 실제로 가장 좋은 것은 아니지만 나는 그것을 좋아합니다.
zessx

23

여기 있습니다 : maze-solver-python (GitHub)

여기에 이미지 설명을 입력하십시오

나는 이것을 가지고 놀았고 Joseph Kern 의 대답을 확장했습니다 . 그것을 방해하지 마십시오. 나는 이것으로 놀고 싶어하는 다른 사람들을 위해 약간의 추가를했습니다.

BFS를 사용하여 최단 경로를 찾는 파이썬 기반 솔버입니다. 당시의 주요 추가 사항은 다음과 같습니다.

  1. 검색하기 전에 이미지가 정리됩니다 (즉, 순수한 흑백으로 변환)
  2. GIF를 자동으로 생성합니다.
  3. AVI를 자동으로 생성합니다.

그대로, 시작 / 종료 지점 이이 샘플 미로에 대해 하드 코딩되어 있지만 적절한 픽셀을 선택할 수 있도록 확장 할 계획입니다.


1
감사합니다. BSD / Darwin / Mac에서 실행되지 않았으며, 일부 의존성 및 셸 스크립트는 Mac에서 시도하려는 사람들을 위해 약간의 변경이 필요했습니다. [maze-solver-python] : github.com/holg/maze- 솔버-파이썬
HolgT

@ HolgT : 유용하다고 생각합니다. 이에 대한 모든 풀 요청을 환영합니다. :)
stefano 2016 년

5

부울 매트릭스 옵션으로갑니다. 표준 파이썬 목록이 너무 비효율적이라는 것을 알게되면 numpy.bool대신 배열을 사용할 수 있습니다 . 1000x1000 픽셀 미로의 저장 용량은 1MB에 불과합니다.

트리 또는 그래프 데이터 구조를 만드는 데 신경 쓰지 마십시오. 그것은 단지 그것에 대해 생각하는 방법 일 뿐이지 만 반드시 그것을 기억에 표현하는 좋은 방법은 아닙니다. 부울 행렬은 코딩하기 쉽고 효율적입니다.

그런 다음 A * 알고리즘을 사용하여 해결하십시오. 거리 휴리스틱의 경우 맨해튼 거리 ( distance_x + distance_y)를 사용하십시오 .

튜플 (row, column)좌표로 노드를 나타냅니다 . 알고리즘 ( 위키피디아 의사 코드 )이 "이웃"을 요구할 때마다 네 개의 가능한 이웃을 반복하는 것은 간단합니다 (이미지의 가장자리를 생각하십시오!).

여전히 너무 느리다면 이미지를로드하기 전에 다운 스케일링을 시도 할 수 있습니다. 프로세스에서 좁은 경로를 잃지 않도록주의하십시오.

파이썬에서 1 : 2 다운 스케일링을 수행하여 실제로 가능한 경로를 잃지 않는지 확인할 수 있습니다. 흥미로운 옵션이지만 조금 더 생각해야합니다.


이 우수한 블로그 게시물 은 mathematica에서 미로를 해결하는 방법을 보여줍니다. 파이썬 방법을 변환하는 것은 문제가되지 않습니다
보리스 Gorelik

질문을 업데이트했습니다. boolean값 대신 RGB 트리플을 사용하기로 선택 하면 스토리지가 여전히 비교됩니까? 매트릭스는 2400 * 1200입니다. BFS보다 A *가 실제 실행 시간에 상당한 영향을 미칩니 까?
Whymarrh

@Whymarrh, 비트 깊이는 보상하기 위해 축소 될 수 있습니다. 픽셀 당 2 비트이면 누구에게나 충분해야합니다.
Brian Cain

5

몇 가지 아이디어가 있습니다.

(1. 이미지 처리 :)

1.1 이미지를 RGB 픽셀 맵 으로로드하십시오 . C # 에서는을 사용하는 것이 간단 system.drawing.bitmap합니다. 이미징을 간단하게 지원하지 않는 언어의 경우 이미지를 PPM ( Portable Pixmap Format ) (유닉스 텍스트 표현, 큰 파일 생성) 또는 쉽게 읽을 수있는 간단한 이진 파일 형식 (예 : BMP 또는 TGA)으로 변환하십시오 . Unix의 ImageMagick 또는 Windows의 IrfanView

1.2 앞에서 언급했듯이 각 픽셀의 (R + G + B) / 3를 회색 톤의 지표로 사용하여 데이터를 단순화 한 다음 값을 임계 값으로 지정하여 흑백 테이블을 생성 할 수 있습니다. 0 = 검정, 255 = 흰색이라고 가정하면 200에 가까운 것이 JPEG 아티팩트를 제거합니다.

(2. 해결책 :)

2.1 깊이 우선 검색 : 시작 위치로 빈 스택을 초기화하고, 사용 가능한 후속 조치를 수집하고, 무작위로 하나를 선택하고 스택으로 밀고, 끝에 도달하거나 막 다른 곳까지 진행하십시오. 스택을 띄워서 데드 엔드 백 트랙에서 맵에서 방문한 위치를 추적해야하므로 사용 가능한 이동을 수집 할 때 동일한 경로를 두 번 사용하지 마십시오. 애니메이션하는 것은 매우 흥미 롭습니다.

2.2 너비 우선 검색 : 앞에서 언급했지만 위와 유사하지만 대기열 만 사용합니다. 애니메이션하는 것도 흥미 롭습니다. 이것은 이미지 편집 소프트웨어의 플러드 필처럼 작동합니다. 이 트릭을 사용하여 Photoshop에서 미로를 해결할 수 있다고 생각합니다.

2.3 벽 추종자 : 기하학적으로 말하자면, 미로는 접히거나 뒤틀린 튜브입니다. 벽에 손을 대면 결국 출구를 찾을 수 있습니다.) 항상 작동하지는 않습니다. 완벽한 미로 등의 특정 가정이 있습니다. 예를 들어, 특정 미로에는 섬이 있습니다. 찾아 봐; 매혹적입니다.

(3. 의견 :)

이것은 까다로운 것입니다. 각 요소가 북쪽, 동쪽, 남쪽 및 서쪽 벽과 방문 플래그 필드가있는 셀 유형으로 공식적인 간단한 배열로 표현되면 미로를 쉽게 해결할 수 있습니다. 그러나 손으로 그린 ​​스케치 에서이 작업을 수행하려고하면 지저분 해집니다. 솔직히 스케치를 합리화하려고하면 불안감을 느끼게 될 것입니다. 이것은 상당히 관련된 컴퓨터 비전 문제와 유사합니다. 이미지 맵으로 직접 이동하는 것이 더 쉽고 더 낭비적일 수 있습니다.


2

다음은 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에서 그레이 스케일로 : https : //.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")

짜잔!

가장 짧은 경로를 올바르게 찾는 솔루션

이것은 일부 경계 픽셀을 채우지 않으면 어떻게됩니까 (Ha!) ...

솔버가 미로를 돌아 다니는 솔루션 버전

전체 공개 : 나는이 질문을 찾기 전에 비슷한 질문을 스스로 대답 했다. 그런 다음 SO의 마법을 통해이 질문을 최상위 "관련 질문"중 하나로 찾았습니다. 추가 테스트 사례로이 미로를 사용할 것이라고 생각했습니다. 저의 답변이이 응용 프로그램에서도 거의 수정없이 작동한다는 것을 알게되어 매우 기뻤습니다.


0

좋은 해결책은 픽셀로 이웃을 찾는 것이 아니라 셀로 수행하는 것입니다. 복도는 15px를 가질 수 있으므로 동일한 복도에서 왼쪽 또는 오른쪽과 같은 작업을 수행 할 수 있지만 변위처럼 수행 된 경우 큐브는 UP, DOWN, LEFT 또는 RIGHT와 같은 간단한 동작입니다.


포인트의 유효성을 검사하기 위해 나머지 답변과 같은 솔루션 그래프와 알고리즘을 추가 할 수 있습니까? 다른 사람들이 실제로 귀하의 답변을 더 잘 이해할 수 있도록 답변에 가중치를 더하기 위해 그것들을 추가하는 것이 좋습니다.
Himanshu Bansal
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.