Optimizing GLViewWidget MeshItem plot - python

I'm plotting 100K+ cylinders which are added to a GLViewWidget like this:
openGlWidget = GLViewWidget()
points = np.array([
[0, 1, 2],
[0, 1, 5],
[4, 1, 6],
[4, 1, 10]
])
cyl = MeshData.cylinder(6, 6, radius=[.1, .1])
for i in range(0, len(points), 2):
mesh = GLMeshItem(meshdata=cyl)
p1, p2 = points[i], points[i+1]
v = p2 - p1
theta = np.arctan2(v[1], v[0])
phi = np.arctan2(np.linalg.norm(v[:2]), v[2])
tr = Transform3D()
tr.translate(*p1)
tr.rotate(theta * 180 / np.pi, 0, 0, 1)
tr.rotate(phi * 180 / np.pi, 0, 1, 0)
tr.translate(0, 0, 1)
tr.scale(1, 1, np.linalg.norm(v1))
mesh.setTransform(tr)
self.openGlWidget.addItem(mesh)
This of course gives low fps when rotating/paning and I assume a good part of it has to do with adding that many singular items. So, I started thinking about the possibility of combining the cylinders into one item, is this possible? And/or should I be using something else than the GLViewWidget?

Related

How can I display displacement and node numbers on a 3D truss?

I am trying to show displacement on a 3D truss example however I am running into an error.I have simplified my code below.I am able to show displacement on a 2D problem however I am unable on a 3D problem.I am also trying to show the node numbers at each node.I managed to put the nodes(green color) however the numbers are not showing even after i used the "plt.annotate" command.Can someone help me get the displacement and node numbers to show?Thank you in advance.
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
import sys
np.set_printoptions(threshold=sys.maxsize)
def plot_truss(nodes, elements, areas,forces):
# plot nodes in 3d
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
x = [i[0] for i in nodes.values()]
y = [i[1] for i in nodes.values()]
z = [i[2] for i in nodes.values()]
# size = 400
# ax.scatter(x, y, z, c='r', marker='o', s=size, zorder=5)
size = 400
offset = size / 4000
ax.scatter(x, y, z, c='y', s=size, zorder=5)
for i, location in enumerate(zip(x, y, z)):
plt.annotate(i + 1, (location[0] - offset, location[1] - offset), zorder=10)
# plot elements in 3d
for element in elements:
fromPoint = np.array(nodes[elements[element][0]])
toPoint = np.array(nodes[elements[element][1]])
x1 = fromPoint[0]
y1 = fromPoint[1]
z1 = fromPoint[2]
x2 = toPoint[0]
y2 = toPoint[1]
z2 = toPoint[2]
ax.plot([x1, x2], [y1, y2], zs=[z1, z2], c='b', linestyle='-', linewidth=5*areas[element], zorder=1)
nodes = {1: [0, 10, 0], 2: [0, 0, 0], 3: [10, 5, 0], 4: [0, 10, 10]}
areas = {1: 1.0, 2: 2.0, 3: 2.0}
elements = {1: [1, 3], 2: [2, 3], 3: [4, 3]}
forces = {1: [0, 0, 0], 2: [0, 0, 0], 3: [0, -200, 0], 4: [0, 0, 0]}
disps = {1: [0, 0, 0], 2: [0, 0, 0], 3: [ 3, -2, 4], 4: [0, 0, 0]}
def plt_displacement(nodes,elements,disps color="red"):
nodes_disp = np.copy(nodes)
nodes_disp[:, 0] += disp[::2, 0]
nodes_disp[:, 1] += disp[1::2, 0]
plt.scatter(nodes_disp[:, 0], nodes_disp[:, 1], color=color)
for e in elements:
x_tmp = [nodes_disp[e[0], 0], nodes_disp[e[1], 0]]
y_tmp = [nodes_disp[e[0], 1], nodes_disp[e[1], 1]]
plt.plot(x_tmp, y_tmp, color=color)
plt_displacement(nodes,elements,disps)
plot_truss(nodes, elements, areas, forces)
plt.show()
when i run the code I am getting the error below;
<ipython-input-47-758895b259be> in plt_displacement(elements, nodes, disp, color)
31 def plt_displacement(elements, nodes, disp, color="red"):
32 nodes_disp = np.copy(nodes)
---> 33 nodes_disp[:, 0] += disp[::2, 0]
34 nodes_disp[:, 1] += disp[1::2, 0]
35 plt.scatter(nodes_disp[:, 0], nodes_disp[:, 1], color=color)
IndexError: too many indices for array
It looks like you may have switched “nodes” and “elements” in your call to plt_displacement() (3rd and 12th to last lines) vs your definition.
plt_displacement(nodes,elements,disps)
def plt_displacement(elements, nodes, disp, color="red"):
I’m not sure exactly what plt_displacement is supposed to do. But looking at nodes_disp it is an array of no shape, so slicing won’t work.
>>> nodes_disp = np.copy(nodes)
>>> nodes_disp
array({1: [0, 10, 0], 2: [0, 0, 0], 3: [10, 5, 0], 4: [0, 10, 10]}, dtype=object)
>>> nodes_disp.shape
()
You can change the values to an array and slice it like this:
>>> npdisp = np.copy(list(disps.values()))
>>> nodes_disp
array([[ 0, 10, 0],
[ 0, 0, 0],
[10, 5, 0],
[ 0, 10, 10]])
But I’m not sure if that’s your intent.
Like wise you’d have to change the type of disp to an array in order to slice it, as it is a dictionary

