Skip to content

Commit

Permalink
Updates to move MotionDetection into a more OO design and fixed the a…
Browse files Browse the repository at this point in the history
…ggregate luma detection

git-svn-id: https://android-motion-detection.googlecode.com/svn/trunk@19 996b93ed-3e4d-4b21-1044-74bd19560d1d
  • Loading branch information
[email protected] committed Sep 13, 2011
1 parent d4a09a4 commit 891f971
Show file tree
Hide file tree
Showing 9 changed files with 366 additions and 229 deletions.
5 changes: 5 additions & 0 deletions src/com/jwetherell/motion_detection/Globals.java
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
package com.jwetherell.motion_detection;

public class Globals {
//Which motion detection to use
public static boolean USE_RGB = true;
public static boolean USE_LUMA = false;
public static boolean USE_STATE = false;

//Which photos to save
public static boolean SAVE_PREVIOUS = false;
public static boolean SAVE_ORIGINAL = false;
public static boolean SAVE_CHANGES = true;

//Time between saving photos
public static int PICTURE_DELAY = 10000;
}
20 changes: 17 additions & 3 deletions src/com/jwetherell/motion_detection/MotionDetectionActivity.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@
import java.io.FileOutputStream;
import java.util.concurrent.atomic.AtomicBoolean;

import com.jwetherell.motion_detection.detection.MotionDetection;
import com.jwetherell.motion_detection.detection.AggregateLumaMotionDetection;
import com.jwetherell.motion_detection.detection.IMotionDetection;
import com.jwetherell.motion_detection.detection.LumaMotionDetection;
import com.jwetherell.motion_detection.detection.RgbMotionDetection;
import com.jwetherell.motion_detection.image.ImageProcessing;

import android.app.Activity;
Expand Down Expand Up @@ -35,6 +38,8 @@ public class MotionDetectionActivity extends Activity {
private static boolean inPreview = false;
private static long mReferenceTime = 0;

private static IMotionDetection detector = null;

private static volatile AtomicBoolean processing = new AtomicBoolean(false);

@Override
Expand All @@ -46,6 +51,15 @@ public void onCreate(Bundle savedInstanceState) {
previewHolder = preview.getHolder();
previewHolder.addCallback(surfaceCallback);
previewHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);

if (Globals.USE_RGB) {
detector = new RgbMotionDetection();
} else if (Globals.USE_LUMA) {
detector = new LumaMotionDetection();
} else {
//Using State based (aggregate map)
detector = new AggregateLumaMotionDetection();
}
}

