RobotVisionCAD  documentation

by RobotVision2 Inc 

Introduction

RobotVisionCAD or in short RvCAD is a computer aided design for Image Processing and Computer Vision,  designed  to take full advantage of multi-cpu(SMP)/multi-core and distributed computing environment. The idea is to divide a image processing tasks into smaller tasks(so called divide and conquer) called "Processor", each of these processors are connected together by FIFO queue called "Link", to form a "Pipeline" or Image Processing Pipeline. For example the pipeline in picture#0 below has 7 processors, each has number of input (represented with red circle) and output ports (represented with green circle), connected with link(blue line).  


Picture#0 shows a pipeline running. The pipeline finds moving objects from a sequence of images, in this case from avi files.

Definitions

RvCAD GUI Application

RvCAD's GUI presents users with a view where users drag and drop Image Processor Elements, and visually connecting them to form Image Processing Pipeline.  RvCAD's design window consist of 2 views:  Processors Tree View on the left and Pipeline View on the right hand side. Processors in the left hand tree view are organized according to their Library(dynamic load library). 

Using RvCAD is similar to using an electonic logic gate simulation program. Basically, you just drag and drop Processor from the left tree view, to the right pipeline view, then start connecting them by clicking at the Input(red triangle)or Output Pin(blue triangle) and draging link line to other RvCadProcessor input or output pin. To Remove a link just right clicking on one of the Port where the link is connected, then you will get a context menu, select "Delete Link Attached with this Port". To Remove all links from a Processor , right clicking on the processor then select "Delete All Links connected with this Processor".  

Picture shows context menu when right clicking on output port number 0 of AviFileReader Processor.
Picture#1 shows context menu when right clicking on output port number 0 of AviFileReader Processor.

Some processors need to be configured before running. For example AviFileReader needs to know the path to an avi file. To config a configurable processor, right clicking on the processor, select "Config this Processor" from the context menu, the processor will show its config dialog.


picture#2 shows context menu when right clicking on AviFileReader processor.


Programming Topics

0) Requirements

1)About Rv2::Image, Rv2::Pixel, Rv2::MetaData and Rv2::OverlayOfAnImage

1.1) Type Of Image

    Image in Rv2 has multi channels pixel, and each channel is byte, long or float wide. There are 6 predefined type of image in Rv2 : Rv2::BinaryImage, Rv2::GrayScaleImage, Rv2::RgbColorImage, Rv2::IntegerImage, Rv2::RealImage and Rv2::ComplexImageRv2::Image itself is an abstract class and cannot be initiated.

Type Of Image

Number Of Channel(s) per Pixel

Channel Size

Possible Value in each Channel

Rv2::BinaryImage 1 byte 1, 0 (1 is black and 0 and white)
Rv2::GrayScaleImage 1 byte 0 -> 255   (0 is white and 255 is black)
Rv2::RgbColorImage 3 byte 0 <-> 255
Rv2::IntegerImage 1 long -XXX <-> max of long int
Rv2::RealImage 1 float -XXX <-> max of float
Rv2::ComplexImage 2 float -XXX <-> max of float

1.2) Access and Manipulate Image

    Accessing and manipulating pixel can be done by using method PixelAt(int iLinearPosition) or PixelAt(int x, int y) where iLinearPosition is a row major linear position of the pixel and x, y is column and row of the pixel. Generally using PixelAt(int iLinearPosition) is faster than PixelAt(int x, int y). Both methods returns reference to class Rv2::Pixel when you can use its public methods Get(..) and Set(..),  for more infomation please look at class reference of the Rv2::Image and Rv2::Pixel. Code below is an example.

int main(intargc,char**argv)
{
  Rv2::RgbColorImage  RgbImage;
  RgbImage.Allocate(80,60);// allocate RgbColorImage with width == 80 and height == 60
  //access it linearly
  for(unsignedinti=0;i<RgbImage.LinearSize();i++)
  {
    //set pixel at position i to blue color
    RgbImage.PixelAt(i).Set(0,0,255);  // first channel is Red, Green and blue repectively
    
    //get pixel at position i;
    Rv2::byter,g,b;
    RgbImage.PixelAt(i).Get(&r,&g,&b);
  }
  
  //access it like matrix
  for(unsignedintx=0;y<RgbImage.Width();x++)
  {
    for(unsignedinty=0;y<RgbImage.Height();y++)
    {
      //set pixel at position x,y to blue color
      RgbImage.PixelAt(x,y).Set(0,0,255);  // first channel is Red, Green and blue repectively
      
      //get pixel at position x,y;
      Rv2::byter,g,b;
      RgbImage.PixelAt(x,y).Get(&r,&g,&b);
    }
  }
  return 0;
}


    Neighborhood operations can be done using method NeighborAt(Rv2::TypeOfNeighbor iWhichNeightbor) of class Rv2::Pixel where iWhichNeighbor can be Rv2::eNorthWest, Rv2::eNorth, Rv2::eNorthEast, Rv2::eEast, Rv2::eSouthEast, Rv2::eSouth , Rv2::eSouthWest or Rv2::eWest. This method returns class Rv2::Pixel that iWhichNeighbor specifies. 