Find Distance to Nearest Zero in NumPy Array

Let's say I have a NumPy array:
x = np.array([0, 1, 2, 0, 4, 5, 6, 7, 0, 0])
At each index, I want to find the distance to nearest zero value. If the position is a zero itself then return zero as a distance. Afterward, we are only interested in distances to the nearest zero that is to the right of the current position. The super naive approach would be something like:
out = np.full(x.shape[0], x.shape[0]-1)
for i in range(x.shape[0]):
j = 0
while i + j < x.shape[0]:
if x[i+j] == 0:
break
j += 1
out[i] = j
And the output would be:
array([0, 2, 1, 0, 4, 3, 2, 1, 0, 0])
I'm noticing a countdown/decrement pattern in the output in between the zeros. So, I might be able to do use the locations of the zeros (i.e., zero_indices = np.argwhere(x == 0).flatten())
What is the fastest way to get the desired output in linear time?
Approach #1 : Searchsorted to the rescue for linear-time in a vectorized manner (before numba guys come in)!
mask_z = x==0
idx_z = np.flatnonzero(mask_z)
idx_nz = np.flatnonzero(~mask_z)
# Cover for the case when there's no 0 left to the right
# (for same results as with posted loop-based solution)
if x[-1]!=0:
idx_z = np.r_[idx_z,len(x)]
out = np.zeros(len(x), dtype=int)
idx = np.searchsorted(idx_z, idx_nz)
out[~mask_z] = idx_z[idx] - idx_nz
Approach #2 : Another with some cumsum -
mask_z = x==0
idx_z = np.flatnonzero(mask_z)
# Cover for the case when there's no 0 left to the right
if x[-1]!=0:
idx_z = np.r_[idx_z,len(x)]
out = idx_z[np.r_[False,mask_z[:-1]].cumsum()] - np.arange(len(x))
Alternatively, last step of cumsum could be replaced by repeat functionality -
r = np.r_[idx_z[0]+1,np.diff(idx_z)]
out = np.repeat(idx_z,r)[:len(x)] - np.arange(len(x))
Approach #3 : Another with mostly just cumsum -
mask_z = x==0
idx_z = np.flatnonzero(mask_z)
pp = np.full(len(x), -1)
pp[idx_z[:-1]] = np.diff(idx_z) - 1
if idx_z[0]==0:
pp[0] = idx_z[1]
else:
pp[0] = idx_z[0]
out = pp.cumsum()
# Handle boundary case and assigns 0s at original 0s places
out[idx_z[-1]:] = np.arange(len(x)-idx_z[-1],0,-1)
out[mask_z] = 0
You could work from the other side. Keep a counter on how many non zero digits have passed and assign it to the element in the array. If you see 0, reset the counter to 0
Edit: if there is no zero on the right, then you need another check
x = np.array([0, 1, 2, 0, 4, 5, 6, 7, 0, 0])
out = x
count = 0
hasZero = False
for i in range(x.shape[0]-1,-1,-1):
if out[i] != 0:
if not hasZero:
out[i] = x.shape[0]-1
else:
count += 1
out[i] = count
else:
hasZero = True
count = 0
print(out)
You can use the difference between the indices of each position and the cumulative max of zero positions to determine the distance to the preceding zero. This can be done forward and backward. The minimum between forward and backward distance to the preceding (or next) zero will be the nearest:
import numpy as np
indices = np.arange(x.size)
zeroes = x==0
forward = indices - np.maximum.accumulate(indices*zeroes) # forward distance
forward[np.cumsum(zeroes)==0] = x.size-1 # handle absence of zero from edge
forward = forward * (x!=0) # set zero positions to zero
zeroes = zeroes[::-1]
backward = indices - np.maximum.accumulate(indices*zeroes) # backward distance
backward[np.cumsum(zeroes)==0] = x.size-1 # handle absence of zero from edge
backward = backward[::-1] * (x!=0) # set zero positions to zero
distZero = np.minimum(forward,backward) # closest distance (minimum)
results:
distZero
# [0, 1, 1, 0, 1, 2, 2, 1, 0, 0]
forward
# [0, 1, 2, 0, 1, 2, 3, 4, 0, 0]
backward
# [0, 2, 1, 0, 4, 3, 2, 1, 0, 0]
Special case where no zeroes are present on outer edges:
x = np.array([3, 1, 2, 0, 4, 5, 6, 0,8,8])
forward: [9 9 9 0 1 2 3 0 1 2]
backward: [3 2 1 0 3 2 1 0 9 9]
distZero: [3 2 1 0 1 2 1 0 1 2]
also works with no zeroes at all
[EDIT] non-numpy solutions ...
if you're looking for an O(N) solution that doesn't require numpy, you can apply this strategy using the accumulate function from itertools:
x = [0, 1, 2, 0, 4, 5, 6, 7, 0, 0]
from itertools import accumulate
maxDist = len(x) - 1
zeroes = [maxDist*(v!=0) for v in x]
forward = [*accumulate(zeroes,lambda d,v:min(maxDist,(d+1)*(v!=0)))]
backward = accumulate(zeroes[::-1],lambda d,v:min(maxDist,(d+1)*(v!=0)))
backward = [*backward][::-1]
distZero = [min(f,b) for f,b in zip(forward,backward)]
print("x",x)
print("f",forward)
print("b",backward)
print("d",distZero)
output:
x [0, 1, 2, 0, 4, 5, 6, 7, 0, 0]
f [0, 1, 2, 0, 1, 2, 3, 4, 0, 0]
b [0, 2, 1, 0, 4, 3, 2, 1, 0, 0]
d [0, 1, 1, 0, 1, 2, 2, 1, 0, 0]
If you don't want to use any library, you can accumulate the distances manually in a loop:
x = [0, 1, 2, 0, 4, 5, 6, 7, 0, 0]
forward,backward = [],[]
fDist = bDist = maxDist = len(x)-1
for f,b in zip(x,reversed(x)):
fDist = min(maxDist,(fDist+1)*(f!=0))
forward.append(fDist)
bDist = min(maxDist,(bDist+1)*(b!=0))
backward.append(bDist)
backward = backward[::-1]
distZero = [min(f,b) for f,b in zip(forward,backward)]
print("x",x)
print("f",forward)
print("b",backward)
print("d",distZero)
output:
x [0, 1, 2, 0, 4, 5, 6, 7, 0, 0]
f [0, 1, 2, 0, 1, 2, 3, 4, 0, 0]
b [0, 2, 1, 0, 4, 3, 2, 1, 0, 0]
d [0, 1, 1, 0, 1, 2, 2, 1, 0, 0]
My first intuition would be to use slicing. If x can be a normal list instead of a numpy array, then you could use
out = [x[i:].index(0) for i,_ in enumerate(x)]
if numpy is necessary then you can use
out = [np.where(x[i:]==0)[0][0] for i,_ in enumerate(x)]
but this is less efficient because you are finding all zero locations to the right of the value and then pulling out just the first. Almost definitely a better way to do this in numpy.
Edit: I am sorry, I misunderstood. This will give you the distance to the nearest zeros - may it be at left or right. But you can use d_right as intermediate result. This does not cover the edge case of not having any zero to the right though.
import numpy as np
x = np.array([0, 1, 2, 0, 4, 5, 6, 7, 0, 0])
# Get the distance to the closest zero from the left:
zeros = x == 0
zero_locations = np.argwhere(x == 0).flatten()
zero_distances = np.diff(np.insert(zero_locations, 0, 0))
temp = x.copy()
temp[~zeros] = 1
temp[zeros] = -(zero_distances-1)
d_left = np.cumsum(temp) - 1
# Get the distance to the closest zero from the right:
zeros = x[::-1] == 0
zero_locations = np.argwhere(x[::-1] == 0).flatten()
zero_distances = np.diff(np.insert(zero_locations, 0, 0))
temp = x.copy()
temp[~zeros] = 1
temp[zeros] = -(zero_distances-1)
d_right = np.cumsum(temp) - 1
d_right = d_right[::-1]
# Get the smallest distance from both sides:
smallest_distances = np.min(np.stack([d_left, d_right]), axis=0)
# np.array([0, 1, 1, 0, 1, 2, 2, 1, 0, 0])

