How to search shapes in a graph? - python

I have an adjancency matrix am of a 5 node undirected graph where am(i,j) = 1 means node i is connected to node j. I generated all possible
versions of this 5-node graph by the following code:
import itertools
graphs = list(itertools.product([0, 1], repeat=10))
This returns me an array of arrays where each element is a possible configuration of the matrix (note that I only generate these for upper triangle since matrix is symetric):
[ (0, 0, 0, 0, 0, 0, 1, 0, 1, 1),
(0, 0, 0, 0, 0, 0, 1, 1, 0, 0),
(0, 0, 0, 0, 0, 0, 1, 1, 0, 1),
(0, 0, 0, 0, 0, 0, 1, 1, 1, 0),
(0, 0, 0, 0, 0, 0, 1, 1, 1, 1),
....]
where (0, 0, 0, 0, 0, 0, 1, 1, 1, 1) actually corresponds to:
m =
0 0 0 0 0
0 0 0 0 0
0 0 1 1 1
0 0 0 0 1
0 0 0 0 0
I would like to search for all possible triangle shapes in this graph. For example, here, (2, 4), (2,5) and (4, 5) together makes a triangle shape:
m =
0 0 0 0 0
0 0 0 1 1
0 0 0 0 0
0 0 0 0 1
0 0 0 0 0
Is there a known algorithm to do such a search in a graph? Note that triangle shape is an example here, ideally I would like to find a solution that would search any particular shape, for example a square or a pentagon. How can I encode these shapes to search in the first place? Any help, reference, algorithm name is appreciated.

Your explanation for the graph representation is not quite understandable.
However, finding cycles of size k is NP-complete problem when k is your input (since it includes the NP-complete hamiltonian-cycle problem).
If that is the case, then you should have a look at these posts:
Finding all cycles of a certain length in a graph
Finding all cycles in undirected graphs
But, if you have fixes size lengths, then this problem can be solved in good polinomial time.
Here is an article about this very issue:
Finding and Counting Given Length Cycles | Algorithmica

Related

PIL's Image.fromarray function() confused me

I want to create a small image with PIL, my idea is first creating an ndarray object with numpy, then transforming it into a Image object, but it doesn't work!
small = np.array([[0, 1, 1, 0, 1, 0],
[0, 1, 1, 0, 1, 0]])
small = Image.fromarray(small, 'L')
print(small.size)
these codes print (6, 2), so why it transposes my original input?
What made my even more confused is that when I try to print all the pixels:
for i in range(6):
for j in range(2):
print(small.getpixel((i, j)), end='')
it prints out : 0 0 0 0 0 1 0 0 0 0 0 0
I had no idea about what have happened ........
Image.fromarray(small,'L')
expects an 8 bit Input? converting the Input Array to np.int8 works for me..
small = np.array([[0, 1, 1, 0, 1, 0],
[0, 1, 1, 0, 1, 0]],dtype=np.int8)
Just removing the 'L' will also work

Count string elements on a map based on where strings are and fill grid with the counts

