I have an image.
I want to obtain a 3x3 window (neighbouring pixels) for every pixel in the image.
I have this Python code:
for x in range(2,r-1,1):
for y in range(2,c-1,1):
mask5=numpy.array([cv.Get2D(copy_img,x-1,y-1),cv.Get2D(copy_img,x-1,y),cv.Get2D(copy_img,x-1,y+1),cv.Get2D(copy_img,x,y-1),cv.Get2D(copy_img,x,y),cv.Get2D(copy_img,x,y+1),cv.Get2D(copy_img,x+1,y-1),cv.Get2D(copy_img,x+1,y),cv.Get2D(copy_img,x+1,y+1)])
cent=[cv.Get2D(copy_img,x,y)]
mask5 is the 3x3 window. cent is the center pixel.
Is there a more efficient way to do this - i.e. using maps, iterators - anything but the two nested loops I've used?
This can be done faster, by reshaping and swapping axes, and then repeating over all kernel elements, like this:
im = np.arange(81).reshape(9,9)
print np.swapaxes(im.reshape(3,3,3,-1),1,2)
This gives you an array of 3*3 tiles which tessalates across the surface:
[[[[ 0 1 2] [[ 3 4 5] [[ 6 7 8]
[ 9 10 11] [12 13 14] [15 16 17]
[18 19 20]] [21 22 23]] [24 25 26]]]
[[[27 28 29] [[30 31 32] [[33 34 35]
[36 37 38] [39 40 41] [42 43 44]
[45 46 47]] [48 49 50]] [51 52 53]]]
[[[54 55 56] [[57 58 59] [[60 61 62]
[63 64 65] [66 67 68] [69 70 71]
[72 73 74]] [75 76 77]] [78 79 80]]]]
To get the overlapping tiles we need to repeat this 8 further times, but 'wrapping' the array, by using a combination of vstack and column_stack. Note that the right and bottom tile arrays wrap around (which may or may not be what you want, depending on how you are treating edge conditions):
im = np.vstack((im[1:],im[0]))
im = np.column_stack((im[:,1:],im[:,0]))
print np.swapaxes(im.reshape(3,3,3,-1),1,2)
#Output:
[[[[10 11 12] [[13 14 15] [[16 17 9]
[19 20 21] [22 23 24] [25 26 18]
[28 29 30]] [31 32 33]] [34 35 27]]]
[[[37 38 39] [[40 41 42] [[43 44 36]
[46 47 48] [49 50 51] [52 53 45]
[55 56 57]] [58 59 60]] [61 62 54]]]
[[[64 65 66] [[67 68 69] [[70 71 63]
[73 74 75] [76 77 78] [79 80 72]
[ 1 2 3]] [ 4 5 6]] [ 7 8 0]]]]
Doing it this way you wind up with 9 sets of arrays, so you then need to zip them back together. This, and all the reshaping generalises to this (for arrays where the dimensions are divisible by 3):
def new(im):
rows,cols = im.shape
final = np.zeros((rows, cols, 3, 3))
for x in (0,1,2):
for y in (0,1,2):
im1 = np.vstack((im[x:],im[:x]))
im1 = np.column_stack((im1[:,y:],im1[:,:y]))
final[x::3,y::3] = np.swapaxes(im1.reshape(rows/3,3,cols/3,-1),1,2)
return final
Comparing this new function to looping through all the slices (below), using timeit, its about 4 times faster, for a 300*300 array.
def old(im):
rows,cols = im.shape
s = []
for x in xrange(1,rows):
for y in xrange(1,cols):
s.append(im[x-1:x+2,y-1:y+2])
return s
I think the following does what you are after. The loop is only over the 9 elements. I'm sure there is a way of vectorizing it, but it's probably not worth the effort.
import numpy
im = numpy.random.randint(0,50,(5,7))
# idx_2d contains the indices of each position in the array
idx_2d = numpy.mgrid[0:im.shape[0],0:im.shape[1]]
# We break that into 2 sub arrays
x_idx = idx_2d[1]
y_idx = idx_2d[0]
# The mask is used to ignore the edge values (or indeed any values).
mask = numpy.ones(im.shape, dtype='bool')
mask[0, :] = False
mask[:, 0] = False
mask[im.shape[0] - 1, :] = False
mask[:, im.shape[1] - 1] = False
# We create and fill an array that contains the lookup for every
# possible 3x3 array.
idx_array = numpy.zeros((im[mask].size, 3, 3), dtype='int64')
# Compute the flattened indices for each position in the 3x3 grid
for n in range(0, 3):
for m in range(0, 3):
# Compute the flattened indices for each position in the
# 3x3 grid
idx = (x_idx + (n-1)) + (y_idx + (m-1)) * im.shape[1]
# mask it, and write it to the big array
idx_array[:, m, n] = idx[mask]
# sub_images contains every valid 3x3 sub image
sub_images = im.ravel()[idx_array]
# Finally, we can flatten and sort each sub array quickly
sorted_sub_images = numpy.sort(sub_images.reshape((idx[mask].size, 9)))
Try the following code as matlab function im2col(...)
import numpy as np
def im2col(Im, block, style='sliding'):
"""block = (patchsize, patchsize)
first do sliding
"""
bx, by = block
Imx, Imy = Im.shape
Imcol = []
for j in range(0, Imy):
for i in range(0, Imx):
if (i+bx <= Imx) and (j+by <= Imy):
Imcol.append(Im[i:i+bx, j:j+by].T.reshape(bx*by))
else:
break
return np.asarray(Imcol).T
if __name__ == '__main__':
Im = np.reshape(range(6*6), (6,6))
patchsize = 3
print Im
out = im2col(Im, (patchsize, patchsize))
print out
print out.shape
print len(out)
Does anybody know of a way (preferably using numpy or something similar) to multiply a matrix by a vector of matrices and obtain the desired product shown below? Basically the idea is to follow the normal rules of matrix multplication of a matrix and a vector, only the elements of the vector are matrices themselves and not numbers.
If I understand the question correctly, you can try this:
import numpy as np
A = np.arange(3*3*3).reshape(3, 3, 3)
b = np.arange(9).reshape(3, 3)
print(f"A=\n{A}\n\nb=\n{b}")
It gives:
A=
[[[ 0 1 2]
[ 3 4 5]
[ 6 7 8]]
[[ 9 10 11]
[12 13 14]
[15 16 17]]
[[18 19 20]
[21 22 23]
[24 25 26]]]
b=
[[0 1 2]
[3 4 5]
[6 7 8]]
Then:
out = (b#A.transpose(2, 0, 1)).transpose(1, 2, 0)
print(out)
which gives:
[[[ 45 48 51]
[ 54 57 60]
[ 63 66 69]]
[[126 138 150]
[162 174 186]
[198 210 222]]
[[207 228 249]
[270 291 312]
[333 354 375]]]
The matrix out[0] is equal to 0*A[0] + 1*A[1] + 2*A[2], out[1] is equal to 3*A[0] + 4*A[1] + 5*A[2] etc.
Is this what you want to calculate:
# Define two matrices
A = np.arange(9).reshape(3, 3)
B = np.arange(9, 18).reshape(3, 3)
# First calculate the desired result:
rows = []
for i in range(3):
rows.append([A[i, j] * B for j in range(3)])
result = np.stack(rows).sum(axis=1)
assert(result.shape == (3, 3, 3))
print(result)
[[[ 27 30 33]
[ 36 39 42]
[ 45 48 51]]
[[108 120 132]
[144 156 168]
[180 192 204]]
[[189 210 231]
[252 273 294]
[315 336 357]]]
Is this correct?
If so, then here is the same calculation using numpy's einsum function:
C = np.array([B] * 3) # shape (3, 3, 3)
result = np.einsum("ij,jkl->ikl", A, C)
So I have this code that makes an array with 4 arrays in it each holding 4 values and it prints it in 4 rows and 4 columns. I was looking at the code and thought there was a more concise way of doing it so if anyone can think of any please tell me
import random
n=[[random.randint(0,100) for i in range(4)] for i in range(4)]
for i in range(len(n)):
o=""
for j in range(len(n[i])):
o+=str(n[i][j])+" "
print(o)
This is what I came up with:
import random
n=[[str(random.randint(0,100)) for i in range(4)] for i in range(4)]
for row in n:
print('\t'.join(row))
OUTPUT:
53 34 62 45
21 45 39 94
52 75 53 94
88 16 97 80
I converted the numbers to string and applied the join method, joining each number with a tab ('\t').
str.join() function is much more efficient than adding multiple strings. You can do it like:
import random
n=[[random.randint(0,100) for i in range(4)] for i in range(4)]
## n = [[64, 77, 76, 72], [43, 41, 30, 50], [59, 0, 34, 20], [41, 73, 81, 42]]
print( "\n".join( " ".join(str(value) for value in row) for row in n ) )
# Output:
# 64 77 76 72
# 43 41 30 50
# 59 0 34 20
# 41 73 81 42
I have the following problem:
I have a matrix. Now, I want to delete one entry in each row of the matrix: In rows that contain a certain number (say 4) I want to delete the entry with that number, and in other rows I simply want to delete the last element.
E.g. if I have the matrix
matrix=np.zeros((2,2))
matrix[0,0]=2
matrix[1,0]=4
matrix
which gives
2 0
4 0
after the deletion it should simply be
2
0
thanks for your help!
so, assuming there's maximum only one 4 in a row, what you want to do is:
iterate all rows, and if there's a four use roll so it becomes the last element
delete the last column
in rows that have 4, it will delete this 4 and shift the remaining values that come after it,
in rows that don't have 4, it will delete the last element.
(I took the liberty of trying with a little bigger matrix just to make sure output is as expected)
try this:
import numpy as np
# Actual solution
def remove_in_rows(mat, num):
for i, row in enumerate(mat):
if num in row.tolist():
index = row.tolist().index(num)
mat[i][index:] = np.roll(row[index:], -1)
return np.delete(mat, -1, 1)
# Just some example to demonstrate it works
matrix = np.array([[10 * y + x for x in range(6)] for y in range(6)])
matrix[1, 2] = 4
matrix[3, 3] = 4
matrix[4, 0] = 4
print("BEFORE:")
print(matrix)
matrix = remove_in_rows(matrix, 4)
print("AFTER:")
print(matrix)
Output:
BEFORE:
[[ 0 1 2 3 4 5]
[10 11 4 13 14 15]
[20 21 22 23 24 25]
[30 31 32 4 34 35]
[ 4 41 42 43 44 45]
[50 51 52 53 54 55]]
AFTER:
[[ 0 1 2 3 5]
[10 11 13 14 15]
[20 21 22 23 24]
[30 31 32 34 35]
[41 42 43 44 45]
[50 51 52 53 54]]
Lets take a 3D array as an example. Or a cube for easier visualizing.
I want to select all the faces of that cube. And I would like to generalize this to arbitrary dimensions.
I'd also like to then add/remove faces to the cube(cuboid), and the generalization to arbitrary dimensions.
I know that for every fixed number of dimensions you can do array[:,:,0], array[-1,:,:] I'd like to know how to generalize to arbitrary dimensions and how to easily iterate over all faces.
To get a face:
def get_face(M, dim, front_side):
if front_side:
side = 0
else:
side = -1
index = tuple(side if i == dim else slice(None) for i in range(M.ndim))
return M[index]
To add a face (untested):
def add_face(M, new_face, dim, front_side):
#assume sizes match up correctly
if front_side:
return np.concatenate((new_face, M), dim)
else:
return np.concatenate((M, new_face), dim)
To remove a face:
def remove_face(M, dim, front_side):
if front_side:
dim_slice = slice(1, None)
else:
dim_slice = slice(None, -1)
index = tuple(dim_slice if i == dim else slice(None) for i in range(M.ndim))
return M[index]
Iterate over all faces:
def iter_faces(M):
for dim in range(M.ndim):
for front_side in (True, False):
yield get_face(M, dim, front_side)
Some quick tests:
In [18]: M = np.arange(27).reshape((3,3,3))
In [19]: for face in iter_faces(M): print face
[[0 1 2]
[3 4 5]
[6 7 8]]
[[18 19 20]
[21 22 23]
[24 25 26]]
[[ 0 1 2]
[ 9 10 11]
[18 19 20]]
[[ 6 7 8]
[15 16 17]
[24 25 26]]
[[ 0 3 6]
[ 9 12 15]
[18 21 24]]
[[ 2 5 8]
[11 14 17]
[20 23 26]]