OpenNI 1.5.4
NiRecordSynthetic.cpp - sample program
<b>Source file:</b> Click the following link to view the source code file:
    - NiRecordSynthetic.cpp

This section describes an OpenNI sample program for generating and recording synthetic data.

The program opens a recording, performs a transformation on the data from the recording, and then re-records the transformed data.

The documentation describes the sample program's code from the top of the program file(s) to bottom.

Every OpenNI feature is described the first time it appears in this sample program. Further appearances of the same feature are not described again.    


@section rec_syn_xform_depthmd transformDepthMD() function

    The following function provides the code for performing a transformation on the image data generated by the OpenNI @ref prod_graph "nodes". The function is described further below feature by feature. 
    @code   
        void transformDepthMD(DepthMetaData& depthMD)
        {
            DepthMap& depthMap = depthMD.WritableDepthMap();
            for (XnUInt32 y = 0; y < depthMap.YRes(); y++)
            {
                for (XnUInt32 x = 0; x < depthMap.XRes(); x++)
                {
                    //Punch vertical cut lines in the depth image
                    if ((x % 2) == 0)
                    {
                        depthMap(x, y) = 0;
                    }
                }
            }
        }       
    @endcode    

    In the above, the @ref xn::DepthMetaData "DepthMetaData" object provides a @ref glos_frame_object "frame object" for the @ref xn::DepthGenerator node. The @ref glos_frame_object "frame object" of a @ref dict_gen_node "generator node" contains a snapshot of generated data frame and all its associated properties. This data frame and its properties are accessible through the node's metadata object.  

    <code>DepthMap</code> is a Map Wrapper class. The Map Wrapper classes are light wrappers for @ref glos_frame_object maps.

    The @ref xn::DepthMetaData::WritableDepthMap() "WritableDepthMap()" method gets a light object to wrap the writable depth map. So this initializes the DepthMap map wrapper class.      
    @code   
        DepthMap& depthMap = depthMD.WritableDepthMap();
    @endcode

    The following double loop provides the x and y indexes to access each pixel in the <code>DepthMap</code>. The @ref xn::DepthMap::XRes "XRes()" and @ref xn::DepthMap::YRes "YRes()" methods return the two dimensions of the data frame, width and height, respectively. 
    @code   
        for (XnUInt32 y = 0; y < depthMap.YRes(); y++)
        {
            for (XnUInt32 x = 0; x < depthMap.XRes(); x++)
            {
             ...
            } 
        }
    @endcode            

    Here is the code that performs the data transformation. It punches vertical 'cut' lines in the depth image.
    @code   
        if ((x % 2) == 0)
        {
            depthMap(x, y) = 0;
        }       
    @endcode    


@section rec_syn_mainprg main() &ndash; Main Program

    @subsection rec_syn_mainprg_dcl_blk Main Program Declaration Block      

    @subsection rec_syn_record Initializing the Log from an XML file

        In the following, the @ref xn::xnLogInitFromXmlFile() method function initializes the log from an XML file. It initializes only the log; not the context or production graph.            
        @code       
            nRetVal = nRetVal = xnLogInitFromXmlFile(SAMPLE_XML_PATH);
            if (nRetVal != XN_STATUS_OK)
            {
                printf("Log couldn't be opened: %s. Running without log", xnGetStatusString(nRetVal));
            }
        @endcode


    @subsection rec_syn_getparams Creating and Setting up the Nodes

        The following initializes the @ref xn::Context object. This is where the application builds an OpenNI production graph. The <i>production graph</i> is a network of <i>production nodes</i> and is the principal OpenNI object structure.
        @code       
            Context context;
            nRetVal = context.Init();
        @endcode


        The @ref xn::Context::OpenFileRecording() "OpenFileRecording()" method replays a recorded file of a session of OpenNI data generation exactly as it was recorded. This includes recreating the whole @ref prod_graph "production graph", with all its nodes, that was built to run the original data generation session. This method performs this replaying by also creating a 'mock node' for each node in the file, and populates it with the recorded configuration. For an example of a mock node, see @ref xn::MockDepthGenerator.
        @code
            Player player;
            nRetVal = context.OpenFileRecording(strInputFile, player);
        @endcode    

        In the following code, the @ref xn::Context::FindExistingNode() "FindExistingNode()" method call gets a reference to a production node in the production graph. This sample program passes the <code>depth</code> parameter to get a reference to a @ref xn::DepthGenerator "DepthGenerator node" so that it can work with it. A @ref xn::DepthGenerator node generates a depth map. Each map pixel value represents a distance from the sensor.
        @code
        DepthGenerator depth;
        nRetVal = context.FindExistingNode(XN_NODE_TYPE_DEPTH, depth);
        @endcode

