001/**
002 * Copyright (c) 2011, The University of Southampton and the individual contributors.
003 * All rights reserved.
004 *
005 * Redistribution and use in source and binary forms, with or without modification,
006 * are permitted provided that the following conditions are met:
007 *
008 *   *  Redistributions of source code must retain the above copyright notice,
009 *      this list of conditions and the following disclaimer.
010 *
011 *   *  Redistributions in binary form must reproduce the above copyright notice,
012 *      this list of conditions and the following disclaimer in the documentation
013 *      and/or other materials provided with the distribution.
014 *
015 *   *  Neither the name of the University of Southampton nor the names of its
016 *      contributors may be used to endorse or promote products derived from this
017 *      software without specific prior written permission.
018 *
019 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
020 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
021 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
022 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
023 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
024 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
025 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
026 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
027 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
028 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
029 */
030package org.openimaj.demos.video;
031
032import java.awt.GraphicsDevice;
033import java.awt.GraphicsEnvironment;
034import java.awt.Toolkit;
035import java.awt.event.KeyEvent;
036import java.awt.event.KeyListener;
037import java.util.LinkedList;
038import java.util.Queue;
039import javax.swing.JFrame;
040import javax.swing.SwingUtilities;
041
042import org.openimaj.demos.Demo;
043import org.openimaj.hardware.kinect.KinectController;
044import org.openimaj.hardware.kinect.KinectException;
045import org.openimaj.image.DisplayUtilities;
046import org.openimaj.image.FImage;
047import org.openimaj.image.MBFImage;
048import org.openimaj.image.processing.resize.ResizeProcessor;
049import org.openimaj.util.pair.IndependentPair;
050import org.openimaj.video.Video;
051import org.openimaj.video.VideoDisplay;
052
053
054/**
055 *      Kinect integration demo. Shows video and depth. Press t to toggle between 
056 *      rgb and ir mode. Pressing w and x moves the device up or down. 
057 *      Pressing s levels the device.
058 * 
059 * @author Jonathon Hare (jsh2@ecs.soton.ac.uk)
060 */
061@Demo(
062                author = "Jonathon Hare", 
063                description = "Kinect integration demo. Shows video and depth. Press t " +
064                "to toggle between rgb and ir mode. Pressing w and x moves the device " +
065                "up or down. Pressing s levels the device.", 
066                keywords = { "kinect", "video" }, 
067                title = "Kinect Integration",
068                screenshot = "/org/openimaj/demos/screens/hardware/kinect.png",
069                icon = "/org/openimaj/demos/icons/hardware/kinect.png"
070)
071public class KinectDepthSnapshot extends Video<MBFImage> implements KeyListener {
072        private static final int MAX_HELD_FRAMES = 10;
073        MBFImage currentFrame;
074        KinectController controller;
075        JFrame frame;
076        private double tilt = 0;
077        private boolean irmode = false;
078        private VideoDisplay<MBFImage> videoFrame;
079        private Queue<IndependentPair<FImage, MBFImage>> heldDepthFrames;
080        private IndependentPair<FImage, MBFImage> currentDepthFrame;
081        private int screenWidth;
082        private int screenHeight;
083        protected ResizeProcessor fullScreenResizeProcessor;
084
085        /**
086         *      Default constructor
087         *  @param id of kinect controller
088         *  @throws KinectException
089         */
090        public KinectDepthSnapshot(int id) throws KinectException {
091                controller = new KinectController(id, irmode,true);
092                GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
093                GraphicsDevice[] devices = ge.getScreenDevices();
094                JFrame frame=new JFrame("Full Screen JFrame");
095                //Set default close operation for JFrame  
096                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
097                //Set JFrame size to full screen size follow current screen size  
098                screenWidth = Toolkit.getDefaultToolkit().getScreenSize().width;
099                screenHeight = Toolkit.getDefaultToolkit().getScreenSize().height;
100                fullScreenResizeProcessor = new ResizeProcessor(screenWidth, screenHeight);
101                frame.setBounds(0,0,screenWidth,screenHeight);  
102                videoFrame = VideoDisplay.createVideoDisplay(this, new DisplayUtilities.ImageComponent(true));
103                JFrame wholeWindow = new JFrame();
104                wholeWindow.setUndecorated(true);
105                wholeWindow.setAlwaysOnTop(true);
106                wholeWindow.getContentPane().add(videoFrame.getScreen());
107                devices[0].setFullScreenWindow(wholeWindow);
108                ((JFrame)SwingUtilities.getRoot(videoFrame.getScreen())).setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
109                SwingUtilities.getRoot(videoFrame.getScreen()).addKeyListener(this);
110
111        }
112        
113        @Override
114        public MBFImage getNextFrame() {
115                FImage tmp = controller.depthStream.getNextFrame();
116                MBFImage frame = (MBFImage) controller.videoStream.getNextFrame();//Transforms.Grey_To_Colour((FImage) tmp);
117
118//              depth.bands.get(0).shiftRightInplace(50);
119//              depth.bands.get(1).shiftRightInplace(50);
120//              depth.bands.get(2).shiftRightInplace(50);
121                
122//              MBFImage depth = ((FImage) controller.videoStream.getNextFrame()).toRGB();
123                
124//              // Clean Frames
125//              if (currentFrame == null || super.currentFrame % 600 == 0)
126//              {
127//                      currentFrame = frame.clone();
128//              }
129//
130//              if(oldDepth == null || super.currentFrame % 600 == 0)
131//              {
132//                      oldDepth = tmp.clone();
133//              }
134                
135                // Update held depths
136                if(super.currentFrame % 30 == 0){
137                        if (super.currentFrame % (60 * 2 * MAX_HELD_FRAMES) == 0)
138                        {
139                                removeAllFrames();
140                        }
141
142                        addDepthFrame(tmp,frame);
143                        currentFrame = this.currentDepthFrame.secondObject();
144                }
145                else{
146                        FImage oldDepth = this.currentDepthFrame.firstObject().clone();
147                        currentFrame = this.currentDepthFrame.secondObject().clone();
148                        for (int y = 0; y < tmp.height; y++) {
149                                for (int x = 0; x < tmp.width; x++) {
150                                        if (oldDepth.pixels[y][x] == 0 || (tmp.pixels[y][x] != 0 && oldDepth.pixels[y][x] > tmp.pixels[y][x])) {
151//                                              oldDepth.pixels[y][x] = tmp.pixels[y][x];
152                                                currentFrame.setPixel(x, y, frame.getPixel(x, y));
153                                        }
154                                }
155                        }
156                }
157
158                super.currentFrame++;
159//              return currentFrame.process(fullScreenResizeProcessor);
160                return currentFrame;
161        }
162
163        private void removeAllFrames() {
164                if (this.heldDepthFrames!=null)this.heldDepthFrames.clear();
165        }
166
167        private void addDepthFrame(FImage tmp, MBFImage frame) {
168                if(this.heldDepthFrames == null){
169                        this.heldDepthFrames = new LinkedList<IndependentPair<FImage,MBFImage>>();
170                }
171                if(this.heldDepthFrames.size() == MAX_HELD_FRAMES){
172                        this.heldDepthFrames.poll();
173                }
174                
175                
176                this.heldDepthFrames.add(IndependentPair.pair(tmp.clone(), frame.clone()));
177                System.out.println("Added frame, new frame size:" + this.heldDepthFrames.size());
178                this.currentDepthFrame = constructDepthFrame();
179        }
180        
181        private IndependentPair<FImage, MBFImage> constructDepthFrame() {
182                FImage compiledDepth = null;
183                MBFImage compiledRGB = null;
184                for (IndependentPair<FImage, MBFImage> heldFrame : this.heldDepthFrames) {
185                        
186                        FImage heldDepth = heldFrame.firstObject();
187                        MBFImage heldRGB = heldFrame.secondObject();
188                        if(compiledDepth == null){
189                                
190                                compiledDepth = heldDepth.clone();
191                                compiledRGB = heldRGB .clone();
192                                continue;
193                        }
194                        
195                        for (int y = 0; y < heldDepth.height; y++) {
196                                for (int x = 0; x < heldDepth.width; x++) {
197                                        if (compiledDepth.pixels[y][x] == 0 || (heldDepth.pixels[y][x] != 0 && compiledDepth.pixels[y][x] > heldDepth.pixels[y][x])) {
198                                                compiledDepth.pixels[y][x] = heldDepth.pixels[y][x];
199                                                compiledRGB.setPixel(x, y, heldRGB.getPixel(x, y));
200                                        }
201                                }
202                        }
203                }
204                return IndependentPair.pair(compiledDepth, compiledRGB);
205        }
206
207        @Override
208        public MBFImage getCurrentFrame() {
209                return currentFrame;
210        }
211
212        @Override
213        public int getWidth() {
214                return this.screenWidth;
215        }
216
217        @Override
218        public int getHeight() {
219                return this.screenHeight;
220        }
221
222        @Override
223        public boolean hasNextFrame() {
224                return true;
225        }
226
227        @Override
228        public long countFrames() {
229                return -1;
230        }
231
232        @Override
233        public void reset() {
234                //do nothing
235        }
236
237        @Override
238        public void keyTyped(KeyEvent e) {
239
240        }
241
242        @Override
243        public void keyPressed(KeyEvent e) {
244                if (e.getKeyChar() == 'w') {
245                        controller.setTilt(tilt+=1);
246                } else if (e.getKeyChar() == 'x') {
247                        controller.setTilt(tilt-=1);
248                } else if (e.getKeyChar() == 's') {
249                        controller.setTilt(tilt=0);
250                } else if (e.getKeyChar() == 't') {
251                        controller.setIRMode(irmode=!irmode );
252                }
253        }
254
255        @Override
256        public void keyReleased(KeyEvent e) {
257
258        }
259
260        /**
261         *      Default main 
262         *  @param args Command-line arguments
263         *  @throws KinectException
264         */
265        public static void main(String[] args) throws KinectException {
266                new KinectDepthSnapshot(0);
267        }
268
269        @Override
270        public long getTimeStamp()
271        {
272                return (long)(super.currentFrame * 1000 / getFPS());
273        }
274
275        @Override
276        public double getFPS()
277        {
278                return 30;
279        }
280
281        /**
282         *      Get the display showing the kinect video
283         *  @return The video display
284         */
285        public VideoDisplay<MBFImage> getDisplay() {
286                return videoFrame;
287        }
288}