Escolar Documentos
Profissional Documentos
Cultura Documentos
Creating Image
Although it is possible to create image by calling CvInvoke.cvCreateImage, it is suggested to construct a Image<TColor, TDepth> object instead. There are several advantages using the managed Image<TColor, TDepth> class Memory is automatically released by the garbage collector Image<TColor, TDepth> class can be examined by Debugger Visualizer Image<TColor, TDepth> class contains advanced method that is not available on OpenCV, for example, generic operation on image pixels, conversion to Bitmap etc.
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. The types of depth supported in Emgu CV 1.4.0.0 include Byte SByte
2
Single (float) Double UInt16 Int16 Int32 (int)
Bgr color = img[y, x]; Setting the pixel on the y-th row and x-th column is also simple img[y,x] = color;
3
You would do this: Byte byCurrent = imageGray.Data[x, y, 0];
Methods
Naming Convention
Method XYZ in Image<TColor, TDepth> class corresponds to the OpenCV function cvXYZ. For example, Image<TColor, TDepth>.Not() function corresponds 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<TColor, TDepth>._Not() function performs the bit-wise 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 an 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<TColor, TDepth> class Image<Gray, Byte> img3 = img1.Convert<Byte>( delegate(Byte b) { return (Byte) (255b); } ); 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.
4
Converting an Image<TColor, TDepth> 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 recommends the use of ImageBox control for display purpose, for the following reasons ImageBox is a high performance control for displaying image. Whenever possible, it displays a Bitmap that shares memory with the Image object, therefore no memory copy is needed (very fast). The user will be able to examine the image pixel values, video frame rates, color types when the image is being displayed. It is convenient to perform simple image operations with just a few mouse clicks.
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
Why do I care?
One of the future of Emgu CV is that Image<TColor, TDepth> 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<TColor, TDepth> class implements ISerializable, when you work in WCF (Windows Communication Fundation), you are free to use Image<TColor, TDepth> 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 central 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.
Conversion to XML
You can use the following code to convert an Image<Bgr, Byte> image to XmlDocument: StringBuilder sb = new StringBuilder(); (new XmlSerializer(typeof(Image<Bgr, Byte>))).Serialize(new StringWriter(sb), o); XmlDocument xDoc = new XmlDocument(); xDoc.LoadXml(sb.ToString());
5
Matrix<Single> matrix = new Matrix<Single>(width, height);
Matrix Depth
The types of depth supported in Emgu CV 1.4.0.0 include Byte SByte Single (float) Double UInt16 Int16 Int32 (int)
XML Serialization
Conversion to XML
You can use the following code to convert an Matrix<double> matrix to XmlDocument: StringBuilder sb = new StringBuilder(); (new XmlSerializer(typeof(Matrix<double>))).Serialize(new StringWriter(sb), o); XmlDocument xDoc = new XmlDocument(); xDoc.LoadXml(sb.ToString());
6
//Show the image CvInvoke.cvShowImage(win1, img.Ptr); //Wait for the key pressing event CvInvoke.cvWaitKey(0); //Destory the window CvInvoke.cvDestroyWindow(win1); } The above code will create an image of 400x200 with blue background color and the text "Hello, world" in green on the foreground. The image will be displayed a window named "Test Window".
Introduction
The following article is designed to show new comers to EMGUcv how to set up a project step by step. This article is designed to make the process a little more user friendly. EMGU is a c# wrapper for OpenCV it differs from other wrappers as it written purely in c# and does not use unsafe code. EMGU opens the OpenCV (Open Source Computer Vision Library) library of programming functions mainly aimed at real time computer vision to C# developers. OpenCV was originally developed by Intel and now supported by Willow Garage. Current versions for both x86 and x64 architectures are available to download at their Sourceforge website. As EMGU is a wrapper for c++ code there are two types of Dynamic-link librarys (DLLs) that are used. There are the EMGU ones with EMGU syntax always reference in the name and the opencv ones that vary. Setting up a first project is a common stumbling block for many newcomers and you are not alone. If you have downloaded the source code you will need to read A Basic Program. If you have copied an example from the EMGU extraction folder then take a look at The EMGU.CV.Invoke Exception and Troubleshooting section.
Assumed Knowledge
It is assumed that a user has basic experience in c# and can generate a new c# project. It is assumed that each user has download the most recent update for their platform and has the HelloWorld Example running from the EMGU extraction folder\EMGU.Examples\Hello World.
Or alternatively using the menu item Project > Add Reference. When the Add Reference window opens select the DLLs listed above and click OK.
You will now see them listed in the References folder in the solution explorer window. These three DLLs are the EMGU specific c# libraries mentioned previously. These alone will not allow you to use any of the image processing functions so please read the rest of the article. Now you need to reference these in any class of form that you will be using the code. The references you will use will depend on what you are doing in image processing terms. Look at the examples and these will have the ones your require. To get you started add the following to the top of the Form1.cs code behind. using Emgu.CV; using Emgu.Util; using Emgu.CV.Structure;
You will now be able to see your files within the solution explorer window. You will need to change there properties so select them both by holding down the Ctl key and left clicking on them (alternatively you can do this individually). Now look at Properties window, you will see 6 fields two of these will be filled with content. You are interested in the Copy to Output Directory. Change this from Do not Copy to Copy always.
If you are using the x64 compilations go to the x64 section and ensure you set up you project to compile to a x64 architecture other than that you are ready to start image processing. The reason this is the preferred method is that now, if you change from Debug to Release these files will always be available to your program and no errors will occur. Jump to the reading and displaying an image section A Basic Program to start you off.
10
Hint: If you are using the express version of visual studio you may not see the x64 option in such a case go to menu option Tools > Options. In this window using the arrows to the left hand side to expand and collapse options. Select Projects and Solutions and select the Show advanced build configurations check box.
This will now allow the compilation to run if this is not done correctly. As soon as you access any EMGU code an exception will be thrown EMGU.CV.Invoke through an exception with the InnerException "An attempt was made to load a program with an incorrect format.....
A Basic Program
To get you started a simple program that loads an image and displays it in a picture box has been provided and a little bit more of an advanced one that will show how to access image data and convert between image types. Only x64 Versions are currently available, x86 will be provided shortly. If you have downloaded the sample code you will start with 3 warnings for the references not being found. Expand the References folder within the solution explorer delete the 3 with yellow warning icons and Add fresh references to them, the steps of which are available The Basic Requirements section. There has been a button item and a picturebox item added to the main form. There default names have not been changed. When we click on the button we wish to open a file dialog select and image and have it displayed in the picturebox. Double click on the button and add the following code: private void button1_Click(object sender, EventArgs e) { OpenFileDialog Openfile = new OpenFileDialog(); if (Openfile.ShowDialog() == DialogResult.OK) { Image<Bgr, Byte> My_Image = new Image<Bgr, byte>(Openfile.FileName); pictureBox1.Image = My_Image.ToBitmap(); }
11
}
The code is very simple an OpenFileDialog called 'Openfile' is used to select and image file. This image is then read into an colour Image object called 'My_Image'. The image is displayed by assigning the Image property of the picturebox. This requires a Bitmap and by calling the .ToBitmap() function of 'My_Image' this is achieved. This is incredibly simple to achieve once the correct process has been taken in setting up the project. An alternative to the Picturbox item is made available through the EMGU.CV.UI library and is used in the examples. Please visit http://www.emgu.com/wiki/index.php/Add_ImageBox_Control to learn how to add this control to visual studio should you wish to use it.
12
Color R = Color.Red; My_Image.Data[0,0,2] = R.R; //Write to the Red Spectrum My_Image.Data[0,0,1] = R.G; //Write to the Green Spectrum My_Image.Data[0,0,0] = R.B; //Write to the Blue Spectrum //Gray Image gray_image[0, 0] = new Gray(200);
So writing to a pixel is fairly simple but what about reading a pixel value. //Colour Image Bgr my_Bgr = My_Image[0, 0]; //Gray Image Gray my_Gray = gray_image[0, 0];
Now in many cases you will not want to work with Bgr or Gray so converting them is important. //BGR to Color Color my_colour = Color.FromArgb((int)value.Red, (int)value.Blue, (int)value.Green); //Gray to int int my_intensity = (int) my_Gray.Intensity;
You will notice that each value is cast to an integer to allow data loss this is because the intensities are stored naturally as doubles. However in this case the easier method is accessing the Image Data property. If you wish to work with the image data there is not a requirement to constantly convert between Gray and integers etc. You can access the image Data directly and use that. //Colour Color my_colour = Color.FromArgb(My_Image.Data[0, 0, 0], My_Image.Data[0, 0, 1], My_Image.Data[0, 0, 2]); //Gray Image int my_intensity = gray_image.Data[0, 0, 0];
Much simpler and far easier to work with when processing the image Data within a loop. To examine how to implement a loop please download the Little More Image Processing source code.
13
If the exception is a EMGU.CV.Invoke Exception, chances are you are not targeting the correct platform for your build. See the section regarding x64 Architecture for details. Now if youve progressed through to an advanced image processing method or simply have copied an example to an alternative location than the EMGU extraction folder you will possibly be seeing one of the following errors.
Unable to load DLL 'opencv_highgui220': The specified module could not be found. (Exception from HRESULT: 0x8007007E)
Usually caused when you are trying to get a image from a web camera. The solution to this is extremely simple. As discussed in The Basic Requirements section you will need to either copy theopencv_highgui220.dll to the output directory or the more preferred method of adding the file to your project as an existing file and then changing its property Copy to Output Directory to Copy always. Look back at this section for reference if required.
Unable to load DLL 'cvextern': The specified module could not be found. (Exception from HRESULT: 0x8007007E)
This error has a little more of a substantial solution. The cvextern.dll error can not be fixed by simply adding the this one file in fact this file requires all of the files listed bellow to correctly execute. As discussed in The Basic Requirements section you will need to either copy files to the output directory or the more preferred method of adding the files to your project as an existing file and then changing each of their properties Copy to Output Directory to Copy always. Look back at this section for reference if required. Note that the "220" is the version number this will change according to updates. cudart64_32_16.dll cufft64_32_16.dll cvextern.dll npp64_32_16.dll opencv_calib3d220.dll opencv_contrib220.dll opencv_core220.dll opencv_features2d220.dll opencv_flann220.dll opencv_gpu220.dll opencv_highgui220.dll opencv_imgproc220.dll opencv_legacy220.dll opencv_ml220.dll opencv_objdetect220.dll opencv_video220.dll
Any other Unable to load DLL ... errors can be fixed by attempting the same process an loading the relevant listed DLLs into your project. If you still get stuck than feel free to ask the community for help.
14
Shape Detection
In this tutorial, we demonstrate how to perform Hough Line and Circle detection using Emgu CV, as well as using the Contour class to detect Triangles and Rectangles in the image. The "pic3.png" file from the OpenCV sample folder is used here.
Source Code
//Load the image from file Image<Bgr, Byte> img = new Image<Bgr, byte>(fileNameTextBox.Text).Resize(400, 400, true); //Convert the image to grayscale and filter out the noise Image<Gray, Byte> gray = img.Convert<Gray, Byte>().PyrDown().PyrUp();
15
Gray cannyThreshold = new Gray(180); Gray cannyThresholdLinking = new Gray(120); Gray circleAccumulatorThreshold = new Gray(120); CircleF[] circles = gray.HoughCircles( cannyThreshold, circleAccumulatorThreshold, 5.0, //Resolution of the accumulator used to detect centers of the circles 10.0, //min distance 5, //min radius 0 //max radius )[0]; //Get the circles from the first channel Image<Gray, Byte> cannyEdges = gray.Canny(cannyThreshold, cannyThresholdLinking); LineSegment2D[] lines = cannyEdges.HoughLinesBinary( 1, //Distance resolution in pixel-related units Math.PI / 45.0, //Angle resolution measured in radians. 20, //threshold 30, //min Line width 10 //gap between lines )[0]; //Get the lines from the first channel #region Find triangles and rectangles List<Triangle2DF> triangleList = new List<Triangle2DF>(); List<MCvBox2D> boxList = new List<MCvBox2D>(); using (MemStorage storage = new MemStorage()) //allocate storage for contour approximation for (Contour<Point> contours = cannyEdges.FindContours(); contours != null; contours = contours.HNext) { Contour<Point> currentContour = contours.ApproxPoly(contours.Perimeter * 0.05, storage); if (contours.Area > 250) //only consider contours with area greater than 250 { if (currentContour.Total == 3) //The contour has 3 vertices, it is a triangle { Point[] pts = currentContour.ToArray(); triangleList.Add(new Triangle2DF( pts[0], pts[1], pts[2] )); } else if (currentContour.Total == 4) //The contour has 4 vertices. { #region determine if all the angles in the contour are within the range of [80, 100] degree bool isRectangle = true; Point[] pts = currentContour.ToArray(); LineSegment2D[] edges = PointCollection.PolyLine(pts, true); for (int i = 0; i < edges.Length; i++) { double angle = Math.Abs( edges[(i + 1) % edges.Length].GetExteriorAngleDegree(edges[i])); if (angle < 80 || angle > 100) {
16
isRectangle = false; break; } } #endregion if (isRectangle) boxList.Add(currentContour.GetMinAreaRect()); } } } #endregion originalImageBox.Image = img; #region draw triangles and rectangles Image<Bgr, Byte> triangleRectangleImage = img.CopyBlank(); foreach (Triangle2DF triangle in triangleList) triangleRectangleImage.Draw(triangle, new Bgr(Color.DarkBlue), 2); foreach (MCvBox2D box in boxList) triangleRectangleImage.Draw(box, new Bgr(Color.DarkOrange), 2); triangleRectangleImageBox.Image = triangleRectangleImage; #endregion #region draw circles Image<Bgr, Byte> circleImage = img.CopyBlank(); foreach (CircleF circle in circles) circleImage.Draw(circle, new Bgr(Color.Brown), 2); circleImageBox.Image = circleImage; #endregion #region draw lines Image<Bgr, Byte> lineImage = img.CopyBlank(); foreach (LineSegment2D line in lines) lineImage.Draw(line, new Bgr(Color.Green), 2); lineImageBox.Image = lineImage; #endregion
Result
17
In the last article we read about Starting with Emgu CV, now here we will start our first Emgu CV project. Emgu CV is not so difficult; all we have to do is to add certain references and make use of Emgu Controls. Let's get started. Let's start creating a blank Windows Forms application.
You will get a new project created for you, before starting further enable "show all settings" under tools, this enables many features like form designer layout, snap to grid and many others.
Now let's start. The first thing we will do is to Add References. Browse for the Emgu bin folder (by default it is located at C:\Emgu\emgucv-windows-x86 2.3.0.1416\bin ), in the bin folder there must be some dlls add all those starting with "Emgu.CV" (choose only one among Emgu.CV.DebuggerVisualizers.VS2008.dll and Emgu.CV.DebuggerVisualizers.VS2010.dll depending on the Visual Studio you are using, in my case it is Emgu.CV.DebuggerVisualizers.VS2010.dll)
18
Now we need to add some existing items. Go to "Project" > "Add Existing Items" and now again browse for the bin directory of Emgu CV and this time choose all the dll files starting with "opencv_", these dll files are required each time the output is generated via Emgu; that is why we added them to our project directory, we will also change there the property so that they get copied always to the output folder. So, select all the DLLs added and select properties and change the "Copy to Output Directory" to "Copy Always".
We already have added the Emgu custom controls to our toolbox, now let's design our form, we will be using two ImageBox (Emgu Control), one Button and a Textbox, design the form as below.
19
Coding Now go to the form1.cs code view, and add the following namespaces: using Emgu.CV; using Emgu.CV.CvEnum; using Emgu.CV.Structure; using Emgu.CV.UI; Next create some member variables : Capture capturecam = null; //instance for capture using webcam bool CapturingProcess = false; //boolean stating the capturing process status Image<Bgr, Byte> imgOrg; //image type RGB (or Bgr as we say in Open CV) Image<Gray, Byte> imgProc; //processed image will be grayscale so a gray image Now it's time to add a "form load event", we will start capturing via webcam under it. capturecam = new Capture(); This will associate the default webcam with the capturecam object.
20
We have added the association to the capture object in the try/catch block to avoid the error if the webcam is already in use. We added an event handler to "Application.Idle", so that it performs the task when the application is idle, and the task is to get the next frame. "ProcessFunction" is called at every idle state of the application. "QueryFrame" gets the next frame from the webcam, if it is null it means there is some problem and hence we stop our app there. The "InRange" function takes two parameters, the minimum and maximum values of the Bgr range. "SmoothGaussian" applies the Gaussian smoothing with the x,y length = 9, we can pass other parameters for x and y also. Now let's come to the coding part of the "playorpause" button:
21
This is the simplest part, it checks the boolean value of CapturingProcess and changes the button text correspondingly and stops the streaming from the webcam. Sample Output
Source Code
using using using using ... System.Drawing; Emgu.CV.Structure; Emgu.CV.ML; Emgu.CV.ML.Structure;
22
int trainSampleCount = 100; #region Generate the traning data and classes Matrix<float> trainData = new Matrix<float>(trainSampleCount, 2); Matrix<float> trainClasses = new Matrix<float>(trainSampleCount, 1); Image<Bgr, Byte> img = new Image<Bgr, byte>(500, 500); Matrix<float> sample = new Matrix<float>(1, 2); Matrix<float> prediction = new Matrix<float>(1, 1); Matrix<float> trainData1 = trainData.GetRows(0, trainSampleCount >> 1, 1); trainData1.SetRandNormal(new MCvScalar(200), new MCvScalar(50)); Matrix<float> trainData2 = trainData.GetRows(trainSampleCount >> 1, trainSampleCount, 1); trainData2.SetRandNormal(new MCvScalar(300), new MCvScalar(50)); Matrix<float> trainClasses1 = trainClasses.GetRows(0, trainSampleCount >> 1, 1); trainClasses1.SetValue(1); Matrix<float> trainClasses2 = trainClasses.GetRows(trainSampleCount >> 1, trainSampleCount, 1); trainClasses2.SetValue(2); #endregion Matrix<int> layerSize = new Matrix<int>(new int[] { 2, 5, 1 }); MCvANN_MLP_TrainParams parameters = new MCvANN_MLP_TrainParams(); parameters.term_crit = new MCvTermCriteria(10, 1.0e-8); parameters.train_method = Emgu.CV.ML.MlEnum.ANN_MLP_TRAIN_METHOD.BACKPROP; parameters.bp_dw_scale = 0.1; parameters.bp_moment_scale = 0.1; using (ANN_MLP network = new ANN_MLP(layerSize, Emgu.CV.ML.MlEnum.ANN_MLP_ACTIVATION_FUNCTION.SIGMOID_SYM, 1.0, 1.0)) { network.Train(trainData, trainClasses, null, null, parameters, Emgu.CV.ML.MlEnum.ANN_MLP_TRAINING_FLAG.DEFAULT); for (int i = 0; i < img.Height; i++) { for (int j = 0; j < img.Width; j++) { sample.Data[0, 0] = j; sample.Data[0, 1] = i; network.Predict(sample, prediction); // estimates the response and get the neighbors' labels float response = prediction.Data[0,0]; // highlight the pixel depending on the accuracy (or confidence) img[i, j] = response < 1.5 ? new Bgr(90, 0, 0) : new Bgr(0, 90, 0); } } } // display the original training samples for (int i = 0; i < (trainSampleCount >> 1); i++) { PointF p1 = new PointF(trainData1[i, 0], trainData1[i, 1]); img.Draw(new CircleF(p1, 2), new Bgr(255, 100, 100), -1); PointF p2 = new PointF((int)trainData2[i, 0], (int)trainData2[i, 1]); img.Draw(new CircleF(p2, 2), new Bgr(100, 255, 100), -1); } Emgu.CV.UI.ImageViewer.Show(img);
23
Result