छवि को देखते हुए एक भूलभुलैया का प्रतिनिधित्व करना और हल करना


271

छवि को देखते हुए भूलभुलैया को दर्शाने और हल करने का सबसे अच्छा तरीका क्या है?

स्कोप इश्यू 134 की कवर इमेज

जेपीईजी छवि को देखते हुए (जैसा कि ऊपर देखा गया है), इसे पढ़ने का सबसे अच्छा तरीका क्या है, इसे कुछ डेटा संरचना में पार्स करें और भूलभुलैया को हल करें? मेरी पहली वृत्ति पिक्सेल द्वारा पिक्सेल में छवि को पढ़ने और बूलियन मूल्यों की एक सूची (सरणी) में संग्रहीत करने के Trueलिए है : एक सफेद पिक्सेल के लिए, और Falseएक गैर-सफेद पिक्सेल (रंगों को त्याग दिया जा सकता है) के लिए। इस पद्धति के साथ समस्या यह है कि छवि "पिक्सेल परफेक्ट" नहीं हो सकती है। उसके द्वारा मेरा सीधा मतलब है कि अगर एक दीवार पर कहीं एक सफेद पिक्सेल है तो यह एक अनपेक्षित मार्ग बना सकता है।

एक अन्य विधि (जो थोड़े विचार के बाद मेरे पास आई) छवि को एसवीजी फ़ाइल में बदलना है - जो कि एक कैनवास पर खींचे गए रास्तों की सूची है। इस तरह, रास्तों को एक ही तरह की सूची (बूलियन मान) में पढ़ा जा सकता है जहां Trueएक पथ या दीवार को इंगित करता है, Falseजो एक यात्रा-सक्षम स्थान को दर्शाता है। इस पद्धति के साथ एक समस्या उत्पन्न होती है यदि रूपांतरण 100% सटीक नहीं है, और अंतराल को बनाते हुए सभी दीवारों को पूरी तरह से जोड़ता नहीं है।

इसके अलावा एसवीजी में परिवर्तित करने के साथ एक मुद्दा यह है कि लाइनें "पूरी तरह से" सीधे नहीं हैं। इससे रास्ते में क्यूबिक बीज़ियर घटता है। पूर्णांक द्वारा अनुक्रमित बूलियन मूल्यों की एक सूची (सरणी) के साथ, वक्र आसानी से स्थानांतरित नहीं होंगे, और वक्र पर मौजूद सभी बिंदुओं की गणना की जाएगी, लेकिन सूचियों को सूचीबद्ध करने के लिए बिल्कुल मेल नहीं खाएगा।

मुझे लगता है कि जबकि इनमें से एक विधि काम कर सकती है (हालांकि शायद नहीं) कि वे इतनी बड़ी छवि को ध्यान में रखते हुए अक्षम हैं, और यह एक बेहतर तरीका है। यह सबसे अच्छा (सबसे कुशलता से और / या कम से कम जटिलता के साथ) कैसे किया जाता है? वहाँ भी एक सबसे अच्छा तरीका है?

फिर भूलभुलैया का हल आता है। यदि मैं पहले दो तरीकों में से किसी एक का उपयोग करता हूं, तो मैं अनिवार्य रूप से एक मैट्रिक्स के साथ समाप्त हो जाऊंगा। इस उत्तर के अनुसार , भूलभुलैया का प्रतिनिधित्व करने का एक अच्छा तरीका एक पेड़ का उपयोग करना है, और इसे हल करने का एक अच्छा तरीका ए * एल्गोरिथ्म का उपयोग करना है । छवि से एक पेड़ कैसे बनेगा? कोई विचार?

TL, डॉ।
पार्स करने का सबसे अच्छा तरीका? क्या डेटा संरचना में? कैसे कहा जाएगा ढांचा मदद / बाधा को हल करने में?

