00001 /* +---------------------------------------------------------------------------+ 00002 | The Mobile Robot Programming Toolkit (MRPT) C++ library | 00003 | | 00004 | http://www.mrpt.org/ | 00005 | | 00006 | Copyright (C) 2005-2011 University of Malaga | 00007 | | 00008 | This software was written by the Machine Perception and Intelligent | 00009 | Robotics Lab, University of Malaga (Spain). | 00010 | Contact: Jose-Luis Blanco <jlblanco@ctima.uma.es> | 00011 | | 00012 | This file is part of the MRPT project. | 00013 | | 00014 | MRPT is free software: you can redistribute it and/or modify | 00015 | it under the terms of the GNU General Public License as published by | 00016 | the Free Software Foundation, either version 3 of the License, or | 00017 | (at your option) any later version. | 00018 | | 00019 | MRPT is distributed in the hope that it will be useful, | 00020 | but WITHOUT ANY WARRANTY; without even the implied warranty of | 00021 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 00022 | GNU General Public License for more details. | 00023 | | 00024 | You should have received a copy of the GNU General Public License | 00025 | along with MRPT. If not, see <http://www.gnu.org/licenses/>. | 00026 | | 00027 +---------------------------------------------------------------------------+ */ 00028 #ifndef CObservation3DRangeScan_H 00029 #define CObservation3DRangeScan_H 00030 00031 #include <mrpt/utils/CSerializable.h> 00032 #include <mrpt/utils/CImage.h> 00033 #include <mrpt/slam/CObservation.h> 00034 #include <mrpt/poses/CPose3D.h> 00035 #include <mrpt/poses/CPose2D.h> 00036 00037 #include <mrpt/math/CPolygon.h> 00038 00039 00040 namespace mrpt 00041 { 00042 namespace slam 00043 { 00044 00045 DEFINE_SERIALIZABLE_PRE_CUSTOM_BASE_LINKAGE( CObservation3DRangeScan, CObservation,OBS_IMPEXP ) 00046 00047 /** Declares a class derived from "CObservation" that 00048 * encapsules a 3D range scan measurement (e.g. from a time of flight range camera). 00049 * This kind of observations can carry one or more of these data fields: 00050 * - 3D point cloud (as float's). 00051 * - 2D range image (as a matrix): Each entry in the matrix "rangeImage(ROW,COLUMN)" contains a distance or a depth (in meters), depending on \a range_is_depth. 00052 * - 2D intensity (grayscale or RGB) image (as a mrpt::utils::CImage): For SwissRanger cameras, a logarithmic A-law compression is used to convert the original 16bit intensity to a more standard 8bit graylevel. 00053 * - 2D confidence image (as a mrpt::utils::CImage): For each pixel, a 0x00 and a 0xFF mean the lowest and highest confidence levels, respectively. 00054 * 00055 * The coordinates of the 3D point cloud are in meters with respect to the depth camera origin of coordinates 00056 * (in SwissRanger, the front face of the camera: a small offset ~1cm in front of the physical focal point), 00057 * with the +X axis pointing forward, +Y pointing left-hand and +Z pointing up. 00058 * The field CObservation3DRangeScan::relativePoseIntensityWRTDepth describes the change of coordinates from 00059 * the depth camera to the intensity (RGB or grayscale) camera. In a SwissRanger camera both cameras coincide, 00060 * so this pose is just a rotation (0,0,0,-90deg,0,-90deg). But in 00061 * Microsoft Kinect there is also an offset, as shown in this figure: 00062 * 00063 * <div align=center> 00064 * <img src="CObservation3DRangeScan_figRefSystem.png"> 00065 * </div> 00066 * 00067 * In any case, check the field \a relativePoseIntensityWRTDepth, or the method \a doDepthAndIntensityCamerasCoincide() 00068 * to determine if both frames of reference coincide, since even for Kinect cameras both can coincide if the images 00069 * have been rectified. 00070 * 00071 * The 2D images and matrices are stored as common images, with an up->down rows order and left->right, as usual. 00072 * Optionally, the intensity and confidence channels can be set to delayed-load images for off-rawlog storage so it saves 00073 * memory by having loaded in memory just the needed images. See the methods load() and unload(). 00074 * Due to the intensive storage requirements of this kind of observations, this observation is the only one in MRPT 00075 * for which it's recommended to always call "load()" and "unload()" before and after using the observation, *ONLY* when 00076 * the observation was read from a rawlog dataset, in order to make sure that all the externally stored data fields are 00077 * loaded and ready in memory. 00078 * 00079 * Classes that grab observations of this type are: 00080 * - mrpt::hwdrivers::CSwissRanger3DCamera 00081 * - mrpt::hwdrivers::CKinect 00082 * 00083 * There are two sets of calibration parameters: 00084 * - cameraParams: Projection parameters of the depth camera. 00085 * - cameraParamsIntensity: Projection parameters of the intensity (gray-level or RGB) camera. 00086 * 00087 * In some cameras, like SwissRanger, both are the same. Also, it is possible in Kinect to rectify the range images such both cameras 00088 * seem to coincide and then both sets of camera parameters will be identical. 00089 * 00090 * Range data can be interpreted in two different ways depending on the 3D camera (this field is already set to the 00091 * correct setting when grabbing observations from an mrpt::hwdrivers sensor): 00092 * - range_is_depth=true -> Kinect-like ranges: entries of \a rangeImage are distances along the +X axis 00093 * - range_is_depth=false -> Ranges in \a rangeImage are actual distances in 3D. 00094 * 00095 * 3D point clouds can be generated at any moment after grabbing with CObservation3DRangeScan::project3DPointsFromDepthImage() 00096 * 00097 * \note Starting at serialization version 2 (MRPT 0.9.1+), the confidence channel is stored as an image instead of a matrix to optimize memory and disk space. 00098 * \note Starting at serialization version 3 (MRPT 0.9.1+), the 3D point cloud and the rangeImage can both be stored externally to save rawlog space. 00099 * \note Starting at serialization version 5 (MRPT 0.9.5+), the new field \a range_is_depth. 00100 * 00101 * \sa mrpt::hwdrivers::CSwissRanger3DCamera, mrpt::hwdrivers::CKinect, CObservation 00102 * \ingroup mrpt_obs_grp 00103 */ 00104 class OBS_IMPEXP CObservation3DRangeScan : public CObservation 00105 { 00106 // This must be added to any CSerializable derived class: 00107 DEFINE_SERIALIZABLE( CObservation3DRangeScan ) 00108 00109 protected: 00110 bool m_points3D_external_stored; //!< If set to true, m_points3D_external_file is valid. 00111 std::string m_points3D_external_file; //!< 3D points are in CImage::IMAGES_PATH_BASE+<this_file_name> 00112 00113 bool m_rangeImage_external_stored; //!< If set to true, m_rangeImage_external_file is valid. 00114 std::string m_rangeImage_external_file; //!< rangeImage is in CImage::IMAGES_PATH_BASE+<this_file_name> 00115 00116 public: 00117 CObservation3DRangeScan( ); //!< Default constructor 00118 virtual ~CObservation3DRangeScan( ); //!< Destructor 00119 00120 /** @name Delayed-load manual control methods. 00121 @{ */ 00122 /** Makes sure all images and other fields which may be externally stored are loaded in memory. 00123 * Note that for all CImages, calling load() is not required since the images will be automatically loaded upon first access, so load() shouldn't be needed to be called in normal cases by the user. 00124 * If all the data were alredy loaded or this object has no externally stored data fields, calling this method has no effects. 00125 * \sa unload 00126 */ 00127 virtual void load() const; 00128 /** Unload all images, for the case they being delayed-load images stored in external files (othewise, has no effect). 00129 * \sa load 00130 */ 00131 virtual void unload(); 00132 /** @} */ 00133 00134 /** Compute the 3D points coordinates from the depth image (\a rangeImage) and the depth camera camera parameters (\a cameraParams). 00135 * There exist two set of formulas for projecting the i'th point, depending on the value of "range_is_depth". 00136 * In all formulas below, "rangeImage" is the matrix of ranges and the pixel coordinates are (r,c). 00137 * 00138 * 1) [range_is_depth=true] With "range equals depth" or "Kinect-like depth mode": the range values 00139 * are in fact distances along the "+X" axis, not real 3D ranges (this is the way Kinect reports ranges): 00140 * 00141 * \code 00142 * x(i) = rangeImage(r,c) 00143 * y(i) = (r_cx - c) * x(i) / r_fx 00144 * z(i) = (r_cy - r) * x(i) / r_fy 00145 * \endcode 00146 * 00147 * 00148 * 2) [range_is_depth=false] With "normal ranges": range means distance in 3D. This must be set when 00149 * processing data from the SwissRange 3D camera, among others. 00150 * 00151 * \code 00152 * Ky = (r_cx - c)/r_fx 00153 * Kz = (r_cy - r)/r_fy 00154 * 00155 * x(i) = rangeImage(r,c) / sqrt( 1 + Ky^2 + Kz^2 ) 00156 * y(i) = Ky * x(i) 00157 * z(i) = Kz * x(i) 00158 * \endcode 00159 * 00160 * \param[in] PROJ3D_USE_LUT (Only when range_is_depth=true) Whether to use a Look-up-table (LUT) to speed up the conversion. It's thread safe in all situations <b>except</b> when you call this method from different threads <b>and</b> with different camera parameter matrices. In all other cases, it's a good idea to left it enabled. 00161 * 00162 * \note In MRPT < 0.9.5, this method always assumes that ranges were in Kinect-like format. 00163 */ 00164 void project3DPointsFromDepthImage(const bool PROJ3D_USE_LUT=true); 00165 00166 bool hasPoints3D; //!< true means the field points3D contains valid data. 00167 std::vector<float> points3D_x; //!< If hasPoints3D=true, the X coordinates of the 3D point cloud detected by the camera. \sa resizePoints3DVectors 00168 std::vector<float> points3D_y; //!< If hasPoints3D=true, the Y coordinates of the 3D point cloud detected by the camera. \sa resizePoints3DVectors 00169 std::vector<float> points3D_z; //!< If hasPoints3D=true, the Z coordinates of the 3D point cloud detected by the camera. \sa resizePoints3DVectors 00170 00171 /** Use this method instead of resizing all three \a points3D_x, \a points3D_y & \a points3D_z to allow the usage of the internal memory pool. */ 00172 void resizePoints3DVectors(const size_t nPoints); 00173 00174 // 3D points external storage functions --------- 00175 inline bool points3D_isExternallyStored() const { return m_points3D_external_stored; } 00176 inline std::string points3D_getExternalStorageFile() const { return m_points3D_external_file; } 00177 void points3D_getExternalStorageFileAbsolutePath(std::string &out_path) const; 00178 inline std::string points3D_getExternalStorageFileAbsolutePath() const { 00179 std::string tmp; 00180 points3D_getExternalStorageFileAbsolutePath(tmp); 00181 return tmp; 00182 } 00183 void points3D_convertToExternalStorage( const std::string &fileName, const std::string &use_this_base_dir ); //!< Users won't normally want to call this, it's only used from internal MRPT programs. 00184 // --------- 00185 00186 bool hasRangeImage; //!< true means the field rangeImage contains valid data 00187 mrpt::math::CMatrix rangeImage; //!< If hasRangeImage=true, a matrix of floats with the range data as captured by the camera (in meters) \sa range_is_depth 00188 bool range_is_depth; //!< true: Kinect-like ranges: entries of \a rangeImage are distances along the +X axis; false: Ranges in \a rangeImage are actual distances in 3D. 00189 00190 void rangeImage_setSize(const int HEIGHT, const int WIDTH); //!< Similar to calling "rangeImage.setSize(H,W)" but this method provides memory pooling to speed-up the memory allocation. 00191 00192 // Range Matrix external storage functions --------- 00193 inline bool rangeImage_isExternallyStored() const { return m_rangeImage_external_stored; } 00194 inline std::string rangeImage_getExternalStorageFile() const { return m_rangeImage_external_file; } 00195 void rangeImage_getExternalStorageFileAbsolutePath(std::string &out_path) const; 00196 inline std::string rangeImage_getExternalStorageFileAbsolutePath() const { 00197 std::string tmp; 00198 rangeImage_getExternalStorageFileAbsolutePath(tmp); 00199 return tmp; 00200 } 00201 void rangeImage_convertToExternalStorage( const std::string &fileName, const std::string &use_this_base_dir ); //!< Users won't normally want to call this, it's only used from internal MRPT programs. 00202 /** Forces marking this observation as non-externally stored - it doesn't anything else apart from reseting the corresponding flag (Users won't normally want to call this, it's only used from internal MRPT programs) */ 00203 void rangeImage_forceResetExternalStorage() { m_rangeImage_external_stored=false; } 00204 // --------- 00205 00206 bool hasIntensityImage; //!< true means the field intensityImage contains valid data 00207 mrpt::utils::CImage intensityImage; //!< If hasIntensityImage=true, a color or gray-level intensity image of the same size than "rangeImage" 00208 00209 bool hasConfidenceImage; //!< true means the field confidenceImage contains valid data 00210 mrpt::utils::CImage confidenceImage; //!< If hasConfidenceImage=true, an image with the "confidence" value [range 0-255] as estimated by the capture drivers. 00211 00212 mrpt::utils::TCamera cameraParams; //!< Projection parameters of the depth camera. 00213 mrpt::utils::TCamera cameraParamsIntensity; //!< Projection parameters of the intensity (graylevel or RGB) camera. 00214 00215 /** Relative pose of the intensity camera wrt the depth camera (which is the coordinates origin for this observation). 00216 * In a SwissRanger camera, this will be (0,0,0,-90deg,0,-90deg) since both cameras coincide. 00217 * In a Kinect, this will include a small lateral displacement and a rotation, according to the drawing on the top of this page. 00218 * \sa doDepthAndIntensityCamerasCoincide 00219 */ 00220 mrpt::poses::CPose3D relativePoseIntensityWRTDepth; 00221 00222 /** Return true if \a relativePoseIntensityWRTDepth equals the pure rotation (0,0,0,-90deg,0,-90deg) (with a small comparison epsilon) 00223 * \sa relativePoseIntensityWRTDepth 00224 */ 00225 bool doDepthAndIntensityCamerasCoincide() const; 00226 00227 00228 float maxRange; //!< The maximum range allowed by the device, in meters (e.g. 8.0m, 5.0m,...) 00229 CPose3D sensorPose; //!< The 6D pose of the sensor on the robot. 00230 float stdError; //!< The "sigma" error of the device in meters, used while inserting the scan in an occupancy grid. 00231 00232 00233 /** A general method to retrieve the sensor pose on the robot. 00234 * Note that most sensors will return a full (6D) CPose3D, but see the derived classes for more details or special cases. 00235 * \sa setSensorPose 00236 */ 00237 void getSensorPose( CPose3D &out_sensorPose ) const { out_sensorPose = sensorPose; } 00238 00239 /** A general method to change the sensor pose on the robot. 00240 * Note that most sensors will use the full (6D) CPose3D, but see the derived classes for more details or special cases. 00241 * \sa getSensorPose 00242 */ 00243 void setSensorPose( const CPose3D &newSensorPose ) { sensorPose = newSensorPose; } 00244 00245 void swap(CObservation3DRangeScan &o); //!< Very efficient method to swap the contents of two observations. 00246 00247 void getZoneAsObs( CObservation3DRangeScan &obs, const unsigned int &r1, const unsigned int &r2, const unsigned int &c1, const unsigned int &c2 ); 00248 00249 /** A Levenberg-Marquart-based optimizer to recover the calibration parameters of a 3D camera given a range (depth) image and the corresponding 3D point cloud. 00250 * \param camera_offset The offset (in meters) in the +X direction of the point cloud. It's 1cm for SwissRanger SR4000. 00251 * \return The final average reprojection error per pixel (typ <0.05 px) 00252 */ 00253 static double recoverCameraCalibrationParameters( 00254 const CObservation3DRangeScan &in_obs, 00255 mrpt::utils::TCamera &out_camParams, 00256 const double camera_offset = 0.01 ); 00257 00258 00259 }; // End of class def. 00260 00261 00262 } // End of namespace 00263 00264 namespace utils 00265 { 00266 using namespace ::mrpt::slam; 00267 // Specialization must occur in the same namespace 00268 MRPT_DECLARE_TTYPENAME_PTR(CObservation3DRangeScan) 00269 } 00270 00271 } // End of namespace 00272 00273 #endif
| Page generated by Doxygen 1.7.5 for MRPT 0.9.5 SVN: at Thu Oct 13 21:25:36 UTC 2011 |