378 lines
18 KiB
HTML
378 lines
18 KiB
HTML
|
|
|
|||
|
|
|
|||
|
|
<!DOCTYPE html>
|
|||
|
|
<!--[if IE 8]><html class="no-js lt-ie9" lang="en" > <![endif]-->
|
|||
|
|
<!--[if gt IE 8]><!--> <html class="no-js" lang="en" > <!--<![endif]-->
|
|||
|
|
<head>
|
|||
|
|
<meta charset="utf-8">
|
|||
|
|
|
|||
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|||
|
|
|
|||
|
|
<title>PCL 2.x API consideration guide</title>
|
|||
|
|
|
|||
|
|
|
|||
|
|
|
|||
|
|
|
|||
|
|
|
|||
|
|
|
|||
|
|
|
|||
|
|
|
|||
|
|
<script type="text/javascript" src="_static/js/modernizr.min.js"></script>
|
|||
|
|
|
|||
|
|
|
|||
|
|
<script type="text/javascript" id="documentation_options" data-url_root="./" src="_static/documentation_options.js"></script>
|
|||
|
|
<script type="text/javascript" src="_static/jquery.js"></script>
|
|||
|
|
<script type="text/javascript" src="_static/underscore.js"></script>
|
|||
|
|
<script type="text/javascript" src="_static/doctools.js"></script>
|
|||
|
|
<script type="text/javascript" src="_static/language_data.js"></script>
|
|||
|
|
|
|||
|
|
<script type="text/javascript" src="_static/js/theme.js"></script>
|
|||
|
|
|
|||
|
|
|
|||
|
|
|
|||
|
|
|
|||
|
|
<link rel="stylesheet" href="_static/css/theme.css" type="text/css" />
|
|||
|
|
<link rel="stylesheet" href="_static/pygments.css" type="text/css" />
|
|||
|
|
<link rel="index" title="Index" href="genindex.html" />
|
|||
|
|
<link rel="search" title="Search" href="search.html" />
|
|||
|
|
</head>
|
|||
|
|
|
|||
|
|
<body class="wy-body-for-nav">
|
|||
|
|
|
|||
|
|
|
|||
|
|
<div class="wy-grid-for-nav">
|
|||
|
|
|
|||
|
|
<nav data-toggle="wy-nav-shift" class="wy-nav-side">
|
|||
|
|
<div class="wy-side-scroll">
|
|||
|
|
<div class="wy-side-nav-search" >
|
|||
|
|
|
|||
|
|
|
|||
|
|
|
|||
|
|
<a href="index.html" class="icon icon-home"> Point Cloud Library
|
|||
|
|
|
|||
|
|
|
|||
|
|
|
|||
|
|
</a>
|
|||
|
|
|
|||
|
|
|
|||
|
|
|
|||
|
|
|
|||
|
|
<div class="version">
|
|||
|
|
1.12.0
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
|
|||
|
|
|
|||
|
|
|
|||
|
|
<div role="search">
|
|||
|
|
<form id="rtd-search-form" class="wy-form" action="search.html" method="get">
|
|||
|
|
<input type="text" name="q" placeholder="Search docs" />
|
|||
|
|
<input type="hidden" name="check_keywords" value="yes" />
|
|||
|
|
<input type="hidden" name="area" value="default" />
|
|||
|
|
</form>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div class="wy-menu wy-menu-vertical" data-spy="affix" role="navigation" aria-label="main navigation">
|
|||
|
|
|
|||
|
|
|
|||
|
|
|
|||
|
|
|
|||
|
|
|
|||
|
|
|
|||
|
|
<!-- Local TOC -->
|
|||
|
|
<div class="local-toc"><ul>
|
|||
|
|
<li><a class="reference internal" href="#">PCL 2.x API consideration guide</a><ul>
|
|||
|
|
<li><a class="reference internal" href="#major-changes">Major changes</a><ul>
|
|||
|
|
<li><a class="reference internal" href="#pcl-pointcloud">1.1 pcl::PointCloud</a></li>
|
|||
|
|
<li><a class="reference internal" href="#pointtypes">1.2 PointTypes</a></li>
|
|||
|
|
<li><a class="reference internal" href="#gpu-support">1.3 GPU support</a></li>
|
|||
|
|
<li><a class="reference internal" href="#keypoints-and-features">1.4 Keypoints and features</a></li>
|
|||
|
|
<li><a class="reference internal" href="#data-slices">1.5 Data slices</a></li>
|
|||
|
|
<li><a class="reference internal" href="#ransac">1.6 RANSAC</a></li>
|
|||
|
|
</ul>
|
|||
|
|
</li>
|
|||
|
|
<li><a class="reference internal" href="#minor-changes">Minor changes</a></li>
|
|||
|
|
<li><a class="reference internal" href="#concepts">Concepts</a></li>
|
|||
|
|
</ul>
|
|||
|
|
</li>
|
|||
|
|
<li><a class="reference internal" href="#references">References</a></li>
|
|||
|
|
</ul>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</nav>
|
|||
|
|
|
|||
|
|
<section data-toggle="wy-nav-shift" class="wy-nav-content-wrap">
|
|||
|
|
|
|||
|
|
|
|||
|
|
<nav class="wy-nav-top" aria-label="top navigation">
|
|||
|
|
|
|||
|
|
<i data-toggle="wy-nav-top" class="fa fa-bars"></i>
|
|||
|
|
<a href="index.html">Point Cloud Library</a>
|
|||
|
|
|
|||
|
|
</nav>
|
|||
|
|
|
|||
|
|
|
|||
|
|
<div class="wy-nav-content">
|
|||
|
|
|
|||
|
|
<div class="rst-content">
|
|||
|
|
|
|||
|
|
|
|||
|
|
|
|||
|
|
|
|||
|
|
|
|||
|
|
|
|||
|
|
|
|||
|
|
|
|||
|
|
|
|||
|
|
|
|||
|
|
|
|||
|
|
|
|||
|
|
|
|||
|
|
|
|||
|
|
|
|||
|
|
|
|||
|
|
|
|||
|
|
<div role="navigation" aria-label="breadcrumbs navigation">
|
|||
|
|
|
|||
|
|
<ul class="wy-breadcrumbs">
|
|||
|
|
|
|||
|
|
<li><a href="index.html">Docs</a> »</li>
|
|||
|
|
|
|||
|
|
<li>PCL 2.x API consideration guide</li>
|
|||
|
|
|
|||
|
|
|
|||
|
|
<li class="wy-breadcrumbs-aside">
|
|||
|
|
|
|||
|
|
|
|||
|
|
|
|||
|
|
</li>
|
|||
|
|
|
|||
|
|
</ul>
|
|||
|
|
|
|||
|
|
|
|||
|
|
<hr/>
|
|||
|
|
</div>
|
|||
|
|
<div role="main" class="document" itemscope="itemscope" itemtype="http://schema.org/Article">
|
|||
|
|
<div itemprop="articleBody">
|
|||
|
|
|
|||
|
|
<div class="section" id="pcl-2-x-api-consideration-guide">
|
|||
|
|
<span id="pcl2"></span><h1>PCL 2.x API consideration guide</h1>
|
|||
|
|
<p>With the PCL 1.x API locked and a few releases already underway, it’s time to
|
|||
|
|
consider what the next generation of libraries should look like. This document
|
|||
|
|
discusses a series of changes to the current API, from base classes to higher
|
|||
|
|
level algorithms.</p>
|
|||
|
|
<div class="section" id="major-changes">
|
|||
|
|
<h2>Major changes</h2>
|
|||
|
|
<div class="section" id="pcl-pointcloud">
|
|||
|
|
<h3>1.1 pcl::PointCloud</h3>
|
|||
|
|
<p>The <span>PointCloud</span> class represents the base class in PCL
|
|||
|
|
for holding <strong>nD</strong> (n dimensional) data.</p>
|
|||
|
|
<dl class="docutils">
|
|||
|
|
<dt>The 1.x API includes the following data members:</dt>
|
|||
|
|
<dd><ul class="first last simple">
|
|||
|
|
<li><span>PCLHeader</span> (coming from ROS)<ul>
|
|||
|
|
<li><strong>std::uint32_t</strong> <span>seq</span> - a sequence number</li>
|
|||
|
|
<li><strong>std::uint64_t</strong> <span>stamp</span> - a timestamp associated with the time when the data was acquired</li>
|
|||
|
|
<li><strong>std::string</strong> <span>frame_id</span> - a TF frame ID</li>
|
|||
|
|
</ul>
|
|||
|
|
</li>
|
|||
|
|
<li><strong>std::vector<T></strong> <span>points</span> - a std C++ vector of T data. T can be a structure of any of the types defined in <cite>point_types.h</cite>.</li>
|
|||
|
|
<li><strong>std::uint32_t</strong> <span>width</span> - the width (for organized datasets) of the data. Set to the number of points for unorganized data.</li>
|
|||
|
|
<li><strong>std::uint32_t</strong> <span>height</span> - the height (for organized datasets) of the data. Set to 1 for unorganized data.</li>
|
|||
|
|
<li><strong>bool</strong> <span>is_dense</span> - true if the data contains only valid numbers (e.g., no NaN or -/+Inf, etc). False otherwise.</li>
|
|||
|
|
<li><strong>Eigen::Vector4f</strong> <span>sensor_origin_</span> - the origin (pose) of the acquisition sensor in the current data coordinate system.</li>
|
|||
|
|
<li><strong>Eigen::Quaternionf</strong> <span>sensor_orientation_</span> - the origin (orientation) of the acquisition sensor in the current data coordinate system.</li>
|
|||
|
|
</ul>
|
|||
|
|
</dd>
|
|||
|
|
</dl>
|
|||
|
|
<p>Proposals for the 2.x API:</p>
|
|||
|
|
<blockquote>
|
|||
|
|
<div><ul>
|
|||
|
|
<li><p class="first">drop templating on point types, thus making <span>PointCloud</span> template free</p>
|
|||
|
|
</li>
|
|||
|
|
<li><p class="first">drop the <span>PCLHeader</span> structure, or consolidate all the above information (width, height, is_dense, sensor_origin, sensor_orientation) into a single struct</p>
|
|||
|
|
</li>
|
|||
|
|
<li><p class="first">make sure we can access a slice of the data as a <em>2D image</em>, thus allowing fast 2D displaying, [u, v] operations, etc</p>
|
|||
|
|
</li>
|
|||
|
|
<li><p class="first">make sure we can access a slice of the data as a subpoint cloud: only certain points are chosen from the main point cloud</p>
|
|||
|
|
</li>
|
|||
|
|
<li><p class="first">implement channels (of a single type!) as data holders, e.g.:
|
|||
|
|
* cloud[“xyz”] => gets all 3D x,y,z data
|
|||
|
|
* cloud[“normals”] => gets all surface normal data
|
|||
|
|
* etc</p>
|
|||
|
|
</li>
|
|||
|
|
<li><p class="first">internals should be hidden : only accessors (begin, end …) are public, this facilitating the change of the underlying structure</p>
|
|||
|
|
</li>
|
|||
|
|
<li><p class="first">Capability to construct point cloud types containing the necessary channels
|
|||
|
|
<em>at runtime</em>. This will be particularly useful for run-time configuration of
|
|||
|
|
input sensors and for reading point clouds from files, which may contain a
|
|||
|
|
variety of point cloud layouts not known until the file is opened.</p>
|
|||
|
|
</li>
|
|||
|
|
<li><p class="first">Complete traits system to identify what data/channels a cloud stores at
|
|||
|
|
runtime, facilitating decision making in software that uses PCL. (e.g.
|
|||
|
|
generic component wrappers.)</p>
|
|||
|
|
</li>
|
|||
|
|
<li><p class="first">Stream-based IO sub-system to allow developers to load a stream of point
|
|||
|
|
clouds and “play” them through their algorithm(s), as well as easily capture
|
|||
|
|
a stream of point clouds (e.g. from a Kinect). Perhaps based on
|
|||
|
|
Boost::Iostreams.</p>
|
|||
|
|
</li>
|
|||
|
|
<li><p class="first">Given the experience on <a class="reference external" href="https://github.com/ethz-asl/libpointmatcher">libpointmatcher</a>,
|
|||
|
|
we (François Pomerleau and Stéphane Magnenat) propose the following data structures:</p>
|
|||
|
|
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">cloud</span> <span class="o">=</span> <span class="nb">map</span><span class="o"><</span><span class="n">space_identifier</span><span class="p">,</span> <span class="n">space</span><span class="o">></span>
|
|||
|
|
<span class="n">space</span> <span class="o">=</span> <span class="nb">tuple</span><span class="o"><</span><span class="nb">type</span><span class="p">,</span> <span class="n">components_identifiers</span><span class="p">,</span> <span class="n">data_matrix</span><span class="o">></span>
|
|||
|
|
<span class="n">components_identifiers</span> <span class="o">=</span> <span class="n">vector</span><span class="o"><</span><span class="n">component_identifier</span><span class="o">></span>
|
|||
|
|
<span class="n">data_matrix</span> <span class="o">=</span> <span class="n">Eigen</span> <span class="n">matrix</span>
|
|||
|
|
<span class="n">space_identifier</span> <span class="o">=</span> <span class="n">string</span> <span class="k">with</span> <span class="n">standardised</span> <span class="n">naming</span> <span class="p">(</span><span class="n">pos</span><span class="p">,</span> <span class="n">normals</span><span class="p">,</span> <span class="n">color</span><span class="p">,</span> <span class="n">etc</span><span class="o">.</span><span class="p">)</span>
|
|||
|
|
<span class="n">component_identifier</span> <span class="o">=</span> <span class="n">string</span> <span class="k">with</span> <span class="n">standardised</span> <span class="n">naming</span> <span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">,</span> <span class="n">r</span><span class="p">,</span> <span class="n">g</span><span class="p">,</span> <span class="n">b</span><span class="p">,</span> <span class="n">etc</span><span class="o">.</span><span class="p">)</span>
|
|||
|
|
<span class="nb">type</span> <span class="o">=</span> <span class="nb">type</span> <span class="n">of</span> <span class="n">space</span><span class="p">,</span> <span class="n">underlying</span> <span class="n">scalar</span> <span class="nb">type</span> <span class="o">+</span> <span class="n">distance</span> <span class="n">definition</span> <span class="p">(</span><span class="nb">float</span> <span class="k">with</span> <span class="n">euclidean</span> <span class="mi">2</span><span class="o">-</span><span class="n">norm</span> <span class="n">distance</span><span class="p">,</span> <span class="nb">float</span> <span class="n">representing</span> <span class="n">gaussians</span> <span class="k">with</span> <span class="n">Mahalanobis</span> <span class="n">distance</span><span class="p">,</span> <span class="n">binary</span> <span class="k">with</span> <span class="n">manhattan</span> <span class="n">distance</span><span class="p">,</span> <span class="nb">float</span> <span class="k">with</span> <span class="n">euclidean</span> <span class="n">infinity</span> <span class="n">norm</span> <span class="n">distance</span><span class="p">,</span> <span class="n">etc</span><span class="o">.</span><span class="p">)</span>
|
|||
|
|
</pre></div>
|
|||
|
|
</div>
|
|||
|
|
<dl class="docutils">
|
|||
|
|
<dt>For instance, a simple point + color scenario could be::</dt>
|
|||
|
|
<dd><p class="first last">cloud = { “pos” => pos_space, “color” => color_space }
|
|||
|
|
pos_space = ( “float with euclidean 2-norm distance”, { “x”, “y”, “z” }, [[(0.3,0,1.3) , … , (1.2,3.1,2)], … , [(1,0.3,1) , … , (2,0,3.5)] )
|
|||
|
|
color_space = ( “uint8 with rgb distance”, { “r”, “g”, “b” }, [[(0,255,0), … , (128,255,32)] … [(12,54,31) … (255,0,192)]] )</p>
|
|||
|
|
</dd>
|
|||
|
|
</dl>
|
|||
|
|
</li>
|
|||
|
|
</ul>
|
|||
|
|
</div></blockquote>
|
|||
|
|
</div>
|
|||
|
|
<div class="section" id="pointtypes">
|
|||
|
|
<h3>1.2 PointTypes</h3>
|
|||
|
|
<blockquote>
|
|||
|
|
<div><ol class="arabic simple">
|
|||
|
|
<li>Eigen::Vector4f or Eigen::Vector3f ??</li>
|
|||
|
|
<li>Large points cause significant performance penalty for GPU. Let’s assume that point sizes up to 16 bytes are suitable. This is some compromise between SOA and AOS. Structures like pcl::Normal (size = 32) is not desirable. SOA is better in this case.</li>
|
|||
|
|
</ol>
|
|||
|
|
</div></blockquote>
|
|||
|
|
</div>
|
|||
|
|
<div class="section" id="gpu-support">
|
|||
|
|
<h3>1.3 GPU support</h3>
|
|||
|
|
<blockquote>
|
|||
|
|
<div><ol class="arabic">
|
|||
|
|
<li><p class="first">Containers for GPU memory. pcl::gpu::DeviceMemory/DeviceMemory2D/DeviceArray<T>/DeviceArray2D<T> (Thrust containers are incinvinient).</p>
|
|||
|
|
<blockquote>
|
|||
|
|
<div><ul class="simple">
|
|||
|
|
<li>DeviceArray2D<T> is container for organized point cloud data (supports row alignment)</li>
|
|||
|
|
</ul>
|
|||
|
|
</div></blockquote>
|
|||
|
|
</li>
|
|||
|
|
<li><p class="first">PointCloud Channels for GPU memory. Say, with “_gpu” postfix.</p>
|
|||
|
|
<blockquote>
|
|||
|
|
<div><ul class="simple">
|
|||
|
|
<li>cloud[“xyz_gpu”] => gets channel with 3D x,y,z data allocated on GPU.</li>
|
|||
|
|
<li>GPU functions (ex. gpu::computeNormals) create new channel in cloud (ex. “normals_gpu”) and write there. Users can preallocate the channel and data inside it in order to save time on allocations.</li>
|
|||
|
|
<li>Users must manually invoke uploading/downloading data to/from GPU. This provides better understanding how much each operation costs.</li>
|
|||
|
|
</ul>
|
|||
|
|
</div></blockquote>
|
|||
|
|
</li>
|
|||
|
|
<li><p class="first">Two layers in GPU part: host layer(nvcc-independent interface) and device(for advanced use, for sharing code compiled by nvcc):</p>
|
|||
|
|
<blockquote>
|
|||
|
|
<div><ul class="simple">
|
|||
|
|
<li>namespace pcl::cuda (can depend on CUDA headers) or pcl::gpu (completely independent from CUDA, OpenCL support in future?).</li>
|
|||
|
|
<li>namespace pcl::device for device layer, only headers.</li>
|
|||
|
|
</ul>
|
|||
|
|
</div></blockquote>
|
|||
|
|
</li>
|
|||
|
|
<li><p class="first">Async operation support???</p>
|
|||
|
|
</li>
|
|||
|
|
</ol>
|
|||
|
|
</div></blockquote>
|
|||
|
|
</div>
|
|||
|
|
<div class="section" id="keypoints-and-features">
|
|||
|
|
<h3>1.4 Keypoints and features</h3>
|
|||
|
|
<blockquote>
|
|||
|
|
<div><ol class="arabic">
|
|||
|
|
<li><p class="first">The name Feature is a bit misleading, since it has tons of meanings. Alternatives are Descriptor or FeatureDescription.</p>
|
|||
|
|
</li>
|
|||
|
|
<li><p class="first">In the feature description, there is no need in separate FeatureFromNormals class and setNormals() method, since all the required channels are contained in one input. We still need separate setSearchSurface() though.</p>
|
|||
|
|
</li>
|
|||
|
|
<li><p class="first">There exist different types of keypoints (corners, blobs, regions), so keypoint detector might return some meta-information besides the keypoint locations (scale, orientation etc.). Some channels of that meta-information are required by some descriptors. There are options how to deliver that information from keypoints to descriptor, but it should be easy to pass it if a user doesn’t change anything. This interface should be uniform to allow for switching implementations and automated benchmarking. Still one might want to set, say, custom orientations, different from what detector returned.</p>
|
|||
|
|
<blockquote>
|
|||
|
|
<div><p>to be continued…</p>
|
|||
|
|
</div></blockquote>
|
|||
|
|
</li>
|
|||
|
|
</ol>
|
|||
|
|
</div></blockquote>
|
|||
|
|
</div>
|
|||
|
|
<div class="section" id="data-slices">
|
|||
|
|
<h3>1.5 Data slices</h3>
|
|||
|
|
<p>Anything involving a slice of data should use std::size_t for indices and not int. E.g the indices of the inliers in RANSAC, the focused points in RANSAC …</p>
|
|||
|
|
</div>
|
|||
|
|
<div class="section" id="ransac">
|
|||
|
|
<h3>1.6 RANSAC</h3>
|
|||
|
|
<blockquote>
|
|||
|
|
<div><ul class="simple">
|
|||
|
|
<li>Renaming the functions and internal variables: everything should be named with _src and _tgt: we have confusing names like indices_ and indices_tgt_ (and no indices_src_), setInputCloud and setInputTarget (duuh, everything is an input, it should be setTarget, setSource), in the code, a sample is named: selection, model_ and samples. getModelCoefficients is confusing with getModel (this one should be getBestSample).</li>
|
|||
|
|
<li>no const-correctness all over, it’s pretty scary: all the get should be const, selectWithinDistance and so on too.</li>
|
|||
|
|
<li>the getModel, getInliers function should not force you to fill a vector: you should just return a const reference to the internal vector: that could allow you to save a useless copy</li>
|
|||
|
|
<li>some private members should be made protected in the sub sac models (like sac_model_registration) so that we can inherit from them.</li>
|
|||
|
|
<li>the SampleConsensusModel should be independent from point clouds so that we can create our own model for whatever library. Then, the one used in the specialize models (like sac_model_registration and so on) should inherit from it and have constructors based on PointClouds like now. Maybe we should name those PclSampleConsensusModel or something (or have SampleConsensusModelBase and keep the naming for SampleConsensusModel).</li>
|
|||
|
|
</ul>
|
|||
|
|
</div></blockquote>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
<div class="section" id="minor-changes">
|
|||
|
|
<h2>Minor changes</h2>
|
|||
|
|
</div>
|
|||
|
|
<div class="section" id="concepts">
|
|||
|
|
<h2>Concepts</h2>
|
|||
|
|
<p>See <a class="reference external" href="http://dev.pointclouds.org/issues/567">http://dev.pointclouds.org/issues/567</a>.</p>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
<div class="section" id="references">
|
|||
|
|
<h1>References</h1>
|
|||
|
|
<ul class="simple">
|
|||
|
|
<li><a class="reference external" href="www4.in.tum.de/~blanchet/api-design.pdf">The Little Manual of API Design</a></li>
|
|||
|
|
</ul>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
</div>
|
|||
|
|
<footer>
|
|||
|
|
|
|||
|
|
|
|||
|
|
<hr/>
|
|||
|
|
|
|||
|
|
<div role="contentinfo">
|
|||
|
|
<p>
|
|||
|
|
© Copyright
|
|||
|
|
|
|||
|
|
</p>
|
|||
|
|
</div>
|
|||
|
|
Built with <a href="http://sphinx-doc.org/">Sphinx</a> using a <a href="https://github.com/rtfd/sphinx_rtd_theme">theme</a> provided by <a href="https://readthedocs.org">Read the Docs</a>.
|
|||
|
|
|
|||
|
|
</footer>
|
|||
|
|
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
</section>
|
|||
|
|
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
|
|||
|
|
|
|||
|
|
<script type="text/javascript">
|
|||
|
|
jQuery(function () {
|
|||
|
|
SphinxRtdTheme.Navigation.enable(true);
|
|||
|
|
});
|
|||
|
|
</script>
|
|||
|
|
|
|||
|
|
|
|||
|
|
|
|||
|
|
|
|||
|
|
|
|||
|
|
|
|||
|
|
</body>
|
|||
|
|
</html>
|