अद्यतन
मैंने numpy@Tikomas की सिफारिश के रूप में, पायथन में @Mikhail ने जो लिखा है, उसे लागू करने में अपना हाथ आजमाया है। मुझे लगता है कि एल्गोरिथ्म सही है, लेकिन यह आशा के अनुरूप काम नहीं कर रहा है। (नीचे कोड।) 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
मैं भूलभुलैया को काले और सफेद में बदल देता हूँ और इसे हल करने के लिए सेलुलर ऑटोमेटा विधि खोजने का एक मार्ग का उपयोग करता हूँ।
दान डी।

क्या आपको केवल उस छवि के साथ, या उस जैसी कई छवियों से निपटने की आवश्यकता है? Ie इस निश्चित छवि के लिए कुछ मैनुअल प्रोसेसिंग विशिष्ट का विकल्प है?
मिखाइल

1
@Whymarrh मैं अजगर को कोड नहीं करता, लेकिन मुझे पूरा यकीन है कि आपको visited.append(s)एक के तहत स्थानांतरित करना चाहिए for.ifऔर इसे प्रतिस्थापित करना चाहिए visited.append(np)। कतार में जुड़ने के बाद एक शीर्ष का दौरा किया जाता है। वास्तव में, इस सरणी को "कतारबद्ध" नाम दिया जाना चाहिए। एक बार समाप्त होने पर आप BFS को समाप्त भी कर सकते हैं।
मिखाइल

2
@Hymarrh और आपको लगता है कि रास्ता निकालने वाले ब्लॉक को लागू करना छोड़ दिया गया है। इसके बिना, आप केवल यह पता लगा सकते हैं कि खत्म हो सकता है या नहीं, लेकिन कैसे नहीं।
मिखाइल

1
पता लगाने के लिए अगर वहाँ है एक समाधान, एक UnionFind और एक रैखिक स्कैन सबसे तेजी से एल्गोरिथ्म है। यह आपको रास्ता नहीं देता है, लेकिन आपको टाइल्स का एक सेट देता है, जिसमें सबसेट के रूप में रास्ता होगा।
st0le

जवाबों:


236

यहाँ एक समाधान है।

  1. छवि को ग्रेस्केल में बदलें (अभी तक बाइनरी नहीं), रंगों के लिए वजन को समायोजित करना ताकि अंतिम ग्रेस्केल छवि लगभग समान हो। आप इसे फ़ोटोशॉप में फ़ोटोशॉप में स्लाइडर को नियंत्रित करके कर सकते हैं -> समायोजन -> ब्लैक एंड व्हाइट।
  2. छवि में फ़ोटोशॉप में उचित थ्रेशोल्ड सेट करके बाइनरी में कनवर्ट करें -> समायोजन -> थ्रेसहोल्ड।
  3. सुनिश्चित करें कि दहलीज सही चुना गया है। मैजिक वैंड टूल का उपयोग करें 0 सहिष्णुता, बिंदु नमूना, सन्निहित, कोई एंटी-अलियासिंग नहीं। जाँच करें कि किन किन बिंदुओं पर चयन विराम गलत सीमा के द्वारा प्रस्तुत किए गए झूठे किनारे नहीं हैं। वास्तव में, इस भूलभुलैया के सभी आंतरिक बिंदु शुरू से ही सुलभ हैं।
  4. यह सुनिश्चित करने के लिए भूलभुलैया पर कृत्रिम सीमाएं जोड़ें कि वर्चुअल यात्री इसके आसपास नहीं चलेगा :)
  5. लागू चौड़ाई-पहले खोज अपने पसंदीदा भाषा में (BFS) है और यह शुरू से ही चलाते हैं। मैं इस कार्य के लिए MATLAB पसंद करता हूं । जैसा कि @Thomas ने पहले ही उल्लेख किया है, रेखांकन के नियमित प्रतिनिधित्व के साथ गड़बड़ करने की कोई आवश्यकता नहीं है। आप सीधे binarized छवि के साथ काम कर सकते हैं।