int main ( int argc , char ** argv )
{
  
Rv2 :: RgbColorImage img , img0 , img1 , img2 ;
  
img . Allocate ( 100 , 100 );
  
//Setting value of some pixels
   img . PixelAt ( 9 , 9 ). Set ( 9 , 9 , 5 );
  
img . PixelAt ( 10 , 9 ). Set ( 10 , 9 , 5 );
  
img . PixelAt ( 11 , 9 ). Set ( 11 , 9 , 5 );
  
img . PixelAt ( 9 , 10 ). Set ( 9 , 10 , 5 );
  
img . PixelAt ( 11 , 10 ). Set ( 11 , 10 , 5 );
  
img . PixelAt ( 9 , 11 ). Set ( 9 , 11 , 5 );
  
img . PixelAt ( 10 , 11 ). Set ( 10 , 11 , 5 );
  
img . PixelAt ( 11 , 11 ). Set ( 11 , 11 , 5 );
  

  int r, g, b ;
  
///-----------------------------------
   /// access it from method NeighborOfPixelAt of class Rv2::Image
   img . NeighborOfPixelAt ( 10 , 10 , Rv2 :: eNorthWest ). Get (& r , & g , & b );
  
:: printf ( "%d %d %d\n" ,    r ,    g ,    b );
  
img . NeighborOfPixelAt ( 10 , 10 , Rv2 :: eNorth ). Get (& r , & g , & b );
  
:: printf ( "%d %d %d\n" ,    r ,    g ,    b );
  
img . NeighborOfPixelAt ( 10 , 10 , Rv2 :: eNorthEast ). Get (& r , & g , & b );
  
:: printf ( "%d %d %d\n" ,    r ,    g ,    b );
  
img . NeighborOfPixelAt ( 10 , 10 , Rv2 :: eEast ). Get (& r , & g , & b );
  
:: printf ( "%d %d %d\n" ,    r ,    g ,    b );
  
img . NeighborOfPixelAt ( 10 , 10 , Rv2 :: eSouthEast ). Get (& r , & g , & b );
  
:: printf ( "%d %d %d\n" ,    r ,    g ,    b );
  
img . NeighborOfPixelAt ( 10 , 10 , Rv2 :: eSouth ). Get (& r , & g , & b );
  
:: printf ( "%d %d %d\n" ,    r ,    g ,    b );
  
img . NeighborOfPixelAt ( 10 , 10 , Rv2 :: eSouthWest ). Get (& r , & g , & b );
  
:: printf ( "%d %d %d\n" ,    r ,    g ,    b );
  
img . NeighborOfPixelAt ( 10 , 10 , Rv2 :: eWest ). Get (& r , & g , & b );
  
:: printf ( "%d %d %d\n" ,    r ,    g ,    b );
  
:: printf ( "--------------\n" );

  
//or you can use method NeighborAt of class Rv2::Pixel
   img . PixelAt ( 10 , 10 ). NeighborAt ( Rv2 :: eNorthWest ). Get (& r , & g , & b );
  
:: printf ( "%d %d %d\n" ,    r ,    g ,    b );
  
img . PixelAt ( 10 , 10 ). NeighborAt ( Rv2 :: eNorth ). Get (& r , & g , & b );
  
:: printf ( "%d %d %d\n" ,    r ,    g ,    b );
  
img . PixelAt ( 10 , 10 ). NeighborAt ( Rv2 :: eNorthEast ). Get (& r , & g , & b );
  
:: printf ( "%d %d %d\n" ,    r ,    g ,    b );
  
img . PixelAt ( 10 , 10 ). NeighborAt ( Rv2 :: eEast ). Get (& r , & g , & b );
  
:: printf ( "%d %d %d\n" ,    r ,    g ,    b );
  
img . PixelAt ( 10 , 10 ). NeighborAt ( Rv2 :: eSouthEast ). Get (& r , & g , & b );
  
:: printf ( "%d %d %d\n" ,    r ,    g ,    b );
  
img . PixelAt ( 10 , 10 ). NeighborAt ( Rv2 :: eSouth ). Get (& r , & g , & b );
  
:: printf ( "%d %d %d\n" ,    r ,    g ,    b );
  
img . PixelAt ( 10 , 10 ). NeighborAt ( Rv2 :: eSouthWest ). Get (& r , & g , & b );
  
:: printf ( "%d %d %d\n" ,    r ,    g ,    b );
  
img . PixelAt ( 10 , 10 ). NeighborAt ( Rv2 :: eWest ). Get (& r , & g , & b );
  
:: printf ( "%d %d %d\n" ,    r ,    g ,    b );
  
:: printf ( "--------------\n" );

  
return 0 ;
}

    When writing your own Rv2::Processor, usually you will get Image as polymorphic pointer of class Rv2::Image , the pointer needs to be casted(dynamic_cast) to correct type that your Rv2::Processor can process, for example

