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 */ 030/** 031 * 032 */ 033package org.openimaj.demos.video; 034 035import java.awt.AWTEvent; 036import java.awt.Dimension; 037import java.awt.GridBagConstraints; 038import java.awt.GridBagLayout; 039import java.awt.HeadlessException; 040import java.awt.Toolkit; 041import java.awt.event.AWTEventListener; 042import java.awt.event.ActionEvent; 043import java.awt.event.ActionListener; 044import java.awt.event.KeyEvent; 045import java.io.File; 046import java.io.IOException; 047 048import javax.swing.JButton; 049import javax.swing.JFrame; 050import javax.swing.JLabel; 051import javax.swing.JPanel; 052import javax.swing.SwingConstants; 053 054import org.openimaj.demos.Demo; 055import org.openimaj.demos.video.utils.NumberKeySeekListener; 056import org.openimaj.demos.video.utils.ProcessingPanel; 057import org.openimaj.demos.video.utils.SourcePanel; 058import org.openimaj.image.DisplayUtilities.ImageComponent; 059import org.openimaj.image.MBFImage; 060import org.openimaj.video.Video; 061import org.openimaj.video.VideoDisplay; 062import org.openimaj.video.VideoDisplay.EndAction; 063import org.openimaj.video.VideoDisplay.Mode; 064import org.openimaj.video.VideoDisplayListener; 065import org.openimaj.video.capture.VideoCapture; 066import org.openimaj.video.xuggle.XuggleVideo; 067 068/** 069 * A demo of the video functions and video processing functions in OpenIMAJ. 070 * This demo shows a window which allows the user to select between webcam 071 * video or video from a file. It also provides a set of pre-defined processing 072 * operators which can be turned on and off for the video. 073 * 074 * @author David Dupplaw (dpd@ecs.soton.ac.uk) 075 * 076 * @created 28 Sep 2011 077 */ 078@Demo( 079 author = "David Dupplaw", 080 description = "A simple GUI that demonstrates various video " + 081 "processing functionalities " + 082 "in OpenIMAJ and allows processing of both file and live videos.", 083 keywords = { "video" }, 084 title = "Video Processing" 085 ) 086public class VideoProcessingDemo extends JPanel implements VideoDisplayListener<MBFImage> 087{ 088 /** */ 089 private static final long serialVersionUID = 1L; 090 091 /** The video */ 092 private Video<MBFImage> video; 093 094 /** The video display which will play the video */ 095 private VideoDisplay<MBFImage> videoDisplay; 096 097 /** The image component into which the video is being painted (reused) */ 098 private final ImageComponent ic; 099 100 /** Button to stop the video */ 101 private JButton stopButton; 102 103 /** Button to play the video */ 104 private JButton playButton; 105 106 /** Button to pause the video */ 107 private JButton pawsButton; 108 109 /** The thread which is running the video playback */ 110 private Thread videoThread; 111 112 /** The panel which provides the processing functions */ 113 private ProcessingPanel processingPanel; 114 115 /** A label to show the number of frames per second being processed */ 116 private JLabel fps; 117 118 /** The time a frame started to be processed. */ 119 private long startTime; 120 121 /** 122 * Default constructor. 123 * 124 * @throws IOException 125 */ 126 public VideoProcessingDemo() throws IOException 127 { 128 this.ic = new ImageComponent( true ); 129 this.ic.setPreferredSize( new Dimension(320,240) ); 130 this.init(); 131 } 132 133 /** 134 * Sets up all the graphical widgets. 135 */ 136 private void init() 137 { 138 this.setLayout( new GridBagLayout() ); 139 140 // -------------------------------------------------------- 141 // Video display 142 // -------------------------------------------------------- 143 final GridBagConstraints gbc = new GridBagConstraints(); 144 gbc.fill = GridBagConstraints.BOTH; 145 gbc.weightx = gbc.weighty = 1; 146 gbc.gridx = gbc.gridy = 0; 147 gbc.gridwidth = 3; 148 149 this.add( this.ic, gbc ); 150 151 // -------------------------------------------------------- 152 // Controls panels 153 // -------------------------------------------------------- 154 gbc.gridx += gbc.gridwidth; gbc.gridwidth = 1; 155 final JPanel p = new JPanel( new GridBagLayout() ); 156 157 final GridBagConstraints sgbc = new GridBagConstraints(); 158 sgbc.fill = GridBagConstraints.BOTH; 159 sgbc.weightx = 0; sgbc.weighty = 1; 160 sgbc.gridx = sgbc.gridy = 0; 161 sgbc.gridwidth = 1; 162 163 p.add( new SourcePanel(this), sgbc ); 164 sgbc.gridy++; 165 p.add( this.processingPanel = new ProcessingPanel(), sgbc ); 166 167 this.add( p, gbc ); 168 final int t = gbc.gridx; 169 170 // -------------------------------------------------------- 171 // Navigation buttons 172 // -------------------------------------------------------- 173 gbc.gridy++; 174 gbc.gridx = 0; 175 gbc.gridwidth = 1; 176 gbc.fill = GridBagConstraints.HORIZONTAL; 177 178 this.stopButton = new JButton( "STOP" ); 179 this.playButton = new JButton( "PLAY" ); 180 this.pawsButton = new JButton( "PAUSE" ); 181 182 this.stopButton.addActionListener( new ActionListener() 183 { 184 @Override 185 public void actionPerformed( final ActionEvent e ) 186 { 187 VideoProcessingDemo.this.videoDisplay.setMode( Mode.STOP ); 188 } 189 }); 190 this.playButton.addActionListener( new ActionListener() 191 { 192 @Override 193 public void actionPerformed( final ActionEvent e ) 194 { 195 VideoProcessingDemo.this.videoDisplay.setMode( Mode.PLAY ); 196 } 197 }); 198 this.pawsButton.addActionListener( new ActionListener() 199 { 200 @Override 201 public void actionPerformed( final ActionEvent e ) 202 { 203 VideoProcessingDemo.this.videoDisplay.setMode( Mode.PAUSE ); 204 } 205 }); 206 207 this.add( this.playButton, gbc ); 208 gbc.gridx++; 209 this.add( this.pawsButton, gbc ); 210 211 gbc.gridx = t; gbc.weightx = 1; 212 this.add( this.fps = new JLabel(""), gbc ); 213 this.fps.setHorizontalTextPosition( SwingConstants.CENTER ); 214 } 215 216 /** 217 * @return The image component used for displaying the video image. 218 */ 219 public ImageComponent getImageComponent() 220 { 221 return this.ic; 222 } 223 224 /** 225 * Set the video source to be the webcam 226 * @throws IOException 227 */ 228 public void useWebcam() throws IOException 229 { 230 // Stop any existing video 231 this.stopVideo(); 232 233 // Setup a new video from the VideoCapture class 234 this.video = new VideoCapture( 320, 240 ); 235 236 // Reset the video displayer to use the capture class 237 this.videoDisplay = new VideoDisplay<MBFImage>( this.video, this.ic ); 238 239 // Make sure the listeners are sorted 240 this.addListeners(); 241 242 // Start the new video playback thread 243 this.videoThread = new Thread(this.videoDisplay); 244 this.videoThread.start(); 245 } 246 247 /** 248 * Set the processing source to be the file 249 * @param f 250 */ 251 public void useFile( final File f ) 252 { 253 // Stop any existing video 254 this.stopVideo(); 255 256 // Setup a new video from the video file 257 this.video = new XuggleVideo( f , false); 258 259 // Reset the video displayer to use the file video 260 this.videoDisplay = new VideoDisplay<MBFImage>( this.video, this.ic ); 261 this.videoDisplay.setEndAction( EndAction.LOOP ); 262 263 // Make sure all the listeners are added to this new display 264 this.addListeners(); 265 this.addVideoFileListeners(); 266 267 // Start the new video playback thread 268 this.videoThread = new Thread(this.videoDisplay); 269 this.videoThread.start(); 270 } 271 272 private void addVideoFileListeners() { 273 final long eventMask = AWTEvent.KEY_EVENT_MASK; 274 final NumberKeySeekListener keyEventListener = new NumberKeySeekListener(this.videoDisplay); 275 Toolkit.getDefaultToolkit().addAWTEventListener(new AWTEventListener() { 276 @Override 277 public void eventDispatched(final AWTEvent event) { 278 switch (event.getID()) { 279 case KeyEvent.KEY_PRESSED: 280 final KeyEvent kevent = (KeyEvent) event; 281 keyEventListener.keyPressed(kevent); 282 break; 283 }; 284 } 285 }, eventMask); 286 // rp.addKeyListener(new NumberKeySeekListener(videoDisplay)); 287 } 288 289 /** 290 * Stops the current video. 291 */ 292 private void stopVideo() 293 { 294 if( this.video instanceof VideoCapture ) 295 ((VideoCapture)this.video).stopCapture(); 296 if( this.videoDisplay != null ) 297 this.videoDisplay.setMode( Mode.STOP ); 298 } 299 300 /** 301 * Adds the default listeners to the video display 302 */ 303 private void addListeners() 304 { 305 this.videoDisplay.addVideoListener( this ); 306 this.videoDisplay.addVideoListener( this.processingPanel ); 307 } 308 309 /** 310 * {@inheritDoc} 311 * @see org.openimaj.video.VideoDisplayListener#afterUpdate(org.openimaj.video.VideoDisplay) 312 */ 313 @Override 314 public void afterUpdate( final VideoDisplay<MBFImage> display ) 315 { 316 final double diff = System.currentTimeMillis() - this.startTime; 317 final double d = Math.round(1d/(diff/10000d))/10d; 318 319 this.fps.setText( ""+d+" fps" ); 320 this.startTime = System.currentTimeMillis(); 321 } 322 323 /** 324 * {@inheritDoc} 325 * @see org.openimaj.video.VideoDisplayListener#beforeUpdate(org.openimaj.image.Image) 326 */ 327 @Override 328 public void beforeUpdate( final MBFImage frame ) 329 { 330 } 331 332 /** 333 * 334 * @param args 335 */ 336 public static void main( final String[] args ) 337 { 338 try 339 { 340 final VideoProcessingDemo demo = new VideoProcessingDemo() ; 341 final JFrame f = new JFrame( "Video Processing Demo" ); 342 f.getContentPane().add(demo ); 343 f.pack(); 344 f.setVisible( true ); 345 // demo.useFile(new File("/Users/ss/Downloads/20070701_185500_bbcthree_doctor_who_confidential.ts")); 346 } 347 catch( final HeadlessException e ) 348 { 349 e.printStackTrace(); 350 } 351 catch( final IOException e ) 352 { 353 e.printStackTrace(); 354 } 355 } 356}