यहाँ 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

यह वास्तव में बहुत सरल और मानक है, पायथन में या जो भी इसे लागू करने में कठिनाइयां नहीं होनी चाहिए ।

और यहाँ जवाब है:

यहां छवि विवरण दर्ज करें


1
"सिर्फ इस छवि" के लिए, आप वास्तव में एक जवाब है। क्या आपके कोई विशिष्ट प्रश्न हैं? मेरी सूची से आइटम 1-4 मैनुअल प्रोसेसिंग हैं जिनके बारे में मैं पूछ रहा था। आइटम 5 एक बीएफएस है - रेखांकन के लिए बहुत ही बुनियादी एल्गोरिथ्म, लेकिन यह सीधे पिक्सेल्स और पड़ोसियों को किनारों पर परिवर्तित किए बिना, छवि पर लागू किया जा सकता है।
मिखाइल

मुझे लगता है कि आपने सब कुछ कवर कर लिया है। मैं अपने हाथ को लागू करने की कोशिश कर रहा हूं जो आपने पायथन में कहा है (बीएफएस के स्थान पर डीएफएस का उपयोग करके, केवल इसलिए कि मैंने एक बार पहले कोडित किया है)। मैं प्रश्न को अपडेट करने / उत्तर को थोड़ा स्वीकार करने के लिए वापस आ जाऊंगा।
Whymarrh

2
@Hymarrhh DFS आपको सबसे छोटा रास्ता नहीं मिलेगा, जबकि BFS करेगा। वे स्वाभाविक रूप से समान हैं, एकमात्र अंतर अंतर्निहित संरचना है। BFS के लिए DFS और कतार (FIFO) के लिए स्टैक (FILO)।
मिखाइल

3
बीएफएस यहां सही विकल्प है, क्योंकि यह सबसे छोटा रास्ता बनाता है, जो गलियारों को 1 पिक्सेल से अधिक व्यापक होने पर भी "समझदार" रास्ता देता है। ओटोह डीएफएस "बाढ़ भराव" पैटर्न के साथ गलियारों और अप्रकाशित भूलभुलैया क्षेत्रों का पता लगाने के लिए करेगा।
j_random_hacker 11

1
@ जोसेफर्न पथ किसी भी दीवार को ओवरलैप नहीं करता है। बस सभी लाल पिक्सेल हटा दें और यहां आप जाएं।
मिखाइल

160

यह समाधान पायथन में लिखा गया है। छवि की तैयारी पर संकेत के लिए धन्यवाद मिखाइल।

एक एनिमेटेड चौड़ाई-पहली खोज:

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
क्योंकि आपके पास वापस आने के लिए और आपके प्रश्न का उत्तर दिए जाने के बाद भी मुझे उभारने के लिए बहुत बढ़िया था, मैंने इस प्रक्रिया को बेहतर ढंग से देखने में मदद करने के लिए बीएफएस का एक एनिमेटेड जिफ़ बनाया।
जोसेफ केर्न

1
अच्छा लगा, धन्यवाद। अन्य लोगों के लिए जो इसके साथ खेलना चाहते हैं, जैसा कि मैंने किया था, मैं अपने सुझावों को उन कठिनाइयों के आधार पर साझा करना चाहूंगा जिन्हें मैंने सामना किया था। 1) या तो छवि को शुद्ध काले और सफेद में बदलें या अपने 'आइसाइट ()' फ़ंक्शन को पास-व्हाइट को स्वीकार करने के लिए संशोधित करें। मैंने एक 'cleanImage' विधि लिखी, जिसने सभी पिक्सल को पहले से सफेद या काले रंग में परिवर्तित कर दिया, अन्यथा एल्गोरिथ्म एक रास्ता खोजने में विफल रहता है। 2) छवि को स्पष्ट रूप से RGB [base_img = Image.open (img_in) के रूप में पढ़ें; base_img = base_img.convert ('RGB')]। एक gif पाने के लिए, कई चित्र आउटपुट करें और फिर 'Convert -delay 5 -loop 1 * .jpg bfs.nifif' चलाएं।
स्टेफानो

