To dig into the picamera python api i just started a simple motion detection script. It runs in the videomode with 2 frames/second and a resolution of 320x240. Frames are captured with the capture_continuous function and the frames i work with are PiRGBArrays. Until now: No performance issues... Here is the setup for the camera:
camera = picamera.PiCamera()
camera.resolution = (320,240)
camera.framerate = 2
rawArr = picamera.array.PiRGBArray(camera, size=(320,240))
...
for arr in camera.capture_continuous(rawArr, format='rgb', use_video_port=True):
curr = rawArr.array
...
The basic idea is to compare the last frame with the currently taken frame. I´ve done that in a pretty straight forward way - looping over the three dimensional arrays and compare the values of each color channel with a given tolerance (i.e. 10 per channel).
The performance was very bad: 230400 checks (320x240x3) took about 20 to 30 seconds. Here´s my code:
for row in range(len(lastArray)):
for col in range(len(lastArray[row])):
for chan in range(3):
old = lastArray[row][col][chan]
new = currArray[row][col][chan]
if (new > old + self.tolerance or new < old - self.tolerance):
changeDetected = True
At first i thought, okay there are not less checks and the hardware isnt that powerful - but so bad?! To compare it and get a feeling about how fast array operations can be done, i´ve written a comparable method in Java (where i´m usally at home) just to get a feeling...
public static void main(String[] args) {
int[][][] threeD = new int[320][240][3];
System.out.println("start filling array at: " + System.currentTimeMillis());
for(int i = 0; i < 320; i++){
for(int j = 0; j < 240; j++){
for(int k = 0; k < 3; k++)
threeD[i][j][k] = RandomUtils.nextInt(10);
}
}
System.out.println("start iterating and comparing at: " + System.currentTimeMillis());
int dummy = 0;
for(int i = 0; i < 320; i++){
for(int j = 0; j < 240; j++){
for(int k = 0; k < 3; k++){
if(threeD[i][j][k] > 8 || threeD[i][j][k] < 2){
dummy = threeD[i][j][k];
}
}
}
}
System.out.println("finished at: " + System.currentTimeMillis());
}
The output was:
start filling array at: 1416812183728
start iterating and comparing at: 1416812183743
finished at: 1416812183748
So it took about 5ms(!) to do these checks...
I then tried to optimize my code with the following actions
- Use YUV format and compare only the y-channel
- Only compare each 3rd row and column
The updated code goes:
for row in range(len(lastArray)):
if row % 3 == 0:
for col in range(len(lastArray[row])):
if col % 3 == 0:
old = lastArray[row][col][0]
new = currArray[row][col][0]
if (new > old + self.tolerance or new < old - self.tolerance):
changeDetected = True
The performance is still not as i expected it to be: About 3 seconds for the comparisons.
Is there a way to make this even faster in pure python?
Thanks in advance!