/******************************************************************** ** 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/CalibrationGrid.cpp ** ** Module : ICLGeom ** ** Authors: Christof Elbrechter ** ** ** ** ** ** 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 #include #include namespace icl{ static std::vector compute_center_brightness(const std::vector &cogs, const std::vector &bbs, const Img8u &maskedImage){ std::vector v(cogs.size()); const Img8u &im = maskedImage; const Channel8u &ci = im[0]; Rect ir = im.getImageRect(); const float cf = 0.3; // center fraction const float lr = (1.0 - cf)/2; // fraction for left and right of center for(unsigned int i=0;i *cogs, int idx): cogs(cogs),idx(idx),distToLine(0.0f){} IdxPoint32f(){} IdxPoint32f(const std::vector *cogs,int idx, const StraightLine2D *line): cogs(cogs),idx(idx){ distToLine = line->signedDistance(StraightLine2D::Pos(p().x,p().y)); } IdxPoint32f(const IdxPoint32f &ip,const StraightLine2D *otherLine): cogs(ip.cogs),idx(ip.idx){ distToLine = otherLine->signedDistance(StraightLine2D::Pos(p().x,p().y)); } const std::vector *cogs; int idx; float distToLine; /*float centerBrightness;*/ const Point32f &p() const { return cogs->at(idx); } bool operator<(const IdxPoint32f &p) const{ return distToLine < p.distToLine; } bool operator==(const IdxPoint32f &p) const { return idx == p.idx; } static bool cmp_less(const IdxPoint32f &a, const IdxPoint32f &b){ return a=(int)p.cogs->size() ? "invalid!" : "ok"; if(valid == "ok"){ return std::cout << "IdxPoint32f(index=" << p.idx << ", valid= " << valid << ", Point=" << p.p() << ")"; }else{ return std::cout << "IdxPoint32f(index=" << p.idx << ", valid= " << valid << ", Point=" << "xxx" << ")"; } } }; struct DistToLineCompare{ const StraightLine2D &line; DistToLineCompare(const StraightLine2D &line):line(line){} bool operator()(const IdxPoint32f &a, const IdxPoint32f &b) const{ float da = line.distance(StraightLine2D::Pos(a.p().x,a.p().y)); float db = line.distance(StraightLine2D::Pos(b.p().x,b.p().y)); return da < db; } }; } bool is_straight(const std::vector &col){ if(col.size() < 2) throw ICLException("is_straight cannot work with less than 2 points..."); Point32f a = col.front().p(), b = col.back().p(); StraightLine2D line(StraightLine2D::Pos(a.x,a.y),StraightLine2D::Pos(b.x-a.x,b.y-a.y)); for(unsigned int i=1;i 5){ return false; } } return true; } /** this function grabs one next column (ny points) from the given set of remaining points 'all'. First, it trys the trivial solution: Here, the furthest ny points are used to build the next column. Only if it turns out, that these points are not collinear (is_straight), a *new* heuristics is used: - The main assumption of this heuristics is that at least the first two furthest points are part of the next column - These points are used to create a StraightLine2D instance. - Now, the next points of all are extracted and sorted by their distance to that line - Since we assumed that the line was correct, the first ny points (including the support points for the line) can be moved from all and are returned as complete last column - Only if also these points are not collinear, an execption is thrown */ static std::vector grab_next_ny_points(std::vector &all, int ny, const StraightLine2D *line){ // 1st: try trivial solution std::vector col(ny); for(int i=0;i use(iclMin(ny*ny,(int)all.size())); std::copy(all.begin(), all.begin()+use.size(),use.begin()); std::sort(use.begin(),use.end(),DistToLineCompare(tmpline)); for(int i=0;i::iterator it = std::find(all.begin(),all.end(),col[i]); if(it == all.end()) throw ICLException("error in grab_next_ny_points: grabbed a point " " that did not exist (which should not happen)"); all.erase(it); } if(!is_straight(col)) throw ICLException("error in grab_next_ny_points: line was not straight"); }else{ all.erase(all.begin(),all.begin()+ny); } return col; } /** adapted version of partition_and_sort_points, where each next ny points (each next column) is checked for being straight. If not, the heuristics implemented in grab_next_ny_points is used! */ static std::vector > partition_and_sort_points_2(std::vector all, int ny, const StraightLine2D *line, bool sortReverse){ std::vector > columns(all.size() / ny); for(unsigned int i=0;i > partition_and_sort_points(const std::vector &all, int ny, const StraightLine2D *line, bool sortReverse){ std::vector > columns(all.size() / ny); std::vector::const_iterator it = all.begin(); for(unsigned int i=0;i count_distance_signs(const std::vector &cogsi){ std::pair ds(0,0); for(unsigned int i=0;i 0.1){ if(cogsi[i].distToLine>0) ds.second++; else ds.first++; } } return ds; } struct DistToPointCompare{ Point32f p; DistToPointCompare(const IdxPoint32f &p):p(p.p()){} bool operator()(const IdxPoint32f &a, const IdxPoint32f &b) const{ return a.p().distanceTo(p) < b.p().distanceTo(p); } }; IdxPoint32f find_closest_point_to_line(const StraightLine2D &line, const std::vector &ps){ if(!ps.size()) throw ICLException("find_closest_point_to_line: given point set size was 0"); return *min_element(ps.begin(),ps.end(),DistToLineCompare(line)); } std::vector find_n_closest_points_to_point(int n, std::vector list, const IdxPoint32f &X){ //if((int)list.size() < n) throw ICLException(str("error in grid fitting process: in find_n_closest_points_to_point\n")+ // str("searching for at least ")+str(n)+str(" points in a list of size ") + // str(list.size())); // we dont need this, since at the end, only ny points are left .. if(!list.size()) throw ICLException("find_n_closest_points_to_point: list size was null"); std::sort(list.begin(),list.end(),DistToPointCompare(X)); list.erase(list.begin()); if((int)list.size()>n) list.resize(n); return list; } int find_index_of_point_with_closest_slope(const StraightLine2D &line, const IdxPoint32f &X, const std::vector &ys){ std::vector ds(ys.size()); for(unsigned int i=0;i perpLine.signedDistance(StraightLine2D::Pos(b.p().x,b.p().y)); } }; bool is_almost(const Point32f &a, const Point32f &b){ return (fabs(a.x-b.x) + fabs(a.y-b.y)) < 0.01; } StraightLine2D fix_line_direction_and_sort_first_points(StraightLine2D line, std::vector &points, const Point32f &s1,const Point32f &s2){ std::sort(points.begin(),points.end(),SortPointsByDistanceToPerpendicularLine(line)); Point32f top; if(is_almost(points.front().p(),s1) || is_almost(points.back().p(),s1)){ top = s1; }else{ top = s2; } if(is_almost(top,points.front().p())){ line.v = -line.v; std::reverse(points.begin(),points.end()); } return line; } std::vector > match_grid(const std::vector &cogs, int s1, int s2, int nx, int ny){ std::vector > columns(cogs.size() / ny); StraightLine2D line(StraightLine2D::Pos(cogs[s1].x,cogs[s1].y), StraightLine2D::Pos(cogs[s2].x,cogs[s2].y)- StraightLine2D::Pos(cogs[s1].x,cogs[s1].y)); std::vector cogsi(cogs.size()); for(unsigned int i=0;i ds = count_distance_signs(cogsi); //if(ds.first < ds.second){ // line.v = -line.v; //} line = fix_line_direction_and_sort_first_points(line, columns[nx], cogs[s1], cogs[s2]); /// now sort points to the left and to the right std::vector left,right; for(unsigned int i=0;i xxx IdxPoint32f X = find_closest_point_to_line(lastLine,left); std::vector Y = find_n_closest_points_to_point(3,left,X); // X will be removed automatically int yi = find_index_of_point_with_closest_slope(lastLine, X, Y); StraightLine2D lastLineSave = lastLine; lastLine = StraightLine2D(StraightLine2D::Pos(X.p().x,X.p().y), StraightLine2D::Pos(Y[yi].p().x,Y[yi].p().y)- StraightLine2D::Pos(X.p().x,X.p().y)); std::sort(left.begin(),left.end(),DistToLineCompare(lastLine)); std::copy(left.begin(),left.begin()+ny, std::back_inserter(columns[nx-1-i])); left.erase(left.begin(),left.begin()+ny); std::sort(columns[nx-1-i].begin(),columns[nx-1-i].end(),SortPointsByDistanceToPerpendicularLine(lastLineSave)); } lastLine = line; // sort right points for(int i=0;i xxx IdxPoint32f X = find_closest_point_to_line(lastLine,right); std::vector Y = find_n_closest_points_to_point(3 ,right,X); int yi = find_index_of_point_with_closest_slope(lastLine, X, Y); StraightLine2D lastLineSave = lastLine; lastLine = StraightLine2D(StraightLine2D::Pos(X.p().x,X.p().y), StraightLine2D::Pos(Y[yi].p().x,Y[yi].p().y)- StraightLine2D::Pos(X.p().x,X.p().y)); std::sort(right.begin(),right.end(),DistToLineCompare(lastLine)); std::copy(right.begin(),right.begin()+ny, std::back_inserter(columns[nx+1+i])); right.erase(right.begin(),right.begin()+ny); std::sort(columns[nx+1+i].begin(),columns[nx+1+i].end(),SortPointsByDistanceToPerpendicularLine(lastLineSave)); } return columns; } // xxx // find the next ny points that are // - closest to the lastLine // - AND almost collinear (most important) // - AND more or less parallel to last line /** * start mit nächstem punkt zu last line X * suche den punkt Yi aus [Y1,Y2,Y3|Yi closest to X] zu X mit dem * die linie X->Yi möglichst parallel zu lastLine sind * lastLine = X->Yi oder Yi->X ?? * sortiere left nach abstand zu lastLine (X->Yi) * kopiere die ersten ny punkte und lösche sie aus 'left' */ // store results in columns[nx-1-i] (sorted from left to right) CalibrationGrid::CalibrationGrid():inputDataReady(false){} #ifdef HAVE_QT static inline float round1(float x){ return float((int)round(x*10))/10.; } void CalibrationGrid::visualize2D(ICLDrawWidget &w){ w.color(0,255,0,200); w.fill(0,0,0,0);//255,0,100); for(unsigned int i=0;i *vs = (h?B:A).world.data(); for(int y=0;y(f["config.world-offset"]); nx = f["config.calibration-object.nx"]; ny = f["config.calibration-object.ny"]; f.setPrefix("config.calibration-object.part-A."); Vec3 A1 = wo + parse(f["offset"]); Vec3 adx = parse(f["dx"])/(nx-1); Vec3 ady = parse(f["dy"])/(ny-1); f.setPrefix("config.calibration-object.part-B."); Vec3 B1 = wo + parse(f["offset"]); Vec3 bdx = parse(f["dx"])/(nx-1); Vec3 bdy = parse(f["dy"])/(ny-1); for(int y=0;y x,y,z-axis so.addVertex(Vec(0,0,0,1),geom_white()); so.addVertex(Vec(100,0,0,1),geom_red()); so.addVertex(Vec(0,100,0,1),geom_green()); so.addVertex(Vec(0,0,100,1),geom_blue()); so.addLine(0,1,geom_red()); so.addLine(0,2,geom_green()); so.addLine(0,3,geom_blue()); for(int i=0;i(1),geom_blue()); } for(int i=0;i(1),geom_red()); } for(int x=0;x(1); } float CalibrationGrid::get_RMSE_on_image(const std::vector &ws, const std::vector &is, const Camera &cam) { float result = 0; for (unsigned int i=0; i CalibrationGrid::findMarkedPoints(const std::vector &cogs, const std::vector &bbs, const Img8u *hintImage){ if(!hintImage) throw ICLException("CalibrationGrid::findMarkedPoints: given hintImage is null"); if(hintImage->getChannels() != 1) throw ICLException("CalibrationGrid::findMarkedPoints: given hintImages channel count is not 1"); std::vector cbs = compute_center_brightness(cogs,bbs,*hintImage); int s1 = int(std::max_element(cbs.begin(),cbs.end()) - cbs.begin()); float cbs_1 = cbs[s1]; cbs[s1] = 0; int s2 = int(std::max_element(cbs.begin(),cbs.end()) - cbs.begin()); cbs[s1] = cbs_1; return std::pair(s1,s2); } void CalibrationGrid::update(const std::vector &cogs, const std::vector &bbs, const Img8u *hintImage){ lastBoundingBoxes = bbs; if((int)cogs.size() != 2*nx*ny || (int)bbs.size() != 2*nx*ny){ inputDataReady = false; return; } const std::pair si = findMarkedPoints(cogs,bbs,hintImage); const int &s1 = si.first; const int &s2 = si.second; // check for wrongly detected marked points if( (s1 < 0) || (s1 >= (int)cogs.size()) || (s2 < 0) || (s2 >= (int)cogs.size()) || (s1 == s2)) return; #if USE_OLD_VERSION StraightLine2D line(StraightLine2D::Pos(cogs[s1].x,cogs[s1].y), StraightLine2D::Pos(cogs[s2].x,cogs[s2].y)- StraightLine2D::Pos(cogs[s1].x,cogs[s1].y)); std::vector cogsi(cogs.size()); for(unsigned int i=0;i ds = count_distance_signs(cogsi); StraightLine2D perpLine = line; if(ds.first > ds.second){ perpLine.v = StraightLine2D::Pos(line.v[1], -line.v[0]); }else{ perpLine.v = StraightLine2D::Pos(-line.v[1], line.v[0]); } std::vector > columns = partition_and_sort_points(cogsi, ny, &perpLine, ds.first > ds.second); #elif 1 StraightLine2D line(StraightLine2D::Pos(cogs[s1].x,cogs[s1].y), StraightLine2D::Pos(cogs[s2].x,cogs[s2].y)- StraightLine2D::Pos(cogs[s1].x,cogs[s1].y)); std::vector cogsi(cogs.size()); for(unsigned int i=0;i ds = count_distance_signs(cogsi); StraightLine2D perpLine = line; if(ds.first > ds.second){ perpLine.v = StraightLine2D::Pos(line.v[1], -line.v[0]); }else{ perpLine.v = StraightLine2D::Pos(-line.v[1], line.v[0]); } std::vector > columns; try{ columns = partition_and_sort_points_2(cogsi, ny, &perpLine, ds.first > ds.second); }catch(ICLException &ex){ WARNING_LOG(ex.what()); return; } #else std::vector > columns; try{ columns = match_grid(cogs,s1,s2,nx,ny); }catch(const ICLException &ex){ SHOW(ex.what()); return; } #endif A.img.resize(nx*ny); B.img.resize(nx*ny); for(int i=0;i imgPts; std::copy(A.img.begin(),A.img.end(),back_inserter(imgPts)); std::copy(B.img.begin(),B.img.end(),back_inserter(imgPts)); std::vector worldPts; std::transform(A.world.begin(),A.world.end(),back_inserter(worldPts),vec3to4); std::transform(B.world.begin(),B.world.end(),back_inserter(worldPts),vec3to4); cam = Camera::calibrate(worldPts, imgPts); cam.getRenderParams().viewport = Rect(Point::null,imageSize); cam.getRenderParams().chipSize = imageSize; return get_RMSE_on_image(worldPts,imgPts, cam); } void CalibrationGrid::create_empty_configuration_file(const std::string &filename){ std::ofstream stream(filename.c_str()); #define LINE(x) << x << std::endl stream << "" << std::endl LINE("") LINE("") LINE("Camera Calibration Config-File") LINE("0,266,0") LINE("
") LINE(" 5") LINE(" 4") LINE("
") LINE(" -21.21 30.8 282.29") LINE(" -200.12 0 -200.12") LINE(" 0 212.25 0") LINE("
") LINE("
") LINE(" 21.92 29.5 281.58") LINE(" 200.12 0 -200.12") LINE(" 0 212.25 0") LINE("
") LINE("
") LINE("
"); #undef LINE } Size CalibrationGrid::getDimension() const{ return Size(nx,ny); } }