bool MyProcessor :: ProcessCode ( void )
{
    
// assuming that MyProcessor has 1 input and 1 output port, and MyProcessor only accepts Rv2::GrayScaleImage
     Rv2 :: Image * pImageIn = this -> InputPortNumber ( 0 ). Pop ();  
    
Rv2 :: GrayScaleImage * pGryImg = dynamic_cast < Rv2 :: GrayScaleImage *>( pImageIn );
    
if ( NULL != pGryImg )
    
{
      
/// good I unserstand it I can processor this image
       /// now I am going to process it
       for ( unsigned int i = 0 ; i < pGryImg -> LinearSize (); i ++)
      
{
        
int b ;
        
pGryImg -> PixelAt ( i ). Get (& b );
        
pGryImg -> PixelAt ( i ). Set ( 255 - b );
      
}
      
this -> OutputPort ( 0 )-> Push ( pGryImg );
    
}
    
else
    
{
      
/// show some error here
       Debug :: Trace ( "MyProcessor::ProcessCode(void) can't processor image that is not type Rv2::GrayScaleImage" );
      
/// now just pass the image thru..
       this -> OutputPort ( 0 )-> Push ( pImageIn );
    
}
    
return true ;
}

1.3 Overlay

    Every Rv2::Image derived classes has an overlay. The purpose of overlay is to illustrate an image without modifying the image's pixel. An overlay is a transparent layer of canvas on top of an image. It is represented by class Rv2::OverlayOfAnImage. The class has usual drawing tool methods, eg DrawText, DrawEllipse, DrawLine, DrawPixel, and etc, for more infomations please look at class reference for Rv2::OverlayOfAnImage. To use overlay, one would usually get reference of Rv2::OverlayOfAnImage  by calling method Rv2::OverlayOfAnImage& Rv2::Image::Overlay(void). Example below shows how to draw on an ovelay.

int main ( int argc , char ** argv )
{
   Rv2 :: BinaryImage img ;
   img . Allocate ( 320 , 240 );
   // :
   // :
   // do something meaning full with the image here
   // :
   // :
   // now I want draw something on overlay to
   // to illustrate
   image . Overlay (). DrawText ( 0 , 0 , "The Object is hightlighted in the circle" );
   image . Overlay (). DrawEllipse ( 10 , 10 , 100 , 100 );
   return 0 ;
}


picture#3 shows overlay which "FindBlobsLocation" and "ContourFollowing" draw (the green line) onto.

1.4) MetaData

    An image, normally,  has curtain number of basic informations. These infomations are width, height, PixelType and number of Channels per pixel. A MetaData is infomation which describes additional details of an image. For example histogram, filename, location of interested objects on the image, and etc. MetaData is usually generated by processor, eg. HistogramGenerator processor computes histogram of the pass thru image then generates HistogramData and attachs it with the image, FindBlobsLocation generates RectangleArrayData, which contains collection of rectangles coordinate where blobs are founded. An image can have number of uniqe MetaData in its container, but not the same type. The example code below shows how to put and get MetaData from an image's MetaData container.

   

  
