View Javadoc

1   /**
2    * Copyright (c) 2011, The University of Southampton and the individual contributors.
3    * All rights reserved.
4    *
5    * Redistribution and use in source and binary forms, with or without modification,
6    * are permitted provided that the following conditions are met:
7    *
8    *   * 	Redistributions of source code must retain the above copyright notice,
9    * 	this list of conditions and the following disclaimer.
10   *
11   *   *	Redistributions in binary form must reproduce the above copyright notice,
12   * 	this list of conditions and the following disclaimer in the documentation
13   * 	and/or other materials provided with the distribution.
14   *
15   *   *	Neither the name of the University of Southampton nor the names of its
16   * 	contributors may be used to endorse or promote products derived from this
17   * 	software without specific prior written permission.
18   *
19   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
20   * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21   * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22   * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
23   * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24   * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25   * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
26   * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27   * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28   * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29   */
30  package org.openimaj.demos.image;
31  
32  import java.io.File;
33  import java.io.IOException;
34  
35  import javax.swing.JFrame;
36  import javax.swing.JLabel;
37  
38  import org.openimaj.demos.Demo;
39  import org.openimaj.feature.local.list.LocalFeatureList;
40  import org.openimaj.feature.local.matcher.FastBasicKeypointMatcher;
41  import org.openimaj.feature.local.matcher.consistent.ConsistentLocalFeatureMatcher2d;
42  import org.openimaj.image.DisplayUtilities;
43  import org.openimaj.image.FImage;
44  import org.openimaj.image.ImageUtilities;
45  import org.openimaj.image.MBFImage;
46  import org.openimaj.image.colour.Transforms;
47  import org.openimaj.image.feature.local.engine.DoGSIFTEngine;
48  import org.openimaj.image.feature.local.keypoints.Keypoint;
49  import org.openimaj.image.processing.resize.ResizeProcessor;
50  import org.openimaj.image.processing.transform.ProjectionProcessor;
51  import org.openimaj.math.geometry.point.Point2d;
52  import org.openimaj.math.geometry.transforms.HomographyModel;
53  import org.openimaj.math.geometry.transforms.residuals.SingleImageTransferResidual2d;
54  import org.openimaj.math.model.fit.RANSAC;
55  
56  /**
57   * Demonstrates using the SIFT keypoint matching and automatic homography
58   * transform calculation to project 3 photos into a basic stitched panorama.
59   * 
60   * @author Sina Samangooei (ss@ecs.soton.ac.uk)
61   * 
62   * @created 15 Feb 2012
63   */
64  @Demo(
65  		author = "Sina Samangooei",
66  		description = "Demonstrates using the SIFT keypoint matching and automatic "
67  				+ "homography transform calculation to project 3 photos into a basic stitched "
68  				+ "panorama.",
69  		keywords = { "image", "photo", "mosaic", "stitch", "panorama" },
70  		title = "Simple Photo Mosaic",
71  		icon = "/org/openimaj/demos/icons/image/mosaic-icon.png",
72  		screenshot = "/org/openimaj/demos/screens/image/mosaic-screen.png",
73  		vmArguments = "-Xmx1G")
74  public class SimpleMosaic
75  {
76  	/**
77  	 * Default main
78  	 * 
79  	 * @param args
80  	 *            Command-line arguments
81  	 * @throws IOException
82  	 */
83  	public static void main(String args[]) throws IOException
84  	{
85  		final JLabel l = new JLabel();
86  		l.setHorizontalAlignment(JLabel.CENTER);
87  		final JFrame f = new JFrame("Mosaic Progress");
88  		f.getContentPane().add(l);
89  		f.setSize(500, 80);
90  		f.setVisible(true);
91  
92  		new Thread(new Runnable()
93  		{
94  			@Override
95  			public void run()
96  			{
97  				try
98  				{
99  					// Set up all the various processors
100 					l.setText("Setting up processors");
101 					final ResizeProcessor rp = new ResizeProcessor(800, 600);
102 					final DoGSIFTEngine engine = new DoGSIFTEngine();
103 
104 					final ConsistentLocalFeatureMatcher2d<Keypoint> matcher =
105 							new ConsistentLocalFeatureMatcher2d<Keypoint>(
106 									new FastBasicKeypointMatcher<Keypoint>(8));
107 					final HomographyModel model = new HomographyModel();
108 					final RANSAC<Point2d, Point2d, HomographyModel> modelFitting = new RANSAC<Point2d, Point2d, HomographyModel>(
109 							model, new SingleImageTransferResidual2d<HomographyModel>(), 8.0, 1600,
110 							new RANSAC.BestFitStoppingCondition(),
111 							true);
112 					matcher.setFittingModel(modelFitting);
113 
114 					// Load in the first (middle) image and calculate the SIFT
115 					// features
116 					final MBFImage imageMiddle = ImageUtilities.readMBF(SimpleMosaic.class
117 							.getResource("/org/openimaj/demos/image/mosaic/trento-view-1.jpg"));
118 					imageMiddle.processInplace(rp);
119 					final FImage workingImageMiddle = Transforms.calculateIntensityNTSC(imageMiddle);
120 					l.setText("Calculating features on middle image");
121 					final LocalFeatureList<Keypoint> middleKP = engine.findFeatures(workingImageMiddle);
122 					matcher.setModelFeatures(middleKP);
123 
124 					// Calculate the projection for the first image
125 					final ProjectionProcessor<Float[], MBFImage> ptp =
126 							new ProjectionProcessor<Float[], MBFImage>();
127 					imageMiddle.accumulateWith(ptp);
128 
129 					// Load in the right-hand image and calculate its features
130 					final MBFImage imageRight = ImageUtilities.readMBF(SimpleMosaic.class
131 							.getResource("/org/openimaj/demos/image/mosaic/trento-view-0.jpg"));
132 					imageRight.processInplace(rp);
133 					final FImage workingImageRight = Transforms.calculateIntensityNTSC(imageRight);
134 					l.setText("Calculating features on right image");
135 					final LocalFeatureList<Keypoint> rightKP = engine.findFeatures(workingImageRight);
136 
137 					// Find matches with the middle image
138 					l.setText("Finding matches with middle image and right image");
139 					matcher.findMatches(rightKP);
140 					ptp.setMatrix(model.getTransform());
141 					l.setText("Projecting right image");
142 					imageRight.accumulateWith(ptp);
143 
144 					// Load in the left-hand image and calculate its features
145 					final MBFImage imageLeft = ImageUtilities.readMBF(SimpleMosaic.class
146 							.getResource("/org/openimaj/demos/image/mosaic/trento-view-2.jpg"));
147 					imageLeft.processInplace(rp);
148 					final FImage workingImageLeft = Transforms.calculateIntensityNTSC(imageLeft);
149 					l.setText("Calculating features on left image");
150 					final LocalFeatureList<Keypoint> leftKP = engine.findFeatures(workingImageLeft);
151 
152 					// Find the matches with the middle image
153 					l.setText("Finding matches with middle image and left image");
154 					matcher.findMatches(leftKP);
155 					ptp.setMatrix(model.getTransform());
156 					l.setText("Projecting left image");
157 					imageLeft.accumulateWith(ptp);
158 
159 					l.setText("Projecting final image");
160 					final MBFImage projected = ptp.performBlendedProjection(
161 							(-imageMiddle.getWidth()),
162 							imageMiddle.getWidth() + imageMiddle.getWidth(),
163 							-imageMiddle.getHeight() / 2, 3 * imageMiddle.getHeight() / 2,
164 							(Float[]) null);
165 
166 					// Display the result
167 					f.setVisible(false);
168 					DisplayUtilities.display(projected.process(rp));
169 
170 					// Write the result
171 					ImageUtilities.write(projected, "png", new File(
172 							"/Users/jsh2/Desktop/mosaic.png"));
173 				}
174 				catch (final IOException e)
175 				{
176 					e.printStackTrace();
177 				}
178 			}
179 		}).start();
180 	}
181 }