Tutorial: Difference between revisions

From EMGU
Jump to navigation Jump to search
No edit summary
Line 189: Line 189:


==Error Handling==
==Error Handling==
For version >= [[ Emgu.CV-1.2.2.0 | 1.2.2.0]], when [[OpenCV]] encounter an error, instead of popping up an error dialogue, it will throw an CvException which encapsulate all the error message.
For version >= [[Version_History#Emgu.CV-1.2.2.0 | 1.2.2.0]], when [[OpenCV]] encounter an error, instead of popping up an error dialogue, it will throw an CvException which encapsulate all the error message.


==Code Documentation==
==Code Documentation==

Revision as of 15:04, 16 September 2008

Namespace

Emgu

All libraries implemented by Emgu® belongs to the namespace of Emgu.

Emgu.CV

The Emgu.CV namespace implement wrapper functions for OpenCV. To use this namespace in your code, it is recommended to include

using Emgu.CV;

in the beginning of your C# code.

Function Mapping - Emgu.CV.CvInvoke

The CvInvoke class provides a way to directly invoke OpenCV function within .NET languages. Each method in this class corresponds to a function in OpenCV of the same name. For example, a call to

 
 IntPtr image = CvInvoke.cvCreateImage(new MCvSize(400, 300), CvEnum.IPL_DEPTH.IPL_DEPTH_8U, 1);

is equivalent to the following function call in C

 
 IplImage* image = cvCreateImage(cvSize(400, 300), IPL_DEPTH_8U, 1);

Both of which create a 400x300 of 8-bit unsigned grayscale image .

Enumeration Mapping - Emgu.CV.CvEnum

The CvEnum namespace provides direct mapping to OpenCV enumerations. For example, CvEnum.IPL_DEPTH.IPL_DEPTH_8U has the same value as IPL_DEPTH_8U in OpenCV; both of which equals 8.

Structure Mapping - Emgu.CV.Mxxx

This type of structure is a direct mapping to OpenCV structures. For example

  • MIplImage is equivalent to IplImage structure in OpenCV
  • MCvSize is equivalent to CvSize structure
  • Mxxxx is equivalent to xxxx structure

The prefix M here stands for Managed structure.

Working with images

Depth and Color as Generic Parameter

An Image is defined by its generic parameters: color and depth. To create a 8bit unsigned Grayscale image, in Emgu CV it is done by calling

Image<Gray, Byte> image = new Image<Gray, Byte>( width, height);

Not only this syntax make you aware the color and the depth of the image, it also restrict the way you use functions and capture errors in compile time. For example, the SetValue(C color, Image<Gray, Byte> mask) function in Image<C, D> class (version >= 1.2.2.0) will only accept colors of the same type, and mask has to be an 8-bit unsigned grayscale image. Any attempts to use a 16-bit floating point or non-grayscale image as a mask will results a compile time error!

Creating Image

Although it is possible to create image by calling CvInvoke.cvCreateImage, it is suggested to construct a Image< Color, Depth> object instead. There are several advantages using the managed Image<Color, Depth> class

Image Color

The first generic parameter of the Image class specific the color of the image type. For example

Image<Gray, ...> img1;

indicates that img1 is a single channel grayscale image.

Color Types supported in Emgu CV 1.3.0.0 includes:

  • Gray
  • Bgr (Blue Green Red)
  • Bgra (Blue Green Red Alpha)
  • Hsv (Hue Saturation Value)
  • Hls (Hue Lightness Saturation)
  • Lab (CIE L*a*b*)
  • Luv (CIE L*u*v*)
  • Xyz (CIE XYZ.Rec 709 with D65 white point)
  • Ycc (YCrCb JPEG)

Image Depth

Image Depth is specified using the second generic parameter Depth Depths supported in Emgu CV 1.3.0.0 includes:

  • Byte
  • Single (float)
  • Double
  • SByte

Creating a new image

To create an 480x320 image of Bgr color and 8-bit unsigned depth. The code in C# would be

 Image<Bgr, Byte> img1 = new Image<Bgr, Byte>(480, 320);

If you wants to specify the background value of the image, let's say in Blue. The code in C# would be

 Image<Bgr, Byte> img1 = new Image<Bgr, Byte>(480, 320, new Bgr(255, 0, 0));

Reading image from file

Creating image from file is also simple. In C# it is

 Image<Bgr, Byte> img1 = new Image<Bgr, Byte>("MyImage.jpg");

assuming the image file is call "MyImage.jpg"

Creating image from Bitmap

It is also possible to create an Image< Color, Depth> from a .Net Bitmap object. The code in C# would be

 Image<Bgr, Byte> img = new Image<Bgr, Byte>(bmp); //where bmp is a Bitmap

Automatic Garbage Collection

The Image class will automatically take care of the memory management and garbage collection.

Once the garbage collector decided that there is no more reference to the Image object, it will call the Disposed method, which involves the DisposeObjects method that release the unmanaged IplImage with a call to the CvInvoke.cvReleaseImage function.

The time of when garbage collector decides to dispose the image is not guaranteed. When working with large image, it is recommend to call the Dispose() method to explicitly release the object. Alternatively, use the using keyword in C# to limit the scope of the image

using (Image<Gray, Single> image = new Image<Gray, Single>(1000, 800))
{
   ... //do something here in the image
} //The image will be disposed here and memory freed

Getting or Setting Pixels

  • Suppose you are working on an Image<Bgr, Byte>. You can obtain the pixel on the y-th row and x-th column by calling
Bgr color = img[y, x];
  • Setting the pixel on the y-th row and x-th column is also simple
img[y,x] = color;

Methods

Naming Convention

  • Method XYZ in Image< Color, Depth> class corresponse to the OpenCV function cvXYZ. For example, Image< Color, Depth>.Not() function corresponse to cvNot function with the resulting image being returned.
  • Method _XYZ is usually the same as Method XYZ except that the operation is performed inplace rather than returning a value. For example, Image< Color, Depth>._Not() function performs the bitwise inversion inplace.

Operators Overload

The operators + - * / has been overloaded (version > 1.2.2.0) such that it is perfectly legal to write codes like:

Image<Gray, Byte> image3 = (image1 + image2 - 2.0) * 0.5;

Generic Operation

One of the advantage of using Emgu CV is the ability to perform generic operations.

It's best if I demonstrate this with example. Suppose we have an grayscale image of bytes

 Image<Gray, Byte> img1 = new Image<Gray, Byte>(400, 300, new Gray(30));

To invert all the pixels in this image we can call the Not function

 Image<Gray, Byte> img2 = img1.Not();

As an alternative, we can also use the generic method Convert available from the Image< Color, Depth> class

 Image<Gray, Byte> img3 = img1.Convert<Byte>( delegate(Byte b) { return (Byte) (255-b); } );

The resulting image img2 and img3 contains the same value for each pixel.

At first glance it wouldn't seems to be a big gain when using generic operations. In fact, since OpenCV already has an implementation of the Not function and performance-wise it is better than the generic version of the equivalent Convert function call. However, there comes to cases when generic functions provide the flexibility with only minor performance penalty.

Let's say you have an Image<Gray, Byte> img1 with pixels set. You wants to create a single channel floating point image of the same size, where each pixel of the new image, correspond to the old image, described with the following delegate

 delegate(Byte b) { return (Single) Math.cos( b * b / 255.0); }

This operation can be completed as follows in Emgu CV

 Image<Gray, Single> img4 = img1.Convert<Single>( delegate(Byte b) { return (Single) Math.cos( b * b / 255.0); }  );

The syntax is simple and meaningful. On the other hand, this operation in OpenCV is hard to perform since equivalent function such as Math.cos is not available.

Drawing Objects on Image

The Draw( ) method in Image< Color, Depth> can be used to draw different types of objects, including fonts, lines, circles, rectangles, boxes, ellipses as well as contours. Use the documentation and intellisense as a guideline to discover the many functionality of the Draw function.

Color and Depth Conversion

Converting an Image< Color, Depth> between different colors and depths are simple. For example, if you have Image<Bgr, Byte> img1 and you wants to convert it to a grayscale image of Single, all you need to do is

 Image<Gray, Single> img2 = img1.Convert<Gray, Single>();

Displaying Image

Using ImageBox

Emgu CV recommand the use of ImageBox control for display purpose. The reasons are

  • ImageBox is a high performance control for displaying image. Whenever possible, it display a Bitmap that shared memory with the Image object, therefore no memory copy is need (very fast).
  • The user will be able to exam the image pixel values, video frame rates, color types when the image is being displayed.
  • It gives the end user the convenience to perform simple image operation with just a few mouse click.

To find out more about ImageBox, go to this page

Converting to Bitmap

The Image class has a ToBitmap() function that return a Bitmap object, which can easily be displayed on a PictureBox control using Windows Form.

XML serialization

One of the future of Emgu CV is that Image< Color, Depth> can be XML serializated. You might ask why we need to serialization an Image. The answer is simple, we wants to use it in a web service!

Since the Image< Color, Depth> class implements ISerializable, when you work in WCF (Windows Communication Fundation), you are free to use Image< Color, Depth> type as parameters or return value of a web service.

This will be ideal, for example, if you are building a cluster of computers to recognize different groups of object and have a centre computer to coordinate the tasks. I will also be useful if your wants to implement remote monitoring software that constantly query image from a remote server, which use the Capture class in Emgu CV to capture images from camera.

Working with Matrix

Depth as Generic Parameter

A Matrix is defined by its generic parameters depth. To create a 32bit floating point matrix, in Emgu CV it is done by calling

Matrix<Single> matrix = new Matrix<Single>( width, height);

Error Handling

For version >= 1.2.2.0, when OpenCV encounter an error, instead of popping up an error dialogue, it will throw an CvException which encapsulate all the error message.

Code Documentation

Xml Documentation

Documentation is embedded in the code using xml format, which can then be compiled as HTML documentation using Sandcastle.

Intellisense in Visual Studio

If you are using Visual Studio as your development tools, you will have intellisense support when developing Emgu CV applications. For example, if you wants to create an image directly using cvCreateImage function, which is wrapped by the CvInvoke Class, just type CvInvoke.

and a list of functions belongs to CvInvoke class is displayed along with a description for each of the function. Since you are creating an image, select the cvCreateImage function

The list of parameters for this function will be displayed as well as a description for each of the parameters.

Examples

Hello, World

We will start by the Hello World sample, written in C#

using Emgu.CV;
using Emgu.CV.CvEnum;

...

String win1 = "Test Window"; //The name of the window
CvInvoke.cvNamedWindow(win1); //Create the window using the specific name

Image<Bgr, Byte> img = new Image<Bgr, byte>(400, 200, new Bgr(255, 0, 0)); //Create an image of 400x200 of Blue color
MCvFont f = new MCvFont(FONT.CV_FONT_HERSHEY_COMPLEX, 1.0, 1.0); //Create the font

img.Draw("Hello, world", ref f, new Point2D<int>(10, 80), new Bgr(0, 255, 0)); //Draw "Hello, world." on the image using the specific font

CvInvoke.cvShowImage(win1, img); //Show the image
CvInvoke.cvWaitKey(0);  //Wait for the key pressing event
CvInvoke.cvDestroyWindow(win1); //Destory the window

The above code will create an image of 400x200 with blue background color and the String "Hello, world" in green on the foreground. The image will be displayed a window named "Test Window".

Hough Line and Circle Detection

The following demonstrate how to perform Hugh Line and Circle detection using Emgu CV, the image used is the "suff.jpg" file from the OpenCV sample folder.

File:OpenCVStuff.jpg
stuff.jpg from opencv


//Load the image from file
using (Image<Bgr, Byte> stuff = new Image<Bgr, byte>("stuff.jpg"))
{
// returns vectors of circles for each channel
Circle<float>[][] circles = stuff.HughCircles(
                new Bgr(200.0, 200.0, 200.0), //canny threshold 
                new Bgr(100.0, 100.0, 100.0), //canny threshold linking
                8.0, //Resolution of the accumulator 
                1.0, //min distance 
                0, //min radius
                0 //max radius
                );

// returns vectors of lines for each channel
LineSegment2D<int>[][] lines = stuff.HughLines(
                new Bgr(50.0, 50.0, 50.0), //canny threshold 
                new Bgr(200.0, 200.0, 200.0), //canny threshold linking
                1, //Distance resolution in pixel-related units
                Math.PI / 180.0, //Angle resolution measured in radians.
                30, //threshold
                50, //min Line width
                10 //gap between lines
                );

for (int i = 0; i < stuff.Color.Dimension; i++)
{
    //set the color of the channel
    Bgr channelColor = new Bgr();
    channelColor.Coordinate[i] = 255.0;

    //draw the circles detected from the specific channel using its color
    foreach (Circle<float> cir in circles[i])
        stuff.Draw<float>(cir, channelColor, 1);

    //draw the lines detected from the specific channel using its color
    foreach (LineSegment2D<int> line in lines[i])
        stuff.Draw(line, channelColor, 1);
}

//display the image
pictureBox1.Image = stuff.ToBitmap();

The result of running the above code is as follows:

File:HughLineAndCircleDetection.PNG
Result of circle and line detection