// this example shows how to put and get
// MetaData to and from an image's ContainerOfMetaData
int main ( int argc , char ** argv )
{
   // allocate a image;
   Rv2 :: RgbColorImage image ;
   image . Allocate ( 80 , 60 );
  
   // :
   // :
   // do something meaning full with the image here.
   // :
   // :
  
   // now compute the image histogram
   // MetaData has to always allocate on heap, it CANNOT be on stack,
   // unless it can't be put onto the Rv2::ContainerOfMetaData.
   Rv2 :: HistogramData * pHD = new Rv2 :: HistogramData ;
   pHD -> Allocate ( 3 );    // allocate 3 channel for our hostogram
   pHD -> Color ( 0 ). Set ( 255 , 0 , 0 ); // set color for the 1st channel to red
   pHD -> Color ( 1 ). Set ( 0 , 255 , 0 ); // set color for the 2nd channel to green
   pHD -> Color ( 2 ). Set ( 0 , 0 , 255 ); // set color for the 3rd channel to blue
   for ( unsigned int i = 0 , j = 0 ; i < image . LinearSize (); i ++ )
   {
     int r , g , b ;
     image . PixelAt ( i ). Get (& r , & g , & b );
     pHD -> SetValue ( r , 0 ); // add red component to ch0
     pHD -> SetValue ( g , 1 ); // add red component to ch1
     pHD -> SetValue ( b , 2 ); // add red component to ch2
   }
  
   // now add Rv2::HistogramData to the image ContainerOfMetaData
   // Rv2::Image::MetaDataContainer() returns
   // a reference to its instance of Rv2::ContainerOfMetaData  
   image . MetaDataContainer (). Put ( pHD );
   // now after pHD is put in the ContainerOfMetaData,
   // you don't need to worry delete pHD, ContainerOfMetaData will delete it
   // when the image is destroy;
   // now whatif there is already another Rv2::Histogram in the ContainerOfMetaData,
   // method ContainerOfMetaData::Put(Rv2::MetaData* pData) will delete the previous
   // ref Rv2::Histogram then put a new one in.
  
   // :
   // :
   // do something meaning full here.
   // :
   // :
  
   // now we want to get Rv2::HistogramData, we have put in previously
   Rv2 :: MetaData * pMetaData = image . MetaDataContainer (). Get ( typeid ( Rv2 :: HistogramData ));
   if ( NULL != pMetaData ) // oh! good, there is a Rv2::HistogramData in there.
   {
     Rv2 :: HistogramData * pMyHist = dynamic_cast < Rv2 :: HistogramData *>( pMetaData );
     // :
     // do something meaning full with pMyHist here
     // :
     // when done, DO NOT delete pMyHist or pMetaData,
     // Rv2::ContainerOfMetaData will take care of it.    
   }
   else // no, sorry Rv2::HistogramData is not in this container;
   {
     Debug :: Trace ( "there is no MetaData Rv2::HistogramData attached with this image" );
   }
  
   return 0 ;
}

    For most of the time, you will want to have a MetaData which is probaby not Rv2::HistogramData or Rv2::RectangleData. You will need to derive a new class from Rv2::MetaData and implement 3 methods; Rv2::MetaData* New(void), void WriteSelf(Rv2::Document& ar), void ReadSelf(Rv2::Document& ar), and  set the Rv2::MetaData::m_strUUID value. And you must always register you new MetaData class with Rv2 system by calling Rv2::GlobalFactoryOfMetaData().Add(Rv2::MetaData* pNewClass); when system starts.

For more details on how to derive a new class from Rv2::MetaData, please read the sample code below. You can also look at the ExampleProcessor code here.

namespace Example // should always enclosed with a name space.
{
   class FileNameData : public Rv2 :: MetaData
   {
     std :: string m_strFilename ;
   public :
     FileNameData ( void )
     {
       // Set m_strUUID to something unique
       m_strUUID = "MyStuff.FileNameData" ;
     }

     void SetFileName ( const char * szFilename )
     {
       m_strFilename = szFilename ;
     }

     // implement New
     Rv2 :: MetaData * New ( void )
     {
       return new Example :: FileNameData ;
     }

     //implement WriteSelf
     void WriteSelf ( Rv2 :: Document & ar )
     {
       ar . Write ( "LoadedFromFile" , m_strFilename . c_str ());
     }

     //implement ReadSelf
     void ReadSelf ( Rv2 :: Document & ar )
     {
       ar . Read ( "LoadedFromFile" , m_strFilename );
     }
   };
}

int main ( int argc , char ** argv
{
   //always register a new MetaData class with Rv2
   // before doing anything else
   Rv2 :: GlobalFactoryOfMetaData (). Add ( new MyStuff :: FileNameData )
   // :
   // :
   // now you can do something meaning full here
   // :
   // :
   // GlobalFactoryOfMetaData will clean up itself
   return 0 ;
}

2) About Creating Processors and package them in a Library(dll, so and bundle).