1
पंक्ति 13 में अनुपस्थित इंडेंट
नारा

81

मैंने खुद को इस समस्या के लिए ए-स्टार खोज को लागू करने की कोशिश की। जोसेफ कर्न द्वारा रूपरेखा और एल्गोरिथ्म स्यूडोकोड के कार्यान्वयन को बारीकी से देखा गया है :

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 एक आनुवांशिक खोज एल्गोरिथ्म है जिसे आपको एक फ़ंक्शन के साथ आने की आवश्यकता है जो लक्ष्य तक पहुंचने तक शेष लागत (यहां: दूरी) का अनुमान लगाता है। जब तक आप एक उप-अपनाने वाले समाधान के साथ सहज नहीं होते हैं, तब तक यह लागत को कम नहीं करना चाहिए। एक रूढ़िवादी विकल्प यहां मैनहट्टन (या टैक्सीकैब) की दूरी होगी क्योंकि यह इस्तेमाल किए गए वॉन न्यूमैन पड़ोस के लिए ग्रिड पर दो बिंदुओं के बीच सीधी रेखा की दूरी का प्रतिनिधित्व करता है। (जो, इस मामले में, कभी भी लागत को कम नहीं करेगा।)

यह हालांकि हाथ में दिए गए भूलभुलैया के लिए वास्तविक लागत को काफी कम करेगा। इसलिए मैंने दो अन्य डिस्टेंस मेट्रिक्स स्क्वेरेड यूक्लिडियन दूरी और मैनहट्टन दूरी को तुलना के लिए चार से गुणा किया है। हालांकि ये वास्तविक लागत को कम कर सकते हैं, और इसलिए सबॉप्टिमल परिणाम प्राप्त कर सकते हैं।

यहाँ कोड है:

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

यहां परिणामों के एक दृश्य के लिए कुछ छवियां दी गई हैं ( जोसेफ केर्न द्वारा पोस्ट किए गए द्वारा प्रेरित )। एनिमेशन मुख्य समय-लूप के 10000 पुनरावृत्तियों के बाद प्रत्येक को एक नया फ्रेम दिखाते हैं।

पहले चौड़ाई खोजो:

पहले चौड़ाई खोजो

ए-स्टार मैनहट्टन दूरी:

ए-स्टार मैनहट्टन दूरी

ए-स्टार स्क्वायड यूक्लिडियन दूरी:

ए-स्टार स्क्वायर्ड यूक्लिडियन दूरी

ए-स्टार मैनहट्टन की दूरी चार गुणा:

ए-स्टार मैनहट्टन की दूरी चार गुणा बढ़ गई

परिणाम दिखाते हैं कि भूलभुलैया के अन्वेषण किए गए क्षेत्रों में उपयोग की जा रही सांख्यिकी के लिए काफी भिन्नता है। जैसे, स्क्वायड यूक्लिडियन दूरी भी अन्य मेट्रिक्स के रूप में एक अलग (सबॉप्टीमल) पथ का निर्माण करती है।

समाप्ति तक रनटाइम के संदर्भ में ए-स्टार एल्गोरिथ्म के प्रदर्शन के संबंध में, ध्यान दें कि चौड़ाई और लागत कार्यों के मूल्यांकन का एक बहुत चौड़ाई-प्रथम खोज (बीएफएस) की तुलना में जोड़ा जाता है जिसे केवल "लक्ष्यता" का मूल्यांकन करने की आवश्यकता होती है प्रत्येक उम्मीदवार की स्थिति। इन अतिरिक्त फ़ंक्शन मूल्यांकन (ए-स्टार) के लिए लागत बड़ी संख्या में नोड्स (बीएफएस) की जांच करने के लिए लागत को पछाड़ती है या नहीं और विशेष रूप से आपके आवेदन के लिए प्रदर्शन बिल्कुल मुद्दा है या नहीं, यह व्यक्तिगत धारणा का विषय है और निश्चित रूप से इसका जवाब नहीं दिया जा सकता है।

