Related
I have 2d numpy array. I would like to set all off the local maxima of that array to 255 and all the rest to 0. I am interested in doing this in y axis.
I am aware of np.where(???, 255, 0) and scipy.signal.argelextrema but I cant figure out how to make those work together.
Example:
input = np.array([
[-33, -57, -77, -83, -70, -42, -5, 45, 107, 160, 183, 172],
[-35, -60, -80, -85, -70, -42, -4, 46, 106, 160, 183, 172],
[-36, -64, -84, -88, -72, -41, -4, 45, 105, 158, 181, 173]
])
Result:
output = np.array([
[0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 255, 0],
[0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 255, 0],
[0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 255, 0]
])
If you want both local maxima and minima, this code does the trick, I think it is reliable and fast.
import numpy as np
diff = np.diff(input, axis=1)
output = np.zeros_like(input)
output[:, 1:-1] = 255*(diff[:, 1:]*diff[:, :-1] < 0)
print(output)
I'm trying to make a speech recognition algorithm. I've a wav file containing +-20 minutes of speech. I've read it into a numpy array, each chunck of 1024 values is a row. As somehow not all chunks, provided by the wave module's file's readframes method, are of the same length, some rows are padded with zeros using the numpy.padd function in order to make the array have a homogenous shape. These paddings are only appended behind the array, and thus cannot cause the following.
I've noticed that there are columns in the array that are only containing zeros. These columns are always occuring in pairs and are always seperated by two columns containing normal values. The sixth column seems to be an exception on this pattern: It does also contain ones sometimes. Are these columns real recorded speach or are they put in between by my computer for some reason? This is important to know, as I don't wan't my algorithm to train on computer generated values. Should I delete those values or is it better to keep them? The array does not have to be playable as audio anymore.
Here's a sample out of the array. I can't attach the complete array, as it is way to big for that. A more complete version can be found here.
[78, 1, 0, 0, 79, 1, 0, 0, 12, 1, 0, 0, 185, 0, 0, 0, 177, 0, 0, 0, 28, 1, 0, 0, 245, 1, 0, 0, 38, 3, 0, 0, 106, 4, 0, 0, 81, 5, 0, 0, 148, 5, 0, 0, 74, 5, 0, 0, 168, 4, 0, 0, 229, 3, 0, 0, 83, 3, 0, 0, 31, 3, 0, 0, 26, 3, 0, 0, 33, 3, 0, 0, 40, 3, 0, 0, 22, 3, 0, 0, 246, 2, 0, 0, 211, 2, 0, 0, 136, 2, 0, 0, 240, 1, 0, 0, 247, 0, 0, 0, 176, 255, 0, 0, 97, 254, 0, 0, 69, 253, 0, 0, 131, 252, 0, 0, 54, 252, 0, 0, 81, 252, 0, 0, 188, 252, 0, 0, 79, 253, 0, 0, 207, 253, 0, 0, 48, 254, 0, 0, 97, 254, 0, 0, 73, 254, 0, 0, 6, 254, 0, 0, 175, 253, 0, 0, 90, 253, 0, 0, 58, 253, 0, 0, 73, 253, 0, 0, 101, 253, 0, 0, 132, 253, 0, 0, 147, 253, 0, 0, 164, 253, 0, 0, 199, 253, 0, 0, 224, 253, 0, 0, 8, 254, 0, 0, 97, 254, 0, 0, 228, 254, 0, 0, 163, 255, 0, 0, 138, 0, 0, 0, 89, 1, 0, 0, 251, 1, 0, 0, 45, 2, 0, 0, 157, 1, 0, 0, 95, 0, 0, 0, 161, 254, 0, 0, 185, 252, 0, 0, 56, 251, 0, 0, 134, 250, 0, 0, 226, 250, 0, 0, 51, 252, 0, 0, 246, 253, 0, 0, 175, 255, 0, 0, 208, 0, 0, 0, 240, 0, 0, 0, 96, 0, 0, 0, 124, 255, 0, 0, 119, 254, 0, 0, 223, 253, 0, 0, 243, 253, 0, 0, 120, 254, 0, 0, 77, 255, 0, 0, 254, 255, 0, 0, 253, 255, 0, 0, 63, 255, 0, 0, 224, 253, 0, 0, 61, 252, 0, 0, 27, 251, 0, 0, 35, 251, 0, 0, 180, 252, 0, 0, 154, 255, 0, 0, 1, 3, 0, 0, 6, 6, 0, 0, 189, 7, 0, 0, 116, 7, 0, 0, 111, 5, 0, 0, 118, 2, 0, 0, 116, 255, 0, 0, 133, 253, 0, 0, 55, 253, 0, 0, 124, 254, 0, 0, 17, 1, 0, 0, 19, 4, 0, 0, 87, 6, 0, 0, 42, 7, 0, 0, 53, 6, 0, 0, 195, 3, 0, 0]
[240, 0, 0, 0, 235, 254, 0, 0, 155, 254, 0, 0, 87, 0, 0, 0, 80, 3, 0, 0, 25, 6, 0, 0, 137, 7, 0, 0, 253, 6, 0, 0, 146, 4, 0, 0, 57, 1, 0, 0, 35, 254, 0, 0, 73, 252, 0, 0, 56, 252, 0, 0, 185, 253, 0, 0, 251, 255, 0, 0, 60, 2, 0, 0, 204, 3, 0, 0, 18, 4, 0, 0, 15, 3, 0, 0, 21, 1, 0, 0, 137, 254, 0, 0, 95, 252, 0, 0, 99, 251, 0, 0, 137, 251, 0, 0, 210, 252, 0, 0, 46, 255, 0, 0, 161, 1, 0, 0, 40, 3, 0, 0, 102, 3, 0, 0, 95, 2, 0, 0, 127, 0, 0, 0, 92, 254, 0, 0, 160, 252, 0, 0, 18, 252, 0, 0, 216, 252, 0, 0, 133, 254, 0, 0, 181, 0, 0, 0, 136, 2, 0, 0, 248, 2, 0, 0, 213, 1, 0, 0, 130, 255, 0, 0, 202, 252, 0, 0, 199, 250, 0, 0, 241, 249, 0, 0, 56, 250, 0, 0, 121, 251, 0, 0, 15, 253, 0, 0, 72, 254, 0, 0, 235, 254, 0, 0, 188, 254, 0, 0, 180, 253, 0, 0, 61, 252, 0, 0, 191, 250, 0, 0, 167, 249, 0, 0, 108, 249, 0, 0, 33, 250, 0, 0, 144, 251, 0, 0, 106, 253, 0, 0, 20, 255, 0, 0, 227, 255, 0, 0, 169, 255, 0, 0, 173, 254, 0, 0, 82, 253, 0, 0, 3, 252, 0, 0, 69, 251, 0, 0, 115, 251, 0, 0, 146, 252, 0, 0, 109, 254, 0, 0, 137, 0, 0, 0, 64, 2, 0, 0, 28, 3, 0, 0, 241, 2, 0, 0, 233, 1, 0, 0, 148, 0, 0, 0, 143, 255, 0, 0, 60, 255, 0, 0, 183, 255, 0, 0, 185, 0, 0, 0, 203, 1, 0, 0, 149, 2, 0, 0, 208, 2, 0, 0, 97, 2, 0, 0, 127, 1, 0, 0, 103, 0, 0, 0, 66, 255, 0, 0, 74, 254, 0, 0, 172, 253, 0, 0, 121, 253, 0, 0, 158, 253, 0, 0, 218, 253, 0, 0, 242, 253, 0, 0, 207, 253, 0, 0, 97, 253, 0, 0, 191, 252, 0, 0, 91, 252, 0, 0, 144, 252, 0, 0, 82, 253, 0, 0, 100, 254, 0, 0, 122, 255, 0, 0, 34, 0, 0, 0]
[1, 0, 0, 0, 65, 255, 0, 0, 89, 254, 0, 0, 151, 253, 0, 0, 47, 253, 0, 0, 69, 253, 0, 0, 221, 253, 0, 0, 237, 254, 0, 0, 76, 0, 0, 0, 166, 1, 0, 0, 187, 2, 0, 0, 133, 3, 0, 0, 3, 4, 0, 0, 86, 4, 0, 0, 179, 4, 0, 0, 47, 5, 0, 0, 198, 5, 0, 0, 96, 6, 0, 0, 197, 6, 0, 0, 210, 6, 0, 0, 145, 6, 0, 0, 37, 6, 0, 0, 217, 5, 0, 0, 226, 5, 0, 0, 73, 6, 0, 0, 35, 7, 0, 0, 86, 8, 0, 0, 105, 9, 0, 0, 11, 10, 0, 0, 252, 9, 0, 0, 222, 8, 0, 0, 224, 6, 0, 0, 151, 4, 0, 0, 73, 2, 0, 0, 111, 0, 0, 0, 152, 255, 0, 0, 125, 255, 0, 0, 130, 255, 0, 0, 105, 255, 0, 0, 214, 254, 0, 0, 135, 253, 0, 0, 223, 251, 0, 0, 108, 250, 0, 0, 144, 249, 0, 0, 117, 249, 0, 0, 255, 249, 0, 0, 244, 250, 0, 0, 239, 251, 0, 0, 101, 252, 0, 0, 38, 252, 0, 0, 99, 251, 0, 0, 105, 250, 0, 0, 195, 249, 0, 0, 239, 249, 0, 0, 4, 251, 0, 0, 216, 252, 0, 0, 218, 254, 0, 0, 61, 0, 0, 0, 161, 0, 0, 0, 19, 0, 0, 0, 227, 254, 0, 0, 190, 253, 0, 0, 69, 253, 0, 0, 179, 253, 0, 0, 209, 254, 0, 0, 21, 0, 0, 0, 250, 0, 0, 0, 35, 1, 0, 0, 76, 0, 0, 0, 162, 254, 0, 0, 204, 252, 0, 0, 104, 251, 0, 0, 239, 250, 0, 0, 177, 251, 0, 0, 127, 253, 0, 0, 162, 255, 0, 0, 42, 1, 0, 0, 95, 1, 0, 0, 246, 255, 0, 0, 23, 253, 0, 0, 154, 249, 0, 0, 220, 246, 0, 0, 235, 245, 0, 0, 32, 247, 0, 0, 33, 250, 0, 0, 214, 253, 0, 0, 203, 0, 0, 0, 227, 1, 0, 0, 234, 0, 0, 0, 137, 254, 0, 0, 202, 251, 0, 0, 251, 249, 0, 0, 42, 250, 0, 0, 77, 252, 0, 0, 154, 255, 0, 0, 24, 3, 0, 0, 162, 5, 0, 0, 107, 6, 0, 0, 139, 5, 0, 0, 154, 3, 0, 0]
It appeared that the file contained two different sound channels. The second sound channel was not recorded, as I have only one microphone. This caused this channel to be defaulted to all zeros. Because .wav files use two bits to record each channel, this resulted in two places being filled with zeros. Then the wave module combined all the different channels into one chunck, each containing 2 correct bits and 2 incorrect (0) bits and combined 100 of these frames, my chunck size, into one array and thus deliver the array given.
I could perfectly remove the zeros out of the array without any audio quality loss. However, I also had to reduce the amount of channels to 1, as I just deleted the second channel.
i am reverse engineering a RGB LED Bias Light. These Values are getting send by my PC via USB to the device.
But i cant figure out the last part behind the rgb values.
Maybe some of you got an idea what it is and how to calculate it?
I dont think it is alpha channel
Samples:
The important part is behind the RGB values, below the 186, 218, 250, etc
If i am sending the same bytes as the software sends to the LEDs it works. But if i change the last value of the RGB-? it doesnt.
[83, 67, 210, 2, 4, 1, 0, 255, 128, 186, 69, 68] 0, 255, 128
[83, 67, 210, 2, 4, 1, 0, 255, 224, 218, 69, 68] 0, 255,224
[83, 67, 210, 2, 4, 1, 0, 255, 192, 250, 69, 68] 0, 255, 192
[83, 67, 210, 2, 4, 1, 0, 255, 255, 197, 69, 68] 0,255,255
[83, 67, 210, 2, 4, 1, 255, 255, 0, 197, 69, 68] 255, 255, 0
[83, 67, 210, 2, 4, 1, 255, 0, 255, 197, 69, 68] 255, 0, 255
[83, 67, 210, 2, 4, 1, 255, 32, 0, 26, 69, 68] 255, 32, 0
[83, 67, 210, 2, 4, 1, 255, 0, 32, 26, 69, 68] 255, 0, 32
[83, 67, 210, 2, 4, 1, 255, 0, 64, 122, 69, 68] 255, 0, 64
[83, 67, 210, 2, 4, 1, 255, 0, 128, 186, 69, 68] 255, 0, 128
[83, 67, 210, 2, 4, 1, 255, 0, 160, 154, 69, 68] 255, 0, 160
[83, 67, 210, 2, 4, 1, 255, 0, 192, 250, 69, 68] 255, 0, 192
[83, 67, 210, 2, 4, 1, 255, 0, 224, 218, 69, 68] 255, 0, 224
[83, 67, 210, 2, 4, 1, 255, 0, 255, 197, 69, 68] 255, 0, 255
[83, 67, 210, 2, 4, 1, 255, 255, 0, 197, 69, 68] 255, 255, 0
[83, 67, 210, 2, 4, 1, 0, 0, 255, 58, 69, 68] 0, 0, 255
[83, 67, 210, 2, 4, 1, 0, 255, 0, 58, 69, 68] 0, 255, 0
[83, 67, 210, 2, 4, 1, 255, 0, 0, 58, 69, 68] 255, 0, 0
Thanks for reading my question
Although there are many instances of the question: "What is the numpy alternative to nested for loops", I was unable to find a suitable answer for my case. Here it goes:
I have a 3D numpy array with "0" background and other integers as foreground. I would like to find and store the foreground voxels which fall within a predefined mask (a sphere defining a given distance from a reference node). I have successfully done the task using nested 'for' loops and a chain of 'if' conditions as shown below. I am looking for a more efficient and compact alternative to avoid the loops and long conditions for this neighborhood search algorithm.
sample input data:
import numpy as np
im = np.array([[[ 60, 54, 47, 52, 57, 53, 46, 48]
, [ 60, 57, 53, 53, 54, 53, 50, 55]
, [ 60, 63, 56, 58, 59, 57, 50, 50]
, [ 70, 70, 64, 69, 74, 72, 64, 47]
, [ 73, 76, 77, 80, 82, 76, 58, 37]
, [ 85, 85, 86, 86, 78, 62, 38, 20]
, [ 94, 94, 92, 78, 54, 33, 16, 255]
, [ 94, 90, 72, 51, 32, 19, 255, 255]
, [ 65, 53, 29, 18, 255, 255, 255, 255]
, [ 29, 22, 255, 255, 255, 255, 255, 0]]
, [[ 66, 67, 70, 69, 75, 73, 72, 63]
, [ 68, 70, 73, 74, 78, 80, 74, 53]
, [ 75, 87, 87, 83, 89, 86, 61, 33]
, [ 81, 89, 88, 98, 99, 77, 41, 18]
, [ 84, 94, 100, 100, 82, 49, 21, 255]
, [ 99, 101, 92, 75, 48, 25, 255, 255]
, [ 93, 77, 52, 32, 255, 255, 255, 255]
, [ 52, 40, 25, 255, 255, 255, 255, 255]
, [ 23, 16, 255, 255, 255, 255, 255, 0]
, [255, 255, 255, 255, 255, 255, 0, 0]]
, [[ 81, 83, 92, 101, 101, 83, 49, 19]
, [ 86, 96, 103, 103, 95, 64, 28, 255]
, [ 94, 103, 107, 98, 79, 41, 255, 255]
, [101, 103, 98, 79, 51, 28, 255, 255]
, [102, 97, 76, 49, 27, 255, 255, 255]
, [ 79, 62, 35, 21, 255, 255, 255, 255]
, [ 33, 23, 15, 255, 255, 255, 255, 255]
, [ 16, 255, 255, 255, 255, 255, 255, 0]
, [255, 255, 255, 255, 255, 255, 0, 0]
, [255, 255, 255, 255, 255, 0, 0, 0]]
, [[106, 107, 109, 94, 58, 26, 15, 255]
, [110, 104, 90, 66, 37, 19, 255, 255]
, [106, 89, 61, 35, 22, 255, 255, 255]
, [ 76, 56, 34, 19, 255, 255, 255, 255]
, [ 40, 27, 18, 255, 255, 255, 255, 255]
, [ 17, 255, 255, 255, 255, 255, 255, 255]
, [255, 255, 255, 255, 255, 255, 255, 0]
, [255, 255, 255, 255, 255, 255, 0, 0]
, [255, 255, 255, 255, 255, 0, 0, 0]
, [255, 255, 255, 0, 0, 0, 0, 0]]
, [[ 68, 51, 33, 19, 255, 255, 255, 255]
, [ 45, 34, 20, 255, 255, 255, 255, 255]
, [ 28, 18, 255, 255, 255, 255, 255, 255]
, [ 17, 255, 255, 255, 255, 255, 255, 255]
, [255, 255, 255, 255, 255, 255, 255, 255]
, [255, 255, 255, 255, 255, 255, 255, 0]
, [255, 255, 255, 255, 255, 255, 0, 0]
, [255, 255, 255, 255, 255, 0, 0, 0]
, [255, 255, 255, 0, 0, 0, 0, 0]
, [255, 0, 0, 0, 0, 0, 0, 0]]
, [[255, 255, 255, 255, 255, 255, 255, 255]
, [255, 255, 255, 255, 255, 255, 255, 255]
, [255, 255, 255, 255, 255, 255, 255, 255]
, [255, 255, 255, 255, 255, 255, 255, 0]
, [255, 255, 255, 255, 255, 255, 0, 0]
, [255, 255, 255, 255, 255, 0, 0, 0]
, [255, 255, 255, 255, 0, 0, 0, 0]
, [255, 255, 255, 0, 0, 0, 0, 0]
, [255, 0, 0, 0, 0, 0, 0, 0]
, [ 0, 0, 0, 0, 0, 0, 0, 0]]
, [[255, 255, 255, 255, 255, 255, 255, 0]
, [255, 255, 255, 255, 255, 255, 255, 0]
, [255, 255, 255, 255, 255, 255, 0, 0]
, [255, 255, 255, 255, 255, 0, 0, 0]
, [255, 255, 255, 255, 0, 0, 0, 0]
, [255, 255, 255, 0, 0, 0, 0, 0]
, [255, 255, 0, 0, 0, 0, 0, 0]
, [ 0, 0, 0, 0, 0, 0, 0, 0]
, [ 0, 0, 0, 0, 0, 0, 0, 0]
, [ 0, 0, 0, 0, 0, 0, 0, 0]]
, [[255, 255, 255, 255, 255, 255, 0, 0]
, [255, 255, 255, 255, 255, 0, 0, 0]
, [255, 255, 255, 255, 0, 0, 0, 0]
, [255, 255, 255, 0, 0, 0, 0, 0]
, [255, 255, 0, 0, 0, 0, 0, 0]
, [255, 0, 0, 0, 0, 0, 0, 0]
, [ 0, 0, 0, 0, 0, 0, 0, 0]
, [ 0, 0, 0, 0, 0, 0, 0, 0]
, [ 0, 0, 0, 0, 0, 0, 0, 0]
, [ 0, 0, 0, 0, 0, 0, 0, 0]]])
The implemented method:
[Z,Y,X]=im.shape
RN = np.array([3,4,4])
################Loading Area search
rad = 3
a,b,c = RN
x,y,z = np.ogrid[-c:Z-c,-b:Y-b,-a:X-a]
neighborMask = x*x + y*y + z*z<= rad*rad
noNodeMask = im > 0
mask = np.logical_and(neighborMask, noNodeMask)
imtemp = im.copy()
imtemp[mask] = -1
for i in range (X):
for j in range (Y):
for k in range (Z):
if imtemp[i,j,k]==-1:
if i in (0, X-1) or j in (0, Y-1) or k in (0, Z-1):
imtemp[i,j,k]=-2
elif imtemp[i+1,j,k] == 0 or imtemp[i-1,j,k] == 0 or imtemp[i,j+1,k] == 0 or imtemp[i,j-1,k] == 0 or imtemp[i,j,k+1] == 0 or imtemp[i,j,k-1] == 0:
imtemp[i,j,k]=-2
LA = np.argwhere(imtemp==-2)
The resulting LA from the above sample code is:
In [90]:LA
Out[90]:
array([[4, 4, 0],
[4, 4, 6],
[4, 5, 5],
[4, 6, 4],
[4, 6, 5],
[4, 7, 3],
[5, 3, 5],
[5, 4, 4],
[5, 4, 5],
[5, 5, 3],
[5, 5, 4],
[5, 6, 2],
[5, 6, 3],
[6, 2, 4],
[6, 3, 3],
[6, 3, 4],
[6, 4, 2],
[6, 4, 3],
[6, 5, 1],
[6, 5, 2]])
And a slice in Z direction (an XY plane instance) which shows different untouched, masked (-1), and target (-2) nodes:
Since your loops are only using direct Numpy indexing, you can use the Numba's #njit to perform this in a much more efficient way.
#njit
def compute_imtemp(imtemp, X, Y, Z):
for i in range (Z):
for j in range (Y-1):
for k in range (X-1):
if imtemp[i,j,k]==-1:
if i==(Z-1):
imtemp[i,j,k]=-2
elif imtemp[i+1,j,k] == 0 or imtemp[i-1,j,k] == 0 or imtemp[i,j+1,k] == 0 or imtemp[i,j-1,k] == 0 or imtemp[i,j,k+1] == 0 or imtemp[i,j,k-1] == 0:
imtemp[i,j,k]=-2
[...]
imtemp = im.copy()
imtemp[mask] = -1
compute_imtemp(imtemp, X, Y, Z)
LA = np.argwhere(imtemp==-2)
Here are performance results on my machine:
281 µs ± 1.43 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
776 ns ± 16.4 ns per loop (mean ± std. dev. of 7 runs, 100 loops each)
The Numba implementation is 362 times faster.
Note that the first call to compute_imtemp will be slow because of the compilation. One way to overcome this is to call compute_imtemp on an empty Numpy array. Another way is to manually compile the function using the Numba API and provide the types to Numba explicitly.
Problem Statement
You have a "solid" shape in a large array. You carve out a ball from that. Your goal is to find the indices of the surface of the solid within the ball. Surfaces are defined as any point neighboring the outside of the solid with 6-point connectivity. Edges of the array are considered to be surfaces too.
Faster Loop Solution
You already computed the mask that represents the intersection of the solid and the ball. You can compute the mask a little more elegantly and convert it to indices instead. I suggest keeping the order of your dimensions constant, instead of switching between different notations. The order of RN is affected, for example, and you run the risk of mismatching your axis limits.
RN = np.array([4, 4, 3])
rad = 3
im = ...
cutout = ((np.indices(im.shape) - RN.reshape(-1, 1, 1, 1))**2).sum(axis=0) <= rad**2
solid = im > 0
mask = solid & cutout
indices = np.argwhere(mask)
You can also get the cutout without reshaping RN by doing
cutout = ((np.rollaxis(np.indices(im.shape, sparse=False), 0, 4) - RN)**2).sum(axis=-1) <= rad**2
The nice thing about computing indices is that your loops don't need to be huge any more. By using argwhere, you basically strip off the outer three loops, leaving only the if statement to loop over. You can also vectorize the connectivity check. This has the nice side effect that you can define arbitrary connectivity for each pixel.
limit = np.array(im.shape) - 1 # Edge of `im`
connectivity = np.array([[ 1, 0, 0], # Add rows to determine connectivity
[-1, 0, 0],
[ 0, 1, 0],
[ 0, -1, 0],
[ 0, 0, 1],
[ 0, 0, -1]], dtype=indices.dtype)
index_mask = np.ones(len(indices), dtype=bool)
for n, ind in enumerate(indices):
if ind.all() and (ind < limit).all() and im[tuple((ind + connectivity).T)].all():
index_mask[n] = False
LA = indices[index_mask, :]
Notice that there is really no point to imtemp at all. Even in your original loop, you could just manipulate mask directly. Instead of setting elements to -2 when they pass your criterion, you could set elements to False if they didn't.
I do something like that here. We check each of the indices that were actually selected, and determine if any of them are inside the solid. These indices are eliminated from the mask. The list of indices is then updated based on the mask.
The check ind.all() and (ind < limit).all() and im[tuple((ind + connectivity).T)].all() is a shortcut for what you were doing with the or conditions, but reversed (testing for non-surface rather than surface).
ind.all() checks that none of the indices are zero: i.e., not on a top/front/left surface.
(ind < limit).all() checks that none of the indices are equal to the corresponding image size minus one.
im[tuple((ind + connectivity).T)].all() checks that none of the connected pixels are zero. (ind + connectivity).T is a (3, 6) array of the six points that we are connected to (currently defined as +/-1 in each axis by the (6, 3) connectivity array). When you turn it into a tuple, it just becomes a fancy index, as if you had done something like im[x + connectivity[:, 0], y + connectivity[:, 1], z + connectivity[:, 2]]. The commas in the index just make it into a tuple. They way I show is better suited for arbitrary numbers of dimensions.
Pixels that pass all three tests are inside the solid, and get removed. You can of course write the loop to check the other way, but then you would have to alter your mask:
index_mask = np.zeros(len(indices), dtype=bool)
for n, ind in enumerate(indices):
if (ind == 0).any() or (ind == limit).any() or (im[tuple((ind + connectivity).T)] == 0).any():
index_mask[n] = True
LA = indices[index_mask, :]
Looping manually is not ideal by any means. However, it shows you how to shorten the loop (probably by a couple of orders of magnitude), and how to define arbitrary connectivity using vectorization and broadcasting, without getting bogged down with hard-coding it.
Fully Vectorized Solution
The loops above can be fully vectorized using the magic of broadcasting. Instead of looping over each row in indices, we can add connectivity to it in bulk and filter the results in bulk. The trick is to add enough dimensions that you add all of connectivity to each element of indices.
You will still want to omit the pixels that are at the edges:
edges = (indices == 0).any(axis=-1) | (indices == limit).any(axis=-1)
conn_index = indices[~edges, None, :] + connectivity[None, ...]
index_mask = np.empty(len(indices), dtype=bool)
index_mask[edges] = True
index_mask[~edges] = (im[tuple(conn_index.T)] == 0).any(axis=0)
LA = indices[index_mask, :]
I expect that a properly written loop compiled with numba will be significantly faster than this solution, because it will avoid much of the overhead with pipelining the operations. It will not require large temporary buffers or special handling.
TL;DR
# Parameters
RN = np.array([4, 4, 3])
rad = 3
im = ...
# Find subset of interest
cutout = ((np.indices(im.shape) - RN.reshape(-1, 1, 1, 1))**2).sum(axis=0) <= rad**2
solid = im > 0
# Convert mask to indices
indices = np.argwhere(solid & cutout)
# Find image edges among indices
edges = (indices == 0).any(axis=-1) | (indices == limit).any(axis=-1)
# Connectivity elements for non-edge pixels
conn_index = indices[~edges, None, :] + connectivity[None, ...]
# Mask the valid surface pixels
index_mask = np.empty(len(indices), dtype=bool)
index_mask[edges] = True
index_mask[~edges] = (im[tuple(conn_index.T)] == 0).any(axis=0)
# Final result
LA = indices[index_mask, :]
I seem to have some problems dividing a integer with an numpy.array..
Doing this seem to return a array of zeroes, instead of decimals values..
Example:
>>> import numpy as np
>>> length = 257
>>> length = 256
>>> x = np.array(range(0,length)
... )
>>> x
array([ 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, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90,
91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103,
104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116,
117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129,
130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142,
143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155,
156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168,
169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181,
182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194,
195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207,
208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220,
221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233,
234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246,
247, 248, 249, 250, 251, 252, 253, 254, 255])
>>> x*1/length
array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0])
>>>
Why is it returning zeros?
That's because it is doing an integer division. Try this
x*1/256.0
Because the array consists out of integers, and in python-2.x / is defined as integer division which round down the result, so 99/100 is considered to be 0.
You can first cast to a float:
x.astype(float)*1/length
A float is a floating point and such datastructures - putting it bold - support a numbers with decimal dot.