
com.github.sarxos.webcam.WebcamMotionDetector Maven / Gradle / Ivy
Go to download
This library allows you to use your PC webcam, IP or network cameras directly from Java. It's compatible with most operating systems (Windows, Linux, MacOS).
package com.github.sarxos.webcam;
import java.awt.Point;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Webcam motion detector.
*
* @author Bartosz Firyn (SarXos)
*/
public class WebcamMotionDetector {
/**
* Logger.
*/
private static final Logger LOG = LoggerFactory.getLogger(WebcamMotionDetector.class);
/**
* Thread number in pool.
*/
private static final AtomicInteger NT = new AtomicInteger(0);
/**
* Thread factory.
*/
private static final ThreadFactory THREAD_FACTORY = new DetectorThreadFactory();
/**
* Default check interval, in milliseconds, set to 500 ms.
*/
public static final int DEFAULT_INTERVAL = 500;
/**
* Create new threads for detector internals.
*
* @author Bartosz Firyn (SarXos)
*/
private static final class DetectorThreadFactory implements ThreadFactory {
@Override
public Thread newThread(Runnable runnable) {
Thread t = new Thread(runnable, String.format("motion-detector-%d", NT.incrementAndGet()));
t.setUncaughtExceptionHandler(WebcamExceptionHandler.getInstance());
t.setDaemon(true);
return t;
}
}
/**
* Run motion detector.
*
* @author Bartosz Firyn (SarXos)
*/
private class Runner implements Runnable {
@Override
public void run() {
running.set(true);
while (running.get() && webcam.isOpen()) {
try {
detect();
Thread.sleep(interval);
} catch (InterruptedException e) {
break;
} catch (Exception e) {
WebcamExceptionHandler.handle(e);
}
}
running.set(false);
}
}
/**
* Change motion to false after specified number of seconds.
*
* @author Bartosz Firyn (SarXos)
*/
private class Inverter implements Runnable {
@Override
public void run() {
int delay = 0;
while (running.get()) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
break;
}
delay = inertia != -1 ? inertia : 2 * interval;
if (lastMotionTimestamp + delay < System.currentTimeMillis()) {
motion = false;
}
}
}
}
/**
* Executor.
*/
private final ExecutorService executor = Executors.newFixedThreadPool(2, THREAD_FACTORY);
/**
* Motion listeners.
*/
private final List listeners = new ArrayList();
/**
* Is detector running?
*/
private final AtomicBoolean running = new AtomicBoolean(false);
/**
* Is motion?
*/
private volatile boolean motion = false;
/**
* Previously captured image.
*/
private BufferedImage previousOriginal = null;
/**
* Previously captured image with blur and gray filters applied.
*/
private BufferedImage previousModified = null;
/**
* Webcam to be used to detect motion.
*/
private Webcam webcam = null;
/**
* Motion check interval (1000 ms by default).
*/
private volatile int interval = DEFAULT_INTERVAL;
/**
* How long motion is valid (in milliseconds). Default value is 2 seconds.
*/
private volatile int inertia = -1;
/**
* Timestamp when motion has been observed last time.
*/
private volatile long lastMotionTimestamp = 0;
/**
* Implementation of motion detection algorithm.
*/
private final WebcamMotionDetectorAlgorithm detectorAlgorithm;
/**
* Create motion detector. Will open webcam if it is closed.
*
* @param webcam web camera instance
* @param motion detector algorithm implementation
* @param interval the check interval
*/
public WebcamMotionDetector(Webcam webcam, WebcamMotionDetectorAlgorithm detectorAlgorithm, int interval) {
this.webcam = webcam;
this.detectorAlgorithm = detectorAlgorithm;
setInterval(interval);
}
/**
* Create motion detector. Will open webcam if it is closed.
* Uses WebcamMotionDetectorDefaultAlgorithm for motion detection.
*
* @param webcam web camera instance
* @param pixelThreshold intensity threshold (0 - 255)
* @param areaThreshold percentage threshold of image covered by motion
* @param interval the check interval
*/
public WebcamMotionDetector(Webcam webcam, int pixelThreshold, double areaThreshold, int interval) {
this(webcam, new WebcamMotionDetectorDefaultAlgorithm(pixelThreshold, areaThreshold), interval);
}
/**
* Create motion detector with default parameter inertia = 0.
* Uses WebcamMotionDetectorDefaultAlgorithm for motion detection.
*
* @param webcam web camera instance
* @param pixelThreshold intensity threshold (0 - 255)
* @param areaThreshold percentage threshold of image covered by motion (0 -
* 100)
*/
public WebcamMotionDetector(Webcam webcam, int pixelThreshold, double areaThreshold) {
this(webcam, pixelThreshold, areaThreshold, DEFAULT_INTERVAL);
}
/**
* Create motion detector with default parameter inertia = 0.
* Uses WebcamMotionDetectorDefaultAlgorithm for motion detection.
*
* @param webcam web camera instance
* @param pixelThreshold intensity threshold (0 - 255)
*/
public WebcamMotionDetector(Webcam webcam, int pixelThreshold) {
this(webcam, pixelThreshold, WebcamMotionDetectorDefaultAlgorithm.DEFAULT_AREA_THREASHOLD);
}
/**
* Create motion detector with default parameters - threshold = 25, inertia
* = 0.
*
* @param webcam web camera instance
*/
public WebcamMotionDetector(Webcam webcam) {
this(webcam, WebcamMotionDetectorDefaultAlgorithm.DEFAULT_PIXEL_THREASHOLD);
}
public void start() {
if (running.compareAndSet(false, true)) {
webcam.open();
executor.submit(new Runner());
executor.submit(new Inverter());
}
}
public void stop() {
if (running.compareAndSet(true, false)) {
webcam.close();
executor.shutdownNow();
}
}
protected void detect() {
if (!webcam.isOpen()) {
motion = false;
return;
}
BufferedImage currentOriginal = webcam.getImage();
if (currentOriginal == null) {
motion = false;
return;
}
BufferedImage currentModified = detectorAlgorithm.prepareImage(currentOriginal);
boolean movementDetected = detectorAlgorithm.detect(previousModified, currentModified);
if (movementDetected) {
motion = true;
lastMotionTimestamp = System.currentTimeMillis();
notifyMotionListeners(currentOriginal);
}
previousOriginal = currentOriginal;
previousModified = currentModified;
}
/**
* Will notify all attached motion listeners.
* @param image with the motion detected
*/
private void notifyMotionListeners(BufferedImage currentOriginal) {
WebcamMotionEvent wme = new WebcamMotionEvent(this, previousOriginal, currentOriginal, detectorAlgorithm.getArea(), detectorAlgorithm.getCog(), detectorAlgorithm.getPoints());
for (WebcamMotionListener l : listeners) {
try {
l.motionDetected(wme);
} catch (Exception e) {
WebcamExceptionHandler.handle(e);
}
}
}
/**
* Add motion listener.
*
* @param l listener to add
* @return true if listeners list has been changed, false otherwise
*/
public boolean addMotionListener(WebcamMotionListener l) {
return listeners.add(l);
}
/**
* @return All motion listeners as array
*/
public WebcamMotionListener[] getMotionListeners() {
return listeners.toArray(new WebcamMotionListener[listeners.size()]);
}
/**
* Removes motion listener.
*
* @param l motion listener to remove
* @return true if listener was available on the list, false otherwise
*/
public boolean removeMotionListener(WebcamMotionListener l) {
return listeners.remove(l);
}
/**
* @return Motion check interval in milliseconds
*/
public int getInterval() {
return interval;
}
/**
* Motion check interval in milliseconds. After motion is detected, it's
* valid for time which is equal to value of 2 * interval.
*
* @param interval the new motion check interval (ms)
* @see #DEFAULT_INTERVAL
*/
public void setInterval(int interval) {
if (interval < 100) {
throw new IllegalArgumentException("Motion check interval cannot be less than 100 ms");
}
this.interval = interval;
}
/**
* Sets pixelThreshold to the underlying detector algorithm, but only if the
* algorithm is (or extends) WebcamMotionDetectorDefaultAlgorithm
*
* @see WebcamMotionDetectorDefaultAlgorithm#setPixelThreshold(int)
*
* @param threshold the pixel intensity difference threshold
*/
public void setPixelThreshold(int threshold) {
if (detectorAlgorithm instanceof WebcamMotionDetectorDefaultAlgorithm) {
((WebcamMotionDetectorDefaultAlgorithm)detectorAlgorithm).setPixelThreshold(threshold);
}
}
/**
* Sets areaThreshold to the underlying detector algorithm, but only if the
* algorithm is (or extends) WebcamMotionDetectorDefaultAlgorithm
*
* @see WebcamMotionDetectorDefaultAlgorithm#setAreaThreshold(double)
*
* @param threshold the percentage fraction of image area
*/
public void setAreaThreshold(double threshold) {
if (detectorAlgorithm instanceof WebcamMotionDetectorDefaultAlgorithm) {
((WebcamMotionDetectorDefaultAlgorithm)detectorAlgorithm).setAreaThreshold(threshold);
}
}
/**
* Set motion inertia (time when motion is valid). If no value specified
* this is set to 2 * interval. To reset to default value,
* {@link #clearInertia()} method must be used.
*
* @param inertia the motion inertia time in milliseconds
* @see #clearInertia()
*/
public void setInertia(int inertia) {
if (inertia < 0) {
throw new IllegalArgumentException("Inertia time must not be negative!");
}
this.inertia = inertia;
}
/**
* Reset inertia time to value calculated automatically on the base of
* interval. This value will be set to 2 * interval.
*/
public void clearInertia() {
this.inertia = -1;
}
/**
* Get attached webcam object.
*
* @return Attached webcam
*/
public Webcam getWebcam() {
return webcam;
}
public boolean isMotion() {
if (!running.get()) {
LOG.warn("Motion cannot be detected when detector is not running!");
}
return motion;
}
/**
* Get percentage fraction of image covered by motion. 0 means no motion on
* image and 100 means full image covered by spontaneous motion.
*
* @return Return percentage image fraction covered by motion
*/
public double getMotionArea() {
return detectorAlgorithm.getArea();
}
/**
* Get motion center of gravity. When no motion is detected this value
* points to the image center.
*
* @return Center of gravity point
*/
public Point getMotionCog() {
Point cog = detectorAlgorithm.getCog();
if (cog == null) {
// detectorAlgorithm hasn't been called so far - get image center
int w = webcam.getViewSize().width;
int h = webcam.getViewSize().height;
cog = new Point(w / 2, h / 2);
}
return cog;
}
/**
* @return the detectorAlgorithm
*/
public WebcamMotionDetectorAlgorithm getDetectorAlgorithm() {
return detectorAlgorithm;
}
public void setMaxMotionPoints(int i){
detectorAlgorithm.setMaxPoints(i);
}
public int getMaxMotionPoints(){
return detectorAlgorithm.getMaxPoints();
}
public void setPointRange(int i){
detectorAlgorithm.setPointRange(i);
}
public int getPointRange(){
return detectorAlgorithm.getPointRange();
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy