Difference between revisions of "Example Image Filter"

From BoofCV
Jump to navigationJump to search
m
m
Line 51: Line 51:
</syntaxhighlight>
</syntaxhighlight>


Notice how in the BlurImageOps.gaussian(input,blurred,-1,2,null) function (-1,2) are passed in for the sigma and radius parameters, see signature below.  A negative sigma indicates that it should be chosen based upon the kernel's radius.  The converse is also true, a negative radius means the radius should be selected based upon the sigma's value. When applying Gaussian blur additional storage is required, since null was passed in for the last parameter it will internally declare that memory.  Declaring temporary memory each time a function is invoked is wasteful, but easier to program.
The two integer parameters in BlurImageOps.gaussian(input,blurred,-1,2,null) specify the blur's sigma and radius parameters.  A negative sigma indicates that it should be chosen based upon the kernel's radius.  The converse is also true, a negative radius means the radius should be selected based upon the sigma's value. When applying Gaussian blur additional storage is required, since null was passed in for the last parameter it will internally declare that memory.  Declaring temporary memory each time a function is invoked is wasteful, but easier to program.
 
{|
! Signature for BlurImageOps.gaussian
|-
|
<syntaxhighlight lang="java">
public static ImageUInt8 gaussian(ImageUInt8 input, ImageUInt8 output, double sigma , int radius , ImageUInt8 storage )
</syntaxhighlight>
|}


When convolving a kernel across an image its not always clear how image borders should be handled where only part of the image intersects the kernel.  In this example, the gradient is computed using a Sobel kernel, and it BorderType.EXTENDED tells it to handle the border by extending border pixels to outside the image.
When convolving a kernel across an image its not always clear how image borders should be handled where only part of the image intersects the kernel.  In this example, the gradient is computed using a Sobel kernel, and it BorderType.EXTENDED tells it to handle the border by extending border pixels to outside the image.
Line 68: Line 59:
= Generalized =
= Generalized =


The following example is very similar to the first, but it now takes in a generalized image type.  Type checking is weaker, but many different images can be passed in now.   
The following example is very similar to the first, but it now takes in a generalized image type using [http://download.oracle.com/javase/tutorial/java/generics/index.html Java generics].  Type checking is weaker, but different image types can be passed in using the exact same code.   


<syntaxhighlight lang="java">
<syntaxhighlight lang="java">
Line 93: Line 84:
</syntaxhighlight>
</syntaxhighlight>


Temporary images are created using GeneralizedImageOps.createImage() sicne the type is not known at compile timeEven though the specific type of image isn't know at runtime, Java generics allow consistency checks to make sure images which should be the same type are the same type.  Notice that blurring and image derivatives are computed using classes that begin with the letter 'G', which signifies they are generalized.
Since the type is not known at compile type, images are created using GeneralizedImageOps.createImage().  Java generics allow consistency checks to make sure images which should be the same type are the same type.  Notice that blurring and image derivatives are computed using classes that begin with the letter 'G', that signifies they are generalized classes.


= Abstracted =
= Abstracted =
Line 124: Line 115:
</syntaxhighlight>
</syntaxhighlight>


Notice how the filters are created using a Factory class. Factories provide an easy to use interface for constructing filters and other algorithms.  Often they will handle all the little details and only let the user specify major parameters.  Notice how FactoryDerivative.sobel() does not allow the user to specify the border type, that's because it assumes it knows the best type and might as well spare the developer from that level of detail.
Notice how Factories are used to create filters. Factories provide an easy to use interface for constructing filters and other algorithms.  They also provide a single location to look for similar algorithms which implement the same interface.  Often they will simplify development by handling all the little details and only let the developer specify major parameters.  Notice how FactoryDerivative.sobel() does not allow the user to specify the border type, that's because it assumes it knows the best type and might as well spare the developer from that level of detail.


= No Generics =
= No Generics =

Revision as of 15:20, 28 October 2011

Introduction

This tutorial introduces basic BoofCV programming concepts through three examples. In BoofCV there are often three ways to invoke a function; 1) procedural, 2) generalized, and 3) abstracted.

Procedural functions are contained in children of the boofcv.alg package, where 'alg' stands for algorithms. These procedural functions are low level implementations which allow greater customization, tighter memory control, and have strong typing. In the same package are generalized versions of procedural functions that provide weaker type checking, but allow generic images to be passed in. Classes which contain generalized functions start with the letter 'G'. For example, BlurImageOps contains a set of functions for each image type and operation pair it supports and GBlurImageOps has a single function for each operation it supports.

Inside the children of 'boofcv.abst' package are abstracted filters. These classes provide an Object Oriented Programming (OOP) interface and handle much of the memory management, but are less flexible. Some times the ability to configure low level parameters has been removed from the algorithms that they are wrappers around to make development more manageable.

Example File: ImageFilterExample

Tutorial Concepts:

  1. Invoke procedural functions.
  2. Invoke generalized functions using Java generics.
  3. Create and invoke abstracted filters.
  4. Use a Factory.
  5. Display images.

Relevant Applets:

  1. Image Blur
  2. Gradient

Procedural

