N = X.shape[0] # number of images X0 = X.reshape((N,-1)) #flattens the images np.std(X0,axis=1) # calculates one value by image
It's however relatively slow. The formula is simple. std could be calculated as
np.mean(X**2,axis=1)-np.mean(X,axis=1)**2or
np.mean((X-np.mean(X,axis=1))**2,axis=1)
I tested the first formula as written above, with X a vector of uint8 (unsigned integer on 8 bits). There was a good surprise: it was much faster. And a bad one: the result was completely wrong. The reason is that the image is made of pixels, which are coded as 3 bytes, each of them containing the luminosity of each channel (red, green, blue) from 0 to 255. It's stored in an unsigned byte, which goes from 0 to 255 also, for memory efficiency. The problem is that by default, it will store the results of its operations in a vector of the same type, which means that instead of calculating $X^2$, it will calculate $X^2 \mod 256$.
Most of the time, numpy is smart and runs the calculation in the proper type: for instance np.std above uses double precision float numbers (float64). However converting to that type requires time, which explains the poor performance. Since I needed also to calculate the mean of the array, the best performance I could achieve was using the following:
mean = np.mean(X0,axis=1) np.sqrt(np.mean(X0.astype(np.uint16)**2,axis=1)-mean**2)
The conversion to unit16 is required to process the squaring, but requires less time than the conversion to double precision. Numpy calculates the mean using a double but we need one by image, not one by pixel. For a vector or 4000 images of 245760 pixels each, the calculation now takes 1.36s compared to 2.46s initially.