Skip to content

Commit

Permalink
Updated the HSL code to use luma instead
Browse files Browse the repository at this point in the history
git-svn-id: https://android-motion-detection.googlecode.com/svn/trunk@14 996b93ed-3e4d-4b21-1044-74bd19560d1d
  • Loading branch information
[email protected] committed Aug 30, 2011
1 parent b9d911e commit 50fd093
Show file tree
Hide file tree
Showing 4 changed files with 60 additions and 109 deletions.
32 changes: 23 additions & 9 deletions src/com/jwetherell/motion_detection/MotionDetectionActivity.java
Original file line number Diff line number Diff line change
Expand Up @@ -159,16 +159,21 @@ public void run() {
if (SAVE_PREVIOUS) pre = MotionDetection.getPrevious();

//Current frame (with changes)
long bRGB = System.currentTimeMillis();
int[] rgb = ImageProcessing.decodeYUV420SPtoRGB(data, width, height);
long aRGB = System.currentTimeMillis();
Log.d(TAG, "Convert to RGB "+(aRGB-bRGB));
long bConversion = System.currentTimeMillis();
int[] img = null;
if (MotionDetection.USE_RGB) {
img = ImageProcessing.decodeYUV420SPtoRGB(data, width, height);
} else {
img = ImageProcessing.decodeYUV420SPtoLuminescence(data, width, height);
}
long aConversion = System.currentTimeMillis();
Log.d(TAG, "Converstion="+(aConversion-bConversion));

//Current frame (without changes)
int[] org = null;
if (SAVE_ORIGINAL && rgb!=null) org = rgb.clone();
if (SAVE_ORIGINAL && img!=null) org = img.clone();

if (rgb!=null && MotionDetection.detect(rgb, width, height)) {
if (img!=null && MotionDetection.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 All @@ -177,13 +182,22 @@ public void run() {
mReferenceTime = now;

Bitmap previous = null;
if (SAVE_PREVIOUS && pre!=null) previous = ImageProcessing.rgbToBitmap(pre, width, height);
if (SAVE_PREVIOUS && pre!=null) {
if (MotionDetection.USE_RGB) previous = ImageProcessing.rgbToBitmap(pre, width, height);
else previous = ImageProcessing.lumToGreyscale(img, width, height);
}

Bitmap original = null;
if (SAVE_ORIGINAL && org!=null) original = ImageProcessing.rgbToBitmap(org, width, height);
if (SAVE_ORIGINAL && org!=null) {
if (MotionDetection.USE_RGB) original = ImageProcessing.rgbToBitmap(org, width, height);
else original = ImageProcessing.lumToGreyscale(img, width, height);
}

Bitmap bitmap = null;
if (SAVE_CHANGES && rgb!=null) bitmap = ImageProcessing.rgbToBitmap(rgb, width, height);
if (SAVE_CHANGES && img!=null) {
if (MotionDetection.USE_RGB) bitmap = ImageProcessing.rgbToBitmap(img, width, height);
else bitmap = ImageProcessing.lumToGreyscale(img, width, height);
}

Log.i(TAG,"Saving.. previous="+previous+" original="+original+" bitmap="+bitmap);
new SavePhotoTask().execute(previous,original,bitmap);
Expand Down
39 changes: 16 additions & 23 deletions src/com/jwetherell/motion_detection/detection/MotionDetection.java
Original file line number Diff line number Diff line change
@@ -1,34 +1,30 @@
package com.jwetherell.motion_detection.detection;

import com.jwetherell.motion_detection.image.ImageProcessing;

import android.graphics.Color;
import android.util.Log;


/**
* This abstract class is used to process integer arrays containing RGB data and detects motion.
*
* @author Justin Wetherell <[email protected]>
*/
public abstract class MotionDetection {
private static final String TAG = "MotionDetection";
private static final boolean USE_RGB = true;
private static final boolean USE_HSL = false;
private static final boolean USE_STATE = false;

//RGB Specific settings
private static final int mRgbThreshold = 10000; //Number of different pixels
private static final int mRgbPixelThreshold = 50; //Difference in pixel

//HSL Specific settings
private static final int mHslThreshold = 1000; //Number of different pixels
private static final int mHslPixelThreshold = 1; //Difference in brightness
//Specific settings
private static final int mThreshold = 10000; //Number of different pixels
private static final int mPixelThreshold = 50; //Difference in pixel

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

public static final boolean USE_RGB = true;
public static final boolean USE_LUM = false;
public static final boolean USE_STATE = false;

public static int[] getPrevious() {
return ((mPrevious!=null)?mPrevious.clone():null);
}
Expand All @@ -54,12 +50,12 @@ protected static boolean isDifferentComparingState(int[] first, int width, int h
Log.d(TAG, output);
}

mPreviousState = state.clone();
mPreviousState = state;

return different;
}

protected static boolean isDifferentComparingHSL(int[] first, int width, int height) {
protected static boolean isDifferentComparingLuminescence(int[] first, int width, int height) {
if (first==null || mPrevious==null) return false;
if (first.length != mPrevious.length) return true;
if (mPreviousWidth != width || mPreviousHeight != height) return true;
Expand All @@ -78,18 +74,15 @@ protected static boolean isDifferentComparingHSL(int[] first, int width, int hei
if (otherPix < 0) otherPix = 0;
if (otherPix > 255) otherPix = 255;

int b1 = ImageProcessing.getBrightnessAtPoint(pix);
int b2 = ImageProcessing.getBrightnessAtPoint(otherPix);

if (Math.abs(b1 - b2) >= mHslPixelThreshold) {
if (Math.abs(pix - otherPix) >= mPixelThreshold) {
totDifferentPixels++;
//Paint different pixel red
first[ij] = Color.RED;
}
}
}
if (totDifferentPixels <= 0) totDifferentPixels = 1;
boolean different = totDifferentPixels > mHslThreshold;
boolean different = totDifferentPixels > mThreshold;

int percent = 100/(size/totDifferentPixels);
String output = "Number of different pixels: " + totDifferentPixels + "> " + percent + "%";
Expand Down Expand Up @@ -121,15 +114,15 @@ protected static boolean isDifferentComparingRGB(int[] first, int width, int hei
if (otherPix < 0) otherPix = 0;
if (otherPix > 255) otherPix = 255;

if (Math.abs(pix - otherPix) >= mRgbPixelThreshold) {
if (Math.abs(pix - otherPix) >= mPixelThreshold) {
totDifferentPixels++;
//Paint different pixel red
first[ij] = Color.RED;
}
}
}
if (totDifferentPixels <= 0) totDifferentPixels = 1;
boolean different = totDifferentPixels > mRgbThreshold;
boolean different = totDifferentPixels > mThreshold;

int percent = 100/(size/totDifferentPixels);
String output = "Number of different pixels: " + totDifferentPixels + "> " + percent + "%";
Expand Down Expand Up @@ -158,7 +151,7 @@ public static boolean detect(int[] rgb, int width, int height) {
long bDetection = System.currentTimeMillis();
boolean motionDetected = false;
if (USE_RGB) motionDetected = isDifferentComparingRGB(rgb, width, height);
if (USE_HSL) motionDetected = isDifferentComparingHSL(rgb, width, height);
if (USE_LUM) motionDetected = isDifferentComparingLuminescence(rgb, width, height);
if (USE_STATE) motionDetected = isDifferentComparingState(rgb, width, height);
long aDetection = System.currentTimeMillis();
Log.d(TAG, "Detection "+(aDetection-bDetection));
Expand Down
7 changes: 3 additions & 4 deletions src/com/jwetherell/motion_detection/detection/State.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package com.jwetherell.motion_detection.detection;

import com.jwetherell.motion_detection.image.ImageProcessing;

/**
* This class is adapted from the web site below. It creates a state object based on the brightness of a RGB image
Expand Down Expand Up @@ -36,9 +35,9 @@ public State(int[] data, int width, int height) {
average = 0;
for (int y = 0, xy=0; y < this.height; y++) {
for (int x = 0; x < this.width; x++, xy++) {
int ta = ImageProcessing.getBrightnessAtPoint(data[xy]);
map[y][x] = ta;
average += ta;
int lum = data[xy];
map[y][x] = lum;
average += lum;
}
}
average = (average / (this.width * this.height));
Expand Down
91 changes: 18 additions & 73 deletions src/com/jwetherell/motion_detection/image/ImageProcessing.java
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
package com.jwetherell.motion_detection.image;

import java.io.ByteArrayOutputStream;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.graphics.Matrix;
import android.util.Log;


/**
* This abstract class is used to process images.
Expand Down Expand Up @@ -74,86 +74,18 @@ public static int[] convertToHSL(int r, int g, int b) {
//Since they were converted from float to int
return (new int[]{(int)h,(int)s,(int)l});
}

public static int getBrightnessAtPoint(int pixel) {
//Get RGB from Integer
int r = (pixel >> 16) & 0xff;
int g = (pixel >> 8) & 0xff;
int b = (pixel) & 0xff;

//Convert RGB to HSL (not using method above because I don't want to create
//an extra float[] for every pixel.
float red = r;
float green = g;
float blue = b;
red = red / 255;
green = green / 255;
blue = blue / 255;

float h=0,s=0,l=0;
float minComponent = Math.min(red, Math.min(green, blue));
float maxComponent = Math.max(red, Math.max(green, blue));
float range = maxComponent - minComponent;

l = (maxComponent + minComponent) / 2;

if(range == 0) { // Monochrome image
h = s = 0;
} else {
s = (l > 0.5) ?
range / (2 - range)
:
range / (maxComponent + minComponent);
if (Float.compare(red,maxComponent)==0) {
h = (blue - green) / range;
} else if(Float.compare(green,maxComponent)==0) {
h = 2 + (blue - red) / range;
} else if(Float.compare(blue,maxComponent)==0) {
h = 4 +(red - green) / range;
} else {
Log.e("TAG", "Should not get here!");
}
}

//convert to 0-360
h = h * 60;
if (h<0) h = h + 360;

//convert to 0-100
s = s * 100;
l = l * 100;

//Convert the HSL into a single "brightness" representation
//brightness is between 0-100 using 50% lightness and 50% hue
int brightness = (int)((l * 0.5) + ((h / 360) * 50));
return brightness;
}

public static int[][] decodeYUV420SPtoHSL(byte[] yuv420sp, int width, int height) {
public static int[] decodeYUV420SPtoLuminescence(byte[] yuv420sp, int width, int height) {
if (yuv420sp==null) return null;

final int frameSize = width * height;
int[][] hsl = new int[frameSize][3];
int[] hsl = new int[frameSize];

for (int j = 0, yp = 0; j < height; j++) {
int uvp = frameSize + (j >> 1) * width, u = 0, v = 0;
for (int i = 0; i < width; i++, yp++) {
int y = (0xff & ((int) yuv420sp[yp])) - 16;
if (y < 0) y = 0;
if ((i & 1) == 0) {
v = (0xff & yuv420sp[uvp++]) - 128;
u = (0xff & yuv420sp[uvp++]) - 128;
}
int y1192 = 1192 * y;
int r = (y1192 + 1634 * v);
int g = (y1192 - 833 * v - 400 * u);
int b = (y1192 + 2066 * u);

if (r < 0) r = 0; else if (r > 262143) r = 262143;
if (g < 0) g = 0; else if (g > 262143) g = 262143;
if (b < 0) b = 0; else if (b > 262143) b = 262143;

hsl[yp] = convertToHSL(r,g,b);
hsl[yp] = y;
}
}
return hsl;
Expand Down Expand Up @@ -197,6 +129,19 @@ public static Bitmap rgbToBitmap(int[] rgb, int width, int height) {
return bitmap;
}

public static Bitmap lumToGreyscale(int[] lum, int width, int height) {
if (lum==null) return null;

Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565);
for(int y=0, xy=0; y<bitmap.getHeight(); y++) {
for(int x=0; x<bitmap.getWidth(); x++, xy++) {
int luma = lum[xy];
bitmap.setPixel(x,y,Color.argb(1,luma,luma,luma));
}
}
return bitmap;
}

public static Bitmap rotate(Bitmap bmp, int degrees) {
if (bmp==null) return null;

Expand Down

0 comments on commit 50fd093

Please sign in to comment.