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.video; 031 032import java.awt.Toolkit; 033import java.util.ArrayList; 034import java.util.List; 035 036import org.openimaj.demos.Demo; 037import org.openimaj.feature.DoubleFV; 038import org.openimaj.image.DisplayUtilities; 039import org.openimaj.image.MBFImage; 040import org.openimaj.image.colour.RGBColour; 041import org.openimaj.image.processing.resize.ResizeProcessor; 042import org.openimaj.image.renderer.MBFImageRenderer; 043import org.openimaj.math.geometry.shape.Rectangle; 044import org.openimaj.video.processing.shotdetector.CombiShotDetector; 045import org.openimaj.video.processing.shotdetector.HistogramVideoShotDetector; 046import org.openimaj.video.processing.shotdetector.LocalHistogramVideoShotDetector; 047import org.openimaj.video.processing.shotdetector.ShotBoundary; 048import org.openimaj.video.processing.shotdetector.ShotDetectedListener; 049import org.openimaj.video.processing.shotdetector.VideoKeyframe; 050import org.openimaj.video.timecode.VideoTimecode; 051import org.openimaj.video.xuggle.XuggleVideo; 052 053/** 054 * Demonstration of the OpenIMAJ HistogramVideoShotDetector and visualisation thereof. 055 * 056 * @author David Dupplaw (dpd@ecs.soton.ac.uk) 057 * @created 1 Jun 2011 058 */ 059@Demo( 060 author = "David Dupplaw", 061 description = "Gives a demo of the video shot detector by displaying an " + 062 "animated visualisation of the process of shot detection using " + 063 "differential histograms.", 064 keywords = { "video", "shots", "shot detector" }, 065 title = "Video Shot Detector") 066public class VideoShotDetectorVisualisation { 067 /** 068 * Testing code. 069 * 070 * @param args 071 */ 072 public static void main(final String[] args) { 073 DisplayUtilities.displayName(new MBFImage(100, 100, 3), "vsd", true); 074 DisplayUtilities.createNamedWindow("video").setLocation(0, 250); 075 076 final List<VideoKeyframe<MBFImage>> keyframes = 077 new ArrayList<VideoKeyframe<MBFImage>>(); 078 final int th = 64; 079 final int tw = 64; 080 final int h = 200; 081 final int w = Toolkit.getDefaultToolkit().getScreenSize().width - tw; 082 final MBFImage m = new MBFImage(w + tw, h, 3); 083 final MBFImageRenderer renderer = m.createRenderer(); 084 final ResizeProcessor rp = new ResizeProcessor(tw, th, true); 085 086 final XuggleVideo video = new XuggleVideo( 087 "src/main/resources/org/openimaj/demos/video/guy_goma.mp4" ); 088 final CombiShotDetector vsd = new CombiShotDetector( video ); 089 vsd.addVideoShotDetector( new HistogramVideoShotDetector( video ), 1 ); 090 vsd.addVideoShotDetector( new LocalHistogramVideoShotDetector( video, 20 ), 1 ); 091 final double threshold = vsd.getThreshold(); 092 vsd.setStoreAllDifferentials(true); 093 vsd.setFindKeyframes(true); 094 vsd.addShotDetectedListener(new ShotDetectedListener<MBFImage>() 095 { 096 private double lastMax = 10000; 097 098 @Override 099 public void shotDetected(final ShotBoundary<MBFImage> sb, final VideoKeyframe<MBFImage> vk) 100 { 101 // Store the keyframe 102 if (vk != null) 103 keyframes.add(vk.clone()); 104 105 // Reset the image 106 m.zero(); 107 108 // Calculate the various variables required to draw the 109 // visualisation. 110 final DoubleFV dfv = vsd.getDifferentials(); 111 double max = Double.MIN_VALUE; 112 for (int x = 0; x < dfv.length(); x++) 113 max = Math.max(max, dfv.get(x)); 114 if (max > 50) 115 this.lastMax = max; 116 117 // Draw all the keyframes found onto the image 118 for (final VideoKeyframe<MBFImage> kf : keyframes) 119 { 120 final long fn = kf.getTimecode().getFrameNumber(); 121 final int x = (int) (fn * w / dfv.length()); 122 123 // We draw the keyframes along the top of the visualisation. 124 // So we draw a line to the frame to match it up to the 125 // differential 126 renderer.drawLine(x, h, x, 0, new Float[] { 0.3f, 0.3f, 0.3f }); 127 renderer.drawImage(kf.getImage().process(rp), x + 1, 0); 128 } 129 130 // This is the threshold line drawn onto the image. 131 renderer.drawLine(0, (int) (h - h / max * threshold), w, 132 (int) (h - h / max * threshold), RGBColour.RED); 133 134 // Now draw all the differentials 135 int x = 0; 136 for (int z = 0; z < dfv.length(); z++) 137 { 138 x = z * w / dfv.length(); 139 renderer.drawLine(x, h, x, (int) (h - h / max * dfv.get(z)), 140 RGBColour.WHITE); 141 } 142 143 // Display the visualisation 144 // DisplayUtilities.updateNamed( "vsd", m, "Video Shot Detector" 145 // ); 146 147 // System.out.println( "Keyframes: "+keyframes ); 148 // DisplayUtilities.display( "Keyframes: ", keyframes.toArray( 149 // new Image<?,?>[0] ) ); 150 } 151 152 @Override 153 public void differentialCalculated(final VideoTimecode vt, final double d, final MBFImage frame) 154 { 155 156 // Display the visualisation 157 // DisplayUtilities.updateNamed( "vsd", m, "Video Shot Detector" 158 // ); 159 this.shotDetected(null, null); 160 161 renderer.drawShapeFilled(new Rectangle(w + tw / 2 - 5, th, 10, h - th), RGBColour.BLACK); 162 renderer.drawLine(w + tw / 2, h, w + tw / 2, (int) (h - h / this.lastMax * d), 10, 163 RGBColour.RED); 164 renderer.drawImage(frame.process(rp), w, 0); 165 166 // Display the visualisation 167 DisplayUtilities.updateNamed("vsd", m, "Video Shot Detector"); 168 169 DisplayUtilities.displayName(frame, "video"); 170 } 171 }); 172 173 vsd.process(); 174 } 175}