/** @page smpl_simple_view_net SimpleViewer.net - sample program (C#/.NET) Source file: Click the following link to view the source code file: - SimpleViewer.net/MainWindow.cs This section describes the SimpleViewer.net sample program in the C# language for .NET. This sample program demonstrates using a @ref xn::DepthGenerator "DepthGenerator" node to build an accumulative histogram from depth values. 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 decribed again. @section svcs_glb_dcl_blk_ref "Declaration Block" section The reader may find it convenient to study the global declaration block before continuing to study the code statements. The global declaration block is documented later in this section, corresponding to its position in the program file – see @ref svcs_glb_dcl_blk . @section svcs_func_main MainWindow() @subsection svcs_scrpt_sets_up_pg Use Script to Set up a Context and Production Graph The @ref xn::Context::InitFromXmlFile() "CreateFromXmlFile()" method is a shorthand combination of two other initialization methods — Create() and then @ref xn::Context::RunXmlScriptFromFile() "RunXmlScriptFromFile()" — which initializes the @ref xn::Context "Context" object and then creates a production graph from an XML file. The method returns a scriptNode which is the owner of all the nodes created by the script. This node should be kept until those nodes aren't needed anymore. The XML script file describes all the nodes you want to create. For each node description in the XML file, this method creates a node in the production graph. @code InitializeComponent(); this.context = Context.CreateFromXmlFile(SAMPLE_XML_FILE, out scriptNode); @endcode Assuming that the above call to CreateFromXmlFile succeeded, a production graph is then created. @subsection svcs_get_dg_node_from_pg Get a DepthGenerator Node from the Production Graph The @ref xn::Context::FindExistingNode() "FindExistingNode()" method in the following code block tries to get a reference to any one of the production nodes. This call specifies NodeType.Depth to get a reference to a @ref xn::DepthGenerator "DepthGenerator" node. A DepthGenerator node generates a depth map as an array of pixels, where each pixel is a depth value representing a distance from the sensor in millimeters. A reference to the node is returned in the depth parameter. @code this.depth = context.FindExistingNode(NodeType.Depth) as DepthGenerator; @endcode The code block that follows the FindExistingNode() call just checks that OpenNI found a DepthGenerator node in the production graph. @code if (this.depth == null) { throw new Exception("Viewer must have a depth node!"); } @endcode @subsection svcs_set_ujp_hist_array Using DepthGenerator characterstics to set up Histogram and Image Buffer The following sets up the array for the histogram array that is a key part of this sample program. (This is not OpenNI specific.) xn::DepthGenerator::GetDeviceMaxDepth() "GetDeviceMaxDepth" gets the maximum depth (depth resolution) that the DepthGenerator node can produce. This is the same as the resolution of the depth axis (i.e., @ref xn::DepthGenerator::GetDeviceMaxDepth() "DeviceMaxDepth()" + 1). @code this.histogram = new int[this.depth.DeviceMaxDepth]; @endcode The following statement gets the Map Output mode of the @ref xn::DepthGenerator "DepthGenerator" node. The xnMapOutputMode value returned by @ref xn::MapGenerator::GetMapOutputMode is the combination of the node's scene resolution and frame rate. @code MapOutputMode mapMode = this.depth.MapOutputMode; @endcode The following statement accesses the Map Output mode to get the DepthGenerator's map dimensions. @ref xn::ImageMap::XRes "XRes" and @ref xn::ImageMap::YRes "YRes" get the frame's X and Y resolutions of the most recently generated data. X and Y are the number of columns and rows, respectively, in the frame after any required cropping has been applied. See @ref conc_map_wrapper_classes "Map Wrapper Classes" for more information. A default format, 'Rgb24', is used for pixel color format. @code this.bitmap = new Bitmap((int)mapMode.XRes, (int)mapMode.YRes, System.Drawing.Imaging.PixelFormat.Format24bppRgb); @endcode The following statements start the main reader thread. These statements are not OpenNI specific. @code this.shouldRun = true; this.readerThread = new Thread(ReaderThread); this.readerThread.Start(); @endcode @subsection svcs_onpaint OnPaint() method This method is not OpenNI specific. @subsection svcs_onpaintbkgd OnPaintBackground() method This method is not OpenNI specific. @subsection svcs_onclosing OnClosing() method This method is not OpenNI specific. @subsection svcs_onkeypress OnKeyPress() method This method is not OpenNI specific. @subsection svcs_calchist CalcHist() method This method uses the depth values to build an accumulative histogram of frequency of occurrence of each depth value. The pDepth pointer accesses each value in the depth buffer and then uses it as an index into the histogram array. @code private unsafe void CalcHist(DepthMetaData depthMD) { ... ... for (int y = 0; y < depthMD.YRes; ++y) { for (int x = 0; x < depthMD.XRes; ++x, ++pDepth) { ushort depthVal = *pDepth; if (depthVal != 0) { this.histogram[depthVal]++; points++; } } } ... ... @endcode @section svcs_reader_thread ReaderThread() method The @ref xn::DepthMetaData "DepthMetaData" object provides a @ref glos_frame_object "frame object" for the @ref xn::DepthGenerator "DepthGenerator" node. A @ref dict_gen_node "generator node's" frame object contains the generated data frame) and all its associated properties. This frame object, comprising the data frame and its properties, is accessible through the node's metadata object. @code DepthMetaData depthMD = new DepthMetaData(); @endcode @subsection svcs_reader_thread_main_loop Main Loop The following statement updates all generator nodes in the context that have new data available, first waiting for a specified node to have new data available. @code try { this.context.WaitOneUpdateAll(this.depth); } @endcode @section svcs_glb_dcl_blk Global Declaration Block The global declaration block is at the bottom of the public MainWindow() block. The declarations are as follows. @code private readonly string SAMPLE_XML_FILE = @"../../../../Data/SamplesConfig.xml"; private Context context; private ScriptNode scriptNode; private DepthGenerator depth; private Thread readerThread; private bool shouldRun; private Bitmap bitmap; private int[] histogram; @endcode Each of these declarations is described separately in the following paragraphs. The following definition is for the path to an OpenNI XML script file for inputting and building a stored production graph. The production graph is a network of production nodes and is the principal OpenNI object model. The identifies blobs as hands or human users (focusing mainly on OpenNI specific declarations). @code #define SAMPLE_XML_FILE "../../../../Data/SamplesConfig.xml" @endcode A @ref xn::Context "Context" object is a workspace in which the application builds an OpenNI production graph. The @ref xn::ScriptNode "ScriptNode" object loads an XML script from a file or string, and then runs the XML script to build a production graph. The ScriptNode object keeps references to all nodes created from the script, so they will not be destroyed. (This is since OpenNI will delete a node that has no remaining references to it.) The @ref xn::DepthGenerator "DepthGenerator" node generates a depth map. Each map pixel value represents a distance from the sensor. The following declarations are not OpenNI specific. histogram is a key part of this sample program. @code private Thread readerThread; private bool shouldRun; private Bitmap bitmap; private int[] histogram; @endcode */