Number of Input Ports Number Of Output Ports Method needs to be overided and implement function
Rv2::InPlaceProcessor 1 1 void ProcessImage(Rv2::Image *pImage)
Rv2::OutputOfPlaceProcessor 1 2 Rv2::Image* ProcessImage(Rv2::Image* pImageIn)
Rv2::ImageProducer 0 1 Rv2::Image* Produce(void)
Rv2::ImageConsumer 1 0 void Consume(Rv2::Image* img)
Rv2::BinaryOperatorProcessor 2 1 Rv2::Image* ProcessImage(Rv2::Image* pLhsImage, Rv2::Image* pRhsImage)

To create a processor, you can dervide a new processor class direct from Rv2::Processor or one of "seed" processor which provides some functionalities.

2.1) Derive from Rv2::InplaceProcessor

InPlaceProcessor is an abstract class derived from Rv2::Processor. It's actually a special version of Rv2::Processor which has a output and an input.  InPlaceProcessor cannot be initiated by itself. A new class have to be derived  from it and implement a pure virtual method void ProcessImage(Rv2::Image *pImage). InPlaceProcessor is for image processing algorithm that can be done inplace, eg. Negation, Threashold, and etc. Example below shows InPlaceGrayScaleNagation derived from Rv2::InplaceProcessor.

2.2) Derive from Rv2::OutOfPlaceProcessor

Rv2::OutOfPlaceProcessor has 1 input and 2 output ports; output port number 0 is just a pass thru from input port number 0, and output port number 1 is the result image. The pure virtual method Rv2::Image* ProcessImage(Rv2::Image* pImageIn)  needs to be overidden and implemented., pImageIn is ptr of Rv2::Image from input port 0, the method must allocate a new Rv2::Image and return it. The return Rv2::Image is pushed out to output port 1, and pImageIn get passed thru to output port 0.

2.3) Derive from Rv2::BinaryOperatorProcessor

Some Image Processing Algorithms need 2 input images(Left Hand Side and Right Hand Side) and and produce a result, this comparable to a mathematic equation  ResultImage = LeftHandSideImage % RightHandSideImage where % is an operator, eg image subtraction. If your algorithm has the same characteristic, you can derive a new class from Rv2::BinaryOperatorProcessor and implement Rv2::Image* ProcessImage(Rv2::Image* pLhsImage, Rv2::Image* pRhsImage) method. Example code below shows GrayScaleXOR which is derived from Rv2::BinaryOperator. GrayScaleXOR takes LHS image and xor it with RHS image, then returns the result.

2.4 Derive from Rv2::ImageProducer

Rv2::ImageProducer has only one output. For example AviFileReader reads an AVI file and creates Rv2::RgbColorImage of each frames in the AVI file then pushs the Rv2::RgbColorImage out to output port 0. If you intend to write a processor with similar function, the easiest way is to derive a new class from Rv2::ImageProducer and implement a virtual method Rv2::Image* Produce(void). The method must return a valid ptr to Rv2::Image.

2.5) Derive from Rv2::ImageConsumer

Rv2::ImageConsumer has configurable number of input ports but must at least has one inputs and has no output ports at all. By default Rv2::ImageConsumer always has one input port. To create Rv2::ImageConsumer with more than one input ports, call method bool Create(int nInputPort);. ImageConsumer is where Rv2::Image is destroyed in a pipeline, so a derive class must implement virtual method void Consume(Rv2::Image* img) ,then after doing whatever the derive class suppose to do, it must delete the Rv2::Image.  

About Integrate a pipeline into your own application.

class Rv2::System is the integation point to your custom application.  

Simplest Example shows 

#include <cstdio>
#include "Rv2Headers.h"

intmain(intargc,char**argv)
{
  // Main entry class to use Rv2Library
  Rv2::SystemRv2Sys;
    
  // read library(dll) listed in RvCAD.Library.Config.xml
  // When RvCAD is first loaded if this file is not exist
  // it will scan to find processor library(dll) in the current
  // directory then create this file.
  if(false==Rv2Sys.ReadLibraryConfigFile("RvCAD.Library.Config.xml"))
  {
    return0;
  }

  // now create a new pipeline
  Rv2::Pipeline*pMyPipeline=Rv2Sys.AllocatePipeline();
  
  //Now we are ready, so load the popeline that we want to run
  if(true==pMyPipeline->LoadFromFile("test.pipeline.xml"))
  {
    //tell pipeline to run slient
    pMyPipeline->SetRunSlient(true);
  
    //now we can run in the pipeline
    pMyPipeline->Run();
  
    //
    std::printf("press return to stop\n");
    getc(stdin);
    std::printf("stopping pipeline\n");
    pMyPipeline->Stop();
    // finish
    // we don't and must not delete pipeline here
    // Rv2::System will take care of it.
  }
  else
  {
    std::printf("fail to open pipeline test.pipeline.xml\n");
  }
  return0;
}

 

