HDevEngine in .NET Applications MultiThreading
Basics
To use HDevEngine in Visual Studio .NET, you must
- add a reference to the HALCON/.NET assembly, either by adding an instance of HSmartWindowControlto the form or by adding the reference directly via the Solution Explorer
- add a reference to the HDevEngine/.NET assembly HDevEngine/.NET DLL via the Solution Explorer
- specify the namespace with the following line
using HalconDotNet;
Executing an HDevelop Program
In this section, we explain how to load and execute an HDevelop program with HDevEngine. The code fragments checks the boundary of a plastic part for fins.
Step 1: Initialization
First, we create a global instance of the main HDevEngine class HDevEngine.
private HDevEngine MyEngine = new HDevEngine();
Upon loading the form, we store the path to the HDevelop program and set the external procedure pathwith the method SetProcedurePath(Note that the latter is only necessary if the HDevelop program calls external procedures.):
string ProgramPathString;
private void ExecProgramForm_Load(object sender, System.EventArgs e)
{
var halconExamples = HSystem.GetSystem("example_dir");
var ProcedurePath = halconExamples + @"\hdevengine\procedures";
ProgramPathString = halconExamples + @"\hdevengine\hdevelop\fin_detection.hdev";
MyEngine.SetProcedurePath(ProcedurePath);
}
Step 2: Load Program
When you load the HDevelop program, an instance of the class HDevProgram is created. Furthermore, an instance of HDevProgramCall is created for later use. Note that the working directory will be changed if a program is loaded.
Exceptions occurring in the constructors, e.g., because the file name was not specified correctly, are handled with the standard C# error handling mechanism:
private void LoadBtn_Click(object sender, System.EventArgs e)
{
try
{
var Program = new HDevProgram(ProgramPathString);
ProgramCall = new HDevProgramCall(Program);
}
catch (HDevEngineException Ex)
{
MessageBox.Show(Ex.Message, "HDevEngine Exception");
return;
}
catch
{
return;
}
}
Step 3: Execute Program
private void ExecuteBtn_Click(object sender, System.EventArgs e)
{
try
{
ProgramCall.Execute();
}
catch (HDevEngineException Ex)
{
MessageBox.Show(Ex.Message, "HDevEngine Exception");
}
}
Step 4: Get Results
Note that program variables can only be accessed when the program has terminated.
double FinArea;
FinArea = ProgramCall.GetCtrlVarTuple("FinArea");
Window.SetTposition(150, 20);
Window.WriteString("Fin Area: ");
Executing HDevelop Procedures
Executing an External HDevelop Procedure
How to load and execute an external HDevelop procedure with HDevEngine. In contrast to the previous example, the result display is programmed explicitly instead of relying on the internal display operators.
Step 1: Initialization
As when executing an HDevelop program, we create a global instance of the main HDevEngine class HDevEngine and set the external procedure path with the method SetProcedurePath upon loading the form. If the external procedure is from a procedure library, the external procedure path may include the name of the library file.
private HDevEngine MyEngine = new HDevEngine();
private void ExecExtProcForm_Load(object sender, System.EventArgs e)
{
var ProcedurePath = halconExamples + @"\hdevengine\procedures";
...
MyEngine.SetProcedurePath(ProcedurePath);
}
For calling the HALCON operators, we declare a global variable of the class HWindow for the underlying HALCON window;
private HWindow Window;
private void WindowControl_Load(object sender, EventArgs e)
{
Window = WindowControl.HalconWindow;
Window.SetDraw("margin");
Window.SetLineWidth(4);
}
Step 2: Load Procedure
private void LoadBtn_Click(object sender, System.EventArgs e)
{
try
{
var Procedure = new HDevProcedure("detect_fin");
ProcCall = new HDevProcedureCall(Procedure);
}
catch (HDevEngineException Ex)
{
MessageBox.Show(Ex.Message, "HDevEngine Exception");
}
}
Executing a procedure consists of multiple steps. First, we load an example image sequence:
private void ExecuteBtn_Click(object sender, System.EventArgs e)
{
var Framegrabber = new HFramegrabber();
Framegrabber.OpenFramegrabber("File",1,1,0,0,0,0,"default",-1,"default",-1,"default","fin.seq","default",-1,-1);
Step 3: Set Input Parameters Of Procedure
It expects an image as (iconic) input parameter and returns the detected fin region and its area as iconic and control output parameter, respectively:procedure detect_fin (Image: FinRegion: : FinArea)
HImage Image = new HImage();
HRegion FinRegion;
HTuple FinArea;
for (int i=0; i<=2; i++)
{
Image.GrabImage(Framegrabber);
Image.DispObj(Window);
ProcCall.SetInputIconicParamObject("Image", Image);
Step 4: Execute Procedure
ProcCall.Execute();
Step 5: Get Output Parameters Of Procedure
FinRegion = ProcCall.GetOutputIconicParamRegion("FinRegion");
FinArea = ProcCall.GetOutputCtrlParamTuple("FinArea");
Step 6: Display Results Of Procedure
Image.DispObj(Window);
Window.SetColor("red");
Window.DispObj(FinRegion);
Window.SetColor("white");
Window.SetTposition(150, 20);
Window.WriteString("FinArea: " + FinArea.D);
}
Executing Local and External HDevelop Procedures
Local and external procedures are created and executed in exactly the same way. The only difference is that in order to use a local procedure, you must load the program it is contained in, whereas to load external procedures you must set the procedure path.
private HDevProcedureCall InitAcqProcCall;
private HDevProcedureCall ProcessImageProcCall;
private HDevProcedureCall VisualizeDetailsProcCall;
private void ExecProceduresForm_Load(object sender, System.EventArgs e)
{
string ProcedurePath = halconExamples + @"\hdevengine\procedures";
...
MyEngine.SetProcedurePath(ProcedurePath);
}
private void LoadBtn_Click(object sender, System.EventArgs e)
{
try
{
var Program = new HDevProgram(ProgramPathString);
var InitAcqProc = new HDevProcedure(Program, "init_acquisition");
var ProcessImageProc = new HDevProcedure(Program, "detect_fin");
var VisualizeDetailsProc = new HDevProcedure(Program, "display_zoomed_region");
InitAcqProcCall = new HDevProcedureCall(InitAcqProc);
ProcessImageProcCall = new HDevProcedureCall(ProcessImageProc);
VisualizeDetailsProcCall = new HDevProcedureCall(VisualizeDetailsProc);
...
}
One of the procedures opens the image acquisition device. It returns the corresponding handle, which we store in an instance of the class HFramegrabber.
HFramegrabber Framegrabber;
private void InitAcqBtn_Click(object sender, System.EventArgs e)
{
InitAcqProcCall.Execute();
Framegrabber = new HFramegrabber(InitAcqProcCall.GetOutputCtrlParamTuple("AcqHandle"));
...
}
As in the previous example, the results of image processing are displayed “manually” by calling HALCON/.NET operators. In contrast, when you click the button Visualize Details, an HDevelop procedure is executed that zooms onto the extracted fin. For this, we pass an implementation of HDevelop's internal display operators and remove it again after the procedure has been executed.
private void VisualizeDetailsBtn_Click(object sender, System.EventArgs e)
{
MyEngine.SetHDevOperators(MyHDevOperatorImpl);
VisualizeDetailsProcCall.SetInputIconicParamObject("Image", Image);
VisualizeDetailsProcCall.SetInputIconicParamObject("Region", FinRegion);
VisualizeDetailsProcCall.SetInputCtrlParamTuple("ZoomScale", 2);
VisualizeDetailsProcCall.SetInputCtrlParamTuple("Margin", 5);
VisualizeDetailsProcCall.Execute();
MyEngine.SetHDevOperators(null);
}
The instance of the implementation class is initialized with the HALCON window of the form.
private HDevOpMultiWindowImpl MyHDevOperatorImpl;
private void WindowControl_Load(object sender, EventArgs e)
{
Window = WindowControl.HalconWindow;
...
MyHDevOperatorImpl = new HDevOpMultiWindowImpl(Window);
}
If the class HDevOpMultiWindowImpl is initialized without specifying the window, a new HALCON window will open automatically to emulate the behavior of HDevelop.
Display
HDevOpFixedWindowImpl directs all display operators to a single graphics window.
HDevOpMultiWindowImpl can handle multiple graphics windows. You can pass an arbitrary number of graphics windows in the constructor; if the HDevelop program or procedure uses more than them, HDevEngine opens additional free-floating windows.
Please note that these classes must not be used in multithreaded applications because they are not reentrant. If you need a reentrant version, please discuss your application requirements with your local distributor.
private void WindowControl_Load(object sender, EventArgs e)
{
Window = WindowControl.HalconWindow;
MyEngine.SetHDevOperators(new HDevOpMultiWindowImpl(Window));
}
If your application has special display requirements that are not satisfied by the two classes, you can provide your own implementation of the display operators. Please note that currently you cannot use any form of display operator implementation with Mono.
Error Handling
private void DisplayException(HDevEngineException Ex)
{
string FullMessage = " Message: <" + Ex.Message + ">" +
", Error in program / procedure: <" + Ex.ProcedureName + ">" +
", program line: <" + Ex.LineText + ">" +
", line number: <" + Ex.LineNumber + ">" +
", HALCON Error Number: <" + Ex.HalconError + ">";
string Title = "HDevEngine Exception (Category: " + Ex.Category.ToString() + ")";
MessageBox.Show(FullMessage, Title);
}
try
{
var Program = new HDevProgram(ProgramPathString);
new HDevProgramCall(Program);
}
catch (HDevEngineException Ex)
{
DisplayException(Ex);
}
Load a non-existing HDevelop program.
Input parameter is not initialized
Parameter is not valid
Creating Multithreaded Applications
In the following, we briefly list the most important rules to observe when creating multithreaded HDevEngine applications. When multiple threads execute HDevelop programs in parallel, each thread must create its own instance of the corresponding HDevProgramCall.
External procedure path and the implementation of HDevelop's display operators are always set globally for all instances of HDevEngine. We recommend to set them via a separate HDevEngine instance to keep the code more readable.
Because the implementation of HDevelop's display operators can only be set globally, it must be thread-safe and reentrant.
Executing a Procedure in Parallel by Multiple Threads
It consists of four threads: The main thread is in charge of the graphical user interface (GUI).
The main thread also initializes the application by training the shape model via an HDevelop procedure and by creating and initializing the other three threads: two processing threads and the so-called control thread, whichs controls the two processing threads.
The control thread acquires the images and passes them to the processing threads, which then process the images and pass back the results. The control thread collects the results, but does not display them itself, because all activities in the HALCON window must be performed by the thread that created.
Initialization
private void InitButton_Click(object sender, System.EventArgs e)
- Switch off automatic operator parallelization
- HOperatorSet.SetSystem("parallelize_operators", "false");
- First, the automatic operator parallelization is switched off, otherwise the two mechanisms would use more than the available number of cores / processors and thus slow down the application instead of speeding it up. If you have a system with more than two cores or processors, you can consider to allocate some of them to the automatic operator parallelization as described in section.
- Set external procedure path
- Then, we create an instance of HDevEngine and set the path for searching the HDevelop procedures. If the external procedure is from a procedure library, the external procedure path may include the name of the library file.
HDevEngine MyEngine = new HDevEngine();
string ProcedurePath = halconExamples + @"\hdevengine\procedures";
...
MyEngine.SetProcedurePath(ProcedurePath);
- Train the shape model
HDevProcedureCall ProcTrain;
HDevProcedure Procedure = new HDevProcedure("train_shape_model");
ProcTrain = new HDevProcedureCall(Procedure);
ProcTrain.Execute();
- Store the model data
public HTuple ModelID;
public HXLD ModelContours;
ModelID = ProcTrain.GetOutputCtrlParamTuple("ModelID");
ModelContours = ProcTrain.GetOutputIconicParamXld("ModelContours");
- Create and initialize the processing engines
public class EngineThread
{
Thread WorkerObject = null;
HDevProcedureCall ProcCall;
HTuple ModelID;
HXLD ModelContours;
public AutoResetEvent EngineIsReady;
public EngineThread(MultiThreadingForm mainForm)
{
ModelID = mainForm.ModelID;
ModelContours = mainForm.ModelContours;
EngineIsReady = new AutoResetEvent(true);
}
EngineThread WorkerEngine1; // processing thread
EngineThread WorkerEngine2; // processing thread
AutoResetEvent Engine1Ready;
AutoResetEvent Engine2Ready;
WorkerEngine1 = new EngineThread(this);
WorkerEngine1.Init();
Engine1Ready = WorkerEngine1.EngineIsReady;
WorkerEngine2 = new EngineThread(this);
WorkerEngine2.Init();
Engine2Ready = WorkerEngine2.EngineIsReady;
An EngineThread initializes itself by creating the procedure call for detecting the caps in the images. Because the input parameters of the procedure that concern the shape model are the same for each call, they can be set once in advance (file: EngineThread.cs).
public void Init()
{
HDevProcedure Procedure = new HDevProcedure("detect_shape");
ProcCall = new HDevProcedureCall(Procedure);
ProcCall.SetInputCtrlParamTuple("ModelID", ModelID);
ProcCall.SetInputIconicParamObject("ModelContours", ModelContours);
}
- Initialize image acquisition
Finally, we initialize the image acquisition. The handle is stored in a variable of the form, so that the control thread can access it (file: MultiThreadingForm.cs).
private HFramegrabber AcqHandle;
string ImagePath = halconExamples + "/images/cap_illumination";
AcqHandle = new HFramegrabber("File", 1, 1, 0, 0, 0, 0, "default", -1,"default", -1, "default", ImagePath, "default", -1, -1);
Image Processing
When you click the Run button, the application starts to process images in a loop.
- Starting the processing threads and the control thread
First, the main thread starts the processing engines (file: MultiThreadingForm.cs).
private void RunButton_Click(object sender, System.EventArgs e)
{
WorkerEngine1.Run();
WorkerEngine2.Run();
The corresponding method creates and starts their thread and sets the “ready” signal (file: EngineThread.cs).
public void Run()
{
EngineIsReady.Set();
WorkerObject = new Thread(new ThreadStart(Process));
WorkerObject.Start();
}
Then, the main thread starts the control thread (file: MultiThreadingForm.cs):
ControlThread = new Thread(new ThreadStart(Run));
ControlThread.Start();
- Triggering the processing threads from the control thread
The control thread's action is contained in the method Run (file: MultiThreadingForm.cs). As long as the Stop is not pressed (please take a look at the project's code for more information), it waits until one of the processing engine is ready.
EngineThread WorkerEngine; // variable to switch between processing threads
public void Run()
{
HImage Image;
while (!StopEventHandle.WaitOne(0, true))
{
if (Engine1Ready.WaitOne(0, true))
WorkerEngine = WorkerEngine1;
else if (Engine2Ready.WaitOne(0, true))
WorkerEngine = WorkerEngine2;
else
continue;
Image = AcqHandle.GrabImageAsync(-1);
WorkerEngine.SetImage(Image);
Then, it acquires the next image and passes it to the engine, which stores it in a member variable (file: EngineThread.cs).
private HImage InputImage = null;
public void SetImage(HImage Img)
{
InputImage = Img;
}
Processing the image
In their action method (Process), the processing threads wait for the image to be set (file: EngineThread.cs). The actual image processing is performed by the HDevelop procedure, passing the image as input parameter.
public void Process()
{
while (!DelegatedStopEvent.WaitOne(0, true))
{
if (InputImage == null)
continue;
ProcCall.SetInputIconicParamObject("Image", InputImage);
ProcCall.Execute();
- Passing the results to the control thread
To pass the results, a class is defined that stores the relevant data: the processed image and the position, orientation, and the contours of the found cap.
public class ResultContainer
{
public HImage InputImage;
public HXLD FoundContours;
public double Row;
public double Column;
public double Angle;
}
After executing the procedure, the processing thread accesses its results and stores them in a new instance of the result class (“result container”), together with the processed image.
ResultContainer Result;
HTuple ResultTuple;
Result = new ResultContainer();
Result.InputImage = InputImage;
Result.FoundContours = ProcCall.GetOutputIconicParamXld("ResultObject");
ResultTuple = ProcCall.GetOutputCtrlParamTuple("ResultData");
Result.Row = ResultTuple[0];
Result.Column = ResultTuple[1];
Result.Angle = ResultTuple[2];
The processing thread then passes the result container to the control thread by appending it to a list.
ResultMutex.WaitOne();
ResultList.Add(Result);
ResultMutex.ReleaseMutex();
This list is a member variable of the main thread (file: MultiThreadingForm.cs). It is protected by a mutex so that the threads can access it safely.
public ArrayList ResultList;
public Mutex ResultDataMutex;
public MultiThreadingForm()
{
ResultDataMutex = new Mutex();
ResultList = new ArrayList();
}
The processing threads store references to the list and to the mutex in own member variables (file: EngineThread.cs).
ArrayList ResultList;
Mutex ResultMutex;
public EngineThread(MultiThreadingForm mainForm)
{
ResultList = mainForm.ResultList;
ResultMutex = mainForm.ResultDataMutex;
}
“Ready again”
Finally, the processing thread signals that it is ready for the next image by setting the corresponding event and by setting the input image to null.
InputImage = null;
this.EngineIsReady.Set();
Result Display
- Checking whether new results are available
Let's return to the action method (Run) of the control thread (file: MultiThreadingForm.cs). After triggering a processing thread by passing the image to process, it checks whether the result list contains new items.
int Count = -1;
ResultDataMutex.WaitOne();
Count = ResultList.Count;
ResultDataMutex.ReleaseMutex();
Delegating the display
The control thread does not perform the display of results itself but delegates it to the main thread (running the form) with the method Invoke.
for( ;Count > 0;Count--)
Invoke(DelegatedDisplay);
The necessary members are defined by the form.
delegate void FuncDelegate();
FuncDelegate DelegatedDisplay;
public MultiThreadingForm()
{
DelegatedDisplay = new FuncDelegate(DisplayResults);
}
Note that, since HALCON 12, all HALCON visualization operators are automatically delegated to the correct thread as described in chapter section “Threading Issues with Graphics”.
- Displaying the results
The actual display is performed by the method DisplayResults. Each time it is called, it removes an item from the result list and displays the processed image with the contours of the found cap. Then, it frees the corresponding HALCON -internal memory.
public void DisplayResults()
{
ResultDataMutex.WaitOne();
Result = (ResultContainer) ResultList[0];
ResultList.Remove(Result);
ResultDataMutex.ReleaseMutex();
Window.ClearWindow();
Window.DispImage(Result.InputImage);
Window.DispObj(Result.FoundContours);
Result.InputImage.Dispose();
Result.FoundContours.Dispose();
}
Executing Multiple Procedures in Parallel by Multiple Threads
In contrast to the previous section, the example application MultiThreadingTwoWindows presented here executes different HDevelop procedures (tasks) in parallel by two threads. One task is to find bottle caps using shape-based matching, the other to read ECC 200 data codes.
Figure shows an overview of the structure of the application. Like the application described in the previous section, it consists of four threads: The main thread (i.e., the form) is in charge of the graphical user interface (GUI), which is depicted in figure. It consists of a HALCON window for the display of results and buttons to initialize, start, and stop the application.
The main thread also initializes the application by creating and initializing the other three threads: two processing threads and the so-called control thread, whichs controls the two processing threads. In contrast to the previous application, here the processing threads initialize the image processing tasks by training the shape model and the data code model, respectively, via HDevelop procedures.
The control thread acquires the images and passes them to the processing threads, which then process the image and pass back the results. The control thread collects the results, but does not display them itself, because all activities in the HALCON window must be performed by the thread that created it, i.e., the main thread. In contrast to the previous application the results of the two tasks are displayed in two separate windows.
Below, we take a closer look at the corresponding code, restricting ourselves, however, to the parts that are diferent to the previous application.
Initialization
As in the previous example, the application is initialized in the event handler of the Init button (file: MultiThreadingTwoWindowsForm.cs).
- Create and initialize the processing engines
The processing engines are created and initialized similarly to the previous example, with some exceptions: First, the shape and the data code model are now trained by the processing threads instead of the control thread (see the step below). Secondly, the processing engines now also have a variable that indicates “their” HALCON window (file: EngineThread.cs).
public class EngineThread
{
...
public int WindowIndex = -1;
...
The control thread sets this variable after creating the engines (file:MultiThreadingTwoWindowsForm.cs).
private void InitButton_Click(object sender, System.EventArgs e)
{
...
WorkerEngine1.WindowIndex = 1;
...
WorkerEngine2.WindowIndex = 2;
- Train the shape and data code model
The training of the shape and data code model is now performed by the initialization method of the processing threads, which now has a parameter that specifies the task of the processing thread (file: MultiThreadingTwoWindowsForm.cs).
WorkerEngine1.Init("shape");
...
WorkerEngine2.Init("datacode");
The HDevelop procedures for training the models and for performing the image processing have similar names for the two tasks, so that their names can be generated automatically (file: EngineThread.cs). The task name itself is stored in a variable of the class EngineThread.
public class EngineThread
{
HDevProcedureCall ProcCall;
string Task;
HTuple ModelID;
HXLD ModelContours;
...
public void Init(string Task)
{
string TrainMethod = "train_" + Task + "_model";
string ProcessingMethod = "detect_" + Task;
HDevProcedureCall ProcTrain;
this.Task = Task;
Then, the model of the shape or datacode, respectively, is trained by executing the corresponding HDevelop procedure and the returned model data is stored in variables of the class.
HDevProcedure Procedure = new HDevProcedure(TrainMethod);
ProcTrain = new HDevProcedureCall(Procedure);
ProcTrain.Execute();
ModelID = ProcTrain.GetOutputCtrlParamTuple("ModelID");
if (Task.Equals("shape"))
{
ModelContours = ProcTrain.GetOutputIconicParamXld("ModelContours");
}
- Store the model data
Finally, those input parameters of the image processing procedure that are the same for each call are set (file: EngineThread.cs).
HDevProcedure Procedure = new HDevProcedure(ProcessingMethod);
ProcCall = new HDevProcedureCall(Procedure);
ProcCall.SetInputCtrlParamTuple("ModelID", ModelID);
if (Task.Equals("shape"))
{
ProcCall.SetInputIconicParamObject("ModelContours", ModelContours);
}
- Initialize image acquisition
The two image processing tasks are performed in different images, therefore, two image acquisition devices are opened by the main thread (file:MultiThreadingTwoWindowsForm.cs, code not shown).
Image Processing
- Triggering the processing threads
The control thread's action is contained in the method Run (file:MultiThreadingTwoWindowsForm.cs). As long as the Stop is not pressed, it checks whether the processing engines are ready and, if this is the case, acquires and passes images..
public void Run()
{
HImage Image;
while (!StopEventHandle.WaitOne(0,true))
{
if (Engine1Ready.WaitOne(0,true))
{
Image = AcqHandle1.GrabImageAsync(-1);
WorkerEngine1.SetImage(Image);
}
if (Engine2Ready.WaitOne(0,true))
{
Image = AcqHandle2.GrabImageAsync(-1);
WorkerEngine2.SetImage(Image);
}
- Passing the results to the control thread
The class storing the result data differs significantly from the one in the previous example: It now also contains a variable that indicates the window in which to display the results and a flag that shows whether the processing was successful. Because the processing results differ between the two tasks, they are encapsulated in a tuple (file: EngineThread.cs).
public class ResultContainer
{
public int WindowIndex; // 1 -> shape, 2 -> datacode
public HImage InputImage;
public HXLD FoundContours;
public HTuple ResultData;
public bool DetectionSuccessful;
}
After executing the procedure, the processing thread accesses its results and stores them in a new instance of the result container, together with the processed image and the window index.
public void Process()
{
ResultContainer Result;
Result = new ResultContainer();
...
Result.InputImage = InputImage;
DetectionSuccessful = ProcCall.GetOutputCtrlParamTuple("DetectionSuccessful").S;
if (DetectionSuccessful.Equals("true"))
{
Result.DetectionSuccessful = true;
Result.FoundContours = ProcCall.GetOutputIconicParamXld("ResultObject");
Result.ResultData = ProcCall.GetOutputCtrlParamTuple("ResultData");
}
else
{
Result.DetectionSuccessful = false;
}
Result.WindowIndex = WindowIndex;
Result Display
As in the previous example, the display of results is performed by the main thread in the method ResultDisplay (file: MultiThreadingTwoWindowsForm.cs). The main difference is that the display now is switched between the two HALCON windows, based on the variable in the result container.
public void DisplayResults()
{
HWindow Window;
if (Result.WindowIndex == 1)
{
Window = Window1;
}
else
{
Window = Window2;
}
Furthermore, the display method now checks the success of the image processing to avoid accessing non-existing result elements. For both tasks, the resulting contours, i.e., the found shape or data code region, respectively, are displayed. For the data code task, also the read code is displayed.
Window.ClearWindow();
Window.DispImage(Result.InputImage);
if (Result.DetectionSuccessful)
{
Window.DispObj(Result.FoundContours);
// additional display for data code result: code
if (Result.WindowIndex == 2)
{
Row = (int) Result.ResultData[0].D;
Col = (int) Result.ResultData[1].D;
Window.SetTposition(Row,Col);
Window.WriteString((string) Result.ResultData[2].S);
}
}
else
{
Window.SetColor("red");
Window.SetTposition(20, 20);
Window.WriteString("Detection failed!");
Window.SetColor("green");
}
Executing an HDevelop Program with Vector Variables
The example application UseVectorVariables shows how to load and execute an HDevelop example that contains vector variables in HDevengine/C#. In the example two vectors are used for processing: one containing the input images and one containing scaling factors. When executing the program the gray values of the input images are scaled according to the scaling factors. Please have a look at the source file UseVectorVariablesForm.cs for more details on how to work with vector variables in HDevengine/.NET.
Using the Just-in-time Compiler with HDevEngine/.NET
The just-in-time compilation of procedures needs to be enabled in your instance of the HDevEngine class:
...
Engine = new HDevEngine();
// enable or disable execution of compiled procedures
Engine.SetEngineAttribute("execute_procedures_jit_compiled", "true");
Procedures (and procedures referenced by it) are compiled at the moment a corresponding instance of HDevProcedureCall or HDevProgramCall is created.
You can also explicitly pre-compile all used procedures of a HDevelop program or procedure using the method CompileUsedProcedures of HDevProgram or HDevProcedure, respectively.
In the following example, all used procedures of a procedure call are just-in-time compiled:
Program = new HDevProgram(ProgramPathString);
// Get local procedure
Proc = new HDevProcedure(Program, name);
...
Proc.CompileUsedProcedures();
HDevEngine in .NET Applications MultiThreading的更多相关文章
- Adding Multithreading Capability to Your Java Applications
src: http://www.informit.com/articles/article.aspx?p=26326&seqNum=3 Interrupting Threads A threa ...
- Part 86 to 88 Talking about Multithreading in C#
Part 86 Multithreading in C# What is a Process: Process is what the operating system uses to facil ...
- Multithreading: How to Use the Synchronization Classes
(Owed by: 春夜喜雨 http://blog.csdn.net/chunyexiyu 转载请标明来源) 翻译文章来源: msdn - Multithreading: How to Use t ...
- Java - Multithreading zz
Java is a multi-threaded programming language which means we can develop multi-threaded program usin ...
- Patterns for Asynchronous MVVM Applications: Commands
Stephen Cleary Download the Code Sample This is the second article in a series on combining async an ...
- Multithreading in C
Multithreading in C, POSIX(可移植操作系统接口Portable Operating System Interface X ) style Multithreading - A ...
- 通过/proc/cpuinfo判断CPU数量、Multicores、Multithreading、Hyper-threading
http://blog.sina.com.cn/s/blog_4a6151550100iowl.html 判断依据:1.具有相同core id的cpu是同一个core的超线程.2.具有相同physic ...
- Multi-threading Android Apps for Multi-core Processors – Part 1 of 2
Can my single-threaded application benefit from multiple cores? How? Even a single-threaded applicat ...
- Authorization in Cloud Applications using AD Groups
If you're a developer of a SaaS application that allows business users to create and share content – ...
随机推荐
- JS中apply和call的用法
JavaScript中有一个call和apply方法,其作用基本相同,但也有略微的区别. 先来看看JS手册中对call的解释: call 方法调用一个对象的一个方法,以另一个对象替换当前对象. cal ...
- HN669打包工具--游戏对接
一. 将游戏工程拖入到工具的HN669Ploy目录下,如下图: 二. xCode打开游戏工程,将Core目录下的HN669SDKCore工程添加入游戏工程,并引用库,如图: 三.调用API 1.A ...
- 微信JSApi支付---常见问题
1.支付一直报 “get_brand_wcpay_request:false” 错误 原因: 商户平台上设置的[支付授权目录]路劲不正确,比如:支付的页面的域名是:www.xxx.com/pay/s ...
- (linux)修改MySQL密码方法
1,在/etc/my.cnf末尾 加入skip-grant-tables,保存,跳过身份验证. 2,重启MySql,使刚才修改的配置生效. 3,终端输入mysql,然后再输入use mysql; 4 ...
- How to modify rosbag?如何修改rosbag?
Ubuntu16.04,kinetic 本文示例修改使用rosbag中的frame id 在http://wiki.ros.org/bag_tools中,能找到 change_camera_info. ...
- IIS中使用子目录文件作为默认文档(Default Document)替代重定向
以前一直以为IIS应用程序的默认文档只能设置根目录下的文件,像index.html,default.aspx等,后来经同事指点,原来子目录或者子应用程序下的文件也可以添加到根应用程序的默认文档列表中. ...
- PAT L2-014【二分】
思路: 最后发现对当前列车比我大的编号的栈有没有就好了,所以开个vector存一下,然后二分一下vector找一下第一个比我大的数就好了 #include <bits/stdc++.h> ...
- Linq to xml 读取xml文件或xml字符串
XMLFile1.xml: XDocument Contacts = XDocument.Load("XMLFile1.xml"); //XElement Contacts = X ...
- BUYING FEED
Problem F: F BUYING FEED Description Farmer John needs to travel to town to pick up K (1 <= K < ...
- 关于webpack的版本导致的postcss-loader的问题
来源自问题 https://segmentfault.com/q/1010000006987956 !!!发现这解决方案还是不能用,估计是webpack又更新了一轮,请看看下下方的答案 某个版本web ...