@Override
Expand Down Expand Up @@ -152,7 +166,7 @@ public void run() {
try {
//Previous frame
int[] pre = null;
if (Globals.SAVE_PREVIOUS) pre = MotionDetection.getPrevious();
if (Globals.SAVE_PREVIOUS) pre = detector.getPrevious();

//Current frame (with changes)
long bConversion = System.currentTimeMillis();
Expand All @@ -169,7 +183,7 @@ public void run() {
int[] org = null;
if (Globals.SAVE_ORIGINAL && img!=null) org = img.clone();

if (img!=null && MotionDetection.detect(img, width, height)) {
if (img!=null && detector.detect(img, width, height)) {
// The delay is necessary to avoid taking a picture while in the
// middle of taking another. This problem can causes some phones
// to reboot.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package com.jwetherell.motion_detection.detection;

import android.util.Log;


/**
* This class is used to process integer arrays containing luma data and detects motion using an aggregate map.
*
* @author Justin Wetherell <[email protected]>
*/
public class AggregateLumaMotionDetection implements IMotionDetection {
private static final String TAG = "AggregateLumaMotionDetection";

//Specific settings
private static final int mLeniency = 10; //Difference of aggregate map of luma values
private static final int mDebugMode = 2; //State based debug
private static final int mXBoxes = 10; //State based debug
private static final int mYBoxes = 10; //State based debug

private static int[] mPrevious = null;
private static int mPreviousWidth;
private static int mPreviousHeight;
private static State mPreviousState = null;

public int[] getPrevious() {
return ((mPrevious!=null)?mPrevious.clone():null);
}

protected static boolean isDifferent(int[] first, int width, int height) {
if (first==null) throw new NullPointerException();

if (mPrevious==null) return false;
if (first.length != mPrevious.length) return true;
if (mPreviousWidth != width || mPreviousHeight != height) return true;

if (mPreviousState==null) {
mPreviousState = new State(mPrevious, mPreviousWidth, mPreviousHeight);
return false;
}

State state = new State(first, width, height);
Comparer comparer = new Comparer(state, mPreviousState, mXBoxes, mYBoxes, mLeniency, mDebugMode);

boolean different = comparer.isDifferent();
String output = "isDifferent="+different;
if (different) {
Log.e(TAG, output);
comparer.paintDifferences(first);
} else {
Log.d(TAG, output);
}

mPreviousState = state;

return different;
}

public boolean detect(int[] luma, int width, int height) {
if (luma==null) throw new NullPointerException();

int[] original = luma.clone();

// Create the "mPrevious" picture, the one that will be used to check the next frame against.
if(mPrevious == null) {
mPrevious = original;
mPreviousWidth = width;
mPreviousHeight = height;
Log.i(TAG, "Creating background image");
}

long bDetection = System.currentTimeMillis();
boolean motionDetected = isDifferent(luma, width, height);
long aDetection = System.currentTimeMillis();
Log.d(TAG, "Detection "+(aDetection-bDetection));

// Replace the current image with the previous.
mPrevious = original;
mPreviousWidth = width;
mPreviousHeight = height;

return motionDetected;
}
}
89 changes: 53 additions & 36 deletions src/com/jwetherell/motion_detection/detection/Comparer.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import android.graphics.Color;


/**
* This class is adapted from the web site below. It is used to compare two State objects.
* http://mindmeat.blogspot.com/2008/11/java-image-comparison.html
Expand All @@ -16,44 +17,58 @@ public class Comparer {
private int yBoxes;
private int xPixelsPerBox;
private int yPixelsPerBox;
private int xLeftOver;
private int yLeftOver;
private int rowWidthInPix;
private int colWidthInPix;
private int leniency;
private int debugMode; // 1: textual indication of change, 2: difference of factors

private int[][] variance = null;
private boolean different = false;

public Comparer(State s1, State s2, int xBoxes, int yBoxes, int leniency) {
public Comparer(State s1, State s2, int xBoxes, int yBoxes, int leniency, int debug) {
this.state1 = s1;
this.state2 = s2;

this.xBoxes = xBoxes;
if (xBoxes > state1.getWidth()) xBoxes = state1.getWidth();
if (this.xBoxes > s1.getWidth()) this.xBoxes = s1.getWidth();

this.yBoxes = yBoxes;
if (yBoxes > state1.getHeight()) yBoxes = state1.getHeight();
if (this.yBoxes > s1.getHeight()) this.yBoxes = s1.getHeight();

this.leniency = leniency;
this.debugMode = 0;

// how many points per box
this.xPixelsPerBox = (int)(Math.floor(state1.getWidth() / xBoxes));
this.xPixelsPerBox = (int)(Math.floor(s1.getWidth() / this.xBoxes));
if (xPixelsPerBox <= 0) xPixelsPerBox = 1;
this.yPixelsPerBox = (int)(Math.floor(state1.getHeight() / yBoxes));
this.yPixelsPerBox = (int)(Math.floor(s1.getHeight() / this.yBoxes));
if (yPixelsPerBox <= 0) yPixelsPerBox = 1;

this.different = isDifferent(state1,state2);
}
this.xLeftOver = s1.getWidth() - (this.xBoxes*this.xPixelsPerBox);
if (xLeftOver>0) this.xBoxes++;
yLeftOver = s1.getHeight() - (this.yBoxes*this.yPixelsPerBox);
if (yLeftOver>0) this.yBoxes++;

this.rowWidthInPix = (this.xBoxes*xPixelsPerBox)-(xPixelsPerBox-xLeftOver);
this.colWidthInPix = this.xPixelsPerBox;

this.debugMode = debug;

this.different = isDifferent(this.state1,this.state2);
}

// compare two images and populate the variance variable
public boolean isDifferent(State s1, State s2) {
if (s1==null || s2==null)
throw new NullPointerException();
if (s1.getWidth()!=s2.getWidth() || s1.getHeight()!=s2.getHeight())
throw new IllegalArgumentException();
return true;

// Boxes
this.variance = new int[yBoxes][xBoxes];

// set to a different by default, if a change is found then flag non-match
boolean different = false;
// loop through whole image and compare individual blocks of images
Expand All @@ -76,52 +91,54 @@ public boolean isDifferent(State s1, State s2) {
private int aggregateMapArea(int[] map, int xBox, int yBox) {
if (map==null) throw new NullPointerException();

int yPix = yPixelsPerBox;
int xPix = xPixelsPerBox;
if (yBox==(yBoxes-1) && yLeftOver>0) yPix = yLeftOver;
if (xBox==(xBoxes-1) && xLeftOver>0) xPix = xLeftOver;

int rowOffset = (yBox*yPixelsPerBox*rowWidthInPix);
int columnOffset = (xBox*colWidthInPix);

int i = 0;
int rowOffset = (yBox*yPixelsPerBox)*(yBoxes*yPixelsPerBox);
int columnOffset = (xBox*xPixelsPerBox);
int iy = 0;
for (int y = 0; y < yPixelsPerBox; y++) {
iy = (y*(yBoxes*yPixelsPerBox));
for (int x = 0; x < xPixelsPerBox; x++) {
for (int y = 0; y < yPix; y++) {
iy = (y*(xBoxes*xPixelsPerBox))-(y*(xPixelsPerBox-xLeftOver));
for (int x = 0; x < xPix; x++) {
i += map[rowOffset+columnOffset+iy+x];
}
}

return (i/(xPixelsPerBox*yPixelsPerBox));
return (i/(xPix*yPix));
}

public void paintDifferences(int[] data) {
if (data==null) throw new NullPointerException();

int diff = 0;
for (int y = 0; y < yBoxes; y++) {
for (int x = 0; x < xBoxes; x++) {
diff = variance[y][x];
if (diff > leniency) paintRed(data, x, y);
if (variance[y][x] > leniency) paint(data, x, y, Color.RED);
}
}
}

private int paintRed(int[] data, int xBox, int yBox) {
private void paint(int[] data, int xBox, int yBox, int color) {
if (data==null) throw new NullPointerException();

int i = 0;
int rowOffset = (yBox*yPixelsPerBox)*(yBoxes*yPixelsPerBox);
int columnOffset = (xBox*xPixelsPerBox);
int yPix = yPixelsPerBox;
int xPix = xPixelsPerBox;
if (yBox==(yBoxes-1) && yLeftOver>0) yPix = yLeftOver;
if (xBox==(xBoxes-1) && xLeftOver>0) xPix = xLeftOver;

int rowOffset = (yBox*yPixelsPerBox*rowWidthInPix);
int columnOffset = (xBox*colWidthInPix);

int iy = 0;
for (int y = 0; y < yPixelsPerBox; y++) {
iy = (y*(yBoxes*yPixelsPerBox));
for (int x = 0; x < xPixelsPerBox; x++) {
data[rowOffset+columnOffset+iy+x] = Color.RED;;
for (int y = 0; y < yPix; y++) {
iy = y*(xBoxes*xPixelsPerBox)-(y*(xPixelsPerBox-xLeftOver));
for (int x = 0; x < xPix; x++) {
if (y==0 || y==(yPix-1) || x==0 || x==(xPix-1)) data[rowOffset+columnOffset+iy+x] = color;
}
}

return (i/(xPixelsPerBox*yPixelsPerBox));
}

// want to see some stuff in the console as the comparison is happening?
public void setDebugMode(int m) {
this.debugMode = m;
}

public int getCompareX() {
Expand Down Expand Up @@ -152,8 +169,8 @@ public String toString() {
output.append('|');
for (int x = 0; x < xBoxes; x++) {
diff = variance[y][x];
if (debugMode == 1) output.append((different ? 'X' : ' '));
if (debugMode == 2) output.append(diff + (x < xBoxes - 1 ? "," : ""));
if (debugMode == 1) output.append((diff > leniency)?'X':' ');
if (debugMode == 2) output.append(diff + ((x<(xBoxes-1))? "," : ""));
}
output.append("|\n");
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.jwetherell.motion_detection.detection;


/**
* This interface is used to represent a class that can detect motion
*
* @author Justin Wetherell <[email protected]>
*/
public interface IMotionDetection {

public int[] getPrevious();

public boolean detect(int[] data, int width, int height);
}
Loading

0 comments on commit 891f971

Please sign in to comment.