Generate image matrix from Freeman chain code

Suppose I have a 8-direction freeman chain code as follows, in a python list:
freeman_code = [3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 5]
Where directions would be defined as follows:
I need to convert this to an image matrix of variable dimensions with valules of 1s and 0s where 1s would depict the shape, as follows, for example:
image_matrix = [
[0, 0, 1, 0, 0, 1],
[0, 0, 0, 1, 0, 1],
[0, 0, 0, 0, 1, 1]
]
Of course, the above is not an exact implementation of the above freeman code. Is there any implementation in python, or in any language that achieves this?
My idea (in python):
Use a defaultdict of defaultdicts with 0 as default:
ImgMatrixDict = defaultdict(lambda: defaultdict(lambda:0))
and then start at a midpoint, say ImgMatrixDict[25][25], and then change values to 1 depending on the freeman code values as I traverse. Afte tis I would convert ImgMatrixDict to a list of lists.
Is this a viable idea or are there any existing libraries or suggestions to implement this? Any idea/pseudo-code would be appreciated.
PS: On performance, yes it would not be important as I won't be doing this in realtime, but generally a code would be around 15-20 charactors in length. I assumed a 50*50 by matrix would suffice for this purpose.
If I am understanding your question correctly:
import numpy as np
import matplotlib.pyplot as plt
freeman_code = [3, 3, 3, 6, 6, 4, 6, 7, 7, 0, 0, 6]
img = np.zeros((10,10))
x, y = 4, 4
img[y][x] = 1
for direction in freeman_code:
if direction in [1,2,3]:
y -= 1
if direction in [5,6,7]:
y += 1
if direction in [3,4,5]:
x -= 1
if direction in [0,1,7]:
x += 1
img[y][x] = 1
plt.imshow(img, cmap='binary', vmin=0, vmax=1)
plt.show()
Here is a solution in python. A dictionary is not adapted to this problem, you would better use a list of list to simulate the table.
D = 10
# DY, DX
FREEMAN = [(0, 1), (-1, 1), (-1, 0), (-1, -1), (0, -1), (1, -1), (1, 0), (1, 1)]
freeman_code = [3, 3, 3, 3, 6, 6, 6, 6, 0, 0, 0, 0]
image = [[0]*D for x in range(D)]
y = D/2
x = D/2
image[y][x] = 1
for i in freeman_code:
dy, dx = FREEMAN[i]
y += dy
x += dx
image[y][x] = 1
print("freeman_code")
print(freeman_code)
print("image")
for line in image:
strline = "".join([str(x) for x in line])
print(strline)
>0000000000
>0100000000
>0110000000
>0101000000
>0100100000
>0111110000
>0000000000
>0000000000
>0000000000
>0000000000
Note that the image creation is a condensed expression of:
image = []
for y in range(D):
line = []
for x in range(D):
line.append(0)
image.append(line)
If one day, you need better performance for bigger images, there are solutions using numpy Library but requiring a good knowledge of basic python. Here is an example:
import numpy as np
D = 10
# DY, DX
FREEMAN = [(0, 1), (-1, 1), (-1, 0), (-1, -1), (0, -1), (1, -1), (1, 0), (1, 1)]
DX = np.array([1, 1, 0, -1, -1, -1, 0, 1])
DY = np.array([0, -1, -1, -1, 0, 1, 1, 1])
freeman_code = np.array([3, 3, 3, 3, 6, 6, 6, 6, 0, 0, 0, 0])
image = np.zeros((D, D), int)
y0 = D/2
x0 = D/2
image[y0, x0] = 1
dx = DX[freeman_code]
dy = DY[freeman_code]
xs = np.cumsum(dx)+x0
ys = np.cumsum(dy)+y0
print(xs)
print(ys)
image[ys, xs] = 1
print("freeman_code")
print(freeman_code)
print("image")
print(image)
Here, all loops built with 'for' on previous solution are fast-processed in C.

