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.video.processing.effects;
031
032import java.util.LinkedList;
033
034import org.openimaj.image.FImage;
035import org.openimaj.image.MBFImage;
036import org.openimaj.image.colour.RGBColour;
037import org.openimaj.image.processing.convolution.FGaussianConvolve;
038import org.openimaj.image.processing.convolution.FImageConvolveSeparable;
039import org.openimaj.video.Video;
040import org.openimaj.video.processor.VideoProcessor;
041
042/**
043 * {@link VideoProcessor} that produces a slit-scan effect.
044 * 
045 * @author Sina Samangooei (ss@ecs.soton.ac.uk)
046 * 
047 */
048public class SlitScanProcessor extends VideoProcessor<MBFImage> {
049        LinkedList<float[][][]> cache = new LinkedList<float[][][]>();
050
051        final float[] blurKern = FGaussianConvolve.makeKernel(0.5f);
052        int cacheSize = 240;
053
054        /**
055         * Default constructor for using the video processor in an ad-hoc manner.
056         * 
057         * @param cacheSize
058         *            The number of frames to retain for creating the slitscan
059         *            effect
060         */
061        public SlitScanProcessor(int cacheSize)
062        {
063                this.cacheSize = cacheSize;
064        }
065
066        /**
067         * Constructor for creating a video processor which is chainable.
068         * 
069         * @param video
070         *            The video to process
071         * @param cacheSize
072         *            The number of frames to retain for creating the slitscan
073         *            effect
074         */
075        public SlitScanProcessor(Video<MBFImage> video, int cacheSize) {
076                super(video);
077                this.cacheSize = cacheSize;
078        }
079
080        @Override
081        public MBFImage processFrame(MBFImage frame) {
082                addToCache(frame);
083
084                final int height = frame.getHeight();
085                final float prop = (float) (cacheSize) / height;
086                frame.fill(RGBColour.BLACK);
087                final float[][] framer = frame.getBand(0).pixels;
088                final float[][] frameg = frame.getBand(1).pixels;
089                final float[][] frameb = frame.getBand(2).pixels;
090                for (int y = 0; y < height; y++) {
091                        final int index = (int) (y * prop);
092                        if (index >= cache.size()) {
093                                break;
094                        }
095                        // System.out.println("y = " + y);
096                        // System.out.println("index = " + index);
097                        final float[][][] cacheImage = cache.get(index);
098                        System.arraycopy(cacheImage[0][y], 0, framer[y], 0, cacheImage[0][y].length);
099                        System.arraycopy(cacheImage[1][y], 0, frameg[y], 0, cacheImage[1][y].length);
100                        System.arraycopy(cacheImage[2][y], 0, frameb[y], 0, cacheImage[2][y].length);
101                }
102
103                for (final FImage f : frame.bands) {
104                        FImageConvolveSeparable.convolveVertical(f, blurKern);
105                }
106
107                if (cache.size() >= cacheSize)
108                        cache.removeLast();
109
110                return frame;
111        }
112
113        private void addToCache(MBFImage frame) {
114                final MBFImage f = frame.clone();
115
116                final float[][][] entry = new float[3][][];
117
118                entry[0] = f.getBand(0).pixels;
119                entry[1] = f.getBand(1).pixels;
120                entry[2] = f.getBand(2).pixels;
121
122                cache.addFirst(entry);
123        }
124}