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);
XN_RECORD_MEDIUM_FILE
@ XN_RECORD_MEDIUM_FILE
Definition XnTypes.h:835

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);
FALSE
#define FALSE
Definition XnPlatform.h:100

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