Related
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)
Is there a way I could index through a numpy list just like how I would be able to within a normal list function. I want to go through 3 elements in the list moving up by one point every single time and summing all the slices. So it would go through 1,2,3 for the first sum and then it would go through 2,3,4 for the second sum etc. The code down below gives me a scalar error, is there a way I could perform the function below without using a for loop.
import numpy as np
n = 3
arr = np.array([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])
start = np.arange(0, len(arr)-n, 1)
stop = np.arange(n-1, len(arr), 1)
sum_arr = np.sum(arr[start:stop])
I think this should work:
sum_arr = arr[1:-1] + arr[2:] + arr[:-2]
This creates an array that's two values shorter than arr because the last element in arr doesn't have two extra elements to create a sum with.
If you wanted the array to be of the same length as the original arr, you could append two extra zeros to the arr array like so:
arr = np.append(arr, [0, 0])
sum_arr = arr[1:-1] + arr[2:] + arr[:-2]
To sum a sliding range of n elements you can use convolve1d with all weights set to 1. Use 'constant' boundary mode with the default fill value of 0. As the filter window is centered by default you need to adjust the length of the result at both ends.
import numpy as np
from scipy.ndimage import convolve1d
arr = np.arange(1,26)
for n in range(2,6):
k,r = divmod(n, 2)
print(n, convolve1d(arr, np.ones(n), mode='constant')[k+r-1:-k])
Result:
2 [ 3 5 7 9 11 13 15 17 19 21 23 25 27 29 31 33 35 37 39 41 43 45 47 49]
3 [ 6 9 12 15 18 21 24 27 30 33 36 39 42 45 48 51 54 57 60 63 66 69 72]
4 [ 10 14 18 22 26 30 34 38 42 46 50 54 58 62 66 70 74 78 82 86 90 94]
5 [ 15 20 25 30 35 40 45 50 55 60 65 70 75 80 85 90 95 100 105 110 115]
Here is my dataframe:
score
1
62
7
15
167
73
25
24
2
76
I want to compare a score with the previous 4 scores and count the number of scores higher than the current one.
This is my expected output:
score count
1
62
7
15
167 0
73 1 (we take 4 previous scores : 167,15,7,62 there is just 167 > 73 so count 1)
25 2
24 3
2 4
76 0
If somebody has an idea on how to do that, you are welcome
Thanks!
I do not think your output is according to your question. However, if you do look only at the previous 4 elements, then you could implement the following:
scores = [1, 62, 7, 15, 167, 73, 25, 24, 2, 76]
highers = []
for index, score in enumerate(scores[4:]):
higher = len([s for s in scores[index:index+4] if score < s])
print(higher)
highers.append(higher)
print(highers)
# [0, 1, 2, 3, 4, 0]
Then, you could just add this highers list as a pandas column:
df['output'] = [0]*4 + highers
Note that I pad the output in such way here that I assign zeros to the first four values.
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]]
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)