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.acmmm11.presentation.slides;
031
032
033import org.openimaj.image.MBFImage;
034import org.openimaj.image.colour.RGBColour;
035import org.openimaj.image.processing.transform.MBFProjectionProcessor;
036import org.openimaj.math.geometry.transforms.TransformUtilities;
037import org.openimaj.video.Video;
038
039import Jama.Matrix;
040
041/**
042 * A video based on a still image that is animated by 
043 * spinning around its centre. 
044 * 
045 * @author Sina Samangooei (ss@ecs.soton.ac.uk)
046 */
047public class SpinningImageVideo extends Video<MBFImage> {
048        private double wh;
049        private MBFImage canvas;
050        private long startTime;
051        private float step;
052        private MBFImage image;
053        private Matrix translate;
054        private boolean hasNextFrame = true;
055        private float start;
056        private MBFImage lastFrame = null;
057        private float oldStep;
058        private boolean isPaused;
059        
060        /**
061         * Default constructor.
062         * 
063         * @param image The image to spin.
064         * @param start Starting angle.
065         * @param step Step angle between frames.
066         */
067        public SpinningImageVideo(MBFImage image, float start, float step) {
068                this.wh = Math.sqrt(image.getWidth()*image.getWidth() + image.getHeight() * image.getHeight());
069                canvas = image.newInstance((int)wh, (int)wh);
070                lastFrame = canvas;
071                this.image = image;
072                startTime = System.currentTimeMillis();
073                this.step = step;
074                this.start = start;
075                
076                translate = TransformUtilities.translateToPointMatrix(image.getBounds().calculateCentroid(), canvas.getBounds().calculateCentroid());
077        }
078
079        @Override
080        public MBFImage getNextFrame() {
081                canvas.fill(RGBColour.BLACK);
082                double timePerFrame = 1000 / getFPS();
083                double frame = getTimeStamp() / timePerFrame;
084                double angle = start + (step * frame);
085                int midx = image.getWidth()/2;
086                int midy = image.getHeight()/2;
087                Matrix spin = TransformUtilities.rotationMatrixAboutPoint(angle, midx, midy);
088                
089                MBFProjectionProcessor pp = new MBFProjectionProcessor();
090                pp.setMatrix(translate.times(spin));
091                pp.accumulate(image);
092                pp.performProjection(0, 0, canvas);
093                this.lastFrame  = canvas;
094                return canvas;
095        }
096
097        @Override
098        public MBFImage getCurrentFrame() {
099                return lastFrame;
100        }
101
102        @Override
103        public int getWidth() {
104                return canvas.getWidth();
105        }
106
107        @Override
108        public int getHeight() {
109                return canvas.getHeight();
110        }
111
112        @Override
113        public long getTimeStamp() {
114                long ellapsed = System.currentTimeMillis() - startTime;
115                return ellapsed;
116        }
117
118        @Override
119        public double getFPS() {
120                return 30;
121        }
122
123        @Override
124        public boolean hasNextFrame() {
125                return hasNextFrame ;
126        }
127
128        @Override
129        public long countFrames() {
130                return -1;
131        }
132
133        @Override
134        public void reset() {
135                this.startTime = System.currentTimeMillis();
136        }
137
138        /**
139         * Stop the video
140         */
141        public void stop() {
142                this.hasNextFrame = true;
143        }
144
145        /**
146         * Adjust the speed
147         * @param f frame rate increment
148         */
149        public void adjustSpeed(float f) {
150                if(isPaused) return;
151                double timePerFrame = 1000 / getFPS();
152                double frame = getTimeStamp() / timePerFrame;
153                double angle = start + (step * frame);
154                this.start = (float) angle;
155                this.startTime = System.currentTimeMillis();
156                this.step +=f;
157        }
158        
159        /**
160         * Pause or unpause the video
161         */
162        public void togglePause() {
163                if(this.step!=0){
164                        this.oldStep = this.step;
165                        this.isPaused = true;
166                        double timePerFrame = 1000 / getFPS();
167                        double frame = getTimeStamp() / timePerFrame;
168                        double angle = start + (step * frame);
169                        this.start = (float) angle;
170                        this.step = 0;
171                        
172                }
173                else{
174                        this.isPaused = false;
175                        this.step = this.oldStep;
176                        this.startTime = System.currentTimeMillis();
177                }
178        }
179
180}