/******************************************************************** ** Image Component Library (ICL) ** ** ** ** Copyright (C) 2006-2012 CITEC, University of Bielefeld ** ** Neuroinformatics Group ** ** Website: www.iclcv.org and ** ** http://opensource.cit-ec.de/projects/icl ** ** ** ** File : ICLGeom/src/RGBDImageSceneObject.cpp ** ** Module : ICLGeom ** ** Authors: Christof Elbrechter, Andre Ückermann ** ** ** ** ** ** Commercial License ** ** ICL can be used commercially, please refer to our website ** ** www.iclcv.org for more details. ** ** ** ** GNU General Public License Usage ** ** Alternatively, this file may be used under the terms of the ** ** GNU General Public License version 3.0 as published by the ** ** Free Software Foundation and appearing in the file LICENSE.GPL ** ** included in the packaging of this file. Please review the ** ** following information to ensure the GNU General Public License ** ** version 3.0 requirements will be met: ** ** http://www.gnu.org/copyleft/gpl.html. ** ** ** ** The development of this software was supported by the ** ** Excellence Cluster EXC 277 Cognitive Interaction Technology. ** ** The Excellence Cluster EXC 277 is a grant of the Deutsche ** ** Forschungsgemeinschaft (DFG) in the context of the German ** ** Excellence Initiative. ** ** ** *********************************************************************/ #include #include #ifdef ICL_SYSTEM_APPLE #include #include #else #include #include #endif namespace icl{ static inline float compute_depth_norm(const Vec &dir, const Vec ¢erDir){ return sprod3(dir,centerDir)/(norm3(dir)*norm3(centerDir)); } struct RGBDImageSceneObject::Data{ FixedMatrix Qi; Size size; std::vector viewRayDirs; std::vector correctionFactors; Vec viewRayOffset; RGBDMapping mapping; RGBDImageSceneObject *parent; Img32f lastCorrectedDepthImage; RGBDImageSceneObject::MappingMode mode; /// internal initialization method void init(const Size &size, const RGBDMapping &mapping, const Camera &cam, MappingMode mode, RGBDImageSceneObject *parent){ this->parent = parent; this->size = size; this->mapping = mapping; this->mode = mode; lastCorrectedDepthImage = Img32f(size,1); Qi = cam.getQMatrix().pinv(true); const int dim = size.getDim(); parent->m_vertices.resize(dim,Vec(0,0,0,1)); parent->m_vertexColors.resize(dim, GeomColor(0.0,0.4,1,1)); parent->setLockingEnabled(true); parent->setVisible(Primitive::vertex,true); parent->setPointSize(3); parent->setPointSmoothingEnabled(false); viewRayDirs.resize(dim); correctionFactors.resize(dim); Array2D viewRays = cam.getAllViewRays(); viewRayOffset = viewRays(0,0).offset; for(int y=0;yaddProperty("openmp threads","range","[1,16]:1",1,0, "number of threads to use if openmp is available"); parent->addProperty("update time","info","","inf",0, "benchmarking result"); parent->addProperty("rendering.point size","range","[1:10]:1",3,0, "point size for rendering (only used of the property\n" "rendering.mode is set to points)"); parent->addProperty("rendering.mode","menu","points,triangles","triangles",0, "Mode for rendering:\n" "points: In this case, the scene object is rendered\n" " in the default manner. It's vertices are\n" " rendered as colored points\n" "triangles: In this, case the SceneObjects vertices are not\n" " rendered, but a custom triangle based rendering\n" " method is called"); parent->addProperty("rendering.max Dz","range","[10,100]:1",15,0, "(For triangle rendering only)\n" "This value defines the max depth difference for\n" "for triangles. Triangles, whose corners depth\n" "difference is larger than this value are not rendered."); parent->addProperty("rendering.max depth","range","[300,10000]:1",5000,0, "(For triangle rendering only)\n" "Triangles that have a corner that is further away than\n" "this value (in mm) are not drawn"); parent->addProperty("rendering.min depth","range","[1,5000]:1",500,0, "(For triangle rendering only)\n" "Triangles that have a corner that is closer to the camera\n" "than this value (in mm) are not drawn"); parent->registerCallback(function(this,&Data::property_callback)); } void property_callback(const Configurable::Property &p){ const std::string &n = p.name; if(n == "update time") return; if(n == "rendering.point size") parent->setPointSize(parent->getPropertyValue(n)); } }; /** This could be useful on the graphics card (however there's still a bug in this!) static inline Vec3 cast_ray(const FixedMatrix &Qi, float x, float y, const Vec &p){ const float dirX = Qi(0,0) * x + Qi(1,0) * y + Qi(2,0); const float dirY = Qi(0,1) * x + Qi(1,1) * y + Qi(2,1); const float dirZ = Qi(0,2) * x + Qi(1,2) * y + Qi(2,2); const float dirH = Qi(0,3) * x + Qi(1,3) * y + Qi(2,3); const float sign = dirH > 0 ? -1 : 1; const float dirX2 = p[0] - dirX/dirH; const float dirY2 = p[1] - dirY/dirH; const float dirZ2 = p[2] - dirZ/dirH; const float len = ::sqrt( dirX2*dirX2 + dirY2*dirY2 + dirZ2*dirZ2); return Vec3(sign * dirX/len, sign * dirY/len, sign * dirZ/len); } */ Camera RGBDImageSceneObject::get_default_kinect_camera(const Size &size) { return Camera(); } RGBDMapping RGBDImageSceneObject::get_default_kinect_rgbd_mapping(const Size &size) { RGBDMapping m; if(size != Size::VGA){ ERROR_LOG("right now, only mappings for size::VGA are available"); (Mat&)m = Mat::id(); return m; } (Mat&)m = Mat(-22701,240.874,35.7513,-397184, 7.69983, -22436.1, 258.607, -1.41846e+06, 0, 0, 0, 0, -0.0563062, 0.596686, 0.800497, -25860.9); return m; } RGBDImageSceneObject::RGBDImageSceneObject(const Size &size,MappingMode mode):m_data(new Data){ m_data->init(size, get_default_kinect_rgbd_mapping(size), get_default_kinect_camera(size), mode,this); } RGBDImageSceneObject::RGBDImageSceneObject(const Size &size, const RGBDMapping &mapping, const Camera &cam, MappingMode mode):m_data(new Data){ m_data->init(size,mapping,cam, mode,this); } RGBDImageSceneObject::~RGBDImageSceneObject(){ delete m_data; } const RGBDMapping &RGBDImageSceneObject::getMapping() const{ return m_data->mapping; } void RGBDImageSceneObject::setMapping(const RGBDMapping &mapping) { m_data->mapping = mapping; } const Size &RGBDImageSceneObject::getSize() const { return m_data->size; } const std::vector &RGBDImageSceneObject::getViewRayDirs() const { return m_data->viewRayDirs; } const std::vector &RGBDImageSceneObject::getCorrectionFactors() const { return m_data->correctionFactors; } const Img32f &RGBDImageSceneObject::getCorrectedDepthImage() const{ return m_data->lastCorrectedDepthImage; } void RGBDImageSceneObject::update(const Img32f &depthImage, const Img8u *rgbImage){ const int W = m_data->size.width; const int H = m_data->size.height; const int DIM = W*H; const Rect imageRect(Point::null,m_data->size); lock(); { #ifdef HAVE_IPP ippsMul_32f(depthImage.begin(0), m_data->correctionFactors.data(), m_data->lastCorrectedDepthImage.begin(0), DIM); #else std::transform(depthImage.begin(0), depthImage.end(0), m_data->correctionFactors.begin(), m_data->lastCorrectedDepthImage.begin(0), std::multiplies()); #endif } const icl32f *d = m_data->lastCorrectedDepthImage.begin(0); Time now = Time::now(); const int ompThreads = getPropertyValue("openmp threads"); omp_set_num_threads(ompThreads); // chache some local variables const RGBDMapping M = m_data->mapping; const Vec off = m_data->viewRayOffset; const FixedMatrix Qi = m_data->Qi; const bool xyzMapping = m_data->mode == XYZ_WORLD; if(rgbImage){ const Channel8u r = (*rgbImage)[0], g = (*rgbImage)[1], b = (*rgbImage)[2]; for(int y=0;yviewRayDirs[idx]; const float depthValue = d[idx];// * dir[3]; Vec &v = m_vertices[idx]; v[0] = off[0] + depthValue * dir[0]; v[1] = off[1] + depthValue * dir[1]; v[2] = off[2] + depthValue * dir[2]; /// todo: move this if out of this loop! Point p = xyzMapping ? M.apply(v[0],v[1],v[2]) : M.apply(x,y,depthValue); GeomColor &c = m_vertexColors[idx]; if(imageRect.contains(p.x, p.y)){ static const float FACTOR = 1.0/255; c[0] = r(p.x,p.y) * FACTOR; c[1] = g(p.x,p.y) * FACTOR; c[2] = b(p.x,p.y) * FACTOR; c[3] = 1; }else{ c[3] = 0; } } } } }else{ // no rgb image -> creation of the pointcloud only const int DIM = W*H; #pragma omp parallel { #pragma omp for for(int i=0;iviewRayDirs[i]; const float depthValue = d[i];// * dir[3]; Vec &v = m_vertices[i]; v[0] = off[0] + depthValue * dir[0]; v[1] = off[1] + depthValue * dir[1]; v[2] = off[2] + depthValue * dir[2]; } } } const float dtMS = (Time::now()-now).toMilliSecondsDouble(); setPropertyValue("update time", str(0.01*(int)(dtMS*100)) + "ms" ); unlock(); } Array2D RGBDImageSceneObject::getPoints() { return Array2D(m_data->size, m_vertices.data(), false); } const Array2D RGBDImageSceneObject::getPoints() const { return const_cast(this)->getPoints(); } Array2D RGBDImageSceneObject::getColors() { return Array2D(m_data->size, m_vertexColors.data(), false); } const Array2D RGBDImageSceneObject::getColors() const { return const_cast(this)->getColors(); } template void map_image(const Img &src, Img &dst, const RGBDMapping M, const float *D, const Array2D &ps, RGBDImageSceneObject::MappingMode mode){ const int W = src.getWidth(), H = src.getHeight(); const Rect imageRect(0,0,W,H); const int C = src.getChannels(); for(int c=0;c s = src[c]; Channel d = dst[c]; if(mode == RGBDImageSceneObject::XYZ_WORLD){ for(int y=0;y()); const int maxDepth = getPropertyValue("rendering.max depth"); const int minDepth = getPropertyValue("rendering.min depth"); // TODO dont draw if max dz is too large, use triangle strip const int W1 = m_data->size.width, H1 = m_data->size.height -1; glBegin(GL_TRIANGLES); const Channel32f d = m_data->lastCorrectedDepthImage[0]; const Array2D colors = getColors(); const Array2D points = getPoints(); for(int y=0;y maxDepth) ) continue; if( relative_distance( d(x,y), d(x+1,y), d(x,y+1), maxDZ )) { glColor3fv(&colors(x,y)[0]); glVertex3fv(&points(x,y)[0]); glColor3fv(&colors(x+1,y)[0]); glVertex3fv(&points(x+1,y)[0]); glColor3fv(&colors(x,y+1)[0]); glVertex3fv(&points(x,y+1)[0]); } if(relative_distance( d(x+1,y+1), d(x+1,y), d(x,y+1), maxDZ) ){ glColor3fv(&colors(x+1,y+1)[0]); glVertex3fv(&points(x+1,y+1)[0]); glColor3fv(&colors(x,y+1)[0]); glVertex3fv(&points(x,y+1)[0]); glColor3fv(&colors(x+1,y)[0]); glVertex3fv(&points(x+1,y)[0]); } } } glEnd(); } void RGBDImageSceneObject::mapImage(const ImgBase *src, ImgBase **dst){ if(!src) throw ICLException(str(__FUNCTION__)+": given source image was null"); if(!dst) throw ICLException(str(__FUNCTION__)+": given destination image was null"); ensureCompatible(dst,src->getDepth(),src->getSize(),src->getChannels()); (*dst)->setTime(src->getTime()); switch(src->getDepth()){ #define ICL_INSTANTIATE_DEPTH(D) \ case depth##D: map_image(*src->as##D(), *(*dst)->as##D(), \ m_data->mapping, \ m_data->lastCorrectedDepthImage.begin(0), \ getPoints(), m_data->mode); \ break; ICL_INSTANTIATE_ALL_DEPTHS #undef ICL_INSTANTIATE_DEPTH } } REGISTER_CONFIGURABLE_DEFAULT(RGBDImageSceneObject); } // end namespace icl