Create mock node based on depth node from recording The following declares a mock node. This is to simulate an actual node when recording or playing data from a recording. A mock node does not contain any logic for generating data. Instead, it allows an outside component (such as an application or a real node implementation) to feed it configuration changes and data.

MockDepthGenerator mockDepth;

In the following statement, the xn::MockDepthGenerator node::CreateBasedOn() "CreateBasedOn()" method creates a "mock" DepthGenerator node based on the type and properties of the base node, in this case the depth node. This node does not represent an actual node, but only keeps a state and implements an interface above it. Mock nodes are specifically used for simulating nodes for playing recordings, or for use in tests.

nRetVal = mockDepth.CreateBasedOn(depth);

In the following, the call to Create() initalizes a Recorder object. This object records to a specified destination medium the frames of data from each node that was added to it.

Recorder recorder;
nRetVal = recorder.Create(context);


In the following statement, the call to SetDestination() specifies to where the recorder must send its recording. This is a disk file of ONI type.

nRetVal = recorder.SetDestination(XN_RECORD_MEDIUM_FILE, strOutputFile);


The following statement adds the node to the recording, and starts recording it.

nRetVal = recorder.AddNodeToRecording(mockDepth);

The following statement specifies that the player will not automatically rewind to the beginning of the recording after reaching the end of the recording (the default behavior is to rewind).

nRetVal = player.SetRepeat(FALSE);

The next code block gets the number of frames of a specific node played. In the case of this sample program this is just a statistic to print later on the console.

XnUInt32 nNumFrames = 0;
nRetVal = player.GetNumFrames(depth.GetName(), nNumFrames);

The following declares a metadata object for the DepthGenerator node. The metadata object is described above in the rec_syn_xform_depthmd transformDepthMD() function.

DepthMetaData depthMD;

Main Program Loop

Following is the main program loop. It waits for available data from the generator and prints it out. The call to the n::Generator::WaitAndUpdateData() "WaitAndUpdateData()" method updates the node's application buffer to the latest available data, first waiting for the  node to have new data available. Since this method is being fed from a recording, the end of the recording is indicated by returning the XN_STATUS_EOF value. 

Note that the @ref xn::Generator::WaitAndUpdateData() "WaitAndUpdateData()" method has to be called for each and every iteration to refresh the node's data.
@code
    while ((nRetVal = depth.WaitAndUpdateData()) != XN_STATUS_EOF)
    {
        ...
    }
@endcode            

The statements of body of the main loop are presented and explained, in the following.

The following statement gets the DepthGenerator node's @ref glos_frame_object "frame object" (the @ref xn::DepthMetaData frame object). The metadata object is described above in the @ref rec_syn_xform_depthmd transformDepthMD() function.
@code   
    depth.GetMetaData(depthMD);
@endcode

Enable the depth map to be modified. In the following statement, the call to the @ref xn::OutputMetaData::MakeDataWritable() "MakeDataWritable()" allocates a buffer and copies current data to it, so that the data can be edited. 
@code   
    nRetVal = depthMD.MakeDataWritable();
@endcode    

The following statement makes a call to the transformDepthMD(), already described above in @ref rec_syn_xform_depthmd. This call transforms the data.
@code   
    transformDepthMD(depthMD);
@endcode    

The following statement passes the transformed data to the @ref xn::MockDepthGenerator object. The call to the @ref xn::MockDepthGenerator::SetData() "SetData()" method sets the data of the mock node from a metadata object.
@code   
    nRetVal = mockDepth.SetData(depthMD);
@endcode    

The following statement records one frame of data from each node that was added to the recorder by the @ref xn::Recorder::AddNodeToRecording() "AddNodeToRecording()" method.  To record continually, the Recorder node must be called repeatedly for each frame.
@code   
    nRetVal = Recorder.Record();
@endcode    

Finally, this sample program prints status information. In the statement below, the @ref xn::OutputMetaData::FrameID() "FrameID()" method gets the frame ID of @ref glos_frame_object "frame object". Frame IDs are a sequential unique number. 
@code   
    printf("Recorded: frame %u out of %u\r", depthMD.FrameID(), nNumFrames);
@endcode    
FALSE
#define FALSE
Definition: XnPlatform.h:94
XN_RECORD_MEDIUM_FILE
@ XN_RECORD_MEDIUM_FILE
Definition: XnTypes.h:835