/******************************************************************** ** Image Component Library (ICL) ** ** ** ** Copyright (C) 2006-2013 CITEC, University of Bielefeld ** ** Neuroinformatics Group ** ** Website: www.iclcv.org and ** ** http://opensource.cit-ec.de/projects/icl ** ** ** ** File : ICLFilter/apps/color-segmentation/color-segmentation.c ** ** pp ** ** Module : ICLFilter ** ** Authors: Christof Elbrechter ** ** ** ** ** ** GNU LESSER GENERAL PUBLIC LICENSE ** ** This file may be used under the terms of the GNU Lesser General ** ** Public License version 3.0 as published by the ** ** ** ** Free Software Foundation and appearing in the file LICENSE.LGPL ** ** included in the packaging of this file. Please review the ** ** following information to ensure the license requirements will ** ** be met: http://www.gnu.org/licenses/lgpl-3.0.txt ** ** ** ** 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 <ICLQt/Common.h> #include <ICLFilter/ColorSegmentationOp.h> #include <ICLFilter/MedianOp.h> #include <ICLFilter/MorphologicalOp.h> #include <ICLCore/CCFunctions.h> #include <ICLCore/Color.h> #include <ICLCV/RegionDetector.h> #include <ICLGeom/Scene.h> #include <ICLGeom/GeomDefs.h> VSplit gui; #define MAX_LUT_3D_DIM 1000000 GenericGrabber grabber; SmartPtr<ColorSegmentationOp> segmenter; Mutex mtex; Img8u currLUT,currLUTColor,segImage; std::vector<ImageRegion> drawIM_AND_SEG; std::vector<ImageRegion> drawLUT; int hoveredClassID = -1; void cc_util_hls_to_rgb_i(int h, int l, int s, int &r, int &g, int &b){ float R,G,B; cc_util_hls_to_rgb(h,l,s,R,G,B); r = R; g = G; b = B; // DEBUG_LOG("h:" << h << " l:" << l << " s:" << s << " --> r:" << r << " g:" << g << " b:" << b); } void rgb_id(int r, int g, int b, int &r2, int &g2, int &b2){ r2=r; g2=g; b2=b; } Scene scene; struct LUT3DSceneObject : public SceneObject { int w,h,t,dx,dy,dz,dim; std::vector<int> rs,gs,bs; Mutex mtex; virtual void lock(){ mtex.lock(); } virtual void unlock(){ mtex.unlock(); } LUT3DSceneObject(){ segmenter->getLUTDims(w,h,t); dim = w*h*t; rs.resize(dim); gs.resize(dim); bs.resize(dim); format f = segmenter->getSegmentationFormat(); void (*cc_func)(int,int,int,int&,int&,int&) = ( f == formatYUV ? cc_util_yuv_to_rgb : f == formatHLS ? cc_util_hls_to_rgb_i : rgb_id ); float cx = float(w)/2; float cy = float(h)/2; float cz = float(t)/2; dx = 256/w; dy = 256/h; dz = 256/t; int i=0; for(int z=0;z<t;++z){ for(int y=0;y<h;++y){ for(int x=0;x<w;++x,++i){ cc_func(w==1 ? 127 : x*dx, h==1 ? 127 : y*dy, t==1 ? 127 : z*dz, rs[i],gs[i],bs[i]); SceneObject *o = addCube(x-cx+0.5,y-cy+0.5,z-cz+0.5,1); o->setColor(Primitive::quad, GeomColor(rs[i],gs[i],bs[i],255)); o->setColor(Primitive::line, GeomColor(255,255,255,255)); o->setVisible(Primitive::line,false); o->setVisible(Primitive::vertex,false); } } } SceneObject *o = addCuboid(0,0,0,w,h,t); o->setVisible(Primitive::line,true); o->setVisible(Primitive::quad,false); o->setVisible(Primitive::vertex,false); o->setColor(Primitive::line,GeomColor(255,255,255,255)); float wl = 1.3*(w/2); float hl = 1.3*(h/2); float tl = 1.3*(t/2); addVertex(Vec(-wl,0,0,1),geom_invisible()); addVertex(Vec(wl,0,0,1),geom_invisible()); addVertex(Vec(0,-hl,0,1),geom_invisible()); addVertex(Vec(0,hl,0,1),geom_invisible()); addVertex(Vec(0,0,-tl,1),geom_invisible()); addVertex(Vec(0,0,tl,1),geom_invisible()); addLine(0,1,geom_red(255)); addLine(2,3,geom_green(255)); addLine(4,5,geom_blue(255)); for(int i=0;i<6;++i){ std::string s = (i&1)? "" : "-"; switch(segmenter->getSegmentationFormat()){ case formatYUV: s += "yuv"[i/2]; break; case formatRGB: s += "rgb"[i/2]; break; case formatHLS: s += "hls"[i/2]; break; default: throw ICLException("invalid segmentation format"); } addText(i,s,2); } } void update(float alpha){ const icl8u *lut = segmenter->getLUT(); for(int i=0;i<dim;++i){ m_children[i]->setVisible( lut[i] ); m_children[i]->setColor(Primitive::quad,GeomColor(rs[i],gs[i],bs[i],alpha)); m_children[i]->setVisible(Primitive::line,hoveredClassID == lut[i]); } // createDisplayList(); // this does not help yet, since we update // the object every cycle even if nothing was changed ... } } *lut3D=0; void init_3D_LUT(){ lut3D = new LUT3DSceneObject; scene.addObject(lut3D); } void highlight_regions(int classID){ hoveredClassID = classID; drawIM_AND_SEG.clear(); drawLUT.clear(); static RegionDetector rdLUT(1,1<<22,1,255); static RegionDetector rdSEG(1,1<<22,1,255); drawLUT = rdLUT.detect(&currLUT); if(classID < 1) return; const std::vector<ImageRegion> &rseg = rdSEG.detect(&segImage); for(unsigned int i=0;i<rseg.size();++i){ if(rseg[i].getVal() == classID){ drawIM_AND_SEG.push_back(rseg[i]); } } } void mouse(const MouseEvent &e){ Mutex::Locker lock(mtex); if(!currLUT.getDim()) return; static const ICLWidget *wIM = *gui.get<DrawHandle>("image"); static const ICLWidget *wLUT = *gui.get<DrawHandle>("lut"); static const ICLWidget *wSEG = *gui.get<DrawHandle>("seg"); Point p = e.getPos(); if(e.isLeaveEvent()){ highlight_regions(-1); return; } if(e.getWidget() == wLUT){ highlight_regions(currLUT.getImageRect().contains(p.x,p.y) ? currLUT(p.x,p.y,0) : 0 ); if(e.isPressEvent()){ gui["currClass"] = (hoveredClassID-1); } }else if (e.getWidget() == wIM){ int cc = gui["currClass"]; int r = gui["radius"]; std::vector<double> c = e.getColor(); if(c.size() == 3){ if(e.isLeft()){ segmenter->lutEntry(formatRGB,(int)c[0],(int)c[1],(int)c[2],r,r,r, (!gui["lb"].as<bool>()) * (cc+1) ); } highlight_regions(segmenter->classifyPixel(c[0],c[1],c[2])); if(e.isRight()){ gui["currClass"] = (hoveredClassID-1); } } }else if(e.getWidget() == wSEG){ highlight_regions(segImage.getImageRect().contains(p.x,p.y) ? segImage(p.x,p.y,0) : 0); if(e.isPressEvent()){ gui["currClass"] = (hoveredClassID-1); } } } void load_dialog(){ try{ segmenter->load(openFileDialog("PGM-Files (*.pgm);;Zipped PGM Files (*.pgm.gz);;All Files (*)")); }catch(...){} } void save_dialog(){ try{ segmenter->save(saveFileDialog("PGM-Files (*.pgm);;Zipped PGM Files (*.pgm.gz)")); }catch(...){} } void clear_lut(){ Mutex::Locker lock(mtex); segmenter->clearLUT(0); } void init(){ std::ostringstream classes; int n = pa("-n"); for(int i=1;i<=n;++i){ classes << "class " << i << ','; } if(pa("-r")){ GenericGrabber::resetBus(); } grabber.init(pa("-i")); grabber.useDesired(formatRGB); grabber.useDesired(depth8u); if( pa("-l").as<bool>() && pa("-s").as<bool>()){ WARNING_LOG("program arguments -l and -s are exclusive:(-l is used here)"); } segmenter = new ColorSegmentationOp(pa("-s",0),pa("-s",1),pa("-s",2),pa("-f")); if(pa("-l")){ segmenter->load(pa("-l")); } gui << ( HSplit() << Draw().handle("image").minSize(16,12).label("camera image") << Draw().handle("seg").minSize(16,12).label("segmentation result") ) << ( HSplit() << (Tab("2D,3D").handle("tab") << ( HBox() << Draw().handle("lut").minSize(16,12).label("lut") << ( VBox().maxSize(3,100).minSize(4,1) << Combo("x,y,z").handle("zAxis") << Slider(0,255,0,true).out("z").label("vis. plane") ) ) << ( HBox() << Draw3D(Size::VGA).handle("lut3D") << Slider(0,255,200,true).maxSize(2,100).out("alpha").label("alpha") ) ) << ( VBox() << ( HBox() << Combo(classes.str()).handle("currClass").label("current class") << Button("current class","background").label("left button").handle("lb") ) << Slider(0,255,4).out("radius").label("color radius") << (HBox().label("smooth LUT") << Slider(0,27,10).out("smoothThresh").label("threshold") << Button("do it").handle("smooth") ) << ( HBox() <<Button("load").handle("load") << Button("save").handle("save") ) << ( HBox() << CheckBox("pre median").out("preMedian") << CheckBox("post median").out("postMedian") ) << ( HBox() << CheckBox("post dilation").out("postDilatation") << CheckBox("post erosion").out("postErosion") ) << ( HBox() << Label("?").handle("time").label("time for segm.") << Fps(10).handle("fps").label("system fps") ) << ( HBox() << CamCfg("") << Button("clear").handle("clear") ) ) ) << Show(); gui["seg"].install(new MouseHandler(mouse)); gui["image"].install(new MouseHandler(mouse)); gui["lut"].install(new MouseHandler(mouse)); DrawHandle lut = gui["lut"]; DrawHandle seg = gui["seg"]; lut->setRangeMode(ICLWidget::rmAuto); seg->setRangeMode(ICLWidget::rmAuto); gui["load"].registerCallback(load_dialog); gui["save"].registerCallback(save_dialog); gui["clear"].registerCallback(clear_lut); int dim = ( (1+(0xff >> pa("-s",0).as<int>())) *(1+(0xff >> pa("-s",1).as<int>())) *(1+(0xff >> pa("-s",2).as<int>())) ); if(dim <= MAX_LUT_3D_DIM){ init_3D_LUT(); scene.addCamera(Camera(Vec(0,0,100,1),Vec(0,0,-1,1),Vec(1,0,0,1))); DrawHandle3D lut3D = gui["lut3D"]; lut3D->link(scene.getGLCallback(0)); lut3D->install(scene.getMouseHandler(0)); } } void run(){ static ButtonHandle smooth = gui["smooth"]; static int &smoothThresh = gui.get<int>("smoothThresh"); if(smooth.wasTriggered()){ icl8u *lut = segmenter->getLUT(); int w, h, t; segmenter->getLUTDims(w,h,t); std::vector<icl8u> buf(w*h*t); std::vector<icl8u*> data; for(int i=0;i<t;++i){ data.push_back(lut+w*h*i); } Img8u l(Size(w,h), t, data); int hs[256]={0}; int n = pa("-n"); for(int z=1;z<t-1;++z){ for(int y=1;y<h-1;++y){ for(int x=1;x<w-1;++x){ std::fill(hs,hs+n+1,0); for(int zz=-1;zz<2;++zz){ for(int yy=-1;yy<2;++yy){ for(int xx=-1;xx<2;++xx){ hs[ l(x+xx,y+yy,z+zz) ]++; } } } int imax = (int)(std::max_element(hs+1,hs+n+1) - hs); if(hs[imax] < smoothThresh) { buf[x + w*y + w*h * z] = 0; }else{ buf[x + w*y + w*h * z] = imax; } } } } std::copy(buf.begin(),buf.end(),lut); } static const Point xys[3]={Point(1,2),Point(0,2),Point(0,1)}; DrawHandle image = gui["image"]; DrawHandle lut = gui["lut"]; DrawHandle seg = gui["seg"]; LabelHandle time = gui["time"]; bool &preMedian = gui.get<bool>("preMedian"); bool &postMedian = gui.get<bool>("postMedian"); bool &postErosion = gui.get<bool>("postErosion"); bool &postDilatation = gui.get<bool>("postDilatation"); int zAxis = gui["zAxis"]; int &z = gui.get<int>("z"); const Img8u *grabbedImage = grabber.grab()->asImg<icl8u>(); Mutex::Locker lock(mtex); if(preMedian){ static MedianOp m(Size(3,3)); grabbedImage = m.apply(grabbedImage)->asImg<icl8u>(); } image = grabbedImage; Time t = Time::now(); segImage = *segmenter->apply(grabbedImage)->asImg<icl8u>(); if(postMedian){ static MedianOp m(Size(3,3)); segImage = *m.apply(&segImage)->asImg<icl8u>(); } if(postErosion){ static MorphologicalOp m(MorphologicalOp::erode3x3); segImage = *m.apply(&segImage)->asImg<icl8u>(); } if(postDilatation){ static MorphologicalOp m(MorphologicalOp::dilate3x3); segImage = *m.apply(&segImage)->asImg<icl8u>(); } seg = &segImage; time = str(t.age().toMilliSecondsDouble())+"ms"; currLUT = segmenter->getLUTPreview(xys[zAxis].x,xys[zAxis].y,z); currLUTColor = segmenter->getColoredLUTPreview(xys[zAxis].x,xys[zAxis].y,z); highlight_regions(hoveredClassID); //--lut-- lut = &currLUTColor; lut->linewidth(2); for(unsigned int i=0;i<drawLUT.size();++i){ float x = drawLUT[i].getCOG().x, y=drawLUT[i].getCOG().y; lut->color(255,255,255,255); lut->linestrip(drawLUT[i].getBoundary(false)); lut->color(0,0,0,255); lut->text(str(drawLUT[i].getVal()),x+0.1, y+0.1, -2); lut->color(255,255,255,255); lut->text(str(drawLUT[i].getVal()),x, y, -2); if(drawLUT[i].getVal() == hoveredClassID){ lut->color(0,100,255,255); lut->fill(0,100,255,40); lut->rect(drawLUT[i].getBoundingBox().enlarged(1)); } } lut.render(); //--image and seg-- ICLDrawWidget *ws[2] = {*image,*seg}; for(int i=0;i<2;++i){ ws[i]->linewidth(2); ws[i]->color(255,255-i*255,255-i*255,255); for(unsigned int j=0;j<drawIM_AND_SEG.size();++j){ ws[i]->linestrip(drawIM_AND_SEG[j].getBoundary()); } ws[i]->render(); } gui["fps"].render(); if(lut3D){ lut3D->update(gui["alpha"]); gui["lut3D"].render(); } } int main(int n,char **args){ return ICLApp(n,args,"-shifts|-s(int=8,int=0,int=0) " "-seg-format|-f(format=YUV) " "[m]-input|-i(2) " "-num-classes|-n(int=12) " "-load|-l(filename) " "-reset-bus|-r", init,run).exec(); }