Here an ImageUInt8 image is passed in, which is then blurred, has its derivative computed and displayed in a window. In BoofCV, unlike many other vision libraries, to provide strong type checking at compile time, images with different primitive data types are a different type. The name 'UInt8' indicates that its elements are unsigned 8-bit integers and that it has only a single band. Image derivative require a different data type because the domain of each element includes negative numbers and has a larger domain, which is why they are stored in a ImageSInt16. ImageSInt16 are single band images with signed 16-bit integer pixels.

	public static void procedural( ImageUInt8 input )
	{
		ImageUInt8 blurred = new ImageUInt8(input.width,input.height);
		ImageSInt16 derivX = new ImageSInt16(input.width,input.height);
		ImageSInt16 derivY = new ImageSInt16(input.width,input.height);

		// Gaussian blur: Convolve a Gaussian kernel with a width of 5 pixels
		BlurImageOps.gaussian(input,blurred,-1,2,null);

		// Calculate image's derivative
		GradientSobel.process(blurred, derivX, derivY, FactoryImageBorder.extend(input));

		// display the results
		BufferedImage outputImage = VisualizeImageData.colorizeSign(derivX,null,-1);
		ShowImages.showWindow(outputImage,"Procedural Fixed Type");
	}

The two integer parameters in BlurImageOps.gaussian(input,blurred,-1,2,null) specify the blur's sigma and radius parameters. A negative sigma indicates that it should be chosen based upon the kernel's radius. The converse is also true, a negative radius means the radius should be selected based upon the sigma's value. When applying Gaussian blur additional storage is required, since null was passed in for the last parameter it will internally declare that memory. Declaring temporary memory each time a function is invoked is wasteful, but easier to program.

When convolving a kernel across an image its not always clear how image borders should be handled where only part of the image intersects the kernel. In this example, the gradient is computed using a Sobel kernel, and it BorderType.EXTENDED tells it to handle the border by extending border pixels to outside the image.

Finally the output is computed by rendering the image derivative into a BufferedImage. BufferedImages come from Java Swing and is Java's standard way for storing image data. They are not used internally in BoofCV due to their complexity and poor performance. Once rendered, it is displayed in an image using the convenient showWindow() function.

Generalized

The following example is very similar to the first, but it now takes in a generalized image type using Java generics. Type checking is weaker, but different image types can be passed in using the exact same code.

	public static <T extends ImageBase, D extends ImageBase>
	void generalized( T input )
	{
		Class<T> inputType = (Class<T>)input.getClass();
		Class<D> derivType = GImageDerivativeOps.getDerivativeType(inputType);

		T blurred = GeneralizedImageOps.createImage(inputType,input.width, input.height);
		D derivX = GeneralizedImageOps.createImage(derivType,input.width, input.height);
		D derivY = GeneralizedImageOps.createImage(derivType,input.width, input.height);

		// Gaussian blur: Convolve a Gaussian kernel with a width of 5 pixels
		GBlurImageOps.gaussian(input, blurred, -1, 2, null);

		// Calculate image's derivative
		GImageDerivativeOps.sobel(blurred, derivX, derivY, BorderType.EXTENDED);

		// display the results
		BufferedImage outputImage = VisualizeImageData.colorizeSign(derivX,null,-1);
		ShowImages.showWindow(outputImage,"Generalized "+inputType.getSimpleName());
	}

Since the type is not known at compile type, images are created using GeneralizedImageOps.createImage(). Java generics allow consistency checks to make sure images which should be the same type are the same type. Notice that blurring and image derivatives are computed using classes that begin with the letter 'G', that signifies they are generalized classes.

Abstracted

Instead of invoking functions, filter classes are created here. Filters are even more flexible and allow greater abstraction.

	public static <T extends ImageBase, D extends ImageBase>
	void filter( T input )
	{
		Class<T> inputType = (Class<T>)input.getClass();
		Class<D> derivType = GImageDerivativeOps.getDerivativeType(inputType);

		T blurred = GeneralizedImageOps.createImage(inputType, input.width, input.height);
		D derivX = GeneralizedImageOps.createImage(derivType, input.width, input.height);
		D derivY = GeneralizedImageOps.createImage(derivType, input.width, input.height);

		// declare image filters
		BlurFilter<T> filterBlur = FactoryBlurFilter.gaussian(inputType, -1, 2);
		ImageGradient<T,D> gradient = FactoryDerivative.sobel(inputType, derivType);

		// process the image
		filterBlur.process(input,blurred);
		gradient.process(blurred,derivX,derivY);

		// display the results
		BufferedImage outputImage = VisualizeImageData.colorizeSign(derivX,null,-1);
		ShowImages.showWindow(outputImage,"Filter "+inputType.getSimpleName());
	}

Notice how Factories are used to create filters. Factories provide an easy to use interface for constructing filters and other algorithms. They also provide a single location to look for similar algorithms which implement the same interface. Often they will simplify development by handling all the little details and only let the developer specify major parameters. Notice how FactoryDerivative.sobel() does not allow the user to specify the border type, that's because it assumes it knows the best type and might as well spare the developer from that level of detail.

No Generics

For sake of completeness, it is demonstrated that Java generics are optional when writing generalized code. No type checking is done here, but the code is less verbose.

	public static void nogenerics( ImageBase input )
	{
		Class inputType = input.getClass();
		Class derivType = GImageDerivativeOps.getDerivativeType(inputType);

		ImageBase blurred = GeneralizedImageOps.createImage(inputType,input.width, input.height);
		ImageBase derivX = GeneralizedImageOps.createImage(derivType,input.width, input.height);
		ImageBase derivY = GeneralizedImageOps.createImage(derivType,input.width, input.height);

		// Gaussian blur: Convolve a Gaussian kernel with a width of 5 pixels
		GBlurImageOps.gaussian(input, blurred, -1, 2, null);

		// Calculate image's derivative
		GImageDerivativeOps.sobel(blurred, derivX, derivY, BorderType.EXTENDED);

		// display the results
		BufferedImage outputImage = VisualizeImageData.colorizeSign(derivX,null,-1);
		ShowImages.showWindow(outputImage,"Generalized "+inputType.getSimpleName());
	}