Difference between revisions of "Example Overhead View"

From BoofCV
Jump to navigationJump to search
m
m
 
(3 intermediate revisions by the same user not shown)
Line 9: Line 9:


Example Code:
Example Code:
* [https://github.com/lessthanoptimal/BoofCV/blob/v0.25/examples/src/boofcv/examples/geometry/ExampleOverheadView.java ExampleOverheadView.java]
* [https://github.com/lessthanoptimal/BoofCV/blob/v0.40/examples/src/main/java/boofcv/examples/geometry/ExampleOverheadView.java ExampleOverheadView.java]


Concepts:
Concepts:
* Perspective Distortion
* Perspective Distortion
* Ground plane
* Ground plane
Relevant Applets:


Related Examples:
Related Examples:
Line 23: Line 21:
<syntaxhighlight lang="java">
<syntaxhighlight lang="java">
/**
/**
  * Creates a synthetic overhead view from an image using the known ground plane. This is commonly used for navigation
  * Creates a synthetic overhead view from an image using the known ground plane. This is commonly used for navigation
  * in man-made environments, such on roads or through hallways. Objects which are not actually on the ground plane
  * in man-made environments, such on roads or through hallways. Objects which are not actually on the ground plane
  * will be heavily distorted in the overhead view.
  * will be heavily distorted in the overhead view.
  *
  *
Line 30: Line 28:
  */
  */
public class ExampleOverheadView {
public class ExampleOverheadView {
public static void main( String args[] ) {
public static void main( String[] args ) {
BufferedImage input = UtilImageIO.loadImage(UtilIO.pathExample("road/left01.png"));
BufferedImage input = UtilImageIO.loadImageNotNull(UtilIO.pathExample("road/left01.png"));


Planar<GrayU8> imageRGB = ConvertBufferedImage.convertFromMulti(input, null,true, GrayU8.class);
Planar<GrayU8> imageRGB = ConvertBufferedImage.convertFromPlanar(input, null, true, GrayU8.class);


StereoParameters stereoParam = UtilIO.loadXML(UtilIO.pathExample("road/stereo01.xml"));
StereoParameters stereoParam = CalibrationIO.load(UtilIO.pathExample("road/stereo01.yaml"));
Se3_F64 groundToLeft = UtilIO.loadXML(UtilIO.pathExample("road/ground_to_left_01.xml"));
Se3_F64 groundToLeft = CalibrationIO.load(UtilIO.pathExample("road/ground_to_left_01.yaml"));


CreateSyntheticOverheadView<Planar<GrayU8>> generateOverhead =
CreateSyntheticOverheadView<Planar<GrayU8>> generateOverhead =
new CreateSyntheticOverheadViewPL<>(InterpolateType.BILINEAR,3,GrayU8.class);
new CreateSyntheticOverheadViewPL<>(InterpolationType.BILINEAR, 3, GrayU8.class);


// size of cells in the overhead image in world units
// size of cells in the overhead image in world units
Line 45: Line 43:


// You can use this to automatically select reasonable values for the overhead image
// You can use this to automatically select reasonable values for the overhead image
SelectOverheadParameters selectMapSize = new SelectOverheadParameters(cellSize,20,0.5);
SelectOverheadParameters selectMapSize = new SelectOverheadParameters(cellSize, 20, 0.5);
selectMapSize.process(stereoParam.left,groundToLeft);
selectMapSize.process(stereoParam.left, groundToLeft);


int overheadWidth = selectMapSize.getOverheadWidth();
int overheadWidth = selectMapSize.getOverheadWidth();
Line 52: Line 50:


Planar<GrayU8> overheadRGB =
Planar<GrayU8> overheadRGB =
new Planar<>(GrayU8.class,overheadWidth,overheadHeight,3);
new Planar<>(GrayU8.class, overheadWidth, overheadHeight, 3);
generateOverhead.configure(stereoParam.left,groundToLeft,
generateOverhead.configure(stereoParam.left, groundToLeft,
selectMapSize.getCenterX(), selectMapSize.getCenterY(), cellSize,overheadRGB.width,overheadRGB.height);
selectMapSize.getCenterX(), selectMapSize.getCenterY(), cellSize, overheadRGB.width, overheadRGB.height);


generateOverhead.process(imageRGB, overheadRGB);
generateOverhead.process(imageRGB, overheadRGB);


// note that the left/right values are swapped in the overhead image. This is an artifact of the plane's
// note that the left/right values are swapped in the overhead image. This is an artifact of the plane's
// 2D coordinate system having +y pointing up, while images have +y pointing down.
// 2D coordinate system having +y pointing up, while images have +y pointing down.
BufferedImage output = ConvertBufferedImage.convertTo(overheadRGB,null,true);
BufferedImage output = ConvertBufferedImage.convertTo(overheadRGB, null, true);


ShowImages.showWindow(input,"Input Image",true);
ShowImages.showWindow(input, "Input Image", true);
ShowImages.showWindow(output,"Overhead Image",true);
ShowImages.showWindow(output, "Overhead Image", true);
}
}
}
}
</syntaxhighlight>
</syntaxhighlight>

Latest revision as of 15:55, 17 January 2022

For some tasks (e.g. autonomous driving) detecting features and understanding the scene is difficult due to perspective distortion. An overhead orthogonal view of the scene can be created if the geometry between the camera and the ground plane is known. Each pixel in the overhead image will have a known size and its location relative to the camera is also known.

Example Code:

Concepts:

  • Perspective Distortion
  • Ground plane

Related Examples:

Example Code

/**
 * Creates a synthetic overhead view from an image using the known ground plane. This is commonly used for navigation
 * in man-made environments, such on roads or through hallways. Objects which are not actually on the ground plane
 * will be heavily distorted in the overhead view.
 *
 * @author Peter Abeles
 */
public class ExampleOverheadView {
	public static void main( String[] args ) {
		BufferedImage input = UtilImageIO.loadImageNotNull(UtilIO.pathExample("road/left01.png"));

		Planar<GrayU8> imageRGB = ConvertBufferedImage.convertFromPlanar(input, null, true, GrayU8.class);

		StereoParameters stereoParam = CalibrationIO.load(UtilIO.pathExample("road/stereo01.yaml"));
		Se3_F64 groundToLeft = CalibrationIO.load(UtilIO.pathExample("road/ground_to_left_01.yaml"));

		CreateSyntheticOverheadView<Planar<GrayU8>> generateOverhead =
				new CreateSyntheticOverheadViewPL<>(InterpolationType.BILINEAR, 3, GrayU8.class);

		// size of cells in the overhead image in world units
		double cellSize = 0.05;

		// You can use this to automatically select reasonable values for the overhead image
		SelectOverheadParameters selectMapSize = new SelectOverheadParameters(cellSize, 20, 0.5);
		selectMapSize.process(stereoParam.left, groundToLeft);

		int overheadWidth = selectMapSize.getOverheadWidth();
		int overheadHeight = selectMapSize.getOverheadHeight();

		Planar<GrayU8> overheadRGB =
				new Planar<>(GrayU8.class, overheadWidth, overheadHeight, 3);
		generateOverhead.configure(stereoParam.left, groundToLeft,
				selectMapSize.getCenterX(), selectMapSize.getCenterY(), cellSize, overheadRGB.width, overheadRGB.height);

		generateOverhead.process(imageRGB, overheadRGB);

		// note that the left/right values are swapped in the overhead image. This is an artifact of the plane's
		// 2D coordinate system having +y pointing up, while images have +y pointing down.
		BufferedImage output = ConvertBufferedImage.convertTo(overheadRGB, null, true);

		ShowImages.showWindow(input, "Input Image", true);
		ShowImages.showWindow(output, "Overhead Image", true);
	}
}