/* * toImage implementation * * Copyright (C) Dorit Borrmann * * Released under the GPL version 3. * */ /** * @file * @author Dorit Borrmann. Institute of Computer Science, University of Osnabrueck, Germany. */ #include #include #ifndef _MSC_VER #include #else #include "XGetopt.h" #endif #include using std::ofstream; using std::flush; using std::cout; using std::string; using std::cerr; using std::endl; #include #ifdef _MSC_VER #define strcasecmp _stricmp #define strncasecmp _strnicmp #include #include #else #include #include #include #include #endif #include "shapes/image.h" #include "shapes/hough.h" #include "shapes/ppair.h" #include "shapes/quadtree.h" #include #include "newmat/newmatap.h" using namespace NEWMAT; //#include "shapes/convexplane.h" #define MAX_PARALLEL_ANGLE 10.0 #define MAX_EQUAL_DIST 10.0 #define MIN_PARALLEL_DIST 50.0 #define MIN_RIGHT_ANGLE 80.0 #define NUM_ITERS 1 void usage(char* prog) { #ifndef _MSC_VER const string bold("\033[1m"); const string normal("\033[m"); #else const string bold(""); const string normal(""); #endif cout << endl << bold << "USAGE " << normal << endl << " " << prog << " [options] directory" << endl << endl; cout << bold << "OPTIONS" << normal << endl << bold << " -f" << normal << " F, " << bold << "--format=" << normal << "F" << endl << " using shared library F for input" << endl << " (chose F from {uos, uos_map, uos_rgb, uos_frames, uos_map_frames, old, rts, rts_map, ifp, riegl_txt, riegl_rgb, riegl_bin, zahn, ply})" << endl << endl << bold << " -m" << normal << " NR, " << bold << "--max=" << normal << "NR" << endl << " neglegt all data points with a distance larger than NR 'units'" << endl << endl << bold << " -M" << normal << " NR, " << bold << "--min=" << normal << "NR" << endl << " neglegt all data points with a distance smaller than NR 'units'" << endl << endl << bold << " -p" << normal << " P, " << bold << "--plane=" << normal << "P" << endl << " using algorithm P for plane detection" << endl << " (chose P from {rht, sht, pht, ppht, apht})" << endl << endl << bold << " -r" << normal << " NR, " << bold << "--reduce=" << normal << "NR" << endl << " turns on octree based point reduction (voxel size=)" << endl << endl << bold << " -s" << normal << " NR, " << bold << "--start=" << normal << "NR" << endl << " start at scan NR (i.e., neglects the first NR scans)" << endl << " [ATTENTION: counting naturally starts with 0]" << endl << endl << endl << endl; cout << bold << "EXAMPLES " << normal << endl << " " << prog << " -m 500 -r 5 dat" << endl << " " << prog << " --max=5000 -r 10.2 dat" << endl << " " << prog << " -s 2 -e 10 -r dat" << endl << endl; exit(1); } int parseArgs(int argc, char **argv, string &dir, double &red, int &start, int &maxDist, int&minDist, int &octree, IOType &type, bool &quiet) { bool reduced = false; int c; // from unistd.h: extern char *optarg; extern int optind; /* options descriptor */ // 0: no arguments, 1: required argument, 2: optional argument static struct option longopts[] = { { "format", required_argument, 0, 'f' }, { "max", required_argument, 0, 'm' }, { "min", required_argument, 0, 'M' }, { "start", required_argument, 0, 's' }, { "reduce", required_argument, 0, 'r' }, { "quit", no_argument, 0, 'q' }, { "octree", optional_argument, 0, 'O' }, { 0, 0, 0, 0} // needed, cf. getopt.h }; cout << endl; while ((c = getopt_long(argc, argv, "f:r:s:e:m:M:p:Oq", longopts, NULL)) != -1) switch (c) { case 'r': red = atof(optarg); reduced = true; break; case 's': start = atoi(optarg); if (start < 0) { cerr << "Error: Cannot start at a negative scan number.\n"; exit(1); } break; case 'f': try { type = formatname_to_io_type(optarg); } catch (...) { // runtime_error cerr << "Format " << optarg << " unknown." << endl; abort(); } break; case 'q': quiet = true; break; case 'm': maxDist = atoi(optarg); break; case 'O': if (optarg) { octree = atoi(optarg); } else { octree = 1; } break; case 'M': minDist = atoi(optarg); break; case '?': usage(argv[0]); return 1; default: abort (); } if (optind != argc-1) { cerr << "\n*** Directory missing ***" << endl; usage(argv[0]); } dir = argv[optind]; #ifndef _MSC_VER if (dir[dir.length()-1] != '/') dir = dir + "/"; #else if (dir[dir.length()-1] != '\\') dir = dir + "\\"; #endif return 0; } void getPPairs(vector &allPlanes, PPair **ppairs) { for (unsigned int i = 0; i < allPlanes.size(); i++) { for (unsigned int j = i+1; j < allPlanes.size(); j++) { PPair pair(allPlanes[i], allPlanes[j]); // parallel or equal planes if (deg(pair.angle) < MAX_PARALLEL_ANGLE) { // planes are equal if (pair.plane_distance < MAX_EQUAL_DIST) { pair.type = PPair::EQUAL; // planes are parallel } else {//if (pair.plane_distance > MIN_PARALLEL_DIST) { pair.type = PPair::PARALLEL; // planes are nothing } // plane is perpendicular } else if (deg(pair.angle) > MIN_RIGHT_ANGLE) { pair.type = PPair::PERPENDICULAR; // planes are nothing in particular } ppairs[i][j] = pair; ppairs[j][i] = pair; } } } void getZHK(unsigned int length, bool **graph, vector > &zhks ) { set unused; for (unsigned int i = 0; i < length; i++) unused.insert(i); // as long as we have nodes cluster while(!unused.empty()) { // start new cluster int index = *unused.begin(); unused.erase(index); set zhk; zhk.insert(index); // now look for all connected nodes vector unvisited; unvisited.push_back(index); while (!unvisited.empty()) { int index = *unvisited.begin(); unvisited.erase(unvisited.begin()); for (unsigned int i = 0; i < length; i++) { if (i != index && graph[index][i] && zhk.count(i) == 0 ) { zhk.insert(i); unvisited.push_back(i); unused.erase(i); } } } zhks.push_back(zhk); } } void flip( const double *normal , double *n ) { // project to the other side double k[3]; n[0] = normal[0]; n[1] = normal[1]; n[2] = 1; toKartesian(n, k); k[0] *= -1; k[1] *= -1; k[2] *= -1; toPolar(k, n); } bool parallel(set &p1, set &p2, double min_angle) { for (set::iterator it = p1.begin(); it != p1.end(); it++) { for (set::iterator jt = p2.begin(); jt != p2.end(); jt++) { double *a = *it; double *b = *jt; //if ( polardist(a,b) > ( rad(180) - min_angle ) ) double flipped[3]; flip(a, flipped); if (polardist(a,b) < min_angle || polardist(flipped, b) < min_angle) { return true; } } } return false; } // TODO no idea what this does /* ColumnVector calcRotRhos(vector &planes) { unsigned int n = planes.size(); double xbar[3]; double *normal; double nx; ColumnVector A(n+3); Matrix B(n+3,n+3); B = 0; A = 0; // fill B and A // for all planes for (unsigned int i = 0; i < n; i++) { vector points = planes[i]->points; normal = planes[i]->n; // for all x_j in plane i for (unsigned int j = 0; j < points.size(); j++) { Point p = points[j]; xbar[0] = normal[1] * p.z - normal[2]*p.y; xbar[1] = normal[2] * p.x - normal[0]*p.z; xbar[2] = normal[0] * p.y - normal[1]*p.x; nx = normal[0]*p.x + normal[1]*p.y + normal[2]*p.z; A(1) += nx * xbar[0]; A(2) += nx * xbar[1]; A(3) += nx * xbar[2]; A(i+1 + 3) -= nx; // upper left 3x3 matrix of B B(1, 1) += xbar[0]*xbar[0]; B(2, 2) += xbar[1]*xbar[1]; B(3, 3) += xbar[2]*xbar[2]; B(1,2) += xbar[0]*xbar[1]; B(2,1) += xbar[0]*xbar[1]; B(1,3) += xbar[0]*xbar[2]; B(3,1) += xbar[0]*xbar[2]; B(2,3) += xbar[1]*xbar[2]; B(3,2) += xbar[1]*xbar[2]; B(1, i+1 +3) -= xbar[0]; B(i+1 +3, 1) -= xbar[0]; B(2, i+1 +3) -= xbar[1]; B(i+1 +3, 2) -= xbar[1]; B(3, i+1 +3) -= xbar[2]; B(i+1 +3, 3) -= xbar[2]; } B(i+1 +3, i+1 +3) = points.size(); } // calc result // cout << "A: " << endl << A << endl; // cout << "B: " << endl << B << endl; ColumnVector X = B.i() * A; // cout << "X: " << endl << X << endl; return X; }*/ void join(vector > &cps, double min_angle, vector > &n_cps) { bool **par = new bool*[cps.size()]; for (unsigned int i = 0; i< cps.size(); i++) { par[i] = new bool[cps.size()]; } for (unsigned int i = 0; i < cps.size(); i++) { par[i][i] = false; for (unsigned int j = i+1; j < cps.size(); j++) { if (parallel(cps[i], cps[j], min_angle) ) { par[i][j] = true; par[j][i] = true; } else { par[i][j] = false; par[j][i] = false; } } } vector > zhks; getZHK(cps.size(), par, zhks); for (unsigned int i = 0; i < zhks.size(); i++) { set zhk; n_cps.push_back(zhk); for (set::iterator it = zhks[i].begin(); it != zhks[i].end(); it++) { n_cps[i].insert( cps[*it].begin(), cps[*it].end() ); } } for (unsigned int i = 0; i< cps.size(); i++) { delete[] par[i]; } delete[] par; } void getModel(vector &model, vector &allPlanes) { int planes = allPlanes.size();//588; double min_angle = rad(MAX_PARALLEL_ANGLE); double perp_angle = rad(MIN_RIGHT_ANGLE); int min_size = 0; // polar coordinates of plane vector norms; for (unsigned int i = 0; i < allPlanes.size(); i ++) {; double *norm = new double[5]; toPolar(allPlanes[i]->n, norm); // 0: phi, 1:theta norm[2] = allPlanes[i]->pointsize; // number of points norm[3] = i; // index norm[4] = allPlanes[i]->rho; // rho norms.push_back(norm); } // copy of polar coordinate representation double **normals = new double*[planes];//[planes][4]; for (int j = 0; j < planes; j++) { normals[j] = norms[j]; } // all normals in Quadtree and cluster QuadTree tree(normals, planes , 0.40, min_angle); vector > cpsold; cpsold = tree.getClusters(); //join similar normals vector > cps; join(cpsold, min_angle, cps); // Filtering for (unsigned int i = 0; i < cps.size(); i++) { cout << "Cluster " << i << ": " << cps[i].size(); set::iterator it = cps[i].begin(); double *rep = *it; // for each normal in this cluster while ( it != cps[i].end()) { double *normal = (*it); double flipped[3]; flip(normal, flipped); // check wether the flipped one is closer if (polardist(rep, flipped) < polardist(rep, normal) ) { normal[0] = flipped[0]; normal[1] = flipped[1]; } cout << " " << normal[3] << " normal: " << normal[0] << " " << normal[1] << endl; it++; } } double **cnorms = new double*[cps.size()]; bool **cnrels = new bool*[cps.size()]; int *normpts = new int[cps.size()]; cout << "Center normals.." << endl; // Calculate the centerd normals for (unsigned int i = 0; i < cps.size(); i++) { cout << "Cluster " << i << ":" << endl; cnorms[i] = new double[3]; cnrels[i] = new bool[cps.size()]; cnorms[i][0] = 0; cnorms[i][1] = 0; cnorms[i][2] = 0; int sumpoints = 0; for (set::iterator it = cps[i].begin(); it != cps[i].end(); it++) { double *normal = (*it); double k[3], n[3]; n[0] = normal[0]; n[1] = normal[1]; n[2] = 1; toKartesian(n, k); cnorms[i][0] += k[0] * normal[2]; cnorms[i][1] += k[1] * normal[2]; cnorms[i][2] += k[2] * normal[2]; sumpoints += normal[2]; } normpts[i] = sumpoints; cnorms[i][0] /= sumpoints; cnorms[i][1] /= sumpoints; cnorms[i][2] /= sumpoints; Normalize3(cnorms[i]); double p[3]; toPolar(cnorms[i], p); cnorms[i][0] = p[0]; cnorms[i][1] = p[1]; cout << " cnormal: " << cnorms[i][0] << " " << cnorms[i][1] << endl; } // Calculate relations between clusters for (unsigned int i = 0; i < cps.size(); i++) { cnrels[i][i] = false; for (unsigned int j = i+1; j < cps.size(); j++) { double angle = polardist(cnorms[i], cnorms[j]); cnrels[i][j] = angle > perp_angle && angle < (rad(180.0) - perp_angle); cnrels[j][i] = cnrels[i][j]; } } // calculate clusters that are big enough vector big_clusters; for (unsigned int i = 0; i < cps.size(); i++) { if (normpts[i] > min_size) { big_clusters.push_back(i); } } cout << "look for biggest cliques..." << endl; // Calculate biggest 3-cliques int ci, cj, ck, cpoints; ci = cj = ck = -1; cpoints = 0; for (vector::iterator it = big_clusters.begin(); it != big_clusters.end(); it++) { unsigned int i = *it; for (vector::iterator jt = it+1; jt != big_clusters.end(); jt++) { unsigned int j = *jt; if (!cnrels[i][j]) continue; for (vector::iterator kt = jt+1; kt != big_clusters.end(); kt++) { unsigned int k = *kt; if (cnrels[i][k] && cnrels[j][k]) { // candidate clique int sum = normpts[i] + normpts[j] + normpts[k]; if (sum > cpoints) { cpoints = sum; ci = i; cj = j; ck = k; cout << i << " " << j << " " << k << " " << sum << endl; } } } } } if (ci == -1 || cj == -1 || ck == -1) { cout << "No clique could be found!" << endl; return; } // Estimate new normals double n[3]; ConvexPlane p1(cnorms[ci]); p1.project(cnorms[cj], n); ConvexPlane p2(n); Cross(p1.n, p2.n, n); ConvexPlane p3(n); // map from allPlane index to ci, cj or ck map idxTotype; int idx = 0; cout << "hmm " << endl; vector numerical_planes; for (set::iterator it = cps[ci].begin(); it != cps[ci].end(); it++) { double *normal = (*it); int index = (int)normal[3]; ConvexPlane *plane = allPlanes[index]; /* plane->n[0] = p1.n[0]; plane->n[1] = p1.n[1]; plane->n[2] = p1.n[2]; */ model.push_back(plane); cout << "for ci = " << ci << " plane index " << idx << " ap index " << index << endl; idxTotype[idx++] = ci; } for (set::iterator it = cps[cj].begin(); it != cps[cj].end(); it++) { double *normal = (*it); int index = (int)normal[3]; ConvexPlane *plane = allPlanes[index]; /* plane->n[0] = p2.n[0]; plane->n[1] = p2.n[1]; plane->n[2] = p2.n[2]; */ model.push_back(plane); cout << "for cj = " << cj << " plane index " << idx << " ap index " << index << endl; idxTotype[idx++] = cj; } for (set::iterator it = cps[ck].begin(); it != cps[ck].end(); it++) { double *normal = (*it); int index = (int)normal[3]; ConvexPlane *plane = allPlanes[index]; /* plane->n[0] = p3.n[0]; plane->n[1] = p3.n[1]; plane->n[2] = p3.n[2]; */ model.push_back(plane); cout << "for ck = " << ck << " plane index " << idx << " ap index " << index << endl; idxTotype[idx++] = ck; } /* int MAX_IT = 100; // int MAX_IT = 10000; int it_nr = 0; ColumnVector X; double MIN_ERR = 0.0005; // double MIN_ERR = 0.0000000000000000000001; double error = 0.0; do { //cout << it_nr << endl; it_nr++; // calc rotation and rho's X = calcRotRhos(numerical_planes); // apply rotation to normals double rPos[3] = {0.0, 0.0, 0.0}; double rPosTheta[3]; rPosTheta[0] = -X(1); rPosTheta[1] = -X(2); rPosTheta[2] = -X(3); error = fabs (X(1)) + fabs(X(2)) + fabs(X(3)) ; // cout << rPosTheta[0] << " " << rPosTheta[1] << " " <n[0]; normal.y = numerical_planes[j]->n[1]; normal.z = numerical_planes[j]->n[2]; // cout << "NP " << j << " " << normal.x << " " << normal.y << " " << normal.z << endl; normal.transform(alignxf); // cout <<"to" << endl; // cout << normal.x << " " << normal.y << " " << normal.z << endl; numerical_planes[j]->n[0] = normal.x; numerical_planes[j]->n[1] = normal.y; numerical_planes[j]->n[2] = normal.z; // cout << "NP " << j << " " << normal.x << " " << normal.y << " " << normal.z << endl; } // repeat } while (error > MIN_ERR || it_nr > MAX_IT); // cout << "ERRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR" << endl; // apply rho's to planes for(unsigned int j = 0; j < numerical_planes.size(); j++) { // cout << j << " s rho is " << numerical_planes[j]->rho << endl; numerical_planes[j]->rho = -X(j+1 +3); // cout << j << " s rho is " << numerical_planes[j]->rho << endl; } bool **graph = new bool*[numerical_planes.size()]; for (unsigned int i = 0; i < numerical_planes.size(); i++) { graph[i] = new bool[numerical_planes.size()]; } for (unsigned int i = 0; i < numerical_planes.size(); i++) { graph[i][i] = false; for (unsigned int j = i+1; j < numerical_planes.size(); j++) { if( fabs(numerical_planes[i]->rho - numerical_planes[j]->rho) < MAX_EQUAL_DIST && idxTotype[i] == idxTotype[j] ) { // check wether planes belong to the same type graph[i][j] = true; graph[j][i] = true; } else { graph[i][j] = false; graph[j][i] = false; } } } vector > zhks; getZHK(numerical_planes.size(), graph, zhks); for (unsigned int i = 0; i < zhks.size(); i++) { vector pplane; for (set::iterator it = zhks[i].begin(); it != zhks[i].end(); it++) { pplane.push_back(numerical_planes[*it]); cout << "zhk " << i << " p " << (*it) << endl; } ConvexPlane *plane = new ConvexPlane(pplane); model.push_back(plane); } */ //exit(0); } /** * Main function. The Hough Transform is called for the scan indicated as * argument. * */ int main(int argc, char **argv) { cout << "(c) Jacobs University Bremen, gGmbH, 2011" << endl << endl; if (argc <= 1) { usage(argv[0]); } // parsing the command line parameters // init, default values if not specified string dir; double red = -1.0; int start = 0; int maxDist = -1; int minDist = -1; int octree = 0; bool quiet = false; IOType type = UOS; cout << "Parse args" << endl; parseArgs(argc, argv, dir, red, start, maxDist, minDist, octree, type, quiet); Scan::dir = dir; int fileNr = start; string planedir = dir + "planes"; #ifdef _MSC_VER int success = mkdir(planedir.c_str()); #else int success = mkdir(planedir.c_str(), S_IRWXU|S_IRWXG|S_IRWXO); #endif if(success == 0) { if(!quiet) { cout << "Writing planes to " << planedir << endl; } } else if(errno == EEXIST) { cout << "Directory " << planedir << " exists already. CONTINUE" << endl; } else { cerr << "Creating directory " << planedir << " failed" << endl; exit(1); } Scan::readScans(type, fileNr, fileNr, dir, maxDist, minDist, 0); // reduction filter for current scan! //Scan::allScans[0]->calcReducedPoints(red, octree); Scan::allScans[0]->toGlobal(red, octree); double id[16]; M4identity(id); for(int i = 0; i < 10; i++) { Scan::allScans[0]->transform(id, Scan::ICP, 0); // write end pose } //Hough hough(Scan::allScans[0], quiet); //hough.RHT(); cout << "Hough done" << endl; // calculate relations between planes //vector * allPlanes = &hough.planes; /* PPair **p_graph; p_graph = new PPair*[allPlanes->size()]; for (unsigned int i = 0; i < allPlanes->size(); i++) { p_graph[i] = new PPair[allPlanes->size()]; } getPPairs(*allPlanes, p_graph); */ // optimize relations //vector model; //getModel(model, *allPlanes); //cout << "GetModel done" << endl; //Hough houghdelete(Scan::allScans[0], quiet); cout << "start range image calculation" << endl; long starttime = GetCurrentTimeInMilliSec(); int size; //double * const* points_red = houghdelete.deletePoints(*allPlanes, size); //double * const* points_red = hough.getPoints(size); cout << "Size " << size << endl; double * const* points_red = Scan::allScans[0]->get_points_reduced(); // Image myImage(0.0, 360.0, 0.0, 180.0, 0.04, points_red, Scan::allScans[0]->get_points_red_size()); Image myImage(0.0, 360.0, 0.0, 180.0, 0.08, Scan::allScans[0]->get_points()); //Image myImage(90.0, 270.0, 40.0, 90.0, 1.0, points_red, size); starttime = (GetCurrentTimeInMilliSec() - starttime); cout << "Time for Constructor call: " << starttime << endl; starttime = GetCurrentTimeInMilliSec(); int** regdat; int width = myImage.getWidth(); regdat = new int*[width]; for(int i = 0; i < width; i++) { regdat[i] = new int[myImage.getHeight()]; } int** regdat2; regdat2 = new int*[width]; for(int i = 0; i < width; i++) { regdat2[i] = new int[myImage.getHeight()]; } //myImage.blobColor(05.0, regdat); //cout << "blobColor done" << endl; //myImage.printImage("blub.ppm", true); myImage.printImage("blub.ppm", false); float min = myImage.getMin(); float max = myImage.getMax(); float tenPercent = (max - min)*0.45; cout << "MinMax " << min << " " << max << " " << tenPercent << " " << (max - min) << " "<< (max - tenPercent) << endl; myImage.calcMarker(min + tenPercent, regdat); cout << "Marker done" << endl; int regions = myImage.cluster(5, regdat, regdat2); cout << "Nu kommen die Punkte" << endl; myImage.writeCenters(regions, regdat2, Scan::allScans[0]->get_points()); cout << "dat woarn se" << endl; //myImage.blobColor(5, regdat2); //cout << "blobColor done" << endl; //myImage.printScans(regdat, points_red, Scan::allScans[0]->get_points_red_size()); cout << "print Scans done" << endl; starttime = (GetCurrentTimeInMilliSec() - starttime); cout << "Time for whatever " << starttime << endl; delete Scan::allScans[0]; Scan::allScans.clear(); }