<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://boofcv.org/index.php?action=history&amp;feed=atom&amp;title=Example_Bundle_Adjustment_Graph</id>
	<title>Example Bundle Adjustment Graph - Revision history</title>
	<link rel="self" type="application/atom+xml" href="https://boofcv.org/index.php?action=history&amp;feed=atom&amp;title=Example_Bundle_Adjustment_Graph"/>
	<link rel="alternate" type="text/html" href="https://boofcv.org/index.php?title=Example_Bundle_Adjustment_Graph&amp;action=history"/>
	<updated>2026-08-08T00:01:49Z</updated>
	<subtitle>Revision history for this page on the wiki</subtitle>
	<generator>MediaWiki 1.36.2</generator>
	<entry>
		<id>https://boofcv.org/index.php?title=Example_Bundle_Adjustment_Graph&amp;diff=3224&amp;oldid=prev</id>
		<title>Peter: Created page with &quot;This Bundle Adjustment example entirely focuses on graph construction.  Example Code: * [https://github.com/lessthanoptimal/BoofCV/blob/v0.41/examples/src/main/java/boofcv/exa...&quot;</title>
		<link rel="alternate" type="text/html" href="https://boofcv.org/index.php?title=Example_Bundle_Adjustment_Graph&amp;diff=3224&amp;oldid=prev"/>
		<updated>2022-09-03T00:48:51Z</updated>

		<summary type="html">&lt;p&gt;Created page with &amp;quot;This Bundle Adjustment example entirely focuses on graph construction.  Example Code: * [https://github.com/lessthanoptimal/BoofCV/blob/v0.41/examples/src/main/java/boofcv/exa...&amp;quot;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;New page&lt;/b&gt;&lt;/p&gt;&lt;div&gt;This Bundle Adjustment example entirely focuses on graph construction.&lt;br /&gt;
&lt;br /&gt;
Example Code:&lt;br /&gt;
* [https://github.com/lessthanoptimal/BoofCV/blob/v0.41/examples/src/main/java/boofcv/examples/sfm/ExampleBundleAdjustmentGraph.java ExampleBundleAdjustmentGraph.java]&lt;br /&gt;
&lt;br /&gt;
Concepts:&lt;br /&gt;
* Bundle Adjustment&lt;br /&gt;
* Multiple Views&lt;br /&gt;
* Scene Reconstruction&lt;br /&gt;
&lt;br /&gt;
Related:&lt;br /&gt;
* [[Example_Sparse_Bundle_Adjustment|Bundle Adjustment (Optimization)]]&lt;br /&gt;
&lt;br /&gt;
= Example Code =&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Example which shows you how to construct the scene graph and observations that are feed into bundle adjustment.&lt;br /&gt;
 *&lt;br /&gt;
 * Here we will optimize a synchronized stereo camera system. The baseline is known and the location of camera[0] in&lt;br /&gt;
 * each time step is estimated. By synchronized we mean that the two cameras capture images at the exact same time.&lt;br /&gt;
 * We will intentionally give it an incorrect set of parameters then see if bundle adjustment will fix it given&lt;br /&gt;
 * perfect observations.&lt;br /&gt;
 *&lt;br /&gt;
 * @author Peter Abeles&lt;br /&gt;
 */&lt;br /&gt;
public class ExampleBundleAdjustmentGraph {&lt;br /&gt;
	public static void main( String[] args ) {&lt;br /&gt;
		var rand = new Random(234);&lt;br /&gt;
		int numPoints = 100;&lt;br /&gt;
		int numMotions = 10;&lt;br /&gt;
		int numCameras = 2;&lt;br /&gt;
		var intrinsic0 = new CameraPinholeBrown(500, 510, 0, 400, 405, 800, 700);&lt;br /&gt;
		var intrinsic1 = new CameraPinholeBrown(300, 299, 0, 500, 400, 1000, 800);&lt;br /&gt;
		var view0_to_view1 = eulerXyz(-0.15, 0.05, 0, 0, 0, 0.05, null);&lt;br /&gt;
&lt;br /&gt;
		// Initialize data structures by telling it the number of features, cameras, views, motions&lt;br /&gt;
		// Homogenous coordinates will be used since they can handle points at infinity&lt;br /&gt;
		var structure = new SceneStructureMetric(/*homogenous*/ true);&lt;br /&gt;
		var observations = new SceneObservations();&lt;br /&gt;
&lt;br /&gt;
		// Index of the motion where the stereo baseline is stored&lt;br /&gt;
		int baselineIndex = 0;&lt;br /&gt;
&lt;br /&gt;
		// Must call initialize() before all other functions.&lt;br /&gt;
		structure.initialize(&lt;br /&gt;
				numCameras, /*views*/ numMotions*numCameras, /* motions */numMotions + 1,&lt;br /&gt;
				numPoints, /* known rigid objects */0);&lt;br /&gt;
		observations.initialize(numMotions*2);&lt;br /&gt;
&lt;br /&gt;
		structure.setCamera(0, true, intrinsic0);&lt;br /&gt;
		structure.setCamera(1, true, intrinsic1);&lt;br /&gt;
&lt;br /&gt;
		// Set up the motion from camera[0] to camera[1] that define the stereo pair&lt;br /&gt;
		structure.motions.grow(); // motion is the one data structure that isn&amp;#039;t predeclared at init()&lt;br /&gt;
		structure.motions.get(baselineIndex).known = false;&lt;br /&gt;
		structure.motions.get(baselineIndex).parent_to_view.setTo(view0_to_view1);&lt;br /&gt;
		structure.motions.get(baselineIndex).parent_to_view.T.y += 0.05; // give it an imperfect estimate&lt;br /&gt;
&lt;br /&gt;
		// A synthetic scene is going to be created to enable us to focus on the main problem&lt;br /&gt;
		List&amp;lt;Point3D_F64&amp;gt; cloud = UtilPoint3D_F64.random(new Point3D_F64(0, 0, 3), -1, 1, 100, rand);&lt;br /&gt;
&lt;br /&gt;
		// Add points to bundle adjustment parameters. Here we will add noise to make it more interesting.&lt;br /&gt;
		for (int pointIdx = 0; pointIdx &amp;lt; cloud.size(); pointIdx++) {&lt;br /&gt;
			// NOTE: All points must be in the global coordinate system&lt;br /&gt;
			Point3D_F64 p = cloud.get(pointIdx);&lt;br /&gt;
			structure.points.get(pointIdx).set(p.x, p.y, p.z, 1.0);&lt;br /&gt;
&lt;br /&gt;
			// ADDING NOISE IS FOR DEMONSTRATION PURPOSES. DO NOT DO THIS WITH REAL DATA&lt;br /&gt;
			for (int i = 0; i &amp;lt; 3; i++) {&lt;br /&gt;
				structure.points.get(pointIdx).coordinate[1] += rand.nextGaussian()*0.05;&lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
&lt;br /&gt;
		System.out.println(&amp;quot;Simulating scene:&amp;quot;);&lt;br /&gt;
		var w2p0 = new WorldToCameraToPixel();&lt;br /&gt;
		var w2p1 = new WorldToCameraToPixel();&lt;br /&gt;
		var pixel = new Point2D_F64();&lt;br /&gt;
&lt;br /&gt;
		for (int motionIdx = 0; motionIdx &amp;lt; numMotions; motionIdx++) {&lt;br /&gt;
			// Two views for every motion. Index of view[0] at this time step&lt;br /&gt;
			int viewIdx0 = motionIdx*2;&lt;br /&gt;
&lt;br /&gt;
			// Specify where the views are located&lt;br /&gt;
			Se3_F64 worldToView0 = eulerXyz(-1.2 + motionIdx*0.4, 0, 0, rand.nextGaussian()*0.1, 0, 0, null);&lt;br /&gt;
			Se3_F64 worldToView1 = worldToView0.concat(view0_to_view1, null);&lt;br /&gt;
&lt;br /&gt;
			// Set up projection from a point in world coordinates to a point in a camera view&lt;br /&gt;
			w2p0.configure(intrinsic0, worldToView0);&lt;br /&gt;
			w2p1.configure(intrinsic1, worldToView1);&lt;br /&gt;
&lt;br /&gt;
			// Get observations for this view&lt;br /&gt;
			// views for the two cameras will be interleaved together&lt;br /&gt;
			SceneObservations.View pview0 = observations.getView(viewIdx0);&lt;br /&gt;
			SceneObservations.View pview1 = observations.getView(viewIdx0 + 1);&lt;br /&gt;
&lt;br /&gt;
			// camera[0] is easy to configure since it&amp;#039;s always relative to the global frame.&lt;br /&gt;
			// We will fix view[0] to stop the global coordinate system from randomly floating around.&lt;br /&gt;
			// If this was a real problem, typically view[0] is defined as the global coordinate system&amp;#039;s origin.&lt;br /&gt;
			structure.setView(/* view */ viewIdx0, /* camera */0, /* known */motionIdx == 0, worldToView0, -1);&lt;br /&gt;
&lt;br /&gt;
			// camera[1] is more difficult since multiple views share the same motion, but link to camera[0] view&lt;br /&gt;
			SceneStructureMetric.View sview1 = structure.views.get(viewIdx0 + 1);&lt;br /&gt;
			sview1.parent = structure.views.get(viewIdx0);&lt;br /&gt;
			sview1.camera = 1;&lt;br /&gt;
			sview1.parent_to_view = baselineIndex;&lt;br /&gt;
&lt;br /&gt;
			for (int pointIdx = 0; pointIdx &amp;lt; cloud.size(); pointIdx++) {&lt;br /&gt;
				Point3D_F64 p = cloud.get(pointIdx);&lt;br /&gt;
&lt;br /&gt;
				// Don&amp;#039;t add the observation if it&amp;#039;s behind the camera or outside the image&lt;br /&gt;
				if (w2p0.transform(p, pixel) &amp;amp;&amp;amp; intrinsic0.isInside(pixel.x, pixel.y)) {&lt;br /&gt;
					// Save the pixel observations&lt;br /&gt;
					pview0.add(/* feature */ pointIdx, (float)pixel.x, (float)pixel.y);&lt;br /&gt;
					// Add a connection between this point and the view in the scene graph&lt;br /&gt;
					structure.connectPointToView(pointIdx, viewIdx0);&lt;br /&gt;
				}&lt;br /&gt;
&lt;br /&gt;
				if (w2p1.transform(p, pixel) &amp;amp;&amp;amp; intrinsic1.isInside(pixel.x, pixel.y)) {&lt;br /&gt;
					pview1.add(/* feature */ pointIdx, (float)pixel.x, (float)pixel.y);&lt;br /&gt;
					structure.connectPointToView(pointIdx, viewIdx0 + 1);&lt;br /&gt;
				}&lt;br /&gt;
			}&lt;br /&gt;
			System.out.printf(&amp;quot; view[%2d] observations=%d\n&amp;quot;, viewIdx0, pview0.size());&lt;br /&gt;
			System.out.printf(&amp;quot; view[%2d] observations=%d\n&amp;quot;, viewIdx0 + 1, pview1.size());&lt;br /&gt;
		}&lt;br /&gt;
&lt;br /&gt;
		// Let&amp;#039;s optimize everything now and see if it fixes the noise we injected&lt;br /&gt;
		BundleAdjustment&amp;lt;SceneStructureMetric&amp;gt; bundleAdjustment = FactoryMultiView.bundleSparseMetric(null);&lt;br /&gt;
&lt;br /&gt;
		// Tell it to print results every iteration. More interesting that way&lt;br /&gt;
		bundleAdjustment.setVerbose(System.out, null);&lt;br /&gt;
		bundleAdjustment.setParameters(structure, observations);&lt;br /&gt;
		bundleAdjustment.configure(1e-12, 1e-12, /* max iterations */ 30);&lt;br /&gt;
&lt;br /&gt;
		// Perform the optimization. This will take a moment. Note you can pass in the same structure for output&lt;br /&gt;
		if (!bundleAdjustment.optimize(/* output */structure))&lt;br /&gt;
			throw new RuntimeException(&amp;quot;Optimization failed&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
		// Print and see if it fixed the incorrect baseline that was passed in&lt;br /&gt;
		structure.motions.get(baselineIndex).parent_to_view.print();&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;/div&gt;</summary>
		<author><name>Peter</name></author>
	</entry>
</feed>