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}