|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Announcements
Chapters
Services
Feature Zones
|
IntroductionIf the first thing you want to do is try out some examples please start with reading the Install section. This is my first contribution for this great forum and I hope the wrapper I'm introducing will be useful for those who are friends of the C# language and are occupied in the area of image processing. Please excuse my mistakes (especially with English grammar) but I will be grateful for references and suggestions. I anticipate that the presented library is not yet complete. With sufficient interest I will gladly extend the missing functions and descriptions. Some years ago I decided to change from C++ (MSVS 6.0) and Java (Eclipse) to C#. I was surprised at how easy it was to pick up, even for understanding complex tasks. At the same time I was seized by the desire to port some of my older C++ image processing projects where I used parts of Intel's OpenCV Library. Porting my software wasn't the hardest obstacle, but I wasn't familiar with interop mechanism which was need to adopt Intel's library. After searching the net, I found two possible solutions. On one side was SharperCV and on the other EmGuCv. Both projects are quite extensive and cover a rich set on functionality and I have to acknowledge that I got many hints from it and also used some small pieces of code. The underlying reason why I decided to write my own library was to learn more about Platform invoke (P/invoke) and to create a library which is usable, where one one can write method calls in a "c-style" manner. Ideally it should take as little effort as possible for a user to learn somthing about the wrapper and it should be sufficient to only read the original OpenCV documentation and of course some of my documentation. Some Features
The OpenCV Library has a very extensive amount of highly developed algorithms (more than 400 functions, hundreds of macros, myriads of constants etc.) so it seemed nearly impossible for a single person to encapsulate all of this. Additionally, there is a high probability of overloading and default values. The idea was to write a simple HTML parser which, one one hand was able to extract all function prototypes and descriptions from the OpenCV HTML documentation, and on the other able to generate the necessary code by using predefined rules. With this, I could save a lot of time but because I hadn't the time to write a perfect converter and the resulting code was not perfect, much additional handwork was necessary. Environment and IDE
Content of the Downloads
Installation
Using of the Library - A Short TutorialThe example illustrates how to implement a simply canny edge detector with its image output in a picture box control and a OpenCV image window. It will then later being extended with error handling, a track bar and it will be shown how to retrieve mouse coordinates from an external OpenCV window. Your first step is to create a windows form application and add the DLL's using a directive as described above. Add two buttons and a picture box control to your form. The one button is simply to exit the application the other is intended to load a image file in your application. Add an "openFileDialog" Control from the Toolbox. Create the event handler for the buttons (by double clicking on the control). In the "Open File" button eventhandler add the following code: private void buttonFile_Click(object sender, EventArgs e)
{
IplImage img;
if (openFileDialog1.ShowDialog() == DialogResult.OK)
{
// Load image from file
img = cvlib.CvLoadImage(openFileDialog.FileName, cvlib.CV_LOAD_IMAGE_COLOR);
// Create external window
cvlib.CvNamedWindow("MyWindow", cvlib.CV_WINDOW_AUTOSIZE);
// Show the image in external Window
cvlib.CvShowImage( "MyWindow", ref img );
// Show the image additional in the Picture Box Control
this.pictureBox1.Image = (Bitmap)img;
}
}
It is assumed that the Visual Studio Code Generator adds the name "openFileDialog1" for the file dialog. As you can see its quite simple to use the library calls. For objects which will be read or written it is necessary to use the "ref" keyword. Maybe you noticed that I didn't take care to release the unmanaged memory (and of course didn't dispose the bitmap image). You are responsible to do this any time the image has to be released (or any other unmanaged memory) by using the desired calls (see below). In the next step we will add the canny edge detector. But to achieve this we first have to create a grey level image and apply a colour conversion. private void buttonFile_Click(object sender, EventArgs e)
{
IplImage img, gray;
if (openFileDialog1.ShowDialog() == DialogResult.OK)
{
// Load image from file
img = cvlib.CvLoadImage(openFileDialog.FileName, cvlib.CV_LOAD_IMAGE_COLOR);
// create grey channel image
gray = cvlib.CvCreateImage(new CvSize(img.width, img.height),
(int)cvlib.IPL_DEPTH_8U, 1);
// color conversion
cvlib.CvCvtColor(ref img, ref gray, cvlib.CV_BGR2GRAY);
// apply the operator
cvlib.CvCanny(ref gray, ref gray, 100, 100, 3);
// Create external window
cvlib.CvNamedWindow("MyWindow", cvlib.CV_WINDOW_AUTOSIZE);
// Show the edge image in external Window
cvlib.CvShowImage( "MyWindow", ref gray );
// Show the input image in the Picture Box Control
this.pictureBox1.Image = (Bitmap)img;
}
}
Did it work? I hope. Finally, let's add a track bar control to the external window and receive mouse events and display the mouse coordinates in a label. Add a label control for the text output anywhere on your form window. Because the C# language uses so called delegates for receiving events we must follow this conventions. In the presented cvlib.dll predefined delegates are already implemented, so one has only to instantiate it. We do this by adding two members and allocate it in the forms constructor. public partial class Form1 : Form
{
...
private cvlib.OnTrackbarChangeCallback onChange;
private cvlib.OnMouseCallback onMouse;
...
public Form1()
{
InitializeComponent();
...
onChange = new cvlib.OnTrackbarChangeCallback(OnChange);
onMouse = new cvlib.OnMouseCallback(OnMouse);
...
}
...
}
Now we can add eventhandler for the callbacks. The names for the handlers (method names) are not restricted. Feel free to select a name of your choice. In the example I used " More important than the names are the parameter lists for the handlers. Either you watch the line that will pop up when you typed the opening brace (in Visual Studio with intellisense on) or you look at the OpenCV documentation. // this Method is called when Trackbar value changed
private void OnChange(int value)
{
// Set the labels text with trackbar value
labelValue.Text = value.ToString();
}
// This Method is called when the mouse is moved around the child window
private void OnMouse(int evnt, int x, int y, int flags, IntPtr param)
{
labelValue.Text =
"Event: " + evnt.ToString() +
"\nx=" + x.ToString() +
"\ny=" + y.ToString() +
"\nFlags: " + flags.ToString();
}
Extend your File Dialog Method now with the following lines // Position of the trackbar
int value = 0;
if (openFileDialog1.ShowDialog() == DialogResult.OK)
{
...
// Create external window
cvlib.CvNamedWindow("MyWindow", cvlib.CV_WINDOW_AUTOSIZE);
// Create the trackbar
cvlib.CvCreateTrackbar("TrackbarName", "MyWindow", ref value, 100,
onChange); // <-- add
// Set the mouse callback eventhandler
cvlib.CvSetMouseCallback("MyWindow", onMouse, IntPtr.Zero); // <-- add
...
}
The "value" variable should be updated every time the trackbar's value has been changed. For me this doesn't work correctly. The identifier for the callback routines are passed as parameters without the <ref> keyword. DLL Calls within the Wrapper and Exported FunctionsMost of my internal DLL-calls follow simple rules. If the called function returns a structure pointer it is converted to the desired structure definition by using the // a array with doubles
double[] a = { 1.1, 2.2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 };
// create a matrix with 3 rows and 4 columns
CvMat ma = cvlib.CvCreateMat(3, 4, cvlib.CV_64FC1);
// as long as the handle (h) is not disposed,
// the garbage collector will not touch the double array
GCHandle h;
// fill our matrix with data
cvlib.CvInitMatHeader(ref ma, 3, 4, cvlib.CV_64FC1, cvtools.Convert1DArrToPtr(a, out h),
cvlib.CV_AUTO_STEP);
//...
// do anything with the matrix
//...
// dispose
cvtools.ReleaseHandel(h);
The cvtool ClassBy using the methods of the
OverloadingOne of the biggest problems is the possibility of overloading OpenCV functions due to the number of combinations of structure types and default values. So I decided to only support a few combinations and added partial overloading for default values. Maybe I will build a better support for the ubiquitous Error HandlingThe OpenCV Library offers two major, different methods to get information if an error occurs. The first way is to ask for the Error status, or you get informed about errors within a callback. Furthermore, there is a way to distinguish whether you want to continue program execution or suspend the application. It is important to know, that, when the program is internally terminated some unwanted behaviour can occur and of course this can also happen if you continue because some values are undefined. OpenCV does not support exceptions. So if you put a try-catch block and a error happens in a function call, a message box pops up by default and depending on which button you choose (cancel, try again, ignore) the application exits or crashes in most cases and the try-catch block is ignored. The first decision you have to make is regarding the error mode. You have three options (some text copied from OpenCV-documentation):
If you use Silent mode you may check after calling a function with The following code snippet illustrates the variants (using the example from above). public partial class Form1 : Form
{
// define the error delegate member
cvlib.OnErrorCallback onError;
// Constructor
public Form1()
{
InitializeComponent();
// create a instance and pass your error handler methode name
onError = new cvlib.OnErrorCallbackDelegate(OnError);
// advise the CvLib to work in Parent mode (call error handler but dont exit)
cvlib.CvSetErrMode(cvlib.CV_ErrModeParent);
// redirect the error to use your error handler against the predefind handler
// (Message Box)
cvlib.CvRedirectError(onError);
}
private void buttonFile_Click(object sender, EventArgs e)
{
...
if (openFileDialog1.ShowDialog() == DialogResult.OK)
{
...
// apply the operator
cvlib.CvCanny(ref gray, ref gray, 100, 100, 3);
// check for error (variant 1)
if (cvlib.CvGetErrStatus() != 0)
{
// do some clean up
cvlib.CvReleaseImage(ref img);
// !! important set error status back
cvlib.CvSetErrStatus(0);
// return for example or throw exeption
return;
}
}
...
}
// your error handler
private int OnError(int status, string func_name,
string err_msg, string file_name, int line)
{
MessageBox.Show(
"Status: " + cvlib.CvErrorStr(status) +
"\nIn Function: " + func_name +
"\nMessage: " + err_msg +
"\nIn File: " + file_name +
"\nOn Line: " + line.ToString(), "CV-Error", MessageBoxButtons.OK,
MessageBoxIcon.Error);
// a return value > 0 means application is suspended, avoid this!!
return 0;
}
Low Level OperationsLow level operations are illustrated in the next sample. It will be shown how to access and manipulate either Image/Matrix data by using macros or by directly accessing the raw data by using unsafe code. For unsafe operations it is necessary to turn on this option in Visual Studio. You can do this by clicking the check on the "allow unsafe code" checkbox in the build tab of your project options. For accessing raw data the OpenCV Library offers some macros. This are:
Here are some examples of how to read/write Data by using one of the macros. // Create a matrix
CvMat m = cvlib.CvCreateMat(3, 3, cvlib.CV_64FC1);
// set data
cvlib.CvSetReal2D(ref m, 0, 0, 12.4);
cvlib.CvSetReal2D(ref m, 0, 1, 2.4);
...
// read data
double res = cvlib.CvGetReal2D(ref m, 0, 0);
The next example shows how to colourize an image area by using unsafe access. Suppose we have a colour image (IplImage image) and want to fill a rectangular area with top left at P(10, 10) and width and height = (100, 100). The image data base address is given by the imageData unsafe
{
...
// get base address
int baseAddress = image.imageData.ToInt32();
// iterate
for (int x = 10; x < 100; x++)
{
for (int y = 10; y < 100; y++)
{
byte* dst = &(((byte*)(baseAddress + image.widthStep * y))[x * 3]);
dst[0] = 0; dst[1] = 0; dst[2] = 255;
}
}
...
}
Table with Conversions for Interop - p/Invoke which has Been UsedThe table shows the used analogies for declaration of OpneCV API types in C#. It is regarded only to the arguments in method calls for the OpenCV library and has not been tested for common Windows API calls. What is explained is HOW the arguments are passed and gives hints when the
Microsoft describes the analogies in the .NET Framework documentation under the Kewords "Programing with with .NET Framework", "Communication with unmanaged code", "Interop-Marshalling", Marshalling data with Platform invoke" and "Datatypes for Platform invoke". Available Functions in the Wrapper Library
To DoThere is so much to-do. Any help is always welcome!
The "Big-example" ApplicationFeatures
The presented application is intended for reference or to be a good starting place to build your own Application. Inside the mainForm.cs file all properties and methods are arranged by regions. Within Visual Studio IDE you should first collapse all definitions to get a good overview. Most importantly, definitions are:
Under "Process Run" you will find the eventhandler that is executed when the "Run" button was pushed. The if-constructs check for the actual selected entry in the 'operations' combo-box and branches to the corresponding image processing method. The 'operations' combo-box For every image processing operation I wrote a separeted function or class placed in different files. So it is simple to find the corresponding code. Each of the functions takes only one parameter, the actual, loaded image in IPL format. Depending on the selected mode the resulting image of an operation will be displayed in a new tab or the actual tab page. History
| |||||||||||||||||||||||||||||||||||||||||||||||||||||