I have a list called my_map that contains two different kinds of string values '.' and '&'. Now, for each value [x][y] that is a '.' I want to count the number of times an '&' was found in any of the eight directions next to the '.'
I created a grid to store the counts but I am just not able to formulate my conditions correctly. I can not use numpy arrays.
Note: 'S' and 'E' are treated like '.'
my_map = ['................' '....&...........' '..........E.....'
'&&..&...........' '....&&&.........' '......&&&&..&&..'
'................' '.......&........' '.....&.&........'
'....S...........' '.......&.&&.....']
def create_grid(my_map):
grid = [[0]*(len(my_map[0])) for x in range(len(my_map))]
return grid
grid = create_grid(my_map)
for x, y in [(x,y) for x in range(len(my_map)) for y in range(len(my_map[0]))]:
#any '&' north ?
if my_map[x][y+1]== '&' and my_map[x][y]=='.':
grid[x][y]+= 1
#any '&' west ?
if my_map[x-1][y]== '&' and my_map[x][y]=='.':
grid[x][y]+=1
#any '&' south ?
if my_map[x][y-1]== '&'and my_map[x][y]=='.':
grid[x][y]+=1
#any '&' east ?
if my_map[x+1][y]== '&'and my_map[x][y]=='.':
grid[x][y]+=1
#any '&' north-east ?
if my_map[x+1][y+1] == '&'and my_map[x][y]=='.':
grid[x][y]+=1
#any '&' south-west ?
if my_map[x-1][y-1] == '&'and my_map[x][y]=='.':
grid[x][y]+=1
#any '&' south-east ?
if my_map[x+1][y-1]== '&'and my_map[x][y]=='.':
grid[x][y]+=1
#any '&' north-west?
if my_map[x-1][y+1]== '&'and my_map[x][y]=='.':
grid[x][y]+=1
#desired output for first 3 rows
grid = [[0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0],[2,2,1,1,2,1,0,0,0,0,0,0,0,0,0,0]]
At the moment, I get an 'IndexError: string index out of range'. I dont know how to limit the range so it will still be correct.The only thing I managed so far was a grid displaying 1s for all '.' and 0s for all '&'.
I don't think the nested conditionals are appropriate here; each outer conditional must be true for the inner ones to be evaluated. They should be independent of each other and sequential.
It's also a lot of work and error-prone to enumerate every conditional by hand. For each cell, there are up to 8 directions in which a neighbor might live, and we do the exact same check on each direction. A loop is the appropriate construct for doing this; each loop iteration checks one neighboring cell, determining whether it's in bounds and of the appropriate character.
Furthermore, since your grid has few &, it makes sense to only perform neighbor checks for & characters. For each one, increment counts for neighboring .s. Do the opposite if the grid is predominantly & characters.
my_map = [
'................',
'....&...........',
'..........E.....',
'&&..&...........',
'....&&&.........',
'......&&&&..&&..',
'................',
'.......&........',
'.....&.&........',
'....S...........',
'.......&.&&.....'
]
grid = [[0] * len(x) for x in my_map]
directions = [
[-1, 0], [1, 0], [0, 1], [0, -1],
[-1, -1], [1, 1], [1, -1], [-1, 1]
]
for row in range(len(my_map)):
for col in range(len(my_map[row])):
if my_map[row][col] == "&":
for x, y in directions:
y += row
x += col
if y < len(my_map) and y >= 0 and \
x < len(my_map[y]) and x >= 0 and \
my_map[y][x] != "&":
grid[y][x] += 1
for row in grid:
print(row)
Output:
[0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[2, 2, 1, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 1, 2, 0, 4, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0]
[2, 2, 1, 2, 0, 0, 0, 4, 3, 2, 1, 1, 2, 2, 1, 0]
[0, 0, 0, 1, 2, 4, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0]
[0, 0, 0, 0, 0, 1, 3, 4, 4, 2, 1, 1, 2, 2, 1, 0]
[0, 0, 0, 0, 1, 1, 3, 0, 2, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 1, 0, 3, 0, 2, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 1, 1, 3, 2, 3, 2, 2, 1, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 1, 0, 2, 0, 0, 1, 0, 0, 0, 0]
And a version that overlays counts with the original map Minesweeper-style:
0 0 0 1 1 1 0 0 0 0 0 0 0 0 0 0
0 0 0 1 & 1 0 0 0 0 0 0 0 0 0 0
2 2 1 2 2 2 0 0 0 0 0 0 0 0 0 0
& & 1 2 & 4 2 1 0 0 0 0 0 0 0 0
2 2 1 2 & & & 4 3 2 1 1 2 2 1 0
0 0 0 1 2 4 & & & & 1 1 & & 1 0
0 0 0 0 0 1 3 4 4 2 1 1 2 2 1 0
0 0 0 0 1 1 3 & 2 0 0 0 0 0 0 0
0 0 0 0 1 & 3 & 2 0 0 0 0 0 0 0
0 0 0 0 1 1 3 2 3 2 2 1 0 0 0 0
0 0 0 0 0 0 1 & 2 & & 1 0 0 0 0
Try it!

How to get an object bounding box given pixel label in python?

Say I have a scene parsing map for an image, each pixel in this scene parsing map indicates which object this pixel belongs to. Now I want to get bounding box of each object, how can I implement this in python?
For a detail example, say I have a scene parsing map like this:
0 0 0 0 0 0 0
0 1 1 0 0 0 0
1 1 1 1 0 0 0
0 0 1 1 1 0 0
0 0 1 1 1 0 0
0 0 0 0 0 0 0
0 0 0 0 0 0 0
So the bounding box is:
0 0 0 0 0 0 0
1 1 1 1 1 0 0
1 0 0 0 1 0 0
1 0 0 0 1 0 0
1 1 1 1 1 0 0
0 0 0 0 0 0 0
0 0 0 0 0 0 0
Actually, in my task, just know the width and height of this object is enough.
A basic idea is to search four edges in the scene parsing map, from top, bottom, left and right direction. But there might be a lot of small objects in the image, this way is not time efficient.
A second way is to calculate the coordinates of all non-zero elements and find the max/min x/y. Then calculate weight and height using these x and y.
Is there any other more efficient way to do this? Thx.
If you are processing images, you can use scipy's ndimage library.
If there is only one object in the image, you can get the measurements with scipy.ndimage.measurements.find_objects (http://docs.scipy.org/doc/scipy-0.16.1/reference/generated/scipy.ndimage.measurements.find_objects.html):
import numpy as np
from scipy import ndimage
a = np.array([[0, 0, 0, 0, 0, 0, 0],
[0, 1, 1, 0, 0, 0, 0],
[1, 1, 1, 1, 0, 0, 0],
[0, 0, 1, 1, 1, 0, 0],
[0, 0, 1, 1, 1, 0, 0],
[0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0]])
# Find the location of all objects
objs = ndimage.find_objects(a)
# Get the height and width
height = int(objs[0][0].stop - objs[0][0].start)
width = int(objs[0][1].stop - objs[0][1].start)
If there are many objects in the image, you first have to label each object and then get the measurements:
import numpy as np
from scipy import ndimage
a = np.array([[0, 0, 0, 0, 0, 0, 0],
[0, 1, 1, 0, 0, 0, 0],
[1, 1, 1, 1, 0, 0, 0],
[0, 0, 1, 1, 1, 0, 0],
[0, 0, 1, 1, 1, 0, 0],
[0, 0, 0, 0, 0, 0, 0],
[0, 0, 1, 1, 1, 0, 0]]) # Second object here
# Label objects
labeled_image, num_features = ndimage.label(a)
# Find the location of all objects
objs = ndimage.find_objects(labeled_image)
# Get the height and width
measurements = []
for ob in objs:
measurements.append((int(ob[0].stop - ob[0].start), int(ob[1].stop - ob[1].start)))
If you check ndimage.measurements, you can get more measurements: center of mass, area...
using numpy:
import numpy as np
ind = np.nonzero(arr.any(axis=0))[0] # indices of non empty columns
width = ind[-1] - ind[0] + 1
ind = np.nonzero(arr.any(axis=1))[0] # indices of non empty rows
height = ind[-1] - ind[0] + 1
a bit more explanation:
arr.any(axis=0) gives a boolean array telling you if the columns are empty (False) or not (True). np.nonzero(arr.any(axis=0))[0] then extract the non zero (i.e. True) indices from that array. ind[0] is the first element of that array, hence the left most column non empty column and ind[-1] is the last element, hence the right most non empty column. The difference then gives the width, give or take 1 depending on whether you include the borders or not.
Similar stuff for the height but on the other axis.

Permutations but with some numbers kept in an order

Ok, I have had a browse around and I have looking for either a C or python solution to this problem. I would prefer python...although it is my weaker language (of 2 very weak languages).
A set of numbers, such as 0 0 1 7 0 0 3 0 0 4
Find all permutations of the set.
The numbers >0 must stay in that order (NOT POSITION!)
There MUST be a 0 between numbers, a 0 is not required at the start and end of the set though. As long as there is AT LEAST ONE 0 between numbers >0.
So firstly, I thought of just finding all possible permutations and then removing the chaff (checking that if n>0 , !n+1>0) for each permutation and then the first number >0 == 1, 2nd # >0 ==7 etc. etc.
I then stopped and thought that was daft, say there were 12 numbers, that would give 12! permutations. This in the order of 500,000,000 permutations of which I would have to run through again to get rid of chaff.
Say I had 40-50 sets of these number sets to go through, that is a fair whack of time.
Is there a more logical way?
I thought of somehow having python do permutations somehow taking those rules in to account (if n>0, n+1 MUST == 0) and (n=first number, n2=2nd etc.)
An example of a smaller set would be (NOT ALL PERMUTATIONS, but gives idea):
1,2,3,0,0,0,0,0
1,0,2,0,3,0,0,0
0,1,0,2,0,3,0,0
0,0,1,0,2,0,3,0
0,0,1,0,0,2,0,3
0,1,0,0,2,0,3,0
etc. etc.
So 1,2,3 is in order but the "0"s are just shifted about?
Thanks!
Basically you want to reduce the number of combinations you have to compute by grouping things according to their invariants. Since the non-zero numbers must be in a fixed order let's start with that:
1 2 3
Since there must be 0's between them add them in
1 0 2 0 3
Now what you are left with is three 0's to place and you need to figure how many combinations give distinct sequences. Clearly from this example the possible positions you have are: before the 1, between the 1 and 2, between the 2 and 3 and after the 3. You have 4 positions in which to decide how to split up the remaining three 0's. This is a combination problem with repetition, for which the solution here is (3 + 4 - 1) Choose 3 (which is 20).
Hopefully the way that I went through this example problem is enough for you to generalize this to arbitrary sequences, so I will leave that as an exercise to the reader.
def find_permutations(l):
n = [e for e in l if e] # Strip zeros.
# Interspace non-zeros with zeros.
m = [j for i in n for j in (i,0)][:-1]
def fill(m):
if len(m) == len(l):
yield tuple(m)
else:
# Recursively fill with zeros.
for i in range(len(m)+1):
for o in fill(m[:i] + [0] + m[i:]):
yield tuple(o)
return sorted(set(fill(m)))
I think this should cover it. So for instance (in python 3), you could do:
>>> [print(p) for p in find_permutations([1,2,3,0,0,0,0,0])]
(0, 0, 0, 1, 0, 2, 0, 3)
(0, 0, 1, 0, 0, 2, 0, 3)
(0, 0, 1, 0, 2, 0, 0, 3)
(0, 0, 1, 0, 2, 0, 3, 0)
(0, 1, 0, 0, 0, 2, 0, 3)
(0, 1, 0, 0, 2, 0, 0, 3)
(0, 1, 0, 0, 2, 0, 3, 0)
(0, 1, 0, 2, 0, 0, 0, 3)
(0, 1, 0, 2, 0, 0, 3, 0)
(0, 1, 0, 2, 0, 3, 0, 0)
(1, 0, 0, 0, 0, 2, 0, 3)
(1, 0, 0, 0, 2, 0, 0, 3)
(1, 0, 0, 0, 2, 0, 3, 0)
(1, 0, 0, 2, 0, 0, 0, 3)
(1, 0, 0, 2, 0, 0, 3, 0)
(1, 0, 0, 2, 0, 3, 0, 0)
(1, 0, 2, 0, 0, 0, 0, 3)
(1, 0, 2, 0, 0, 0, 3, 0)
(1, 0, 2, 0, 0, 3, 0, 0)
(1, 0, 2, 0, 3, 0, 0, 0)
Was this similar to what you had in mind?
Edit: basically what the function called fill does is insert a zero between each number of the list, and recurse. Whenever enough numbers are recorded in the fill function (list length of recursively generated numbers equals list length of original input) a tuple of numbers is returned.
The only reason for converting to tuples when returning is that the type must be hashable to use in a set, as seen on the last line of the find_permutations function. sorted is for niceness.

Filling in a concave polygon represented as a binary matrix

In my task, I represent a concave polygon as a matrix of ones and zeros, where one means that the given point belongs to the polygon. For instance, the following are a simple square and a u-shaped polygon:
0 0 0 0 0 0 0 0 0 0 0
0 1 1 0 0 1 1 0 0 1 1
0 1 1 0 0 1 1 1 1 1 1
0 0 0 0 0 1 1 1 1 1 1
However, sometimes I get an incomplete representation, in which: (1) all boundary points are included, and (2) some internal points are missing. For example, in the following enlarged version of the u-shaped polygon, the elements at positions (1,1), (1,6), (3,1), ..., (3,6)* are "unfilled". The goal is to fill them (i.e., change their value to 1).
1 1 1 0 0 1 1 1
1 0 1 0 0 1 0 1
1 1 1 1 1 1 0 1
1 0 0 0 0 0 0 1
1 1 1 1 1 1 1 1
Do you know if there's an easy way to do this in Python/NumPy?
*(row, column), starting counting from the top left corner
This is a very well known problem in image processing that can be solved using morphological operators.
With that, you can use scipy's binary_fill_holes to fill the holes in your mask:
>>> import numpy as np
>>> from scipy.ndimage import binary_fill_holes
>>> data = np.array([[1, 1, 1, 0, 0, 1, 1, 1],
[1, 0, 1, 0, 0, 1, 0, 1],
[1, 1, 1, 1, 1, 1, 0, 1],
[1, 0, 0, 0, 0, 0, 0, 1],
[1, 1, 1, 1, 1, 1, 1, 1]])
>>> filled = binary_fill_holes(data).astype(int)
>>> filled
array([[1, 1, 1, 0, 0, 1, 1, 1],
[1, 1, 1, 0, 0, 1, 1, 1],
[1, 1, 1, 1, 1, 1, 1, 1],
[1, 1, 1, 1, 1, 1, 1, 1],
[1, 1, 1, 1, 1, 1, 1, 1]])
I do not believe there would exist some generic purpose solution in Python or whatever. This is classic breadth-first graph search. For each 0 either exists a path of adjacent zeros, so that at least one of those zeros is at position (y,x) so that (x = 0 or y = 0 or x = maxx or y = maxy) or this 0 should be changed to 1.
Maybe an answer here will be helpful to you: How to trace the path in a Breadth-First Search?

Categories