Motion Detection

From: Mikee26 Jun 2012 01:33
To: ALL10 of 14
Reduced it down to this:

java code:
 
 
	public boolean detect(byte[] data, int width, int height) {
		for (int y = 0, i = 0; y < height; y++) {
			for (int x = 0; x < width; x++, i++) {
				int l = (0xff & ((int) data[i])) - 16;
				// if theres a previous frame, lets do a diff on it
				if (previous_frame != null) {
					int pl = (0xff & ((int) previous_frame[i])) - 16;
					previous_frame = data;
					// but first, lets morph 30% of the background in
					int d = ((int) ((0.7 * l) + (0.3 * pl))) - pl;
					if (d < 0)
						d = -d;
					if (d > 27) return true;
				}else {
					previous_frame = data;
				}
			}
		}
		return false;
	}
 


Taken the erosion off, increased the threshold. Hopefully I won't need to worry.. My test data were frames that were quite a few seconds apart, so in theory it shouldn't need to be AS precise as I was trying to make it.
EDITED: 26 Jun 2012 01:36 by MIKEE
From: Peter (BOUGHTONP)26 Jun 2012 01:40
To: Mikee 11 of 14
On the speed front, you're casting everything to integers here?

Isn't floating point maths faster than integer maths? So maybe if you didn't cast stuff and kept it (as much as possible) as floats/decimal then it'd be quicker?
From: Mikee26 Jun 2012 02:55
To: Peter (BOUGHTONP) 12 of 14
I thought ints were faster :O
Oh.

Well this is what I have so far. Not too happy with it. I'll give it a test tomorrow...

Getting about 25fps out of this when it's finding stuff (it jumps out early), and 7fps when it's not.

 
java code:
 
 
package com.mikeefranklin.birdcam;
 
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.ImageFormat;
import android.graphics.Matrix;
import android.graphics.YuvImage;
import android.util.Log;
 
public class Detector {
 
	byte[] previous_frame;
 
	public Detector() {
 
	}
 
	public boolean detect(byte[] data, int width, int height) {
		for (int y = 0, i = 0; y < height; y++) {
			for (int x = 0; x < width; x++, i++) {
				int l = (0xff & ((int) data[i])) - 16;
				// if theres a previous frame, lets do a diff on it
				if (previous_frame != null) {
					int pl = (0xff & ((int) previous_frame[i])) - 16;
					int d = pl;
					d = ((int)(((0.7 * l) + (0.3 * d)))-d);
					if (d < 0){
						d = -d;
					}
					if (d > 50) {
						previous_frame = data;
						return true;
					}
				}else {
				}
			}
		}
		previous_frame = data;
		return false;
	}
}
 
 
EDITED: 26 Jun 2012 02:56 by MIKEE
From: Mikee26 Jun 2012 10:30
To: ALL13 of 14
Decided to only check a fraction of the pixels. I seem to be getting a similar result, but now it's a bit faster and I can add erosion to clear any dead pixels.

http://www.mikeefranklin.co.uk/birds/birdy2.html

Hopefully this will be enough and HOPEFULLY it'll transfer over to java well enough. In this javascript version I'm having to convert over to YUV first to simulate android so it's a bit slower.

javascript code:
 
fastdetect : function(that){
 
				var half_height = this.pixels.height / 2;
				var half_width = this.pixels.width / 2;
 
				for(var y=0; y<half_height; y++){
					for(var x=0; x<half_width; x++){
						var i = (y * 8) * this.pixels.width + x * 8;
 
						var r = this.pixels.data[i];
						var g = this.pixels.data[i+1];
						var b = this.pixels.data[i+2];
 
						var tr = that.pixels.data[i];
						var tg = that.pixels.data[i+1];
						var tb = that.pixels.data[i+2];
 
						var yuv = rgb_to_yuv(r, g, b);
						var yuv2 = rgb_to_yuv(tr, tg, tb);
 
						var this_col = yuv[0];
						var that_col = yuv2[0];
 
						this_col = (((5 * this_col) + (5 * that_col))/10)-that_col;
 
						if (this_col < 0) this_col = -this_col;
 
						this_col = this_col > 15 ?  255 : 0;
 
						this.pixels.data[i] = this_col;
						this.pixels.data[i+1] = this_col;
						this.pixels.data[i+2] = this_col;
 
					}
				}
				var mask = [];
				for(var y=0; y<half_height; y++){
					for(var x=0; x<half_width; x++){
						var i = (y * 8) * this.pixels.width + x * 8;
						var erode = false;
						if (this.pixels.data[i] == 255){
							for (var yi=-2;yi<=2;yi+=2){
								for (var xi=-2;xi<=2;xi+=2){
									var px = ((y+yi) * 8) * this.pixels.width + (x+xi) * 8;
									if (this.pixels.data[px] == 0){
										erode = true;
										break;
									}
								}
							}
							mask.push({'i' : i, 'erode' : erode ? 0 : 255});
						}else {
							mask.push({'i' : i, 'erode' : 0});
						}
 
					}
				}
				for (var i=0; i<mask.length; i++){
					this.pixels.data[mask[i].i] = mask[i].erode;
					this.pixels.data[mask[i].i+1] = mask[i].erode;
					this.pixels.data[mask[i].i+2] = mask[i].erode;
				}
 
return false;
			}
 
EDITED: 26 Jun 2012 10:31 by MIKEE
From: Mikee26 Jun 2012 11:43
To: ALL14 of 14
This seems to be the fastest, most accurate version yet:

java code:
 
	public boolean detect(byte[] data, int width, int height) {
 
 		// half width and height to reduce loops
		int half_height = height / 2;
		int half_width = width / 2;
 
		// boolean map to hold our threshold pic
		boolean[] map = new boolean[half_height * half_width];
		for (int y = 0, i = 0, ii = 0; y < half_height; y++) {
			for (int x = 0; x < half_width; x++, i+=2,ii++) {
				int l = (0xff & ((int) data[i])) - 16;
				if (previous_frame != null) {
					int pl = (0xff & ((int) previous_frame[i])) - 16;
					int d = pl;
					// merge 50% of each frame, lets not use division
					d = ((int)((5 * l) + (5 * d))-(d*10));
					// if theres a negative diff, lets reverse it
					if (d < 0){
						d = -d;
					}
					//build up our threshold map
					map[ii] = d > 150;
				}
			}
		}
		previous_frame = data;
		boolean erode = false;
		for (int y = 0, i = 0; y < half_height; y++) {
			for (int x = 0; x < half_width; x++, i++) {
				erode = false;
				if (map[i]) {
					loop through the pixels around it
					for (int yi=-2;yi<=2;yi++){
						for (int xi=-2;xi<=2;xi++){
							int px = (y+yi) * half_width + (x+xi);
							if (px >= 0 && px < map.length) {
								// if a pixel is eroded, carry on
								if (!map[px]) {
									erode = true;
									break;
								}
							}
						}
					}
					// if no pixels around this pixel have been eroded, we know there's probably some motion
					if (!erode) {
						return true;
					}
				}
			}
		}
		return false;
	}
 


I'm getting plenty of frames and I'm eroding noise. I might try to fit my 'normalize' code back in now, too, so I can make sure it works well at night.

 
EDITED: 26 Jun 2012 11:59 by MIKEE