image_calibration.cpp
Go to the documentation of this file.
00001 
00009 #include <youbot_overhead_vision/image_calibration.h>
00010 #include <sys/time.h>
00011 #include <math.h>
00012 
00013 using namespace std;
00014 using namespace cv;
00015 
00016 imageCalibration::imageCalibration()
00017 {
00018   count = 0;
00019 
00020 //These variable is red from a file.  IF the file is empty, zeroes will break the program.  As such, they are preset
00021   //As such, it is initialized to 1.
00022   blurringFactor = 1;
00023   calibAccuracy = 5;
00024 
00025   seq_num = 0;
00026 
00027   //Initializing marker linked list
00028   robotMarkersVect = *(new vector<markerPoints>());
00029   robotMarkersVect.push_back(*(new markerPoints()));
00030   robotMarkersVect[0].numPoints = 0;
00031 
00032   ros::NodeHandle nh;
00033 
00034   imgSub1 = nh.subscribe<sensor_msgs::Image>(OVERHEAD_NORTH, 0, &imageCalibration::imageCallback1, this);
00035   imgSub2 = nh.subscribe<sensor_msgs::Image>(OVERHEAD_SOUTH, 0, &imageCalibration::imageCallback2, this);
00036 
00037   pubImgCal = nh.advertise<sensor_msgs::Image>("/image_calibration/image_cal", 1);
00038   pubRobotPosition = nh.advertise<youbot_overhead_vision::PositionFromCamera>("/image_calibration/position_from_camera",
00039                                                                               1);
00040 
00041   //Gets the file locations of the calibration files from the param server
00042 
00043   nh.getParam("/image_calibration/runFromSimulation", useSim);
00044 
00045   nh.getParam("/image_calibration/floor_cal", filenames[FLOOR]);
00046   nh.getParam("/image_calibration/robot_lb_file", filenames[ROBOT_LEFT_BACK]);
00047   nh.getParam("/image_calibration/robot_rb_file", filenames[ROBOT_RIGHT_BACK]);
00048   nh.getParam("/image_calibration/robot_lf_file", filenames[ROBOT_LEFT_FRONT]);
00049   nh.getParam("/image_calibration/robot_rf_file", filenames[ROBOT_RIGHT_FRONT]);
00050   calTree = new calibTree();
00051 
00052   getCalibration(FLOOR, 255, 255, 255);
00053   getCalibration(ROBOT_RIGHT_BACK, 255, 0, 0);
00054   getCalibration(ROBOT_LEFT_BACK, 0, 255, 0);
00055   getCalibration(ROBOT_LEFT_FRONT, 0, 0, 255);
00056   getCalibration(ROBOT_RIGHT_FRONT, 0, 255, 255);
00057 
00058   string roomBoundaryFileName;
00059   nh.getParam("/image_calibration/room_boundary_map", roomBoundaryFileName);
00060   roomBoundary = cv::imread(roomBoundaryFileName);
00061 
00062   //Setting the colors of the NOT_SET calibration type to black.  In otherwords, pixels without a calibration will be black.
00063   colors[NOT_SET][0] = (uint8_t)0;
00064   colors[NOT_SET][1] = (uint8_t)0;
00065   colors[NOT_SET][2] = (uint8_t)0;
00066 
00067   lastFrameMidpointX = -SIDE_MARKER_DIST;
00068   lastFrameMidpointY = -SIDE_MARKER_DIST;
00069 
00070   if (useSim)
00071     pastObstImg = *(new cv::Mat(600 - SIM_OVERLAP, 400, CV_8UC3));
00072   else
00073     pastObstImg = *(new cv::Mat(600 - REAL_OVERLAP, 400, CV_8UC3));
00074 
00075   cv::rectangle(pastObstImg, cv::Point(0, 0), cv::Point(pastObstImg.cols, pastObstImg.rows), CV_RGB(255,255,255),
00076                 CV_FILLED);
00077 }
00078 
00079 imageCalibration::~imageCalibration()
00080 {
00081   freerobotVector();
00082 }
00083 
00084 void imageCalibration::getCalibration(int calibType, int calibR, int calibB, int calibG)
00085 {
00086 
00087   string filename = filenames[calibType];
00088   colors[calibType][0] = calibR;
00089   colors[calibType][1] = calibG;
00090   colors[calibType][2] = calibB;
00091 
00092   ifstream calFile(filename.c_str());
00093   YAML::Parser parser(calFile);
00094   YAML::Node doc;
00095   parser.GetNextDocument(doc);
00096 
00097   if ((doc.size() > 0 && calibType != FLOOR) || doc.size() > 1)
00098   {
00099     int max = doc.size();
00100 
00101     //Getting Blurring factor and calibAccuracy parameters in the floor file
00102     if (calibType == FLOOR)
00103     {
00104       max -= 2;
00105       try
00106       {
00107         doc["blur"] >> blurringFactor;
00108         doc["calib_accuracy"] >> calibAccuracy;
00109       }
00110       catch (YAML::InvalidScalar &)
00111       {
00112         ROS_WARN("The floor file does not have a blurring or accuracy value.  Values are set to defaults");
00113       }
00114     }
00115     //Load up calibration structures with data from files.
00116     for (int i = 1; i <= max; i++)
00117     {
00118       string aPoint = "Point " + convertInt(i);
00119 
00120       int red, green, blue, camera;
00121       (*(doc.FindValue(aPoint.c_str()))->FindValue("rgb"))[0] >> red;
00122       (*(doc.FindValue(aPoint.c_str()))->FindValue("rgb"))[1] >> green;
00123       (*(doc.FindValue(aPoint.c_str()))->FindValue("rgb"))[2] >> blue;
00124       (*(doc.FindValue(aPoint.c_str()))->FindValue("camera")) >> camera;
00125       calTree->newCalibPoint((uint8_t)red, (uint8_t)green, (uint8_t)blue, camera, calibType);
00126 
00127     }
00128     calTree->orderCalibsByRed();
00129     calTree->createTree();
00130   }
00131   else if (calibType == FLOOR)
00132     ROS_INFO("Floor file did not have proper format. No blur/calibration variables found.");
00133 
00134 }
00135 
00136 void imageCalibration::processImage()
00137 {
00138 
00139   vector<int> ImageXCoords;
00140   vector<int> ImageYCoords;
00141   resetForNextFrame();
00142   ros::NodeHandle nh;
00143 
00144   //Filling up vector with 4 NOT_SET ints
00145   for (int x = 0; x < 4; x++)
00146   {
00147     ImageXCoords.push_back(NOT_SET);
00148     ImageYCoords.push_back(NOT_SET);
00149   }
00150 
00151   //Copy the current raw_Img into the output_img for calibration
00152 
00153   cv_bridge::CvImagePtr * temp1 = new cv_bridge::CvImagePtr(new cv_bridge::CvImage());
00154   cv_bridge::CvImagePtr * temp2 = new cv_bridge::CvImagePtr(new cv_bridge::CvImage());
00155   output_Img1 = *(temp1);
00156   output_Img2 = *(temp2);
00157 
00158   while (raw_Img1->encoding != ENCODING && raw_Img2->encoding != ENCODING)
00159     cout << "Images Not Loaded" << endl;
00160 
00161   raw_Img1->image.copyTo(output_Img1->image);
00162   raw_Img2->image.copyTo(output_Img2->image);
00163 
00164   //Making a big combined version of raw_Img1 & raw_Img2
00165   cv::Mat * out1 = (new cv::Mat(output_Img1->image.rows / 2, output_Img1->image.cols / 2, output_Img1->image.type()));
00166   cv::Mat * out2 = (new cv::Mat(output_Img2->image.rows / 2, output_Img2->image.cols / 2, output_Img2->image.type()));
00167 
00168   cv::resize(output_Img1->image, *out1, out1->size(), cv::INTER_NEAREST);
00169   cv::resize(output_Img2->image, *out2, out2->size(), cv::INTER_NEAREST);
00170   output_Img1->image = (output_Img1->image)(cv::Rect(0, 0, out1->cols, out1->rows));
00171   output_Img2->image = (output_Img2->image)(cv::Rect(0, 0, out2->cols, out2->rows));
00172 
00173   cv::Mat * cvImg;
00174   if (useSim)
00175     cvImg = new cv::Mat(out1->rows * 2 - SIM_OVERLAP, out1->cols, out1->type());
00176   else
00177     cvImg = new cv::Mat(out1->rows * 2 - REAL_OVERLAP, out1->cols, out1->type());
00178 
00179   cv::Mat roiCvImgBot, roiCvImgTop, roiOut2;
00180   roiCvImgTop = (*cvImg)(cv::Rect(0, 0, out1->cols, out1->rows));
00181   if (useSim)
00182   {
00183     roiCvImgBot = (*cvImg)(cv::Rect(0, out1->rows - SIM_OVERLAP / 2, out2->cols, out2->rows - SIM_OVERLAP / 2));
00184     roiOut2 = (*out2)(cv::Rect(0, SIM_OVERLAP / 2, out2->cols, out2->rows - SIM_OVERLAP / 2));
00185   }
00186   else
00187   {
00188     roiCvImgBot = (*cvImg)(cv::Rect(0, out1->rows - REAL_OVERLAP / 2, out2->cols, out2->rows - REAL_OVERLAP / 2));
00189     roiOut2 = (*out2)(cv::Rect(0, REAL_OVERLAP / 2, out2->cols, out2->rows - REAL_OVERLAP / 2));
00190   }
00191 
00192   cv::Mat roiOut1 = (*out1)(cv::Rect(0, 0, out1->cols, out1->rows));
00193 
00194   roiOut1.copyTo(roiCvImgTop);
00195   roiOut2.copyTo(roiCvImgBot);
00196 
00197   (*cvImg).copyTo(output_Img1->image);
00198 
00199   output_Img1->encoding = ENCODING;
00200   output_Img1->header.seq = seq_num++;
00201   output_Img1->header.stamp = ros::Time::now();
00202   output_Img1->header.frame_id = FRAME_ID;
00203 
00204   //Applies calib 
00205 
00206   applyCalib();
00207 
00208   addClusters(&ImageXCoords, &ImageYCoords);
00209 
00210   //Determines how many robot marker points were found.
00211   int pointsFound = 0;
00212   for (int i = 0; i < (int)ImageXCoords.size(); i++)
00213   {
00214     if (ImageXCoords[i] != NOT_SET)
00215       pointsFound++;
00216   }
00217 
00218   //If between 2 or three points are found, it is possible to extrapolate the position of the missing points.
00219   if (pointsFound == 2 || pointsFound == 3)
00220     extrapolateMarkers(&ImageXCoords, &ImageYCoords);
00221 
00222   // If the robot is found, publish its position, and remove it from the image.
00223   if (ImageXCoords[0] != NOT_SET && ImageXCoords[1] != NOT_SET && ImageXCoords[2] != NOT_SET
00224       && ImageXCoords[3] != NOT_SET)
00225   {
00226 
00227     double heading = atan2((double)ImageYCoords[ROBOT_LEFT_FRONT] - ImageYCoords[ROBOT_LEFT_BACK],
00228                            ImageXCoords[ROBOT_LEFT_FRONT] - ImageXCoords[ROBOT_LEFT_BACK]);
00229 
00230     youbot_overhead_vision::PositionFromCamera pos;
00231     pos.heading = heading;
00232     pos.midpointX = (ImageXCoords[ROBOT_LEFT_FRONT] + ImageXCoords[ROBOT_RIGHT_BACK]) / 2;
00233     pos.midpointY = (ImageYCoords[ROBOT_LEFT_FRONT] + ImageYCoords[ROBOT_RIGHT_BACK]) / 2;
00234 
00235     //Save midpoint of the robot in the frame if the robot was found. To be used next frame to determine more accurately which points are the real robot marker points. If the robot isnt found, the values are set negative enough so that they will not affect the probability calculations
00236     lastFrameMidpointX = pos.midpointX;
00237     lastFrameMidpointY = pos.midpointY;
00238 
00239     //Expands box and removes robot if all points have been found.
00240     removeRobot(ImageXCoords, ImageYCoords, pos.midpointX, pos.midpointY);
00241 
00242     pos.midpointX *= 2;
00243     pos.midpointY *= 2;
00244     pubRobotPosition.publish(pos);
00245   }
00246 
00247   //Resize image for publishing
00248   cv::resize(output_Img1->image, output_Img1->image,
00249              cv::Size(output_Img1->image.cols / 2.0, output_Img1->image.rows / 2.0));
00250   //Add the room boundaries to the image
00251   cv::bitwise_and(roomBoundary, output_Img1->image, output_Img1->image);
00252   //Publishes calibrated image for the map server.
00253   pubImgCal.publish(output_Img1->toImageMsg());
00254 
00255   delete temp1;
00256   delete temp2;
00257   resetForNextFrame();
00258   delete cvImg;
00259   delete out1;
00260   delete out2;
00261 }
00262 
00263 void imageCalibration::imageCallback1(const sensor_msgs::ImageConstPtr& image_msg)
00264 {
00265   ros::Time time = image_msg->header.stamp;
00266   raw_Img1 = cv_bridge::toCvCopy(image_msg, ENCODING);
00267 
00268   // set the encoding
00269   raw_Img1->encoding = ENCODING;
00270   // set and update the sequence number
00271   raw_Img1->header.seq = seq_num++;
00272   // get the current timestamp
00273   raw_Img1->header.stamp = ros::Time::now();
00274   // set the frame ID
00275   raw_Img1->header.frame_id = FRAME_ID;
00276 }
00277 
00278 void imageCalibration::imageCallback2(const sensor_msgs::ImageConstPtr& image_msg)
00279 {
00280   ros::Time time = image_msg->header.stamp;
00281 
00282   raw_Img2 = cv_bridge::toCvCopy(image_msg, ENCODING);
00283 
00284   // set the encoding
00285   raw_Img2->encoding = ENCODING;
00286   // set and update the sequence number
00287   raw_Img2->header.seq = seq_num++;
00288   // get the current timestamp
00289   raw_Img2->header.stamp = ros::Time::now();
00290   // set the frame ID
00291   raw_Img2->header.frame_id = FRAME_ID;
00292 }
00293 
00294 void imageCalibration::applyCalib()
00295 {
00296   const cv::Mat& image = output_Img1->image;
00297 
00298   cv::Mat roiCvImgBot, roiCvImgTop;
00299   if (useSim)
00300   {
00301     roiCvImgBot = image(
00302         cv::Rect(0, output_Img2->image.rows - SIM_OVERLAP / 2, image.cols, output_Img2->image.rows - SIM_OVERLAP / 2));
00303     roiCvImgTop = image(cv::Rect(0, 0, image.cols, output_Img2->image.rows - SIM_OVERLAP / 2));
00304   }
00305   else
00306   {
00307     roiCvImgBot = image(
00308         cv::Rect(0, output_Img2->image.rows - REAL_OVERLAP / 2, image.cols,
00309                  output_Img2->image.rows - REAL_OVERLAP / 2));
00310     roiCvImgTop = image(cv::Rect(0, 0, image.cols, output_Img2->image.rows - REAL_OVERLAP / 2));
00311   }
00312 
00313   cv::blur(image, image, cv::Size(blurringFactor, blurringFactor), cv::Point(-1, -1), cv::BORDER_DEFAULT);
00314 
00315   cv::Mat * imagePtr;
00316   //Apply The calibrations on both cameras.
00317   for (int camera = 1; camera <= 2; camera++)
00318   {
00319     if (camera == NORTH_CAMERA)
00320       imagePtr = &roiCvImgTop;
00321     else
00322       imagePtr = &roiCvImgBot;
00323 
00324     int numBytes = imagePtr->step * imagePtr->rows;
00325     // applies calibration file on each pixel
00326     for (int pixelItt = 0; pixelItt < numBytes; pixelItt += 3)
00327     {
00328       //This call looks for applicable calibrations
00329 
00330       int calibType = calTree->parseCalibTree((int)imagePtr->data[pixelItt], (int)imagePtr->data[pixelItt + 1],
00331                                               (int)imagePtr->data[pixelItt + 2], camera, calibAccuracy);
00332 
00333       imagePtr->data[pixelItt] = (uint8_t)colors[calibType][0];
00334       imagePtr->data[pixelItt + 1] = (uint8_t)colors[calibType][1];
00335       imagePtr->data[pixelItt + 2] = (uint8_t)colors[calibType][2];
00336       //If the calib type is smaller than the floor or "NOT_SET", it must be one of the robot markers.  These points are stored using the MarkerPoint function.
00337       if (calibType < FLOOR)
00338       {
00339         int pointX = pixelItt / (output_Img1->image.step);
00340         int pointY = pixelItt - pointX * (output_Img1->image.step);
00341 
00342         if (camera == SOUTH_CAMERA)
00343         {
00344           pointX += roiCvImgTop.rows;
00345         }
00346 
00347         pointY /= 3;
00348         addMarkerPoint(pointX, pointY, calibType);
00349       }
00350     }
00351   }
00352 
00353   //The morphologyEx(dilate +erode) eliminates pixels that are by themselves.  dilate reduces black zones by 1 pixel in each direction, erode increases them back their original position.  Large obstacles remain unchanged, small obstacles disappear
00354   int erosion_size = 1;
00355   cv::Mat element = getStructuringElement(cv::MORPH_ELLIPSE, cv::Size(2 * erosion_size + 1, 2 * erosion_size + 1),
00356                                           cv::Point(erosion_size, erosion_size));
00357   morphologyEx(image, image, cv::MORPH_CLOSE, element);
00358 }
00359 
00360 void imageCalibration::addMarkerPoint(int pointX, int pointY, int calibType)
00361 {
00362 
00363   for (int markerItt = 0; markerItt < (int)robotMarkersVect.size(); markerItt++)
00364   {
00365     //Getting the x,y coordinates of a point.
00366 
00367     //If the point fits in one of the clusters, add it in, and recalculate cluster 
00368     if (robotMarkersVect[markerItt].markerType
00369         == calibType&& abs(pointX - robotMarkersVect[markerItt].avgX) < MARKER_CLUSTER_RADIUS && abs(pointY - robotMarkersVect[markerItt].avgY) < MARKER_CLUSTER_RADIUS){
00370 
00371     //Calculating New ave
00372 
00373     robotMarkersVect[markerItt].avgX = (robotMarkersVect[markerItt].avgX * robotMarkersVect[markerItt].numPoints + pointX) /(robotMarkersVect[markerItt].numPoints + 1);
00374     robotMarkersVect[markerItt].avgY = (robotMarkersVect[markerItt].avgY * robotMarkersVect[markerItt].numPoints + pointY) /(robotMarkersVect[markerItt].numPoints + 1);
00375 
00376     robotMarkersVect[markerItt].numPoints++;
00377     return;
00378   }
00379   //If the point does not fit in any found clusters, create a new cluster
00380   else if(robotMarkersVect[markerItt].numPoints == 0)
00381   {
00382 
00383     robotMarkersVect[markerItt].numPoints ++;
00384     robotMarkersVect[markerItt].avgX = pointX;
00385     robotMarkersVect[markerItt].avgY = pointY;
00386     robotMarkersVect[markerItt].markerType = calibType;
00387 
00388     //Initializing new point.
00389     robotMarkersVect.push_back(*(new markerPoints()));
00390     robotMarkersVect[markerItt + 1].numPoints = 0;
00391     robotMarkersVect[markerItt + 1].avgX = (-1 * MARKER_CLUSTER_RADIUS - 1);
00392     robotMarkersVect[markerItt + 1].avgY = (-1 * MARKER_CLUSTER_RADIUS - 1);
00393     robotMarkersVect[markerItt + 1].markerType = NOT_SET;
00394     return;
00395   }
00396 }
00397 }
00398 
00399 void imageCalibration::addClusters(vector<int> *ImageXCoords, vector<int> *ImageYCoords)
00400 {
00401 
00402   //This loop removes small clusters
00403   for (int itt = 0; itt < (int)robotMarkersVect.size(); itt++)
00404   {
00405     if (robotMarkersVect[itt].numPoints < MIN_MARKER_PIXELS)
00406     {
00407       robotMarkersVect.erase(robotMarkersVect.begin() + itt);
00408       itt--;
00409     }
00410 
00411   }
00412 
00413   //Holds the marker that has the highest probability to be the real marker in the list, where [x][0] - index, [x][1]- probability
00414   int highestProb[4][2];
00415   for (int clearItt = 0; clearItt < 4; clearItt++)
00416   {
00417     highestProb[clearItt][0] = -1;
00418     highestProb[clearItt][1] = -1;
00419 
00420   }
00421 
00422   //Calculates Probabilities
00423 
00424   for (int markerItt = 0; markerItt < (int)robotMarkersVect.size(); markerItt++)
00425   {
00426     int probCount = 0;
00427     for (int markerColor = 0; markerColor < FLOOR; markerColor++)
00428     {
00429       bool probIncreased = false;
00430 
00431       //The probability counter should only be increased once per color, since if a cluster is in range of two different markers of the same color, one of them has to be wrong.
00432       for (int comparisonItt = 0; (!probIncreased) && comparisonItt < (int)robotMarkersVect.size(); comparisonItt++)
00433       {
00434         if (robotMarkersVect[comparisonItt].markerType == markerColor)
00435         {
00436           int dist = pow(
00437               pow((double)robotMarkersVect[markerItt].avgX - robotMarkersVect[comparisonItt].avgX, 2)
00438                   + pow((double)robotMarkersVect[markerItt].avgY - robotMarkersVect[comparisonItt].avgY, 2),
00439               .5);
00440 
00441           //If the two point's distance to each other is close to one of the robot distances (sides, front or diagonal), there is more chance the point is a robot marker
00442           if (abs(dist - SIDE_MARKER_DIST) < MARKER_ACCURACY || abs(dist - FRONT_MARKER_DIST) < MARKER_ACCURACY
00443               || abs(dist - DIAG_MARKER_DIST) < MARKER_ACCURACY)
00444           {
00445             probCount++;
00446             probIncreased = true;
00447           }
00448         }
00449       }
00450     }
00451 
00452     //If the point is close to the right distance away from the midpoint of the robot as calculated on the last frame, there is a higher chance that the point is a robot marker.
00453     int distToMid = pow(
00454         pow((double)robotMarkersVect[markerItt].avgX - lastFrameMidpointX, 2)
00455             + pow((double)robotMarkersVect[markerItt].avgY - lastFrameMidpointY, 2),
00456         .5);
00457     if (abs(distToMid - DIAG_MARKER_DIST / 2) < MARKER_ACCURACY)
00458     {
00459       probCount = probCount + 2;
00460     }
00461 
00462     //If the point has the highest probability of being the marker, record it.
00463     int markerType = robotMarkersVect[markerItt].markerType;
00464 
00465     if (highestProb[markerType][1] < probCount
00466         || (highestProb[markerType][1] == probCount
00467             && robotMarkersVect[highestProb[markerType][0]].numPoints < robotMarkersVect[markerItt].numPoints))
00468     {
00469       highestProb[markerType][0] = markerItt;
00470       highestProb[markerType][1] = probCount;
00471     }
00472   }
00473 
00474   //Highest Probability Sanity Checking.
00475   //Checking that the distance between the points with the highest probability actually match the dimensions of the robot.
00476   bool pointRemoved = false;
00477   for (int i = 0; i < 4 && (!pointRemoved); i++)
00478   {
00479     for (int j = i + 1; j < 4 && (!pointRemoved); j++)
00480     {
00481       if (highestProb[j][1] >= 1 && highestProb[i][1] >= 1)
00482       {
00483         int dist = pow(
00484             pow((double)robotMarkersVect[highestProb[i][0]].avgX - robotMarkersVect[highestProb[j][0]].avgX, 2)
00485                 + pow((double)robotMarkersVect[highestProb[i][0]].avgY - robotMarkersVect[highestProb[j][0]].avgY, 2),
00486             .5);
00487         //Checking Side Dist, Front dist, and diag dist
00488 
00489         if ((j == (i + 2) % 4 && abs(dist - SIDE_MARKER_DIST) > MARKER_ACCURACY)
00490             || (j == (5 - i) % 4 && abs(dist - FRONT_MARKER_DIST) > MARKER_ACCURACY)
00491             || (j == (3 - i) % 4 && abs(dist - DIAG_MARKER_DIST) > MARKER_ACCURACY))
00492         {
00493           int distiToMid = pow(
00494               pow((double)robotMarkersVect[highestProb[i][0]].avgX - lastFrameMidpointX, 2)
00495                   + pow((double)robotMarkersVect[highestProb[i][0]].avgY - lastFrameMidpointY, 2),
00496               .5);
00497           int distjToMid = pow(
00498               pow((double)robotMarkersVect[highestProb[j][0]].avgX - lastFrameMidpointX, 2)
00499                   + pow((double)robotMarkersVect[highestProb[j][0]].avgY - lastFrameMidpointY, 2),
00500               .5);
00501           //The marker less likely to be a real marker is removed
00502           if (abs(distiToMid - DIAG_MARKER_DIST / 2) > abs(distjToMid - DIAG_MARKER_DIST / 2))
00503           {
00504             robotMarkersVect.erase(robotMarkersVect.begin() + highestProb[i][0]);
00505             pointRemoved = true;
00506           }
00507           else
00508           {
00509             robotMarkersVect.erase(robotMarkersVect.begin() + highestProb[j][0]);
00510             pointRemoved = true;
00511           }
00512         }
00513       }
00514     }
00515   }
00516 
00517   //If a point was removed, recalls the function. Otherwise, adds the marker point cluster with the highest probability of being the robot marker if it is above a certain threshold.
00518   if (pointRemoved)
00519   {
00520     addClusters(ImageXCoords, ImageYCoords);
00521     return;
00522   }
00523   else
00524   {
00525     int threshold = 1;
00526     for (int itt = 0; itt < 4; itt++)
00527     {
00528       if (highestProb[itt][0] > -1 && highestProb[itt][1] >= threshold)
00529       {
00530         (*ImageXCoords)[itt] = robotMarkersVect[highestProb[itt][0]].avgX;
00531         (*ImageYCoords)[itt] = robotMarkersVect[highestProb[itt][0]].avgY;
00532       }
00533       else
00534       {
00535         (*ImageXCoords)[itt] = NOT_SET;
00536         (*ImageYCoords)[itt] = NOT_SET;
00537       }
00538     }
00539 
00540   }
00541 }
00542 
00543 bool imageCalibration::extrapolateMarkers(vector<int> *ImageXCoords, vector<int> *ImageYCoords)
00544 {
00545 
00546   vector<int> * xCoords = ImageXCoords;
00547   vector<int> * yCoords = ImageYCoords;
00548 
00549   double heading;
00550   //If the known edge is the front 
00551   if ((*xCoords)[ROBOT_LEFT_FRONT] != NOT_SET && (*xCoords)[ROBOT_RIGHT_FRONT] != NOT_SET)
00552   {
00553     heading = atan2((double)(*yCoords)[ROBOT_LEFT_FRONT] - (*yCoords)[ROBOT_RIGHT_FRONT],
00554                     (*xCoords)[ROBOT_LEFT_FRONT] - (*xCoords)[ROBOT_RIGHT_FRONT]) - PI / 2;
00555 
00556     if ((*xCoords)[ROBOT_LEFT_BACK] == NOT_SET)
00557     {
00558       (*xCoords)[ROBOT_LEFT_BACK] = (*xCoords)[ROBOT_LEFT_FRONT] - SIDE_MARKER_DIST * cos(heading);
00559       (*yCoords)[ROBOT_LEFT_BACK] = (*yCoords)[ROBOT_LEFT_FRONT] - SIDE_MARKER_DIST * sin(heading);
00560     }
00561     if ((*xCoords)[ROBOT_RIGHT_BACK] == NOT_SET)
00562     {
00563       (*xCoords)[ROBOT_RIGHT_BACK] = (*xCoords)[ROBOT_RIGHT_FRONT] - SIDE_MARKER_DIST * cos(heading);
00564       (*yCoords)[ROBOT_RIGHT_BACK] = (*yCoords)[ROBOT_RIGHT_FRONT] - SIDE_MARKER_DIST * sin(heading);
00565     }
00566     return true;
00567   }
00568   //if the known edge is the back
00569   else if ((*xCoords)[ROBOT_LEFT_BACK] != NOT_SET && (*xCoords)[ROBOT_RIGHT_BACK] != NOT_SET)
00570   {
00571     heading = atan2((double)(*yCoords)[ROBOT_LEFT_BACK] - (*yCoords)[ROBOT_RIGHT_BACK],
00572                     (*xCoords)[ROBOT_LEFT_BACK] - (*xCoords)[ROBOT_RIGHT_BACK]) - PI / 2;
00573 
00574     if ((*xCoords)[ROBOT_LEFT_FRONT] == NOT_SET)
00575     {
00576       (*xCoords)[ROBOT_LEFT_FRONT] = (*xCoords)[ROBOT_LEFT_BACK] + SIDE_MARKER_DIST * cos(heading);
00577       (*yCoords)[ROBOT_LEFT_FRONT] = (*yCoords)[ROBOT_LEFT_BACK] + SIDE_MARKER_DIST * sin(heading);
00578     }
00579     if ((*xCoords)[ROBOT_RIGHT_FRONT] == NOT_SET)
00580     {
00581       (*xCoords)[ROBOT_RIGHT_FRONT] = (*xCoords)[ROBOT_RIGHT_BACK] + SIDE_MARKER_DIST * cos(heading);
00582       (*yCoords)[ROBOT_RIGHT_FRONT] = (*yCoords)[ROBOT_RIGHT_BACK] + SIDE_MARKER_DIST * sin(heading);
00583     }
00584     return true;
00585   }
00586   //If the known edge is the left side
00587   else if ((*xCoords)[ROBOT_LEFT_FRONT] != NOT_SET && (*xCoords)[ROBOT_LEFT_BACK] != NOT_SET)
00588   {
00589     heading = atan2((double)(*yCoords)[ROBOT_LEFT_FRONT] - (*yCoords)[ROBOT_LEFT_BACK],
00590                     (*xCoords)[ROBOT_LEFT_FRONT] - (*xCoords)[ROBOT_LEFT_BACK]);
00591     if ((*xCoords)[ROBOT_RIGHT_FRONT] == NOT_SET)
00592     {
00593       (*xCoords)[ROBOT_RIGHT_FRONT] = (*xCoords)[ROBOT_LEFT_FRONT] + FRONT_MARKER_DIST * cos(heading - PI / 2);
00594       (*yCoords)[ROBOT_RIGHT_FRONT] = (*yCoords)[ROBOT_LEFT_FRONT] + FRONT_MARKER_DIST * sin(heading - PI / 2);
00595     }
00596     if ((*xCoords)[ROBOT_RIGHT_BACK] == NOT_SET)
00597     {
00598       (*xCoords)[ROBOT_RIGHT_BACK] = (*xCoords)[ROBOT_LEFT_BACK] + FRONT_MARKER_DIST * cos(heading - PI / 2);
00599       (*yCoords)[ROBOT_RIGHT_BACK] = (*yCoords)[ROBOT_LEFT_BACK] + FRONT_MARKER_DIST * sin(heading - PI / 2);
00600     }
00601     return true;
00602   }
00603   //If the known edge is the right side
00604   else if ((*xCoords)[ROBOT_RIGHT_FRONT] != NOT_SET && (*xCoords)[ROBOT_RIGHT_BACK] != NOT_SET)
00605   {
00606     heading = atan2((double)(*yCoords)[ROBOT_RIGHT_FRONT] - (*yCoords)[ROBOT_RIGHT_BACK],
00607                     (*xCoords)[ROBOT_RIGHT_FRONT] - (*xCoords)[ROBOT_RIGHT_BACK]);
00608     if ((*xCoords)[ROBOT_LEFT_FRONT] == NOT_SET)
00609     {
00610       (*xCoords)[ROBOT_LEFT_FRONT] = (*xCoords)[ROBOT_RIGHT_FRONT] + FRONT_MARKER_DIST * cos(heading + PI / 2);
00611       (*yCoords)[ROBOT_LEFT_FRONT] = (*yCoords)[ROBOT_RIGHT_FRONT] + FRONT_MARKER_DIST * sin(heading + PI / 2);
00612     }
00613     if ((*xCoords)[ROBOT_LEFT_BACK] == NOT_SET)
00614     {
00615       (*xCoords)[ROBOT_LEFT_BACK] = (*xCoords)[ROBOT_RIGHT_BACK] + FRONT_MARKER_DIST * cos(heading + PI / 2);
00616       (*yCoords)[ROBOT_LEFT_BACK] = (*yCoords)[ROBOT_RIGHT_BACK] + FRONT_MARKER_DIST * sin(heading + PI / 2);
00617     }
00618     return true;
00619   }
00620   //If the known edge is a diagonal
00621   else if ((*xCoords)[ROBOT_LEFT_FRONT] != NOT_SET && (*xCoords)[ROBOT_RIGHT_BACK] != NOT_SET)
00622   {
00623     heading = atan2((double)(*yCoords)[ROBOT_LEFT_FRONT] - (*yCoords)[ROBOT_RIGHT_BACK],
00624                     (*xCoords)[ROBOT_LEFT_FRONT] - (*xCoords)[ROBOT_RIGHT_BACK])
00625         - atan((double)FRONT_MARKER_DIST / (double)SIDE_MARKER_DIST);
00626     if ((*xCoords)[ROBOT_RIGHT_FRONT] == NOT_SET)
00627     {
00628       (*xCoords)[ROBOT_RIGHT_FRONT] = (*xCoords)[ROBOT_RIGHT_BACK] + SIDE_MARKER_DIST * cos(heading);
00629       (*yCoords)[ROBOT_RIGHT_FRONT] = (*yCoords)[ROBOT_RIGHT_BACK] + SIDE_MARKER_DIST * sin(heading);
00630     }
00631     if ((*xCoords)[ROBOT_LEFT_BACK] == NOT_SET)
00632     {
00633       (*xCoords)[ROBOT_LEFT_BACK] = (*xCoords)[ROBOT_LEFT_FRONT] - SIDE_MARKER_DIST * cos(heading);
00634       (*yCoords)[ROBOT_LEFT_BACK] = (*yCoords)[ROBOT_LEFT_FRONT] - SIDE_MARKER_DIST * sin(heading);
00635     }
00636     return true;
00637   }
00638   else if ((*xCoords)[ROBOT_RIGHT_FRONT] != NOT_SET && (*xCoords)[ROBOT_LEFT_BACK] != NOT_SET)
00639   {
00640     heading = atan2((double)((*yCoords)[ROBOT_RIGHT_FRONT] - (*yCoords)[ROBOT_LEFT_BACK]),
00641                     ((*xCoords)[ROBOT_RIGHT_FRONT] - (*xCoords)[ROBOT_LEFT_BACK]))
00642         + atan((double)FRONT_MARKER_DIST / (double)SIDE_MARKER_DIST);
00643 
00644     if ((*xCoords)[ROBOT_LEFT_FRONT] == NOT_SET)
00645     {
00646       (*xCoords)[ROBOT_LEFT_FRONT] = (*xCoords)[ROBOT_LEFT_BACK] + SIDE_MARKER_DIST * cos(heading);
00647       (*yCoords)[ROBOT_LEFT_FRONT] = (*yCoords)[ROBOT_LEFT_BACK] + SIDE_MARKER_DIST * sin(heading);
00648     }
00649     if ((*xCoords)[ROBOT_RIGHT_BACK] == NOT_SET)
00650     {
00651       (*xCoords)[ROBOT_RIGHT_BACK] = (*xCoords)[ROBOT_RIGHT_FRONT] - SIDE_MARKER_DIST * cos(heading);
00652       (*yCoords)[ROBOT_RIGHT_BACK] = (*yCoords)[ROBOT_RIGHT_FRONT] - SIDE_MARKER_DIST * sin(heading);
00653     }
00654     return true;
00655   }
00656   else
00657   {
00658     return false;
00659   }
00660 
00661 }
00662 
00663 void imageCalibration::removeRobot(vector<int> xCoords, vector<int> yCoords, int xpos, int ypos)
00664 {
00665   cv::Mat cvImg = output_Img1->image;
00666   int minDstIndex;
00667 
00668   vector<int> outerxCoords;
00669   vector<int> outeryCoords;
00670 
00671   float heading = atan2((double)yCoords[ROBOT_LEFT_FRONT] - yCoords[ROBOT_LEFT_BACK],
00672                         xCoords[ROBOT_LEFT_FRONT] - xCoords[ROBOT_LEFT_BACK]);
00673   float theta;
00674   float l;
00675 
00676   //Front Left point
00677   theta = heading + atan((float)(LEFT_EXTENDED / 3.0) / (float)(FRONT_EXTENDED / 3.0));
00678   l = sqrt(pow((LEFT_EXTENDED / 3.0), 2) + pow((FRONT_EXTENDED / 3.0), 2));
00679   outerxCoords.push_back((int)(yCoords[ROBOT_LEFT_FRONT] + l * sin(theta)));
00680   outeryCoords.push_back((int)(xCoords[ROBOT_LEFT_FRONT] + l * cos(theta)));
00681 
00682   //Front Right point
00683   theta = heading - atan((float)(RIGHT_EXTENDED / 3.0) / (float)(FRONT_EXTENDED / 3.0));
00684   l = sqrt(pow((RIGHT_EXTENDED / 3.0), 2) + pow((FRONT_EXTENDED / 3.0), 2));
00685   outerxCoords.push_back((int)(yCoords[ROBOT_RIGHT_FRONT] + l * sin(theta)));
00686   outeryCoords.push_back((int)(xCoords[ROBOT_RIGHT_FRONT] + l * cos(theta)));
00687 
00688   //Back Left point
00689   theta = heading + 3.14159 - atan((float)(LEFT_EXTENDED / 3.0) / (float)(BACK_EXTENDED / 3.0));
00690   l = sqrt(pow((LEFT_EXTENDED / 3.0), 2) + pow((BACK_EXTENDED / 3.0), 2));
00691   outerxCoords.push_back((int)(yCoords[ROBOT_LEFT_BACK] + l * sin(theta)));
00692   outeryCoords.push_back((int)(xCoords[ROBOT_LEFT_BACK] + l * cos(theta)));
00693 
00694   //Back Right point
00695   theta = heading + 3.14159 + atan((float)(RIGHT_EXTENDED / 3.0) / (float)(BACK_EXTENDED / 3.0));
00696   l = sqrt(pow((RIGHT_EXTENDED / 3.0), 2) + pow((BACK_EXTENDED / 3.0), 2));
00697   outerxCoords.push_back((int)(yCoords[ROBOT_RIGHT_BACK] + l * sin(theta)));
00698   outeryCoords.push_back((int)(xCoords[ROBOT_RIGHT_BACK] + l * cos(theta)));
00699 
00700   cv::Point points[4];
00701   points[0].x = outerxCoords[0];
00702   points[0].y = outeryCoords[0];
00703   points[1].x = outerxCoords[1];
00704   points[1].y = outeryCoords[1];
00705   points[2].x = outerxCoords[3];
00706   points[2].y = outeryCoords[3];
00707   points[3].x = outerxCoords[2];
00708   points[3].y = outeryCoords[2];
00709 
00710   cv::fillConvexPoly(cvImg, points, 4, cv::Scalar(0, 0, 0));
00711 
00712   //Detect robot using edge detection
00713   vector<cv::RotatedRect> minRect = findBoundingBox(cvImg, xpos, ypos, &minDstIndex);
00714 
00715   if (minRect.size() > 0)
00716   {
00717     applyBoundingBox(&cvImg, minRect[minDstIndex]);
00718   }
00719   //if robot edge detection failed, create the bounding box based on the
00720   //position and heading of the robot
00721   else
00722   {
00723     outerxCoords.clear();
00724     outeryCoords.clear();
00725 
00726     //Front Left point
00727     theta = heading + atan((float)LEFT_EXTENDED / (float)FRONT_EXTENDED);
00728     l = sqrt(pow((double)LEFT_EXTENDED, 2) + pow((double)FRONT_EXTENDED, 2));
00729     outerxCoords.push_back((int)(yCoords[ROBOT_LEFT_FRONT] + l * sin(theta)));
00730     outeryCoords.push_back((int)(xCoords[ROBOT_LEFT_FRONT] + l * cos(theta)));
00731 
00732     //Front Right point
00733     theta = heading - atan((float)RIGHT_EXTENDED / (float)FRONT_EXTENDED);
00734     l = sqrt(pow((double)RIGHT_EXTENDED, 2) + pow((double)FRONT_EXTENDED, 2));
00735     outerxCoords.push_back((int)(yCoords[ROBOT_RIGHT_FRONT] + l * sin(theta)));
00736     outeryCoords.push_back((int)(xCoords[ROBOT_RIGHT_FRONT] + l * cos(theta)));
00737 
00738     //Back Left point
00739     theta = heading + 3.14159 - atan((float)LEFT_EXTENDED / (float)BACK_EXTENDED);
00740     l = sqrt(pow((double)LEFT_EXTENDED, 2) + pow((double)BACK_EXTENDED, 2));
00741     outerxCoords.push_back((int)(yCoords[ROBOT_LEFT_BACK] + l * sin(theta)));
00742     outeryCoords.push_back((int)(xCoords[ROBOT_LEFT_BACK] + l * cos(theta)));
00743 
00744     //Back Right point
00745     theta = heading + 3.14159 + atan((float)RIGHT_EXTENDED / (float)BACK_EXTENDED);
00746     l = sqrt(pow((double)RIGHT_EXTENDED, 2) + pow((double)BACK_EXTENDED, 2));
00747     outerxCoords.push_back((int)(yCoords[ROBOT_RIGHT_BACK] + l * sin(theta)));
00748     outeryCoords.push_back((int)(xCoords[ROBOT_RIGHT_BACK] + l * cos(theta)));
00749 
00750     cv::Point points[4];
00751     points[0].x = outerxCoords[0];
00752     points[0].y = outeryCoords[0];
00753     points[1].x = outerxCoords[1];
00754     points[1].y = outeryCoords[1];
00755     points[2].x = outerxCoords[3];
00756     points[2].y = outeryCoords[3];
00757     points[3].x = outerxCoords[2];
00758     points[3].y = outeryCoords[2];
00759 
00760     //Filled a specific color so we know what points to avoid.
00761     cv::fillConvexPoly(cvImg, points, 4, cv::Scalar(251, 144, 2));
00762 
00763     int numBytes = cvImg.step * cvImg.rows;
00764 
00765     /*Goes through every pixel.  If that pixel is not in the area being removed, that pixel is updated in the pastObstImg image as it contains current obstacle information.  If it is in the area where the robot is (with rgb 251, 144, 2), then the pixel value for pastObstImg is used as it contains old obstacle information for that pixel.  While not as good as current information, it is better than having no information at all in the area*/
00766     for (int pixelItt = 0; pixelItt < numBytes; pixelItt += 3)
00767     {
00768       if (cvImg.data[pixelItt] != 251 && cvImg.data[pixelItt + 1] != 144 && cvImg.data[pixelItt + 2] != 2)
00769       {
00770         pastObstImg.data[pixelItt] = cvImg.data[pixelItt];
00771         pastObstImg.data[pixelItt + 1] = cvImg.data[pixelItt + 1];
00772         pastObstImg.data[pixelItt + 2] = cvImg.data[pixelItt + 2];
00773       }
00774       else
00775       {
00776         cvImg.data[pixelItt] = pastObstImg.data[pixelItt];
00777         cvImg.data[pixelItt + 1] = pastObstImg.data[pixelItt + 1];
00778         cvImg.data[pixelItt + 2] = pastObstImg.data[pixelItt + 2];
00779       }
00780     }
00781   }
00782 
00783 }
00784 
00785 vector<cv::RotatedRect> imageCalibration::findBoundingBox(cv::Mat image, int xpos, int ypos, int *minDstIndex)
00786 {
00787   cv::Mat imgCopy;
00788 
00789   cv::cvtColor(image, imgCopy, CV_BGR2GRAY);
00790   cv::blur(imgCopy, imgCopy, cv::Size(3, 3));
00791 
00792   vector<vector<cv::Point> > contours;
00793 
00794   // Detect edges using Threshold
00795   cv::threshold(imgCopy, imgCopy, 60, 255, cv::THRESH_BINARY);
00796 
00797   cv::Mat element = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(3, 3), cv::Point(1, 1));
00798   cv::morphologyEx(imgCopy, imgCopy, cv::MORPH_CLOSE, element);
00799   element = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(3, 3), cv::Point(1, 1));
00800   cv::morphologyEx(imgCopy, imgCopy, cv::MORPH_OPEN, element);
00801 
00802   // Find contours
00803   cv::findContours(imgCopy, contours, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, cv::Point(0, 0));
00804 
00805   // Find the rotated rectangles to bound each contour
00806   vector<cv::RotatedRect> minRect(contours.size());
00807 
00808   for (unsigned int i = 0; i < contours.size(); i++)
00809   {
00810     minRect[i] = cv::minAreaRect(cv::Mat(contours[i]));
00811   }
00812 
00813   //Filter bounding boxes by size and distance from robot center
00814   unsigned int i = 0;
00815   float minDst = 999999;
00816   int minIndex = 0;
00817   while (i < minRect.size())
00818   {
00819     float width = minRect[i].size.width;
00820     float length = minRect[i].size.height;
00821 
00822     if (length < width)
00823     {
00824       float temp = width;
00825       width = length;
00826       length = temp;
00827     }
00828 
00829     int xbox = minRect[i].center.y;
00830     int ybox = minRect[i].center.x;
00831 
00832     float dst = sqrt(pow((double)xpos - xbox, 2) + pow((double)ypos - ybox, 2));
00833 
00834     if (!(width > MIN_BOUND_WIDTH && width < MAX_BOUND_WIDTH && length > MIN_BOUND_LENGTH && length < MAX_BOUND_LENGTH)
00835         || dst > DST_BOUND_THRESH)
00836       minRect.erase(minRect.begin() + i);
00837     else
00838     {
00839       if (dst < minDst)
00840       {
00841         minDst = dst;
00842         minIndex = i;
00843       }
00844       i++;
00845     }
00846   }
00847   *minDstIndex = minIndex;
00848 
00849   return minRect;
00850 }
00851 
00852 void imageCalibration::applyBoundingBox(cv::Mat *image, cv::RotatedRect rect)
00853 {
00854   cv::Point2f rectPoints[4];
00855   rect.size.width += 8;
00856   rect.size.height += 8;
00857   rect.points(rectPoints);
00858   cv::Point rectPointsConverted[4];
00859   for (int i = 0; i < 4; i++)
00860   {
00861     rectPointsConverted[i].x = (int)rectPoints[i].x;
00862     rectPointsConverted[i].y = (int)rectPoints[i].y;
00863   }
00864   cv::fillConvexPoly(*image, rectPointsConverted, 4, cv::Scalar(255, 255, 255));
00865 }
00866 
00867 string imageCalibration::convertInt(int number)
00868 {
00869   stringstream ss; //create a stringstream
00870   ss << number; //add number to the stream
00871   return ss.str(); //return a string with the contents of the stream
00872 }
00873 
00874 void imageCalibration::resetForNextFrame()
00875 {
00876   //free the robotMarkersHead linked list
00877   freerobotVector();
00878   //Initializing marker linked list
00879   robotMarkersVect = *(new vector<markerPoints>());
00880   robotMarkersVect.push_back(*(new markerPoints()));
00881   robotMarkersVect[0].numPoints = 0;
00882 }
00883 
00884 void imageCalibration::freerobotVector()
00885 {
00886   for (int x = 0; x < (int)robotMarkersVect.size(); x++)
00887   {
00888     robotMarkersVect.erase(robotMarkersVect.begin() + x);
00889   }
00890 }
00891 
00892 int main(int argc, char** argv)
00893 {
00894   // Initialize ROS
00895   ros::init(argc, argv, "image_calibration");
00896 
00897   imageCalibration * dispPtr = new imageCalibration();
00898 
00902   ros::Rate loop_rate(20);
00903   while (ros::ok())
00904   {
00905     if (dispPtr->raw_Img1 != NULL && dispPtr->raw_Img2 != NULL)
00906     {
00907       dispPtr->processImage();
00908     }
00909     ros::spinOnce();
00910     loop_rate.sleep();
00911   }
00912 
00913   return (0);
00914 }


youbot_overhead_vision
Author(s): Fred Clinckemaillie, Maintained by David Kent
autogenerated on Thu Jan 2 2014 12:14:12