एक सामान्य खोज एल्गोरिथ्म (जैसे ए-स्टार) के बारे में सामान्य रूप से कहा जा सकता है कि एक विस्तृत खोज की तुलना में बेहतर विकल्प हो सकता है (जैसे, बीएफएस) निम्नलिखित है। भूलभुलैया के आयामों की संख्या के साथ, अर्थात, खोज पेड़ का शाखापूर्ण कारक, एक संपूर्ण खोज का नुकसान (विस्तृत रूप से खोज करने के लिए) तेजी से बढ़ता है। बढ़ती जटिलता के साथ ऐसा करना कम और कम संभव हो जाता है और कुछ बिंदु पर आप किसी भी परिणाम पथ से बहुत अधिक खुश होते हैं , यह (लगभग) इष्टतम है या नहीं।


1
"ए-स्टार मैनहट्टन दूरी चार से गुणा"? ए-स्टार ए-स्टार नहीं है यदि हेयुरिस्टिक दूरी को कम कर सकता है। (और इस तरह एक छोटा रास्ता खोजने की गारंटी नहीं है)
उदाहरण

@ निस्संदेह, यदि कोई गैर-स्वीकार्य अनुवर्ती कार्य को लागू करता है, तो एल्गोरिथ्म इष्टतम समाधान खोजने में विफल हो सकता है (जैसे मैंने अपने उत्तर में बताया)। लेकिन मैं उस कारण के लिए बुनियादी एल्गोरिथ्म का नाम बदलने के लिए इतनी दूर नहीं जाऊंगा।
मूओइपिप

38

पेड़ की खोज बहुत ज्यादा है। समाधान पथ के साथ भूलभुलैया स्वाभाविक रूप से अलग है।

( मेरे लिए इस ओर इशारा करने के लिए Reddit से 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
दिलचस्प भोली संकल्प, हाथ से दीवार पद्धति पर आधारित है। वास्तव में, सबसे अच्छा नहीं है, लेकिन मुझे यह पसंद है।
zxx

23

यहाँ तुम जाओ: भूलभुलैया-सॉल्वर-अजगर (GitHub)

यहां छवि विवरण दर्ज करें

मुझे इसके साथ खेलने में मज़ा आया और जोसेफ कर्न के जवाब पर बढ़ाया गया । इससे अलग न होने के लिए; मैंने अभी किसी और के लिए कुछ मामूली जोड़ दिए हैं, जो इसके साथ खेलने में दिलचस्पी ले सकते हैं।

यह एक अजगर आधारित सॉल्वर है जो सबसे छोटे रास्ते को खोजने के लिए BFS का उपयोग करता है। मेरे मुख्य जोड़, उस समय हैं:

  1. छवि को खोज से पहले साफ किया जाता है (यानी शुद्ध काले और सफेद में परिवर्तित करें)
  2. स्वचालित रूप से एक GIF उत्पन्न।
  3. स्वचालित रूप से एक AVI उत्पन्न करते हैं।

जैसा कि यह खड़ा है, इस नमूना भूलभुलैया के लिए स्टार्ट / एंड-पॉइंट्स हार्ड-कोडेड हैं, लेकिन मैं इसे इस तरह से विस्तारित करने की योजना बनाता हूं कि आप उपयुक्त पिक्सल चुन सकें।


1
महान, धन्यवाद, यह बीएसडी / डार्विन / मैक पर नहीं चला, कुछ निर्भरता और शेल स्क्रिप्ट को उन लोगों के लिए मामूली बदलाव की आवश्यकता थी, जो मैक पर कोशिश करना चाहते हैं: [भूलभुलैया-सॉल्वर-पाइथन]: github.com/holg-maze- सॉल्वर-पाइथन
होल्ग

@ हैल्गट: खुशी है कि आपने इसे उपयोगी पाया। मैं इसके लिए किसी भी पुल अनुरोध का स्वागत करता हूं। :)
स्टेफनो