Plotting binary data in python

I have some data that looks like:
data = [1,2,4,5,9] (random pattern of increasing integers)
And I want to plot it in a binary horizontal line so that y=1 for every x value specified in data and zero otherwise.
I have a few different data arrays that I'd like to stack, similar to this style (this is CCD clocking data but the plot format looks ideal)
I think I need to create a list of ones for my data array, but how do I specify the zero value for everything not in the array?
Thanks
You got the point. You can create a list with 1 in any position specified in data and 0 elsewhere. This can be done very easily with a list comprehension
def binary_data(data):
return [1 if x in data else 0 for x in range(data[-1] + 1)]
which will act like this:
>>> data = [1, 2, 4, 5, 9]
>>> bindata = binary_data(data)
>>> bindata
[0, 1, 1, 0, 1, 1, 0, 0, 0, 1]
Now all you have to do is plot it... or better step it since it's binary data and step() looks way better:
import numpy as np
from matplotlib.pyplot import step, show
def binary_data(data):
return [1 if x in data else 0 for x in range(data[-1] + 1)]
data = [1, 2, 4, 5, 9]
bindata = binary_data(data)
xaxis = np.arange(0, data[-1] + 1)
yaxis = np.array(bindata)
step(xaxis, yaxis)
show()
To plot multiple data arrays stacked on the same figure you could tweak binary_data() like this:
def binary_data(data, yshift=0):
return [yshift+1 if x in data else yshift for x in range(data[-1] + 1)]
so now you can set yshift parameter to shift data arrays on the y-axis. E.g,
>>> data = [1, 2, 4, 5, 9]
>>> bindata1 = binary_data(data)
>>> bindata1
[0, 1, 1, 0, 1, 1, 0, 0, 0, 1]
>>> bindata2 = binary_data(data, 2)
>>> bindata2
[2, 3, 3, 2, 3, 3, 2, 2, 2, 3]
Let's say you have data1, data2 and data3 to plot stacked, you'd go like:
import numpy as np
from matplotlib.pyplot import step, show
def binary_data(data, yshift=0):
return [yshift+1 if x in data else yshift for x in range(data[-1] + 1)]
data1 = [1, 2, 4, 5, 9]
bindata1 = binary_data(data1)
x1 = np.arange(0, data1[-1] + 1)
y1 = np.array(bindata1)
data2 = [1, 4, 9]
bindata2 = binary_data(data2, 2)
x2 = np.arange(0, data2[-1] + 1)
y2 = np.array(bindata2)
data3 = [1, 2, 8, 9]
bindata3 = binary_data(data3, 4)
x3 = np.arange(0, data3[-1] + 1)
y3 = np.array(bindata3)
step(x1, y1, x2, y2, x3, y3)
show()
that you can easily edit to make it work with an arbitrary amount of data arrays:
data = [ [1, 2, 4, 5, 9],
[1, 4, 9],
[1, 2, 8, 9] ]
for shift, d in enumerate(data):
bindata = binary_data(d, 2 * shift)
x = np.arange(0, d[-1] + 1)
y = np.array(bindata)
step(x, y)
show()
Finally if you are dealing with data arrays with different length (say [1,2] and [15,16]) and you don't like plots that vanish in the middle of the figure you can tweak binary_data() again to force its range to the maximum range of your data.
import numpy as np
from matplotlib.pyplot import step, show
def binary_data(data, limit, yshift=0):
return [yshift+1 if x in data else yshift for x in range(limit)]
data = [ [1, 2, 4, 5, 9, 12, 13, 14],
[1, 4, 10, 11, 20, 21, 22],
[1, 2, 3, 4, 15, 16, 17, 18] ]
# find out the longest data to plot
limit = max( [ x[-1] + 1 for x in data] )
x = np.arange(0, limit)
for shift, d in enumerate(data):
bindata = binary_data(d, limit, 2 * shift)
y = np.array(bindata)
step(x, y)
show()
Edit: As #ImportanceOfBeingErnest suggested, if you prefer to perform data to bindata conversion without having to define your own binary_data() function you could use numpy.zeros_like(). Just pay more attention when you stack them:
import numpy as np
from matplotlib.pyplot import step, show
data = [ [1, 2, 4, 5, 9, 12, 13, 14],
[1, 4, 10, 11, 20, 21, 22],
[1, 2, 3, 4, 15, 16, 17, 18] ]
# find out the longest data to plot
limit = max( [ x[-1] + 1 for x in data] )
x = np.arange(0, limit)
for shift, d in enumerate(data):
y = np.zeros_like(x)
y[d] = 1
# don't forget to shift
y += 2*shift
step(x, y)
show()
You can create an array with all zeros and assign 1 for those elements in data
import numpy as np
data = [1,2,4,5,9]
t = np.arange(0,data[-1]+1)
x = np.zeros_like(t)
x[data] = 1
You might then plot it with the step function
import matplotlib.pyplot as plt
plt.step(t,x, where="post")
plt.show()
or with where = "pre", depending on how to interprete your data

