Sabtu, 23 Oktober 2010

Combining Intel OpenCV and NI Labview

1 Introduction

NI Labview is a powerful software for instrumentation and data acquisition. It also has complete library for Vision and Image processing (Vision Development Module).

Intel OpenCV is an open source C/C++ based Computer Vision and Image processing library developed by Intel. Despite it’s simplicity, NI Vision Development Module lacks of flexibility and expandability of Intel OpenCV, For example if we want to modify complex image processing algorithm, it is easier to use OpenCV. Due to it’s open source nature, OpenCV also has more advanced and up to date vision algorithms. Below is the set of application areas of the OpenCV :
• 2D and 3D feature toolkits
• Egomotion estimation
• Facial recognition system
• Gesture recognition
• Human-Computer Interface (HCI)
• Mobile robotics
• Motion understanding
• Object Identification
• Segmentation and Recognition
• Stereopsis Stereo vision: depth perception from 2 cameras
• Structure from motion (SFM)
• Motion tracking

In addition, OpenCV also has the following statistical machine learning libraries :
• Boosting
• Decision tree learning
• Expectation-maximization algorithm
• k-nearest neighbor algorithm
• Naive Bayes classifier
• Artificial neural networks
• Random forest
• Support vector machine (SVM)


The main power of OpenCV other than it’s completeness is its speed. Compared to other image processing library such as Mathworks Matlab Image Processing tool box, OpenCV which is based on C/C++ is faster.


In this tutorial, a method to combine Intel OpenCV and NI Labview using Dynamic Link Library is presented. The interfacing between Intel OpenCV and Labview is not a trivial task because it involves a few tricks on pointers and memory management.

The objective is to be able to use any OpenCV library in any Labview project hence we will have the power of both.


This tutorial will be divided into two series, first method using IMAQ array handle, and the second using Array data pointer, the second method is more complex but more general, in this series, only the first method is covered.



2 Interfacing Intel OpenCV and NI Labview with Dynamic Link Library

Dynamic Link Library (DLL) is a file of code, containing functions that can be called from other executable code. (Either an application or another DLL). DLL is a file which does a particular job and allows other program to use it to do that particular job as well. Microsoft Windows systems use DLL, for example comctl32.dll which does all the user interface jobs so other programs do not have to create their own interface.


2.1 Anatomy of DLL


Dynamic linking is mechanism that links applications to libraries at run time. The libraries remain in their own files and are not copied into executable files of the applications. DLL link to an application when the application is run, rather then when it is created. DLLs may contain links to other DLLs.

Below is the the basic structure of a DLL for Microsoft Visual C++ environment:



//==============================================================================
// DLL main entry-point functions

#include <windows.h>

BOOL APIENTRY DllMain(HANDLE hModule,DWORD ul_reason_for_call,LPVOID lpReserved ) // Reserved
{
switch ( ul_reason_for_call )
{
case DLL_PROCESS_ATTACH:
// A process is loading the DLL.
break;
case DLL_THREAD_ATTACH:
// A process is creating a new thread.
break;
case DLL_THREAD_DETACH:
// A thread exits normally.
break;
case DLL_PROCESS_DETACH:
// A process unloads the DLL.
break;
}
return TRUE;
}



In Win32 the DllMain function is the main DLL entry point.

The DllMain function is called when a DLL is loaded or unloaded. The DllMain function is also called when a new thread is being created in a process already attached to the DLL; or when a thread has exited cleanly.

A DLL also contains functions that perform the activities the DLL expects to accomplish. Additionally the EXPORTS section header file (definition files) must be used to export these functions. For example :



__declspec(dllexport) float bcv_calculate(IMAQ_Image *ImageSrc);


2.2 Example – Writing a DLL with Visual C++ (v6.0, .Net 2003)


Here the DLL that communicates between Intel OpenCV and Labview is written. The OpenCV library is compiled with Microsoft Visual C++ as DLL.