5

मैं मैट्रिक्स-ऑफ-बूल्स विकल्प के लिए जाऊँगा। यदि आप पाते हैं कि मानक पायथन सूची इसके लिए बहुत अक्षम हैं, तो आप numpy.boolइसके बजाय एक सरणी का उपयोग कर सकते हैं । एक 1000x1000 पिक्सेल भूलभुलैया के लिए भंडारण तो सिर्फ 1 एमबी है।

किसी भी पेड़ या ग्राफ डेटा संरचनाओं को बनाने के साथ परेशान मत करो। यह सिर्फ इसके बारे में सोचने का एक तरीका है, लेकिन जरूरी नहीं कि यह स्मृति में प्रतिनिधित्व करने का एक अच्छा तरीका है; एक बूलियन मैट्रिक्स कोड और अधिक कुशल दोनों आसान है।

फिर इसे हल करने के लिए ए * एल्गोरिथ्म का उपयोग करें। दूरी के अनुमान के लिए, मैनहट्टन दूरी ( distance_x + distance_y) का उपयोग करें ।

(row, column)निर्देशांक के एक समूह द्वारा नोड का प्रतिनिधित्व करते हैं । जब भी एल्गोरिथ्म ( विकिपीडिया छद्मकोड ) "पड़ोसियों" के लिए कहता है, तो यह चार संभावित पड़ोसियों (छवि के किनारों को ध्यान में रखते हुए!) को लूप करने का एक सरल मामला है।

यदि आप पाते हैं कि यह अभी भी बहुत धीमा है, तो आप इसे लोड करने से पहले छवि को डाउनस्कॉल कर सकते हैं। सावधान रहें कि इस प्रक्रिया में किसी भी संकरे रास्ते को न खोएं।

हो सकता है कि पायथन में 1: 2 की गिरावट के साथ-साथ यह जांचना भी संभव हो कि आप वास्तव में कोई संभावित रास्ता नहीं खो रहे हैं। एक दिलचस्प विकल्प, लेकिन इसे थोड़ा और अधिक सोचने की जरूरत है।


इस उत्कृष्ट ब्लॉग पोस्ट से पता चलता है कि गणित में एक भूलभुलैया को कैसे हल किया जाए। अजगर के लिए विधि का अनुवाद एक समस्या नहीं होनी चाहिए
बोरिस गोरेलिक

मैंने सवाल अपडेट किया है। यदि मैं booleanमूल्यों के बदले RGB ट्राइब्स का उपयोग करना चुनता हूं , तो क्या स्टोरेज अभी भी तुलना करेगा? मैट्रिक्स तब 2400 * 1200 है। और BFS पर A * का वास्तविक चलने के समय पर महत्वपूर्ण प्रभाव पड़ेगा?
०।

@ थोड़ी बहुत गहराई, क्षतिपूर्ति करने के लिए सिकुड़ सकती है। पिक्सेल प्रति 2 बिट किसी के लिए पर्याप्त होना चाहिए।
ब्रायन कैन

5

यहाँ कुछ विचार हैं।

(1.) डाटा प्रोसेसिंग :)

1.1 छवि को RGB पिक्सेल मैप के रूप में लोड करें। में सी # में प्रयोग मामूली बात है system.drawing.bitmap। इमेजिंग के लिए कोई सरल समर्थन वाली भाषाओं में, बस छवि को पोर्टेबल पिक्समैप प्रारूप (पीपीएम) (एक यूनिक्स पाठ प्रतिनिधित्व, बड़ी फ़ाइलों का निर्माण) या कुछ सरल बाइनरी फ़ाइल प्रारूप में आसानी से पढ़ सकते हैं, जैसे बीएमपी या टीजीए । Windows में Unix या IrfanView में ImageMagick