About Running a pipeline as a processor inside another pipeline

A pipeline can be executed as processor on another pipeline. Under library "RemoteConnectionProcessors", "PipelineExecutor" is able to load a pipeline from file and execute it as a processor.  The purpose of InputPlate and OutputPlate are to act as input and output ports of PipelineExecutor and make the pipeline complete.

Consider the pipeline below, the purpose of this pipeline is to smooth RgbColorImage. Since GaussianSmooth processor will only accept  GrayScaleImage, so RgbSeparator is needed to separate red, green and blue channel from the RgbColorImage.  The outputs from RgbSeparator are gray value of red, green and blue of the RgbColorImage represented in GrayScaleImage. Once each channels has been smoothed, processor RgbCombiner is used to combine the 3 channels and produces the result RgbColorImage.

As you can see that there are 3 paths of this pipeline that perform exactly the same task which to smooth GrayScaleImage. To reduce complexity of this pipeline, we can build a sub pipeline that would Smooth GrayScaleImage like the following, then save it to file: "ex1.pipeline.xml"

In a sub-pipeline, it needs to have at least one InputPlate or OutputPlate but not more than one of each. They act as Input and Output Port(s) of PipelineExecutor. 

InputPlate can be configured (right click on it and select "config") to have number of OutputPorts, the number will tell PipelineExecutor how many InputPorts does it need. So InputPlate's OutputPort number 0 is correspond to InputPort number 0 of the PipelineExecutor and so on.

OutputPlate can also be configured to have number of InputPorts, the number will tell PipelineExecutor how many OutputPorts does it need. So OutputPlate's InputPort number 0 is correspond to OutputPort number 0 of the PipelineExecutor and so on.

Now, all we need to do is, use PipelineExecutor processor to load "GrayScaleThreshold.pipeline.xml". Now our new pipeline look like a bit better.

As you can see, PipelineExecutor is comparable to subroutine in programming. To go further , by using RemotePipelineExecutor and RemoteProcessorExecutor under RemoteConnectionProcessors library, RvCAD can execute pipeline and processor on  remote machines, running in local area network. This makes a group of machines act like one big powerful computer. For more infomation, please look at "Distributed Computing (Running Pipeline or Processor on a remote machine)(RpcServer)

About Distributed Computing (Running Pipeline or Processor on a remote machine)(RpcServer)

RvCAD has ability to run Processors and Pipelines over remote machines,  to distribute load from the main pipeline.  Runnning a pipeline over a remote machine is the same as running a pipeline as a processor, please read section "Running a pipeline as a processor inside another pipeline" before continue. 

Requirements

1) Make sure that you have RemoteConnectionProcessors.dll in the bin directory where you've installed RvCAD, it's loaded, you should see "RemoteConnectionProcessors" on the left hand tree view.

 

(picture shows RemoteConnectionProcessors.dll loaded in RvCAD.)

2) Run RpcServer on all slave machines. From MainFrame window select File->"Open RpcServer Pipeline". RpcServer is listening on port 3000 by default, you can change that by right clicking on the processor then config it. Once done, execute the pipeline.

 

Running a processor over a remote machine.

To run a processor over a remote machine, use "RemoteProcessorExecutor" under "RemoteConnectionProcessor" library. Right Click on the processor and Select a processor and enter RpcServer host name or ip address. If the config dialog does not show the processor name that you want, you can enter the processor name directly, also please remember that processor and its library name are case sensitive.

Running a pipeline over a remote machine.

To run a pipeline over a remote machine, first the pipeline has to completed and has Input and Output plate(for more infomation, please read section "Running a pipeline as a processor inside another pipeline" . Use "RemotePipelineEcecutor" under "RemoteConnectionProcessors" Library. Right Click on the processor to get config dialog, enter RpcServer host name or address and enter the filename of the pipeline. Make sure that the pipline file is already on the remote machine where RpcServer is running.