Creating new directory for all Java benchmarks
[iot2.git] / benchmarks / Java / SmartLightsController / MotionDetection.java
1 package SmartLightsController;
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         throws RemoteException {
146
147                 // Configure the Gaussian model used for background detection
148                 configGaussian = new ConfigBackgroundGaussian(_threshold, _learnSpeed);
149                 configGaussian.initialVariance = _initialVariance;
150                 configGaussian.minimumDifference = _minDifference;
151
152                 // setup the background detector
153                 ImageType imageType = ImageType.single(ImageFloat32.class);
154                 backgroundDetector = FactoryBackgroundModel.stationaryGaussian(configGaussian, imageType);
155
156                 // setup the gui if we are going to use it
157                 if (DO_GRAPHICAL_USER_INTERFACE) {
158
159                         // create an image grid for images to place on, tile fashion
160                         gui = new ImageGridPanel(1, 2);
161
162                         // make the window large so we dont have to manually resize with the mouse
163                         gui.setSize(1920, 1080);
164
165                         // Make the window visible and set the title
166                         ShowImages.showWindow(gui, "Static Scene: Background Segmentation", true);
167                 }
168
169                 // Launch the worker thread
170                 workThread = new Thread(new Runnable() {
171                         public void run() {
172
173                                 while (true) {
174                                         runMotionDetection();
175                                 }
176                         }
177                 });
178                 workThread.start();
179
180
181                 // Launch the callback thread
182                 callBackThread = new Thread(new Runnable() {
183                         public void run() {
184
185                                 while (true) {
186                                         doCallbacks();
187                                 }
188                         }
189                 });
190                 callBackThread.start();
191         }
192
193
194         /*******************************************************************************************************************************************
195         **
196         **  Public Methods
197         **
198         *******************************************************************************************************************************************/
199
200         /** Method to add a new frame to the motion detector.
201          *
202          *   @param _newFrame  [byte[]], Frame data of new frame.
203          *   @param _timestamp [Date]  , Timestamp of new frame.
204          *
205          *   @return [void] None.
206          */
207         public void addFrame(byte[]  _newFrame, Date _timestamp) {
208                 BufferedImage img = null;
209
210                 try {
211                         // Parse the byte array into a Buffered Image
212                         InputStream in = new ByteArrayInputStream(_newFrame);
213                         img = ImageIO.read(in);
214
215                 } catch (Exception e) {
216                         e.printStackTrace();
217                         return;
218                 }
219
220                 // Save the image and timestamp for use later
221                 imageWriteLock.lock();// lock the image and timestamp buffers since multithread
222                 latestImage = img;// image into image buffer
223                 possibleDate = _timestamp;// timestamp into timestamp buffer
224                 imageWriteLock.unlock();// Never forget to unlock
225
226                 // flag the worker thread that there is new data ready for processing
227                 newFrameAvailable.set(true);
228         }
229
230         /** Method to get the timestamp of the last time motion was detected
231          *
232          *   @return [Date] timestamp of last motion or null if no motion was ever detected.
233          */
234         public Date getTimestampOfLastMotion() {
235                 Date ret = null;
236
237                 // Be safe because multithread
238                 timestampReadLock.lock();
239
240                 // Checks if there was ever motion, if not then timestampOfLastMotion
241                 // will be null
242                 if (timestampOfLastMotion != null) {
243                         // Clone since we don't know what the other person is going to do
244                         // with the timestamp
245                         ret = (Date)timestampOfLastMotion.clone();
246                 }
247
248                 timestampReadLock.unlock();
249
250                 return ret;
251         }
252
253
254         /** Method to add a new frame to the motion detector from a camera
255          *
256          *   @param _camera  [Camera], Camera that has the new data.
257          *
258          *   @return [void] None.
259          */
260         public void newCameraFrameAvailable(byte[] latestFrame, long timeStamp) {
261                 BufferedImage img = null;
262
263                 try {
264                         // Parse the byte array into a Buffered Image
265                         //InputStream in = new ByteArrayInputStream(_camera.getLatestFrame());
266                         InputStream in = new ByteArrayInputStream(latestFrame);
267                         img = ImageIO.read(in);
268
269                 } catch (RemoteException e) {
270                         e.printStackTrace();
271                         return;
272
273                 } catch (Exception e) {
274                         e.printStackTrace();
275                         return;
276
277                 }
278
279                 // Save the image and timestamp for use later
280                 imageWriteLock.lock();  // lock the image and timestamp buffers since multithread
281                 latestImage = img;              // image into image buffer
282
283                 // timestamp from camera into timestamp buffer
284                 long dateLong = timeStamp;
285                 possibleDate = new Date(dateLong);
286
287                 imageWriteLock.unlock();        // Never forget to unlock
288
289                 // flag the worker thread that there is new data ready for processing
290                 newFrameAvailable.set(true);
291         }
292
293         /** Method to register an object to recieve callbacks from this motion detector
294          *
295          *   @param _mdc  [MotionDetectionCallback], object to recieve callbacks.
296          *
297          *   @return [void] None.
298          */
299         public void registerCallback(MotionDetectionCallback _mdc) {
300                 callbackList.add(_mdc);
301         }
302
303         /*******************************************************************************************************************************************
304         **
305         **  Helper Methods
306         **
307         *******************************************************************************************************************************************/
308
309         /** Method that constantly loops checking if new data is available.  If there is
310          *   new data, it is processed.
311          *   This method should be run on a separate thread.
312          *
313          *   @return [void] None.
314          */
315         private void runMotionDetection() {
316
317                 // check if there is a new frame availble, only runs detection if there is new data to save
318                 // computation time
319                 if (!newFrameAvailable.get()) {
320                         return;
321                 }
322
323                 // Lock since we are accessing the data buffers
324                 imageReadLock.lock();
325
326                 // processing data so now the buffered data is old
327                 newFrameAvailable.set(false);
328
329                 // copy from buffer to local for processing
330                 Date tmpDate = possibleDate;
331
332                 // Allocate space for the segmented image based on the first image we received
333                 // cannot pre-allocate this since we do not know what the size of the images is
334                 // before the first image arrives
335                 if (segmented == null) {
336                         segmented = new ImageUInt8(latestImage.getWidth(), latestImage.getHeight());
337                 }
338
339                 // copy from data buffers and convert into correct data type for BoofCv libraries
340                 newFrameFloat = ConvertBufferedImage.convertFrom(latestImage, newFrameFloat);
341
342                 // All done accessing the data buffers
343                 imageReadLock.unlock();
344
345                 // Run background detection
346                 backgroundDetector.segment(newFrameFloat, segmented);
347
348                 // Update the background baseline every 10 frames, helps the algorithm
349                 frameCounter++;
350                 if (frameCounter > 10) {
351                         backgroundDetector.updateBackground(newFrameFloat);
352                         frameCounter = 0;
353                 }
354
355                 // get the raw pixel data, gray-scale image
356                 byte[] frameData = segmented.getData();
357
358                 // count the number of pixels of the image that was deemed as "motion"
359                 double count = 0;
360                 double countMotion = 0;
361                 for (byte b : frameData) {
362                         count++;
363                         if (b > 0) {
364                                 countMotion++;
365                         }
366                 }
367
368                 // calculate the percentage of the image that was in motion
369                 double percentMotion = (countMotion / count) * 100.0;
370
371                 // Check if a high enough percentage of the image was in motion to say that there was motion in this frame of data
372                 if (percentMotion > MOTION_DETECTED_THRESHOLD_PERCENTAGE) {
373
374                         // Motion detected so save timestamp of this frame to another buffer
375                         timestampWriteLock.lock();
376                         timestampOfLastMotion = (Date)tmpDate.clone();                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          // clone to a different buffer
377                         timestampWriteLock.unlock();
378
379                         System.out.println("Motion Detected (with percentage: " + Double.toString(percentMotion) + "%)");
380                 }
381
382                 // Do output to the screen if we are using gui mode
383                 if (DO_GRAPHICAL_USER_INTERFACE) {
384
385                         // change image data unto correct type for rendering
386                         BufferedImage visualized1 = new BufferedImage(segmented.width, segmented.height, BufferedImage.TYPE_INT_RGB);
387                         VisualizeBinaryData.renderBinary(segmented, false, visualized1);
388
389                         // change image data unto correct type for rendering
390                         BufferedImage visualized2 = null;
391                         visualized2 = ConvertBufferedImage.convertTo(newFrameFloat, visualized2, true);
392
393                         // place the images into the image grid
394                         gui.setImage(0, 1, visualized1);
395                         gui.setImage(0, 2, visualized2);
396
397                         // trigger rendering
398                         gui.repaint();
399                 }
400         }
401
402         /** Method that constantly loops checking if the callbacks should be issues and
403          *   issues the callbacks if they should be issues.
404          *   This method should be run on a separate thread.
405          *
406          *   @return [void] None.
407          */
408         private void doCallbacks() {
409
410                 // Keep looping forever for callback
411                 while (true) {
412
413                         // If motion detected
414                         if (motionDetected.compareAndSet(true, false)) {
415
416                                 // Motion was detected so issue callbacks to all objects that registered
417                                 // to receive callback from this class.
418                                 for (MotionDetectionCallback c : callbackList) {
419                                         //try {
420                                                 c.motionDetected(this);
421                                         //} catch (RemoteException re) {
422                                         //}
423                                 }
424
425                         } else {
426
427                                 // Sleep for 15 millisec to give time for new frame to arrive
428                                 try {
429                                         Thread.sleep(15);
430                                 } catch (InterruptedException ie) {
431                                 }
432                         }
433                 }
434         }
435
436
437
438
439         // /*******************************************************************************************************************************************
440         // **  Main Method used for testing
441         // *******************************************************************************************************************************************/
442         // public static void main(String[] args) {
443         //     MotionDetection mo = new MotionDetection(12, 0.5f, 10, 10);
444
445         //     AmcrestCamera cam = null;
446         //     try {
447
448         //         InetAddress addr = InetAddress.getByName("192.168.1.29");
449         //         cam = new AmcrestCamera(addr, "admin", "55779CatSoundz32");
450         //         cam.registerCallback(mo);
451         //         cam.start();
452         //         // cam.streamDisconnect();
453
454         //     } catch (Exception e) {
455
456         //     }
457
458         //     while (true) {
459
460         //     }
461
462         // }
463 }
464