counting 2*2 squares in n*n binary matrix

I've got an n*n binary matrix (only 1 and 0), how can I go about counting 2*2 squares (squares are made by 1)
for example A=[[1,1],[1,1]] is considered to make one 2*2 square. or
A = [[1, 1, 0, 1],
[1, 1, 1, 1],
[1, 1, 1, 0],
[0, 1, 1, 1]]
is considered to make four 2*2 squares.
here's my code for this , but I just don't know why it doesn't work.
A = [[1, 1, 0, 1] , [1, 1, 1, 1], [1, 1, 1, 0], [0, 1, 1, 1]]
result=[]
for x in range(len(A)-1):
for y in range(len(A)-1):
if A[x][y]==1:
if A[x+1][y]==1:
if A[x][y+1]==1 or A[x][y-1]==1 and A[x+1][y] or A[x+1][y-1]==1:
result.append(1)
if A[x-1][y]==1:
if A[x][y+1]==1 or A[x][y-1]==1 and A[x-1][y] or A[x-1][y-1]==1:
result.append(1)
print(len(result))
`
Generate indices for width - 1 by height - 1; itertools.product() can do this for us.
Test 4 coordinates for each generated index using all() to only test as many as needed to disprove a square exists.
Use sum() with a generator to count the number of squares found; faster than manually counting with a list or a counter.
Together with lambda to test for squares, this then becomes:
from itertools import product
def count_squares(A):
width, height = len(A[0]), len(A)
indices = product(range(width - 1), range(height - 1))
is_square = lambda x, y: all(A[a][b] == 1 for a, b in product((x, x + 1), (y, y + 1)))
return sum(1 for x, y in indices if is_square(x, y))
Demo:
>>> from itertools import product
>>> count_squares([[1,1],[1,1]])
>>> def count_squares(A):
... width, height = len(A[0]), len(A)
... indices = product(range(width - 1), range(height - 1))
... is_square = lambda x, y: all(A[a][b] == 1 for a, b in product((x, x + 1), (y, y + 1)))
... return sum(1 for x, y in indices if is_square(x, y))
...
>>> count_squares([[1,1],[1,1]])
1
>>> count_squares([[1, 1, 0, 1] , [1, 1, 1, 1], [1, 1, 1, 0], [0, 1, 1, 1]])
4
To get the column count use len(A[x]) so
for y in range(len(A)-1)
becomes
for y in range(len(A[x])-1)
Change
if A[x][y]==1:
if A[x+1][y]==1:
if A[x][y+1]==1 or A[x][y-1]==1 and A[x+1][y] or A[x+1][y-1]==1:
result.append(1)
if A[x-1][y]==1:
if A[x][y+1]==1 or A[x][y-1]==1 and A[x-1][y] or A[x-1][y-1]==1:
result.append(1)
To
if A[x][y]==1 and A[x+1][y]==1 and a[x+1][y+1]==1 and a[x][y+1]:
result.append(1)
Unless you want to count squares multiple times.
Using scipy.signal there is a simple solution that finds the correlation between your target and the input. This is nice since it generalizes to "almost matches" and arbitrary shapes!
import numpy as np
from scipy import signal
A = np.array([[1,1,0,1] ,[1,1,1,1],[1,1,1,0],[0,1,1,1]],dtype=int)
b = np.ones((2,2),dtype=int)
c = signal.correlate(A, b, 'valid')
idx = np.where(c==4)
count = sum(idx[0])
print count
This gives 4 as expected. If you find this interesting, there is a (longer) answer that uses this same idea:
Finding matching submatrices inside a matrix
I multipliply the values of every 2*2-submatrix and sum up:
A = [[1, 1, 0, 1],
[1, 1, 1, 1],
[1, 1, 1, 0],
[0, 1, 1, 1]]
sum( A[x][y]*A[x+1][y]*A[x][y+1]*A[x+1][y+1]
for y in range(len(A)-1)
for x in range(len(A[y])-1)
)
Out[79]: 4

Categories