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.video.processor; 034 035import org.openimaj.image.Image; 036import org.openimaj.image.processor.ImageProcessor; 037import org.openimaj.video.Video; 038 039/** 040 * A super class for classes which are able to process videos. This class can be 041 * used in two ways: one as a stand-alone video processor and another as a 042 * chainable video processor. 043 * <p> 044 * As a stand-alone processor it may be used as such: 045 * {@code 046 * MyVideoProcessor vp = new MyVideoProcessor(); 047 * vp.process( myVideo ); 048 * } 049 * <p> 050 * As a chainable processor it may be used as so: 051 * {@code 052 * MyVideoProcessor vp = new MyVideoProcessor( myVideo ); 053 * MyOtherProcessor op = new MyOtherProcessor( vp ); 054 * AnotherProcessor ap = new AnotherProcessor( op ); 055 * ap.process(); 056 * } 057 * <p> 058 * If any of the chain-based functions are called when the video has not been 059 * set, an {@link UnsupportedOperationException} is thrown. 060 * <p> 061 * In the same way that {@link ImageProcessor}s are expected to change the image 062 * content, video processors should change the video frame content in place, 063 * returning new altered frames. If you do not need to do this then use the 064 * VideoAnalyser. 065 * 066 * @author David Dupplaw (dpd@ecs.soton.ac.uk) 067 * 068 * @param <T> 069 * Type of {@link Image} 070 * @created 1 Jun 2011 071 */ 072public abstract class VideoProcessor<T extends Image<?, T>> 073extends Video<T> 074{ 075 /** The video in a video processing chain */ 076 private Video<T> video = null; 077 078 /** A buffer of the current frame */ 079 private T currentFrame = null; 080 081 /** 082 * Default constructor for using the video processor in an ad-hoc manner. 083 */ 084 public VideoProcessor() 085 { 086 } 087 088 /** 089 * Constructor for creating a video processor which is chainable. 090 * 091 * @param v 092 * The video to process 093 */ 094 public VideoProcessor(Video<T> v) 095 { 096 this.video = v; 097 } 098 099 /** 100 * Process a frame in this video. The processor must determine itself what 101 * is to be done with the frame that is processed. It is suggest that 102 * subclass processors add listeners for processed frames if they are 103 * required. The implementation must process the frame in-place and the 104 * frame should be returned. 105 * 106 * @param frame 107 * The frame to process. 108 * @return the processed frame 109 */ 110 public abstract T processFrame(T frame); 111 112 /** 113 * A hook for subclasses to be called when processing for the video has 114 * completed to clean up after themselves. 115 */ 116 public void processingComplete() 117 { 118 // No implementation 119 } 120 121 /** 122 * Process the given video using this processor. 123 * 124 * @param video 125 * The video to process. 126 */ 127 public void process(Video<T> video) 128 { 129 T frame = null; 130 while ((frame = video.getNextFrame()) != null) 131 processFrame(frame); 132 processingComplete(); 133 } 134 135 /** 136 * This is a sugar function that will call {@link #process(Video)} with the 137 * current video (for chainable processors). 138 */ 139 public void process() 140 { 141 if (this.video == null) 142 throw new UnsupportedOperationException( 143 "Chain method called on non-chainable processor"); 144 process(this.video); 145 } 146 147 /** 148 * {@inheritDoc} 149 * 150 * @see org.openimaj.video.Video#getWidth() 151 */ 152 @Override 153 public int getWidth() 154 { 155 if (this.video == null) 156 throw new UnsupportedOperationException( 157 "Chain method called on non-chainable processor"); 158 return this.video.getWidth(); 159 } 160 161 /** 162 * {@inheritDoc} 163 * 164 * @see org.openimaj.video.Video#getHeight() 165 */ 166 @Override 167 public int getHeight() 168 { 169 if (this.video == null) 170 throw new UnsupportedOperationException( 171 "Chain method called on non-chainable processor"); 172 return this.video.getHeight(); 173 } 174 175 /** 176 * {@inheritDoc} 177 * 178 * @see org.openimaj.video.Video#getNextFrame() 179 */ 180 @Override 181 public T getNextFrame() 182 { 183 if (this.video == null) 184 throw new UnsupportedOperationException( 185 "Chain method called on non-chainable processor"); 186 currentFrame = this.video.getNextFrame(); 187 if (currentFrame == null) 188 return null; 189 return processFrame(currentFrame); 190 } 191 192 /** 193 * {@inheritDoc} 194 * 195 * @see org.openimaj.video.Video#hasNextFrame() 196 */ 197 @Override 198 public boolean hasNextFrame() 199 { 200 if (this.video == null) 201 throw new UnsupportedOperationException("Chain method called on non-chainable processor"); 202 return this.video.hasNextFrame(); 203 } 204 205 /** 206 * {@inheritDoc} 207 * 208 * @see org.openimaj.video.Video#hasNextFrame() 209 */ 210 @Override 211 public long countFrames() 212 { 213 if (this.video == null) 214 throw new UnsupportedOperationException("Chain method called on non-chainable processor"); 215 return this.video.countFrames(); 216 } 217 218 /** 219 * {@inheritDoc} 220 * 221 * @see org.openimaj.video.Video#getCurrentFrame() 222 */ 223 @Override 224 public T getCurrentFrame() 225 { 226 if (this.video == null) 227 throw new UnsupportedOperationException( 228 "Chain method called on non-chainable processor"); 229 230 if (this.currentFrame == null) 231 this.currentFrame = processFrame(getNextFrame()); 232 233 return currentFrame; 234 } 235 236 /** 237 * No implementation. 238 */ 239 @Override 240 public void reset() 241 { 242 // No implementation 243 } 244 245 /** 246 * {@inheritDoc} 247 * 248 * @see org.openimaj.video.Video#getTimeStamp() 249 */ 250 @Override 251 public long getTimeStamp() 252 { 253 return this.video.getTimeStamp(); 254 } 255 256 /** 257 * {@inheritDoc} 258 * 259 * @see org.openimaj.video.Video#getFPS() 260 */ 261 @Override 262 public double getFPS() 263 { 264 return this.video.getFPS(); 265 } 266 267}