2020-05-09 12:53:26 +02:00

149 lines
6.1 KiB
Python

from typing import List, Union, Tuple
import copy
import numpy as np
import interpreter.colors as colors
import interpreter.imageFunctions as imageWrapper
import interpreter.tokens as tokens
import interpreter.helperFunctions as helperFunctions
import interpreter.movementFunctions as movement
from interpreter.dataStructures import position, codel, edge, graphNode, graph, direction
def cyclePosition(image: np.ndarray, startPosition: position) -> Union[position, bool]:
"""
:param image: numpy image array
:param startPosition: from where to go to Tuple (x,y)
:return: newPosition (x,y), or false if new coords would fall out of bounds
"""
if not imageWrapper.boundsChecker(image, startPosition):
return False
if startPosition.coords[0] == image.shape[1] - 1:
if startPosition.coords[1] < image.shape[0] - 1:
return position((0, startPosition.coords[1] + 1))
return False
return position((startPosition.coords[0] + 1, startPosition.coords[1]))
def getCodels(image: np.ndarray, positionList: List[position]) -> List[codel]:
"""
Makes a list of codels from an image and a lits of positions to check
:param image: an np.ndarray representing the image
:param positionList: A list of positions, for which to find adjacent pixels of the same color
:return: A list of codels found in the given image
"""
if len(positionList) == 0:
return []
copiedList = positionList.copy()
newPosition = copiedList.pop(0)
if colors.isBlack(imageWrapper.getPixel(image, newPosition)):
return getCodels(image, copiedList)
newCodel = imageWrapper.getCodel(image, newPosition)
# Remove found positions from coords list
copiedList = list(set(copiedList) - newCodel.codel)
codelList = getCodels(image, copiedList)
codelList.append(newCodel)
return codelList
def edgesToGraphNode(image: np.ndarray, edges: List[edge]) -> Tuple[graphNode, List[BaseException]]:
"""
Constructs a dictionary with each pointer possibility as key and (token, coords) as value
:param image: Image required to find calculate tokens
:param edges: List[Tuple[coords, pointers]]
:return: A graphNode containing tokens for each edge given, and a list of exceptions occurred during creation
"""
node = graphNode(dict(map(lambda x, lambdaImage=image: (x.edge[1], (helperFunctions.edgeToToken(lambdaImage, x), x.edge[0])), edges)))
# Extract the exceptions from each edge
exceptions = list(map(lambda x: x[1][0], filter(lambda graphNodeItem: isinstance(graphNodeItem[1][0], BaseException), node.graphNode.items())))
return (node, exceptions)
def isGraphNodeTerminate(inputNode: graphNode) -> bool:
"""
Gets the token from the graphNode, and compares it against the toBlackToken from tokens.
:param inputNode: A graph node
:return: True if all tokens in graph node are toBlackTokens, False otherwise.
"""
return all(map(lambda x: isinstance(x[1][0], tokens.toBlackToken), inputNode.graphNode.items()))
def graphNodeToTerminate(inputNode: graphNode) -> graphNode:
"""
Replaces all tokens in the graphNode to terminate tokens
:param inputNode: A graph node
:return: A new graph node with only terminateTokens
"""
return graphNode(dict(map(lambda x: (x[0], (tokens.terminateToken(), x[1][1])), inputNode.graphNode.items())))
def codelToGraphNode(image: np.ndarray, inputCodel: codel, edgePointers: List[direction]) -> Tuple[graphNode, List[BaseException]]:
"""
:param image: image
:param inputCodel: set of positions within the same color
:param edgePointers: list of pointers to find tokens for
:return: A dictionary with each pointer possibility as key and (token, coords) as value, and a list of exceptions
"""
# make codel immutable
copiedCodel = copy.copy(inputCodel)
# Find all edges along the codel and edgepointers
edges = list(map(lambda pointers, lambdaCodel=copiedCodel: edge((movement.findEdge(lambdaCodel, pointers), pointers)), edgePointers))
newGraphNode = edgesToGraphNode(image, edges)
# If there were exceptions in the graph node, there is no need to terminate them
if len(newGraphNode[1]) > 0:
return newGraphNode
# Check if all tokens go either towards black pixels, or towards the edge. If thats the case, this is a terminate-node
if isGraphNodeTerminate(newGraphNode[0]):
return (graphNodeToTerminate(newGraphNode[0]), newGraphNode[1])
return newGraphNode
def codelsToGraph(image: np.ndarray, codels: List[codel]) -> Tuple[graph, List[BaseException]]:
"""
Converts a list of codels into a graph
:param image: Input image
:param codels: Input list of codels
:return: A tuple of a graph and a list of exceptions
"""
codels = codels.copy()
# Get an iterator of all possible directions (0,0), (0,1), (1,0) etc...
edgePointers = list(map(lambda i: direction((i % 4, int(i / 4))), iter(range(8))))
# If no more codels are to be graphed, return
if len(codels) == 0:
newGraph = graph(dict())
return (newGraph, [])
newNode = codelToGraphNode(image, codels[0], edgePointers)
newGraph = codelsToGraph(image, codels[1:])
newGraph[0].graph[codels[0]] = newNode[0]
errorList = newNode[1]
errorList.extend(newGraph[1])
return (newGraph[0], errorList)
def graphImage(image: np.ndarray) -> Tuple[graph, List[BaseException]]:
"""
Returns a dict with hashes of each codel as keys, and a codelDict as value. That codelDict contains hashed pointers (Tuple[int, int]) as keys to tokens as values.
:param image:
:return:
"""
coords = np.ndindex(image.shape[1], image.shape[0])
# Converts tuples of coordinates into position objects
positions = map(position, coords)
# Makes a list of non-black pixel positions
nonBlackPositions = list(filter(lambda pos: not colors.isBlack(imageWrapper.getPixel(image, pos)), positions))
# Gets all codels from all non-black pixel positions
allCodels = getCodels(image, nonBlackPositions)
# Makes a graph with the codel as key, and the node as value
return codelsToGraph(image, allCodels)