1313import org .slf4j .Logger ;
1414import org .slf4j .LoggerFactory ;
1515
16- import com .github .sarxos .webcam .util .jh .JHBlurFilter ;
17- import com .github .sarxos .webcam .util .jh .JHGrayFilter ;
18-
1916
2017/**
2118 * Webcam motion detector.
@@ -39,21 +36,11 @@ public class WebcamMotionDetector {
3936 */
4037 private static final ThreadFactory THREAD_FACTORY = new DetectorThreadFactory ();
4138
42- /**
43- * Default pixel difference intensity threshold (set to 25).
44- */
45- public static final int DEFAULT_PIXEL_THREASHOLD = 25 ;
46-
4739 /**
4840 * Default check interval, in milliseconds, set to 500 ms.
4941 */
5042 public static final int DEFAULT_INTERVAL = 500 ;
5143
52- /**
53- * Default percentage image area fraction threshold (set to 0.2%).
54- */
55- public static final double DEFAULT_AREA_THREASHOLD = 0.2 ;
56-
5744 /**
5845 * Create new threads for detector internals.
5946 *
@@ -166,65 +153,50 @@ public void run() {
166153 */
167154 private volatile int interval = DEFAULT_INTERVAL ;
168155
169- /**
170- * Pixel intensity threshold (0 - 255).
171- */
172- private volatile int pixelThreshold = DEFAULT_PIXEL_THREASHOLD ;
173-
174- /**
175- * Pixel intensity threshold (0 - 100).
176- */
177- private volatile double areaThreshold = DEFAULT_AREA_THREASHOLD ;
178-
179156 /**
180157 * How long motion is valid (in milliseconds). Default value is 2 seconds.
181158 */
182159 private volatile int inertia = -1 ;
183160
184- /**
185- * Motion strength (0 = no motion, 100 = full image covered by motion).
186- */
187- private double area = 0 ;
188-
189- /**
190- * Center of motion gravity.
191- */
192- private Point cog = new Point (0 , 0 );
193-
194161 /**
195162 * Timestamp when motion has been observed last time.
196163 */
197164 private volatile long lastMotionTimestamp = 0 ;
198165
199166 /**
200- * Blur filter instance .
167+ * Implementation of motion detection algorithm .
201168 */
202- private final JHBlurFilter blur = new JHBlurFilter ( 6 , 6 , 1 ) ;
169+ private final WebcamMotionDetectorAlgorithm detectorAlgorithm ;
203170
204171 /**
205- * Gray filter instance.
172+ * Create motion detector. Will open webcam if it is closed.
173+ *
174+ * @param webcam web camera instance
175+ * @param motion detector algorithm implementation
176+ * @param interval the check interval
206177 */
207- private final JHGrayFilter gray = new JHGrayFilter ();
208-
178+ public WebcamMotionDetector (Webcam webcam , WebcamMotionDetectorAlgorithm detectorAlgorithm , int interval ) {
179+ this .webcam = webcam ;
180+ this .detectorAlgorithm = detectorAlgorithm ;
181+ setInterval (interval );
182+ }
183+
209184 /**
210- * Create motion detector. Will open webcam if it is closed.
185+ * Create motion detector. Will open webcam if it is closed.
186+ * Uses WebcamMotionDetectorDefaultAlgorithm for motion detection.
211187 *
212188 * @param webcam web camera instance
213189 * @param pixelThreshold intensity threshold (0 - 255)
214190 * @param areaThreshold percentage threshold of image covered by motion
215191 * @param interval the check interval
216192 */
217193 public WebcamMotionDetector (Webcam webcam , int pixelThreshold , double areaThreshold , int interval ) {
218-
219- this .webcam = webcam ;
220-
221- setPixelThreshold (pixelThreshold );
222- setAreaThreshold (areaThreshold );
223- setInterval (interval );
194+ this (webcam , new WebcamMotionDetectorDefaultAlgorithm (pixelThreshold , areaThreshold ), interval );
224195 }
225196
226197 /**
227198 * Create motion detector with default parameter inertia = 0.
199+ * Uses WebcamMotionDetectorDefaultAlgorithm for motion detection.
228200 *
229201 * @param webcam web camera instance
230202 * @param pixelThreshold intensity threshold (0 - 255)
@@ -237,12 +209,13 @@ public WebcamMotionDetector(Webcam webcam, int pixelThreshold, double areaThresh
237209
238210 /**
239211 * Create motion detector with default parameter inertia = 0.
212+ * Uses WebcamMotionDetectorDefaultAlgorithm for motion detection.
240213 *
241214 * @param webcam web camera instance
242215 * @param pixelThreshold intensity threshold (0 - 255)
243216 */
244217 public WebcamMotionDetector (Webcam webcam , int pixelThreshold ) {
245- this (webcam , pixelThreshold , DEFAULT_AREA_THREASHOLD );
218+ this (webcam , pixelThreshold , WebcamMotionDetectorDefaultAlgorithm . DEFAULT_AREA_THREASHOLD );
246219 }
247220
248221 /**
@@ -252,19 +225,12 @@ public WebcamMotionDetector(Webcam webcam, int pixelThreshold) {
252225 * @param webcam web camera instance
253226 */
254227 public WebcamMotionDetector (Webcam webcam ) {
255- this (webcam , DEFAULT_PIXEL_THREASHOLD );
228+ this (webcam , WebcamMotionDetectorDefaultAlgorithm . DEFAULT_PIXEL_THREASHOLD );
256229 }
257230
258231 public void start () {
259232 if (running .compareAndSet (false , true )) {
260-
261233 webcam .open ();
262-
263- int w = webcam .getViewSize ().width ;
264- int h = webcam .getViewSize ().height ;
265-
266- cog = new Point (w / 2 , h / 2 );
267-
268234 executor .submit (new Runner ());
269235 executor .submit (new Inverter ());
270236 }
@@ -291,49 +257,16 @@ protected void detect() {
291257 return ;
292258 }
293259
294- BufferedImage currentModified = blur .filter (currentOriginal , null );
295- currentModified = gray .filter (currentModified , null );
296-
297- int p = 0 ;
298-
299- int cogX = 0 ;
300- int cogY = 0 ;
301-
302- int w = currentModified .getWidth ();
303- int h = currentModified .getHeight ();
304-
305- if (previousModified != null ) {
306- for (int x = 0 ; x < w ; x ++) {
307- for (int y = 0 ; y < h ; y ++) {
308-
309- int cpx = currentModified .getRGB (x , y );
310- int ppx = previousModified .getRGB (x , y );
311- int pid = combinePixels (cpx , ppx ) & 0x000000ff ;
312-
313- if (pid >= pixelThreshold ) {
314- cogX += x ;
315- cogY += y ;
316- p += 1 ;
317- }
318- }
319- }
320- }
321-
322- area = p * 100d / (w * h );
323-
324- if (area >= areaThreshold ) {
325-
326- cog = new Point (cogX / p , cogY / p );
260+ BufferedImage currentModified = detectorAlgorithm .prepareImage (currentOriginal );
261+
262+ boolean movementDetected = detectorAlgorithm .detect (previousModified , currentModified );
327263
264+ if (movementDetected ) {
328265 motion = true ;
329266 lastMotionTimestamp = System .currentTimeMillis ();
330-
331267 notifyMotionListeners (currentOriginal );
332-
333- } else {
334- cog = new Point (w / 2 , h / 2 );
335268 }
336-
269+
337270 previousOriginal = currentOriginal ;
338271 previousModified = currentModified ;
339272 }
@@ -343,7 +276,7 @@ protected void detect() {
343276 * @param image with the motion detected
344277 */
345278 private void notifyMotionListeners (BufferedImage currentOriginal ) {
346- WebcamMotionEvent wme = new WebcamMotionEvent (this , previousOriginal , currentOriginal , area , cog );
279+ WebcamMotionEvent wme = new WebcamMotionEvent (this , previousOriginal , currentOriginal , detectorAlgorithm . getArea (), detectorAlgorithm . getCog () );
347280 for (WebcamMotionListener l : listeners ) {
348281 try {
349282 l .motionDetected (wme );
@@ -404,39 +337,31 @@ public void setInterval(int interval) {
404337 }
405338
406339 /**
407- * Set pixel intensity difference threshold above which pixel is classified
408- * as "moved". Minimum value is 0 and maximum is 255. Default value is 10.
409- * This value is equal for all RGB components difference.
410- *
340+ * Sets pixelThreshold to the underlying detector algorithm, but only if the
341+ * algorithm is (or extends) WebcamMotionDetectorDefaultAlgorithm
342+ *
343+ * @see WebcamMotionDetectorDefaultAlgorithm#setPixelThreshold(int)
344+ *
411345 * @param threshold the pixel intensity difference threshold
412- * @see #DEFAULT_PIXEL_THREASHOLD
413346 */
414347 public void setPixelThreshold (int threshold ) {
415- if (threshold < 0 ) {
416- throw new IllegalArgumentException ( "Pixel intensity threshold cannot be negative!" );
348+ if (detectorAlgorithm instanceof WebcamMotionDetectorDefaultAlgorithm ) {
349+ (( WebcamMotionDetectorDefaultAlgorithm ) detectorAlgorithm ). setPixelThreshold ( threshold );
417350 }
418- if (threshold > 255 ) {
419- throw new IllegalArgumentException ("Pixel intensity threshold cannot be higher than 255!" );
420- }
421- this .pixelThreshold = threshold ;
422351 }
423352
424353 /**
425- * Set percentage fraction of detected motion area threshold above which it
426- * is classified as "moved". Minimum value for this is 0 and maximum is 100,
427- * which corresponds to full image covered by spontaneous motion.
428- *
354+ * Sets areaThreshold to the underlying detector algorithm, but only if the
355+ * algorithm is (or extends) WebcamMotionDetectorDefaultAlgorithm
356+ *
357+ * @see WebcamMotionDetectorDefaultAlgorithm#setAreaThreshold(double)
358+ *
429359 * @param threshold the percentage fraction of image area
430- * @see #DEFAULT_AREA_THREASHOLD
431360 */
432361 public void setAreaThreshold (double threshold ) {
433- if (threshold < 0 ) {
434- throw new IllegalArgumentException ( "Area fraction threshold cannot be negative!" );
362+ if (detectorAlgorithm instanceof WebcamMotionDetectorDefaultAlgorithm ) {
363+ (( WebcamMotionDetectorDefaultAlgorithm ) detectorAlgorithm ). setAreaThreshold ( threshold );
435364 }
436- if (threshold > 100 ) {
437- throw new IllegalArgumentException ("Area fraction threshold cannot be higher than 100!" );
438- }
439- this .areaThreshold = threshold ;
440365 }
441366
442367 /**
@@ -485,7 +410,7 @@ public boolean isMotion() {
485410 * @return Return percentage image fraction covered by motion
486411 */
487412 public double getMotionArea () {
488- return area ;
413+ return detectorAlgorithm . getArea () ;
489414 }
490415
491416 /**
@@ -495,54 +420,21 @@ public double getMotionArea() {
495420 * @return Center of gravity point
496421 */
497422 public Point getMotionCog () {
498- return cog ;
499- }
500-
501- private static int combinePixels (int rgb1 , int rgb2 ) {
502-
503- // first ARGB
504-
505- int a1 = (rgb1 >> 24 ) & 0xff ;
506- int r1 = (rgb1 >> 16 ) & 0xff ;
507- int g1 = (rgb1 >> 8 ) & 0xff ;
508- int b1 = rgb1 & 0xff ;
509-
510- // second ARGB
511-
512- int a2 = (rgb2 >> 24 ) & 0xff ;
513- int r2 = (rgb2 >> 16 ) & 0xff ;
514- int g2 = (rgb2 >> 8 ) & 0xff ;
515- int b2 = rgb2 & 0xff ;
516-
517- r1 = clamp (Math .abs (r1 - r2 ));
518- g1 = clamp (Math .abs (g1 - g2 ));
519- b1 = clamp (Math .abs (b1 - b2 ));
520-
521- // in case if alpha is enabled (translucent image)
522-
523- if (a1 != 0xff ) {
524- a1 = a1 * 0xff / 255 ;
525- int a3 = (255 - a1 ) * a2 / 255 ;
526- r1 = clamp ((r1 * a1 + r2 * a3 ) / 255 );
527- g1 = clamp ((g1 * a1 + g2 * a3 ) / 255 );
528- b1 = clamp ((b1 * a1 + b2 * a3 ) / 255 );
529- a1 = clamp (a1 + a3 );
423+ Point cog = detectorAlgorithm .getCog ();
424+ if (cog == null ) {
425+ // detectorAlgorithm hasn't been called so far - get image center
426+ int w = webcam .getViewSize ().width ;
427+ int h = webcam .getViewSize ().height ;
428+ cog = new Point (w / 2 , h / 2 );
530429 }
531-
532- return (a1 << 24 ) | (r1 << 16 ) | (g1 << 8 ) | b1 ;
430+ return cog ;
533431 }
534432
535433 /**
536- * Clamp a value to the range 0..255
434+ * @return the detectorAlgorithm
537435 */
538- private static int clamp (int c ) {
539- if (c < 0 ) {
540- return 0 ;
541- }
542- if (c > 255 ) {
543- return 255 ;
544- }
545- return c ;
436+ public WebcamMotionDetectorAlgorithm getDetectorAlgorithm () {
437+ return detectorAlgorithm ;
546438 }
547439
548440}
0 commit comments