To create a DLL the following four files are needed :
• a C/C++ Language source file
• a header file
• a project file
2.2.1 C language Source File

The code on the following page is the C language source file for the DLL using OpenCV classes.

The example DLL defines two simple functions using OpenCV library using two different methods:

First Method : using IMAQ Image pointer (from Labview) which will be converted to IplImage (from OpenCV).

• Calculate_BCV, calculate normalized Between Classes Variance, input:image, output:double values

• Equalization_OpenCV, Perform histogram equalization on the image. input:image, output:image


Second Method : Using Char pointer which will be converted to IplImage (from OpenCV), this is more complex method and will be covered in the next series of this tutorial.

• bcv_calculate_NI, calculate normalized Between Classes Variance, input:image, Line Width, Image Width, Image Height, output:double values.

• Equalization_OpenCV, Perform histogram equalization on the image. input:image, Line Width, Image Width, Image Height, output:image



The key trick is by utilizing the header from National Instruments, called NIVision.h as it has class that has access to the IMAQ classes called Image *. This class has pointer to address member that can be casted to OpenCV classes using ImageInfo *.

In this example, we will create a VI that calls one of these functions.
The source code for the DLL is as follows :

First method :






#include "stdafx.h"
#include <NIVision.h>
#include <CV.h>
#include "opencv_labview.h"
#include <iostream.h>
#include <windows.h>


float bcv_calculate(IMAQ_Image *ImageSrc)