1.2 आप पहले बताए गए अनुसार, ग्रे पिक्सेल के संकेतक के रूप में प्रत्येक पिक्सेल के लिए (R + G + B) / 3 ले कर डेटा को सरल बना सकते हैं और फिर एक ब्लैक एंड व्हाइट टेबल बनाने के लिए मूल्य को थ्रेशोल्ड कर सकते हैं। 200 के करीब कुछ 0 = काले और 255 = सफेद मानने से जेपीईजी की कलाकृतियां निकल जाएंगी।

(2. समाधान :)

2.1 गहराई-पहली खोज: आरंभिक स्थान के साथ एक खाली स्टैक सम्मिलित करें, उपलब्ध अनुवर्ती चालें एकत्र करें, एक को यादृच्छिक रूप से चुनें और स्टैक पर धक्का दें, अंत तक पहुंचने या एक समय सीमा तक आगे बढ़ें। स्टैक को पॉप करने से डेडएंड बैकट्रैक पर, आपको मैप पर किन-किन पोजिशन पर नज़र रखने की ज़रूरत थी, ताकि जब आप उपलब्ध मूव्स इकट्ठा करें तो आप कभी भी एक ही रास्ते को दो बार न लें। चेतन के लिए बहुत दिलचस्प है।

2.2 चौड़ाई-पहली खोज: पहले उल्लेख किया गया था, ऊपर के समान लेकिन केवल कतारों का उपयोग करके। चेतन के लिए भी दिलचस्प है। यह इमेज एडिटिंग सॉफ्टवेयर में फ्लड-फिल की तरह काम करता है। मुझे लगता है कि आप इस चाल का उपयोग करके फ़ोटोशॉप में एक भूलभुलैया को हल करने में सक्षम हो सकते हैं।

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 से greyscale, देखें: 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")

देखा!

समाधान जो सही तरीके से सबसे छोटा रास्ता ढूंढता है

यदि आप कुछ बॉर्डर पिक्सेल (Ha!) को नहीं भरते हैं, तो ऐसा होता है ...

समाधान संस्करण जहां विलायक भूलभुलैया के चारों ओर जाता है

पूर्ण प्रकटीकरण: मैंने यह पूछने से पहले खुद से एक बहुत ही समान प्रश्न पूछा और उत्तर दिया । तब एसओ के जादू के माध्यम से, इसे शीर्ष "संबंधित प्रश्न" के रूप में पाया गया। मुझे लगा कि मैं इस चक्रव्यूह को एक अतिरिक्त परीक्षण मामले के रूप में उपयोग करूँगा ... मुझे यह जानकर बहुत खुशी हुई कि मेरा उत्तर इस आवेदन के लिए बहुत ही संशोधन के साथ काम करता है।


0

अच्छा समाधान यह होगा कि पिक्सेल द्वारा पड़ोसियों को खोजने के बजाय, यह सेल द्वारा किया जाएगा, क्योंकि एक गलियारे में 15px हो सकता है इसलिए एक ही गलियारे में यह बाएं या दाएं की तरह कार्रवाई कर सकता है, जबकि अगर ऐसा किया गया तो विस्थापन क्यूब था यह यूपी, डीएटी, लेफ्ट या राइट जैसी सरल कार्रवाई होगी


क्या आप अपनी बात को मान्य करने के लिए उत्तर के बाकी की तरह समाधान ग्राफ और एल्गोरिदम जोड़ सकते हैं? यह बेहतर होगा यदि आप अपने उत्तर में अधिक वेटेज जोड़ने के लिए उन्हें जोड़ सकते हैं ताकि अन्य लोग वास्तव में आपके उत्तर की अधिक समझ प्राप्त कर सकें।
हिमांशु बंसल
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.