Adding iotcloud as a JAR file (Java library)
[iot2.git] / benchmarks / Java / HomeSecurityController / MotionDetection.java
1 package HomeSecurityController;
2
3 // IoT packages
4 import iotcode.annotation.*;
5 import iotcode.interfaces.*;
6
7 // BoofCv packages
8 import boofcv.alg.background.BackgroundModelStationary;
9 import boofcv.factory.background.ConfigBackgroundGaussian;
10 import boofcv.factory.background.FactoryBackgroundModel;
11 import boofcv.gui.binary.VisualizeBinaryData;
12 import boofcv.gui.image.ImageGridPanel;
13 import boofcv.gui.image.ShowImages;
14 import boofcv.io.MediaManager;
15 import boofcv.io.UtilIO;
16 import boofcv.io.image.SimpleImageSequence;
17 import boofcv.io.image.ConvertBufferedImage;
18 import boofcv.io.wrapper.DefaultMediaManager;
19 import boofcv.struct.image.ImageBase;
20 import boofcv.struct.image.ImageFloat32;
21 import boofcv.struct.image.ImageType;
22 import boofcv.struct.image.ImageUInt8;
23 import boofcv.alg.filter.blur.BlurImageOps;
24
25
26 // Standard Java Packages
27 import java.awt.image.BufferedImage;
28 import java.util.Date;
29 import java.util.concurrent.locks.Lock;
30 import java.util.concurrent.locks.ReadWriteLock;
31 import java.util.concurrent.locks.ReentrantReadWriteLock;
32 import java.util.concurrent.atomic.AtomicBoolean;
33 import java.awt.image.ColorModel;
34 import java.awt.image.WritableRaster;
35 import java.util.List;
36 import java.util.ArrayList;
37 import java.io.ByteArrayInputStream;
38 import java.io.ByteArrayOutputStream;
39 import java.io.File;
40 import java.io.IOException;
41 import java.io.InputStream;
42 import javax.imageio.ImageIO;
43
44 // RMI Packages
45 import java.rmi.RemoteException;
46
47 // For testing
48 import java.net.*;
49
50 import java.rmi.Remote;
51 import java.rmi.RemoteException;
52
53 import java.util.Iterator;
54
55 // Checker annotations
56 //import iotchecker.qual.*;
57
58 /** Class MotionDetection to do motion detection using images
59  *
60  *
61  * @author      Ali Younis <ayounis @ uci.edu>
62  * @version     1.0
63  * @since       2016-03-21
64  */
65 class MotionDetection implements CameraCallback {
66
67         // Define Like variables
68         private static boolean DO_GRAPHICAL_USER_INTERFACE = false;
69
70         /*******************************************************************************************************************************************
71         **
72         **  Constants
73         **
74         *******************************************************************************************************************************************/
75         private final float MOTION_DETECTED_THRESHOLD_PERCENTAGE = 15;
76
77
78         /*******************************************************************************************************************************************
79         **
80         **  Variables
81         **
82         *******************************************************************************************************************************************/
83
84         // Timestamp buffer and locks needed for that safety on that buffer
85         // This is the buffer for post-detection algorithm use
86         private Date timestampOfLastMotion = null;
87         private ReadWriteLock timestampReadWriteLock = new ReentrantReadWriteLock();
88         private Lock timestampReadLock = timestampReadWriteLock.readLock();
89         private Lock timestampWriteLock = timestampReadWriteLock.writeLock();
90
91         // Flag for when new data is available and ready for processing
92         private AtomicBoolean newFrameAvailable = new AtomicBoolean(false);
93
94         // Flag for determining if motion has been detected and therefore
95         // the callbacks should be issued
96         private AtomicBoolean motionDetected = new AtomicBoolean(false);
97
98         // Image and timestamp buffers and  locks needed for that safety on those buffers
99         // Timestamp buffer for pre-detection algorithm use
100         private BufferedImage latestImage = null;
101         private Date possibleDate = null;
102         private ReadWriteLock imageReadWriteLock = new ReentrantReadWriteLock();
103         private Lock imageReadLock = imageReadWriteLock.readLock();
104         private Lock imageWriteLock = imageReadWriteLock.writeLock();
105
106         // List of objects wishing to receive callbacks from this class.
107         private List<MotionDetectionCallback>
108         callbackList = new ArrayList<MotionDetectionCallback>();
109
110         // Variables to help with motion detection
111         private ConfigBackgroundGaussian configGaussian = null;
112         private BackgroundModelStationary backgroundDetector = null;
113         private ImageUInt8 segmented = null;
114         private ImageFloat32 newFrameFloat = null;
115
116         // counts the number of frames since a background image is added to algorithm
117         private int frameCounter = 0;
118
119         private CameraSmart _camera;
120
121         /*******************************************************************************************************************************************
122         **
123         **  Threads
124         **
125         *******************************************************************************************************************************************/
126         private Thread workThread = null;
127         private Thread callBackThread = null;
128
129         /*******************************************************************************************************************************************
130         **
131         **  GUI Stuff (Used Only for Testing)
132         **
133         *******************************************************************************************************************************************/
134         ImageGridPanel gui;
135
136         /** Constructor
137          *
138          *   @param _threshold       [float], Variable for gaussian background detector.
139          *   @param _learnSpeed      [float], Variable for gaussian background detector.
140          *   @param _initialVariance [float], Variable for gaussian background detector.
141          *   @param _minDifference   [float], Variable for gaussian background detector.
142          *
143          */
144         public MotionDetection(float _threshold, float _learnSpeed, float _initialVariance, float _minDifference) {
145
146                 // Configure the Gaussian model used for background detection
147                 configGaussian = new ConfigBackgroundGaussian(_threshold, _learnSpeed);
148                 configGaussian.initialVariance = _initialVariance;
149                 configGaussian.minimumDifference = _minDifference;
150
151                 // setup the background detector
152                 ImageType imageType = ImageType.single(ImageFloat32.class);
153                 backgroundDetector = FactoryBackgroundModel.stationaryGaussian(configGaussian, imageType);
154
155                 // setup the gui if we are going to use it
156                 if (DO_GRAPHICAL_USER_INTERFACE) {
157
158                         // create an image grid for images to place on, tile fashion
159                         gui = new ImageGridPanel(1, 2);
160
161                         // make the window large so we dont have to manually resize with the mouse
162                         gui.setSize(1920, 1080);
163
164                         // Make the window visible and set the title
165                         ShowImages.showWindow(gui, "Static Scene: Background Segmentation", true);
166                 }
167
168                 // Launch the worker thread
169                 workThread = new Thread(new Runnable() {
170                         public void run() {
171
172                                 while (true) {
173                                         runMotionDetection();
174                                 }
175                         }
176                 });
177                 workThread.start();
178
179
180                 // Launch the callback thread
181                 callBackThread = new Thread(new Runnable() {
182                         public void run() {
183
184                                 while (true) {
185                                         doCallbacks();
186                                 }
187                         }
188                 });
189                 callBackThread.start();
190         }
191
192
193         /*******************************************************************************************************************************************
194         **
195         **  Public Methods
196         **
197         *******************************************************************************************************************************************/
198
199         /** Method to add a new frame to the motion detector.
200          *
201          *   @param _newFrame  [byte[]], Frame data of new frame.
202          *   @param _timestamp [Date]  , Timestamp of new frame.
203          *
204          *   @return [void] None.
205          */
206         public void addFrame(byte[]  _newFrame, Date _timestamp) {
207                 BufferedImage img = null;
208
209                 try {
210                         // Parse the byte array into a Buffered Image
211                         InputStream in = new ByteArrayInputStream(_newFrame);
212                         img = ImageIO.read(in);
213
214                 } catch (Exception e) {
215                         e.printStackTrace();
216                         return;
217                 }
218
219                 // Save the image and timestamp for use later
220                 imageWriteLock.lock();// lock the image and timestamp buffers since multithread
221                 latestImage = img;// image into image buffer
222                 possibleDate = _timestamp;// timestamp into timestamp buffer
223                 imageWriteLock.unlock();// Never forget to unlock
224
225                 // flag the worker thread that there is new data ready for processing
226                 newFrameAvailable.set(true);
227         }
228
229         /** Method to get the timestamp of the last time motion was detected
230          *
231          *   @return [Date] timestamp of last motion or null if no motion was ever detected.
232          */
233         public Date getTimestampOfLastMotion() {
234                 Date ret = null;
235
236                 // Be safe because multithread
237                 timestampReadLock.lock();
238
239                 // Checks if there was ever motion, if not then timestampOfLastMotion
240                 // will be null
241                 if (timestampOfLastMotion != null) {
242                         // Clone since we don't know what the other person is going to do
243                         // with the timestamp
244                         ret = (Date)timestampOfLastMotion.clone();
245                 }
246
247                 timestampReadLock.unlock();
248
249                 return ret;
250         }
251
252
253         /** Method to add a new frame to the motion detector from a camera
254          *
255          *   @param _camera  [Camera], Camera that has the new data.
256          *
257          *   @return [void] None.
258          */
259         public void newCameraFrameAvailable(byte[] latestFrame, long timeStamp) {
260                 BufferedImage img = null;
261
262                 try {
263                         // Parse the byte array into a Buffered Image
264                         //InputStream in = new ByteArrayInputStream(_camera.getLatestFrame());
265                         InputStream in = new ByteArrayInputStream(latestFrame);
266                         img = ImageIO.read(in);
267
268                 } catch (RemoteException e) {
269                         e.printStackTrace();
270                         return;
271
272                 } catch (Exception e) {
273                         e.printStackTrace();
274                         return;
275
276                 }
277
278                 // Save the image and timestamp for use later
279                 imageWriteLock.lock();  // lock the image and timestamp buffers since multithread
280                 latestImage = img;              // image into image buffer
281
282                 // timestamp from camera into timestamp buffer
283                 long dateLong = timeStamp;
284                 possibleDate = new Date(dateLong);
285
286                 imageWriteLock.unlock();        // Never forget to unlock
287
288                 // flag the worker thread that there is new data ready for processing
289                 newFrameAvailable.set(true);
290         }
291
292         /** Method to register an object to recieve callbacks from this motion detector
293          *
294          *   @param _mdc  [MotionDetectionCallback], object to recieve callbacks.
295          *
296          *   @return [void] None.
297          */
298         public void registerCallback(MotionDetectionCallback _mdc) {
299                 callbackList.add(_mdc);
300         }
301
302         /*******************************************************************************************************************************************
303         **
304         **  Helper Methods
305         **
306         *******************************************************************************************************************************************/
307
308         /** Method that constantly loops checking if new data is available.  If there is
309          *   new data, it is processed.
310          *   This method should be run on a separate thread.
311          *
312          *   @return [void] None.
313          */
314         private void runMotionDetection() {
315
316                 // check if there is a new frame availble, only runs detection if there is new data to save
317                 // computation time
318                 if (!newFrameAvailable.get()) {
319                         return;
320                 }
321
322                 // Lock since we are accessing the data buffers
323                 imageReadLock.lock();
324
325                 // processing data so now the buffered data is old
326                 newFrameAvailable.set(false);
327
328                 // copy from buffer to local for processing
329                 Date tmpDate = possibleDate;
330
331                 // Allocate space for the segmented image based on the first image we received
332                 // cannot pre-allocate this since we do not know what the size of the images is
333                 // before the first image arrives
334                 if (segmented == null) {
335                         segmented = new ImageUInt8(latestImage.getWidth(), latestImage.getHeight());
336                 }
337
338                 // copy from data buffers and convert into correct data type for BoofCv libraries
339                 newFrameFloat = ConvertBufferedImage.convertFrom(latestImage, newFrameFloat);
340
341                 // All done accessing the data buffers
342                 imageReadLock.unlock();
343
344                 // Run background detection
345                 backgroundDetector.segment(newFrameFloat, segmented);
346
347                 // Update the background baseline every 10 frames, helps the algorithm
348                 frameCounter++;
349                 if (frameCounter > 10) {
350                         backgroundDetector.updateBackground(newFrameFloat);
351                         frameCounter = 0;
352                 }
353
354                 // get the raw pixel data, gray-scale image
355                 byte[] frameData = segmented.getData();
356
357                 // count the number of pixels of the image that was deemed as "motion"
358                 double count = 0;
359                 double countMotion = 0;
360                 for (byte b : frameData) {
361                         count++;
362                         if (b > 0) {
363                                 countMotion++;
364                         }
365                 }
366
367                 // calculate the percentage of the image that was in motion
368                 double percentMotion = (countMotion / count) * 100.0;
369
370                 // Check if a high enough percentage of the image was in motion to say that there was motion in this frame of data
371                 if (percentMotion > MOTION_DETECTED_THRESHOLD_PERCENTAGE) {
372
373                         // Motion detected so save timestamp of this frame to another buffer
374                         timestampWriteLock.lock();
375                         timestampOfLastMotion = (Date)tmpDate.clone();                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          // clone to a different buffer
376                         timestampWriteLock.unlock();
377
378                         System.out.println("Motion Detected (with percentage: " + Double.toString(percentMotion) + "%)");
379                 }
380
381                 // Do output to the screen if we are using gui mode
382                 if (DO_GRAPHICAL_USER_INTERFACE) {
383
384                         // change image data unto correct type for rendering
385                         BufferedImage visualized1 = new BufferedImage(segmented.width, segmented.height, BufferedImage.TYPE_INT_RGB);
386                         VisualizeBinaryData.renderBinary(segmented, false, visualized1);
387
388                         // change image data unto correct type for rendering
389                         BufferedImage visualized2 = null;
390                         visualized2 = ConvertBufferedImage.convertTo(newFrameFloat, visualized2, true);
391
392                         // place the images into the image grid
393                         gui.setImage(0, 1, visualized1);
394                         gui.setImage(0, 2, visualized2);
395
396                         // trigger rendering
397                         gui.repaint();
398                 }
399         }
400
401         /** Method that constantly loops checking if the callbacks should be issues and
402          *   issues the callbacks if they should be issues.
403          *   This method should be run on a separate thread.
404          *
405          *   @return [void] None.
406          */
407         private void doCallbacks() {
408
409                 // Keep looping forever for callback
410                 while (true) {
411
412                         // If motion detected
413                         if (motionDetected.compareAndSet(true, false)) {
414
415                                 // Motion was detected so issue callbacks to all objects that registered
416                                 // to receive callback from this class.
417                                 for (MotionDetectionCallback c : callbackList) {
418                                         //try {
419                                                 c.motionDetected(this);
420                                         //} catch (RemoteException re) {
421                                         //}
422                                 }
423
424                         } else {
425
426                                 // Sleep for 15 millisec to give time for new frame to arrive
427                                 try {
428                                         Thread.sleep(15);
429                                 } catch (InterruptedException ie) {
430                                 }
431                         }
432                 }
433         }
434
435
436
437
438         // /*******************************************************************************************************************************************
439         // **  Main Method used for testing
440         // *******************************************************************************************************************************************/
441         // public static void main(String[] args) {
442         //     MotionDetection mo = new MotionDetection(12, 0.5f, 10, 10);
443
444         //     AmcrestCamera cam = null;
445         //     try {
446
447         //         InetAddress addr = InetAddress.getByName("192.168.1.29");
448         //         cam = new AmcrestCamera(addr, "admin", "55779CatSoundz32");
449         //         cam.registerCallback(mo);
450         //         cam.start();
451         //         // cam.streamDisconnect();
452
453         //     } catch (Exception e) {
454
455         //     }
456
457         //     while (true) {
458
459         //     }
460
461         // }
462 }
463