{

ImageInfo *src;
IplImage* imgHistogram = 0;
IplImage* gray= 0;
int maxThreshold;
CvHistogram* hist;
float alpha[256],PT[256],varMax=0,varBetween;
int height,width,step,channels,heightgray,widthgray,stepgray,channelsgray;
uchar *data;
uchar *datagray;
int numberofgreyLevel=256;
float sum,sumB;
int i,j,k,begin=0;
float wB,wF,mB,mF,mT=0,varTotal=0,bcv,mU;
int t,Threshold;
//size of the histogram -1D histogram
int bins = numberofgreyLevel;
int hsize[] = {bins};

//max and min value of the histogram
float max_value = 0, min_value = 0;

//value and normalized value
float value;
int normalized;
int totalnumberPixel;


//ranges - grayscale 0 to 256
float xranges[] = { begin, numberofgreyLevel };
float* ranges[] = { xranges };

if (!ImageSrc) return ERR_NOT_IMAGE;

src = (ImageInfo *) ImageSrc->address;
imaqGetImageSize((Image *)src, &width, &height);
gray = cvCreateImageHeader( cvSize(width, height), IPL_DEPTH_8U, 1);
gray->imageData = (char *) src->imageStart;
gray->widthStep = src->pixelsPerLine;

// get the image data
height = gray->height;
width = gray->width;
step = gray->widthStep;
channels = gray->nChannels;
data = (uchar *)gray->imageData;


//planes to obtain the histogram, in this case just one
IplImage* planes[] = { gray };

//get the histogram and some info about it
hist = cvCreateHist( 1, hsize, CV_HIST_ARRAY, ranges,1);
cvCalcHist( planes, hist, 0, NULL);
cvGetMinMaxHistValue( hist, &min_value, &max_value);

//create an 8 bits single channel image to hold the histogram
//paint it white
imgHistogram = cvCreateImage(cvSize(bins, 50),8,1);
cvRectangle(imgHistogram, cvPoint(0,0), cvPoint(256,50), CV_RGB(255,255,255),-1);

// calculate total number of pixel
totalnumberPixel=0;
for(i=begin;i sum=0;

for(t=begin; t < numberofgreyLevel; t++) sum += t * cvQueryHistValue_1D( hist, t);
//calculate total variance
for(i=begin;i sumB=0;
wB=0;
wF=0;
varMax=0;
Threshold=0;


for(t=begin;t {

wB+=cvQueryHistValue_1D( hist, t);
if (wB==0) continue;
wF=(totalnumberPixel-wB);
if(wF==0)break;
sumB+=(float) (t*cvQueryHistValue_1D( hist, t));
mB=sumB/wB;
mF=(sum-sumB)/wF;




// Calculate Between Class Variance
varBetween = wB * wF * (mB - mF) * (mB - mF);

// Check if new maximum found
if (varBetween > varMax) {
varMax = varBetween;
Threshold = t; }

}

// calculate BCV metric
bcv=varMax/varTotal;

return bcv;
}

// histogram equalization
int Equalization_OpenCV (IMAQ_Image *ImageSrc, IMAQ_Image *ImageDst)
{

ImageInfo *src, *dst;
int width, height;
IplImage *CVImageSrc, *CVImageDst;
if (!ImageSrc) return ERR_NOT_IMAGE;
if (!ImageDst) return ERR_NOT_IMAGE;
src = (ImageInfo *) ImageSrc->address;
dst = (ImageInfo *) ImageDst->address;
imaqGetImageSize((Image *)src, &width, &height);
imaqSetImageSize((Image *)dst, width, height);
CVImageSrc = cvCreateImageHeader( cvSize(width, height), IPL_DEPTH_8U, 1);
CVImageSrc->imageData =(char *) src->imageStart;
VImageSrc->widthStep = src->pixelsPerLine;
CVImageDst = cvCreateImageHeader( cvSize(width, height), IPL_DEPTH_8U, 1);
CVImageDst->imageData =(char *) dst->imageStart;
CVImageDst->widthStep = dst->pixelsPerLine;


// Perform histogram equalization
cvEqualizeHist( CVImageSrc, CVImageDst );


return 0;

}





We need to to declare the function with the __declspec (dllexport) keyword in the header file, otherwise the function will be exported with the mangled name and the actual function name will be unavailable to applications that call the DLL. We also enclose the declarations of the functions to be exported in header file with extern “C” statement.

The header file is defined as :






//==============================================================================
//
// Title: bcv DLL
// Purpose: A header to bcv dll .
//
// Created by : Adhiguna Mahendra.
// Copyright: -. All Rights Reserved.
//
//==============================================================================

#ifndef __bcv_opencv_H__
#define __bcv_opencv_H__

#ifdef __cplusplus
extern "C" {
#endif

//==============================================================================

#include


//==============================================================================
// Constants

//==============================================================================
// Types

typedef struct
{
char *name;
Image *address;
} IMAQ_Image;

//==============================================================================
// External variables

//==============================================================================
// Global functions

// export functions
__declspec(dllexport) float bcv_calculate(IMAQ_Image *ImageSrc);
*ImageDst);
__declspec(dllexport) int Equalization_OpenCV (IMAQ_Image *ImageSrc, IMAQ_Image *ImageDst);


#ifdef __cplusplus
}
#endif

#endif /* ndef __bcv_opencv_H__ */





2.3 building the project


First we need to create the project, add the C and header source code then compile it into DLL.

To compile the DLL successfully, we have to make sure that the precompiled header is deactivated (Project->Settings->All Configurations->C/C++->precompiled headers).




For example we compiled the above codes with the name bcv_opencv.dll.





1. Calling the DLL from Labview.

The DLL can now be called using Labview Call Library Function. We create a VI that calls the bcv_opencv.dll.





We set the library to the DLL we have just created. Then put the correct parameters. The input is void *ImaqImage and the output is double.




The VI is connected as follows :





3 Remarks and conclusion


In this tutorial, a tricky interfacing between Intel OpenCV and NI Labview is performed. In the future tutorial we will try to combine Matlab image processing library with NI Labview.


If you want the example VI files and Visual C/C++ project files, just contact me :
adhiguna.mahendra@yahoo.com




















View Adhiguna Mahendra,Ph.D's profile on LinkedIn