Difference between revisions of "Example Image Pyramid"

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


Example Code:
Example Code:
* [https://github.com/lessthanoptimal/BoofCV/blob/v0.11/examples/src/boofcv/examples/ExamplePyramidDiscrete.java ExamplePyramidDiscrete.java ]
* [https://github.com/lessthanoptimal/BoofCV/blob/v0.27/examples/src/boofcv/examples/imageprocessing/ExamplePyramidDiscrete.java ExamplePyramidDiscrete.java ]
* [https://github.com/lessthanoptimal/BoofCV/blob/v0.11/examples/src/boofcv/examples/ExamplePyramidFloat.java ExamplePyramidFloat.java ]
* [https://github.com/lessthanoptimal/BoofCV/blob/v0.27/examples/src/boofcv/examples/imageprocessing/ExamplePyramidFloat.java ExamplePyramidFloat.java ]


Concepts:
Concepts:
Line 20: Line 20:
* Discrete Vs. Float pyramids
* Discrete Vs. Float pyramids
* Image scaling
* Image scaling
Relevant Applets:
* [[Applet_Pyramid_Discrete| Discrete Pyramid]]
* [[Applet_Pyramid_Float| Float Pyramid]]


= Image Pyramid Discrete =
= Image Pyramid Discrete =
Line 34: Line 30:
  * @author Peter Abeles
  * @author Peter Abeles
  */
  */
public class ExamplePyramidDiscrete<T extends ImageSingleBand> {
public class ExamplePyramidDiscrete<T extends ImageGray<T>> {


// specifies the image type
// specifies the image type
Line 40: Line 36:
// The pyramid data structure
// The pyramid data structure
PyramidDiscrete<T> pyramid;
PyramidDiscrete<T> pyramid;
// update the pyramid given from
PyramidUpdaterDiscrete<T> updater;


public ExamplePyramidDiscrete(Class<T> imageType) {
public ExamplePyramidDiscrete(Class<T> imageType) {
Line 52: Line 46:
public void standard() {
public void standard() {
// Each level in the pyramid must have a ratio with the previously layer that is an integer value
// Each level in the pyramid must have a ratio with the previously layer that is an integer value
pyramid = new PyramidDiscrete<T>(imageType,true,1,2,4,8);
pyramid = FactoryPyramid.discreteGaussian(new int[]{1,2,4,8},-1,2,true, ImageType.single(imageType));
 
// In most cases sub-sampling with a Gaussian is preferred
updater = FactoryPyramid.discreteGaussian(imageType,-1,2);
}
}


Line 63: Line 54:
public void unusual() {
public void unusual() {
// Note that the first level does not have to be one
// Note that the first level does not have to be one
pyramid = new PyramidDiscrete<T>(imageType,true,2,6);
pyramid = FactoryPyramid.discreteGaussian(new int[]{2,6},-1,2,true, ImageType.single(imageType));


// Other kernels can also be used besides Gaussian
// Other kernels can also be used besides Gaussian
Line 72: Line 63:
kernel = FactoryKernel.table1D_I32(2);
kernel = FactoryKernel.table1D_I32(2);
}
}
updater = new PyramidUpdateIntegerDown<T>(kernel,imageType);
}
}


Line 81: Line 70:
public void process( BufferedImage image ) {
public void process( BufferedImage image ) {
T input = ConvertBufferedImage.convertFromSingle(image, null, imageType);
T input = ConvertBufferedImage.convertFromSingle(image, null, imageType);
updater.update(input,pyramid);
pyramid.process(input);


DiscretePyramidPanel gui = new DiscretePyramidPanel();
DiscretePyramidPanel gui = new DiscretePyramidPanel();
Line 92: Line 81:
T imageAtScale = pyramid.getLayer(1);
T imageAtScale = pyramid.getLayer(1);


ShowImages.showWindow(ConvertBufferedImage.convertTo(imageAtScale,null),"Image at layer 1");
ShowImages.showWindow(ConvertBufferedImage.convertTo(imageAtScale,null,true),"Image at layer 1");
}
}


public static void main( String[] args ) {
public static void main( String[] args ) {
BufferedImage image = UtilImageIO.loadImage("../data/evaluation/standard/barbara.png");
BufferedImage image = UtilImageIO.loadImage(UtilIO.pathExample("standard/barbara.jpg"));


ExamplePyramidDiscrete<ImageFloat32> app = new ExamplePyramidDiscrete<ImageFloat32>(ImageFloat32.class);
ExamplePyramidDiscrete<GrayF32> app = new ExamplePyramidDiscrete<>(GrayF32.class);
// ExamplePyramidDiscrete<ImageUInt8> app = new ExamplePyramidDiscrete<ImageUInt8>(ImageUInt8.class);
// ExamplePyramidDiscrete<GrayU8> app = new ExamplePyramidDiscrete<>(GrayU8.class);


app.standard();
app.standard();
Line 120: Line 109:
  * @author Peter Abeles
  * @author Peter Abeles
  */
  */
public class ExamplePyramidFloat<T extends ImageSingleBand> {
public class ExamplePyramidFloat<T extends ImageGray<T>> {


// specifies the image type
// specifies the image type
Line 126: Line 115:
// The pyramid data structure
// The pyramid data structure
PyramidFloat<T> pyramid;
PyramidFloat<T> pyramid;
// update the pyramid given from
PyramidUpdaterFloat<T> updater;


public ExamplePyramidFloat(Class<T> imageType) {
public ExamplePyramidFloat(Class<T> imageType) {
Line 139: Line 126:
// Scale factory for each layer can be any floating point value which is larger than
// Scale factory for each layer can be any floating point value which is larger than
// the previous layer's scale.
// the previous layer's scale.
pyramid = new PyramidFloat<T>(imageType,1,1.5,2,2.5,3,5,8,15);
double scales[] = new double[]{1,1.5,2,2.5,3,5,8,15};
 
// the amount of blur which is applied to each layer in the pyramid after the previous layer has been sampled
// Specify the amount of blur to apply to each scale
double sigmas[] = new double[]{1,1,1,1,1,1,1,1};
// Using a custom updater other types of blur and interpolation can be applied
pyramid = FactoryPyramid.floatGaussian(scales,sigmas,imageType);
updater = FactoryPyramid.floatGaussian(imageType, 1,1,1,1,1,1,1,1);
}
}


Line 151: Line 137:
public void process( BufferedImage image ) {
public void process( BufferedImage image ) {
T input = ConvertBufferedImage.convertFromSingle(image, null, imageType);
T input = ConvertBufferedImage.convertFromSingle(image, null, imageType);
updater.update(input,pyramid);
pyramid.process(input);


ImagePyramidPanel<T> gui = new ImagePyramidPanel<T>();
ImagePyramidPanel<T> gui = new ImagePyramidPanel<>();
gui.set(pyramid, true);
gui.set(pyramid, true);
gui.render();
gui.render();
Line 162: Line 148:
T imageAtScale = pyramid.getLayer(1);
T imageAtScale = pyramid.getLayer(1);


ShowImages.showWindow(ConvertBufferedImage.convertTo(imageAtScale,null),"Image at layer 1");
ShowImages.showWindow(ConvertBufferedImage.convertTo(imageAtScale,null,true),"Image at layer 1");
}
}




public static void main( String[] args ) {
public static void main( String[] args ) {
BufferedImage image = UtilImageIO.loadImage("../data/evaluation/standard/barbara.png");
BufferedImage image = UtilImageIO.loadImage(UtilIO.pathExample("standard/barbara.jpg"));


ExamplePyramidFloat<ImageFloat32> app = new ExamplePyramidFloat<ImageFloat32>(ImageFloat32.class);
ExamplePyramidFloat<GrayF32> app = new ExamplePyramidFloat<>(GrayF32.class);
// ExamplePyramidFloat<ImageUInt8> app = new ExamplePyramidFloat<ImageUInt8>(ImageUInt8.class);
// ExamplePyramidFloat<GrayU8> app = new ExamplePyramidFloat<>(GrayU8.class);


app.standard();
app.standard();

Revision as of 09:03, 17 August 2017

Image pyramids are a common way to represent multi-resolution image information. In an image pyramid, upper layers are lower resolution versions of the lower layers. BoofCV provides two types of image pyramids built in; PyramidDiscrete and PyramidFloat. PyramidDiscrete only allows the ratio between adjacent to have positive integer values, while PyramidFloat allows any arbitrary positive value. Discrete pyramids are much faster than float pyramids, but much more restrictive.

Inside of BoofCV several algorithms make use of these two types of pyramids. The KLT feature tracker uses a discrete pyramid and is the fastest feature tracker. Pyramid based scale space feature detectors use the float pyramid.

Two code examples are provided below which demonstrate how to construct and visualize each type of pyramid.

Example Code:

Concepts:

  • Multi-resolution image processing
  • Discrete Vs. Float pyramids
  • Image scaling

Image Pyramid Discrete

/**
 * Demonstrates how to construct and display a {@link PyramidDiscrete}.  Discrete pyramids require that
 * each level has a relative scale with an integer ratio and is updated by sparsely sub-sampling.  These
 * restrictions allows a very quick update across scale space.
 *
 * @author Peter Abeles
 */
public class ExamplePyramidDiscrete<T extends ImageGray<T>> {

	// specifies the image type
	Class<T> imageType;
	// The pyramid data structure
	PyramidDiscrete<T> pyramid;

	public ExamplePyramidDiscrete(Class<T> imageType) {
		this.imageType = imageType;
	}

	/**
	 * Creates a fairly standard pyramid and updater.
	 */
	public void standard() {
		// Each level in the pyramid must have a ratio with the previously layer that is an integer value
		pyramid = FactoryPyramid.discreteGaussian(new int[]{1,2,4,8},-1,2,true, ImageType.single(imageType));
	}

	/**
	 * Creates a more unusual pyramid and updater.
	 */
	public void unusual() {
		// Note that the first level does not have to be one
		pyramid = FactoryPyramid.discreteGaussian(new int[]{2,6},-1,2,true, ImageType.single(imageType));

		// Other kernels can also be used besides Gaussian
		Kernel1D kernel;
		if(GeneralizedImageOps.isFloatingPoint(imageType) ) {
			kernel = FactoryKernel.table1D_F32(2,true);
		} else {
			kernel = FactoryKernel.table1D_I32(2);
		}
	}

	/**
	 * Updates and displays the pyramid.
	 */
	public void process( BufferedImage image ) {
		T input = ConvertBufferedImage.convertFromSingle(image, null, imageType);
		pyramid.process(input);

		DiscretePyramidPanel gui = new DiscretePyramidPanel();
		gui.setPyramid(pyramid);
		gui.render();

		ShowImages.showWindow(gui,"Image Pyramid");

		// To get an image at any of the scales simply call this get function
		T imageAtScale = pyramid.getLayer(1);

		ShowImages.showWindow(ConvertBufferedImage.convertTo(imageAtScale,null,true),"Image at layer 1");
	}

	public static void main( String[] args ) {
		BufferedImage image = UtilImageIO.loadImage(UtilIO.pathExample("standard/barbara.jpg"));

		ExamplePyramidDiscrete<GrayF32> app = new ExamplePyramidDiscrete<>(GrayF32.class);
//		ExamplePyramidDiscrete<GrayU8> app = new ExamplePyramidDiscrete<>(GrayU8.class);

		app.standard();
//		app.unusual();
		app.process(image);
	}
}


Image Pyramid Float

/**
 * Demonstrates how to construct and display a {@link PyramidFloat}.  Float pyramids require only require
 * that each layer's scale be larger than the scale of the previous layer. Interpolation is used to allow
 * sub-sampling at arbitrary scales.  All of this additional flexibility comes at the cost of speed
 * when compared to a {@link PyramidDiscrete}.
 *
 * @author Peter Abeles
 */
public class ExamplePyramidFloat<T extends ImageGray<T>> {

	// specifies the image type
	Class<T> imageType;
	// The pyramid data structure
	PyramidFloat<T> pyramid;

	public ExamplePyramidFloat(Class<T> imageType) {
		this.imageType = imageType;
	}

	/**
	 * Creates a fairly standard pyramid and updater.
	 */
	public void standard() {
		// Scale factory for each layer can be any floating point value which is larger than
		// the previous layer's scale.
		double scales[] = new double[]{1,1.5,2,2.5,3,5,8,15};
		// the amount of blur which is applied to each layer in the pyramid after the previous layer has been sampled
		double sigmas[] = new double[]{1,1,1,1,1,1,1,1};
		pyramid = FactoryPyramid.floatGaussian(scales,sigmas,imageType);
	}

	/**
	 * Updates and displays the pyramid.
	 */
	public void process( BufferedImage image ) {
		T input = ConvertBufferedImage.convertFromSingle(image, null, imageType);
		pyramid.process(input);

		ImagePyramidPanel<T> gui = new ImagePyramidPanel<>();
		gui.set(pyramid, true);
		gui.render();

		ShowImages.showWindow(gui,"Image Pyramid Float");

		// To get an image at any of the scales simply call this get function
		T imageAtScale = pyramid.getLayer(1);

		ShowImages.showWindow(ConvertBufferedImage.convertTo(imageAtScale,null,true),"Image at layer 1");
	}


	public static void main( String[] args ) {
		BufferedImage image = UtilImageIO.loadImage(UtilIO.pathExample("standard/barbara.jpg"));

		ExamplePyramidFloat<GrayF32> app = new ExamplePyramidFloat<>(GrayF32.class);
//		ExamplePyramidFloat<GrayU8> app = new ExamplePyramidFloat<>(GrayU8.class);

		app.standard();
		app.process(image);
	}
}