diff --git a/indoor_Navigation_master.py b/indoor_Navigation_master.py index 8965f79..bcdea63 100644 --- a/indoor_Navigation_master.py +++ b/indoor_Navigation_master.py @@ -11,7 +11,7 @@ from tools import Worker, Isovist, TrackCollection, IndoorToolset # , Track, Is if __name__ == '__main__': - walkableTiles = 255 + walkableTiles = [255, 103, 210, 79] trackColor = 103 startColor = 210 endColor = 79 @@ -82,7 +82,7 @@ if __name__ == '__main__': if False: point = baseToolset.getRandomPos() print(point) - i = Isovist(*point, array=baseToolset.imgArray, walkable=walkableTiles, rangeLimit=30) + i = Isovist(*point, array=baseToolset.imgArray, walkables=walkableTiles, rangeLimit=30) i.saveImg() baseToolset.imgArray[point] = 160 imshow(baseToolset.imgArray) diff --git a/tools.py b/tools.py index 3078fd6..145458e 100644 --- a/tools.py +++ b/tools.py @@ -19,7 +19,7 @@ from PCHA import PCHA from operator import itemgetter from dtw import dtw -workercount = 1 +workercount = 6 class Worker(object): @@ -62,26 +62,26 @@ class Worker(object): class IsovistCollection(UserDict): - def __init__(self, walkable, rangeLimit, tileArray, worker=None, single_threaded=False): + def __init__(self, walkables, rangeLimit, tileArray, worker=None, single_threaded=False): super(IsovistCollection, self).__init__() if not isinstance(worker, Worker): raise TypeError self.data = dict() - self.walkable = walkable + self.walkables = walkables self.tileArray = tileArray self.rangeLimit = rangeLimit self.lfr = None if rangeLimit: if not single_threaded: workerResult = worker.init_many( - Isovist, [(*npIdx, self.tileArray, self.walkable, self.rangeLimit) - for npIdx, value in np.ndenumerate(self.tileArray) if value == self.walkable]) + Isovist, [(*npIdx, self.tileArray, self.walkables, self.rangeLimit) + for npIdx, value in np.ndenumerate(self.tileArray) if value == self.walkables]) self.data = {isovist.vertex: isovist for isovist in workerResult} # The following would be a non multithreaded approach, maybe activate it for smaller blueprints later # TODO: Activate this for smaller Blueprints, when multithreading would lead to overhead else: for ndIndex, value in np.ndenumerate(self.tileArray): - if value == self.walkable or value > 0: + if value in self.walkables: self.add_isovist(*ndIndex) else: pass @@ -99,7 +99,7 @@ class IsovistCollection(UserDict): :rtype: bool """ - self[(x, y)] = (Isovist(x, y, self.tileArray, self.walkable, self.rangeLimit)) + self[(x, y)] = (Isovist(x, y, self.tileArray, self.walkables, self.rangeLimit)) return True @staticmethod @@ -211,7 +211,7 @@ class IsovistCollection(UserDict): class Isovist(object): - def __init__(self, x, y, array, walkable, rangeLimit): + def __init__(self, x, y, array, walkables, rangeLimit): """ "Calculate lit squares from the given location and radius through 'Shadow Casting'" Source: @@ -224,8 +224,8 @@ class Isovist(object): :type y: int :param array: Numpy Array holding the background image :type array: np.ndarray - :param walkable: The value which identifies positions in the array through which light can travel - :type walkable: int or (int, int, int) + :param walkables: The value which identifies positions in the array through which light can travel + :type walkables: int or (int, int, int) :param rangeLimit: Determine the radius in which pixels of the shadow needs to be calculated :type rangeLimit: int """ @@ -236,14 +236,17 @@ class Isovist(object): self.x = x self.y = y self.vertex = (self.x, self.y) + self.walkables = walkables + if isinstance(array, np.ndarray): self.rangeLimit = rangeLimit if rangeLimit else int(sqrt(array.shape[0] * array.shape[1])) + self.radius = rangeLimit // 2 + 1 self.visArray = np.zeros(array.shape, dtype=bool) for octant in range(8): - self.__cast_light(self.x, self.y, 1, 1.0, 0.0, self.rangeLimit, + self.__cast_light(self.x, self.y, 1, 1.0, 0.0, mult[0][octant], mult[1][octant], - mult[2][octant], mult[3][octant], 0, array, walkable) + mult[2][octant], mult[3][octant], 0, array) offset = int(rangeLimit/2) # self.visArray = self.visArray[ @@ -260,12 +263,11 @@ class Isovist(object): self.Xcent = centroid[0] self.Ycent = centroid[1] - @staticmethod - def __blocksLight(x, y, array, walkable): + def __blocksLight(self, x, y, array): if x < 0 or y < 0: return True try: - return False if array[x, y] == walkable or array[x, y] > 0 else True + return False if array[x, y] in self.walkables else True except IndexError: return True @@ -280,12 +282,12 @@ class Isovist(object): def __isVisible(self, x, y): return self.visArray[x, y] - def __cast_light(self, cx, cy, row, start, end, radius, xx, xy, yx, yy, idx, array, walkable): - """Recursive lightcasting function""" + def __cast_light(self, cx, cy, row, start, end, xx, xy, yx, yy, idx, array): + """Recursive lightcasting function from roguebasin.com""" if start < end: return - radius_squared = radius * radius - for j in range(row, radius + 1): + radius_squared = self.radius * self.radius + for j in range(row, self.radius + 1): dx, dy = -j - 1, -j blocked = False while dx <= 0: @@ -305,18 +307,17 @@ class Isovist(object): self.__setVisible(X, Y) if blocked: # we're scanning a row of blocked squares: - if self.__blocksLight(X, Y, array, walkable): + if self.__blocksLight(X, Y, array): new_start = r_slope continue else: blocked = False start = new_start else: - if self.__blocksLight(X, Y, array, walkable) and j < radius: + if self.__blocksLight(X, Y, array) and j < self.radius: # This is a blocking square, start a child scan: blocked = True - self.__cast_light(cx, cy, j + 1, start, l_slope, - radius, xx, xy, yx, yy, idx + 1, array, walkable) + self.__cast_light(cx, cy, j + 1, start, l_slope, xx, xy, yx, yy, idx + 1, array) new_start = r_slope # Row is scanned; do next row unless last square was blocked: if blocked: @@ -381,13 +382,12 @@ class TrackCollection(UserDict): self[key] = track return True - def add_n_bunch_tracks(self, n, start, target, nbunch=None, penalty=None): def build_track(segment1, segment2): combined = list() combined.extend(segment1 + list(reversed(segment2))) - return Track(combined, self.map.walkableTile) + return Track(combined, self.map.walkableTiles) if isinstance(penalty, int) or isinstance(penalty, float): for i in range(n): @@ -565,7 +565,7 @@ class TrackCollection(UserDict): return [self[key] for key in [self.archeArray[:, 0][idx] for idx in idxArray]] def return_walkableTileList(self): - return [npIndex for npIndex, value in np.ndenumerate(self.map.imgArray) if value == self.map.walkableTile] + return [npIndex for npIndex, value in np.ndenumerate(self.map.imgArray) if value in self.map.walkableTiles] def save_to_disc(self, filename): filename = filename if filename.endswith('.pik') else '%s%s' % (filename, '.pik') @@ -699,7 +699,7 @@ class TrackCollection(UserDict): position = c[0] if c[0] not in currentTrack else c[1] else: raise ValueError('Something went wrong here, maybe no stop position?') - tracks.append(Track(currentTrack, self.map.walkableTile, qhull=False)) + tracks.append(Track(currentTrack, self.map.walkableTiles, qhull=False)) self.add_n_bunch_tracks(0, 0, 0, tracks) @@ -711,13 +711,12 @@ class TrackCollection(UserDict): pass - class Track(UserList): - def __init__(self, NodeList, walkableTile, qhull=True): + def __init__(self, NodeList, walkableTiles, qhull=True): if not isinstance(NodeList, list): raise TypeError super(UserList, self).__init__() - self.walkableTile = walkableTile + self.walkableTiles = walkableTiles self.data = NodeList.copy() self.group = None self.vertex = None @@ -799,7 +798,7 @@ class Track(UserList): ('an' if typ.startswith(prefix) else 'a', typ)) l = self.data l.extend(l2) - return Track(l, self.walkableTile) + return Track(l, self.walkableTiles) def return_isovists(self, trackCollection=None, indoorToolset=None): if isinstance(trackCollection, TrackCollection): @@ -812,7 +811,7 @@ class Track(UserList): class IndoorToolset(object): - def __init__(self, imageArray, walkableTile, graph=None, worker=None, isoVistSize=0): + def __init__(self, imageArray, walkableTiles, graph=None, worker=None, isoVistSize=0): """ :param graph: An optional Graph :type graph: nx.Graph @@ -820,7 +819,7 @@ class IndoorToolset(object): if not isinstance(imageArray, np.ndarray) or not isinstance(worker, Worker): raise TypeError - self.walkableTile = walkableTile + self.walkableTiles = walkableTiles self.imgArray = imageArray self.shape = self.imgArray.shape self.height = self.shape[0] @@ -830,14 +829,14 @@ class IndoorToolset(object): else: self.graph = self.translate_to_graph() self.__rand = random.Random() - self.isovists = IsovistCollection(self.walkableTile, isoVistSize, self.imgArray, worker=worker) - + self.isovists = IsovistCollection(self.walkableTiles, isoVistSize, self.imgArray, + worker=worker, single_threaded=True) def refresh_random_clock(self): self.__rand.seed(time.clock()) def copy(self): - return IndoorToolset(self.imgArray.copy(), self.walkableTile, graph=self.graph.copy()) + return IndoorToolset(self.imgArray.copy(), self.walkableTiles, graph=self.graph.copy()) def __len__(self): return self.width * self.height @@ -898,9 +897,11 @@ class IndoorToolset(object): rs = self.graph.nodes()[self.__rand.randrange(len(self.graph))] if verbose: print(rs, ' is random Position -> checking accessibiliy') - notWalkable = False - if self.imgArray[rs] != self.walkableTile: + if self.imgArray[rs] in self.walkableTiles: notWalkable = True + else: + notWalkable = False + while notWalkable: self.refresh_random_clock() rs = self.graph.nodes()[self.__rand.randrange(len(self.graph))] @@ -913,7 +914,7 @@ class IndoorToolset(object): def translate_to_graph(self): graph = nx.Graph() for idx, value in np.ndenumerate(self.imgArray): - if value > 0 or value == self.walkableTile: + if value in self.walkableTiles: x, y = idx graph.add_node((x, y), count=0) @@ -972,7 +973,7 @@ class IndoorToolset(object): oldWeight = self.graph.edge[currNode][nextNode]['weight'] self.graph.add_edge(currNode, nextNode, weight=oldWeight + penalty) - track = Track(dij_s_p, self.walkableTile, qhull=qhull) + track = Track(dij_s_p, self.walkableTiles, qhull=qhull) print(len(dij_s_p), ' Len Path generated -> Nodes: ', len(self.graph), ' -> Edges: ', len(self.graph.edges())) return track