ImageProcessing.cpp
Go to the documentation of this file.
00001 /*********************************************************************
00002 * Software License Agreement (BSD License)
00003 *
00004 *  Copyright (c) 2010, LABUST, UNIZG-FER
00005 *  All rights reserved.
00006 *
00007 *  Redistribution and use in source and binary forms, with or without
00008 *  modification, are permitted provided that the following conditions
00009 *  are met:
00010 *
00011 *   * Redistributions of source code must retain the above copyright
00012 *     notice, this list of conditions and the following disclaimer.
00013 *   * Redistributions in binary form must reproduce the above
00014 *     copyright notice, this list of conditions and the following
00015 *     disclaimer in the documentation and/or other materials provided
00016 *     with the distribution.
00017 *   * Neither the name of the LABUST nor the names of its
00018 *     contributors may be used to endorse or promote products derived
00019 *     from this software without specific prior written permission.
00020 *
00021 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
00022 *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00023 *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
00024 *  FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
00025 *  COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
00026 *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
00027 *  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00028 *  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
00029 *  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
00030 *  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
00031 *  ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00032 *  POSSIBILITY OF SUCH DAMAGE.
00033 *********************************************************************/
00034 #include <labust/blueview/ImageProcessing.hpp>
00035 #include <labust/blueview/TrackerROI.hpp>
00036 #include <labust/math/uBlasOperations.hpp>
00037 
00038 #include <opencv2/imgproc/imgproc.hpp>
00039 #include <opencv2/highgui/highgui.hpp>
00040 #include <boost/numeric/ublas/matrix.hpp>
00041 
00042 #define deg2rad M_PI/180;
00043 
00044 using namespace labust::blueview;
00045 
00046 BVImageProcessor::BVImageProcessor():
00047   roi_len(1.5),
00048   resolution(0),
00049   hasVehicle(false),
00050   foundVehicle(false),
00051   hasTarget(false){};
00052 
00053 BVImageProcessor::~BVImageProcessor(){};
00054 
00055 
00056 bool BVImageProcessor::processROI(TrackerROI& roi)
00057 {
00058   resolution = roi.headData.resolution;
00059   if (hasVehicle || foundVehicle)
00060   {
00061     std::cout<<"ROI h-p:"<<","<<roi.headData.heading<<","<<roi.headData.panAngle<<std::endl;
00062     //Calculation of the new position
00063     //Tracklet position update, calculate new x,y,z based on last known position
00064     //std::cout<<"Before:"<<std::scientific<<tracklet.position.x<<","<<tracklet.position.y<<std::endl;
00065     //std::cout<<"Before:"<<std::scientific<<tracklet.latlon.x<<","<<tracklet.latlon.y<<std::endl;
00066     llz2xy(roi.headData,tracklet);
00067     meter2pixel(roi,tracklet);
00068 
00069     if (hasTarget)
00070     {
00071       llz2xy(roi.headData,target);
00072       meter2pixel(roi,target);
00073     }
00074     std::cout<<"debug point 2"<<std::endl;
00075 
00076     //Where it is expected to be
00077     cv::Mat disp = roi.roi.clone();
00078 
00079     std::cout<<"debug point 3"<<std::endl;
00080     cv::circle(disp,getTracklet().pposition,10,cv::Scalar(65536));
00081 
00082     cv::imshow("Expected",disp*500);
00083     //cv::waitKey(10);
00084 
00085     std::cout<<"Before:"<<std::scientific<<tracklet.pposition.x<<","<<tracklet.pposition.y<<std::endl;
00086     std::cout<<"After:"<<std::scientific<<tracklet.position.x<<","<<tracklet.position.y<<std::endl;
00087     //std::cout<<"After:"<<std::scientific<<tracklet.latlon.x<<","<<tracklet.latlon.y<<std::endl;
00088 
00089     //From here things are the same as in the old version
00090     //Calculate the pixel region (3x3) meters
00091     roi_len = 3;
00092     int add = int(roi_len/roi.headData.resolution);
00093 
00094     //    if (roi->roi.size().height<200)
00095     //    {
00096     //      roiImg = roi->roi;
00097     //      roiOffset.x = 0;
00098     //      roiOffset.y = 0;
00099     //std::cout<<"No selection"<<std::endl;
00100     //    }
00101     //    else
00102     //    {
00103     roiImg = roi.roi;
00104     //roiImg = roi.roi(cv::Rect(tracklet.pposition.x - add,tracklet.pposition.y - add,2*add,2*add));
00105     roiOffset.x = 0;
00106     //roiOffset.x = tracklet.pposition.x - add;
00107     roiOffset.y = 0;
00108     //roiOffset.y = tracklet.pposition.y - add;
00109     std::cout<<"ROI width:"<<add<<std::endl;
00110     //    }
00111 
00112     //Do adjustment
00113     cv::Mat adjusted = adjust(roiImg);
00114     cv::imshow("Adjusted",adjusted);
00115     //cv::waitKey(10);
00116     //Threshold
00117     cv::Mat binary = threshold(adjusted);
00118     cv::imshow("Binary",binary);
00119     //cv::waitKey(10);
00120     //Label
00121     boost::shared_ptr<std::vector<TrackedFeature> > features = label(binary);
00122     //Associate the results
00123     bool retVal = associate(features);
00124 
00125     pixel2meter(roi,tracklet);
00126     xy2llz(roi.headData,tracklet);
00127     //tracklet.latlon.x += 0.00001;
00128     //llz2xy(roi.headData,tracklet);
00129 
00130     std::cout<<"After:"<<std::scientific<<tracklet.position.x<<","<<tracklet.position.y<<std::endl;
00131     std::cout<<"After:"<<std::scientific<<tracklet.latlon.x<<","<<tracklet.latlon.y<<std::endl;
00132     std::cout<<"After:"<<std::scientific<<tracklet.pposition.x<<","<<tracklet.pposition.y<<std::endl;
00133 
00134     return retVal;
00135 
00136     //exit(0);
00137   }
00138   else
00139   {
00140     //Do automatic detection on the region
00141   }
00142 
00143   return false;
00144 }
00145 
00146 cv::Mat BVImageProcessor::adjust(cv::Mat& original)
00147 {
00148   cv::Mat adjust(original.size(),CV_32FC1);
00149   double min = 0, max = 0;
00150   cv::minMaxLoc(original,&min,&max);
00151   original.convertTo(adjust,CV_32FC1,1/max);
00152 
00153   return adjust;
00154 }
00155 
00156 cv::Mat BVImageProcessor::threshold(cv::Mat& adjusted)
00157 {
00158   cv::Mat thresholded(adjusted.size(),CV_8UC1);
00159   cv::threshold(adjusted, thresholded, 0.5, 255, CV_THRESH_BINARY);
00160   //thresholded = histthresh(adjusted, 0.3,0.5,255);
00161   thresholded.convertTo(thresholded,CV_8UC1);
00162 
00163   return thresholded;
00164 }
00165 
00166 cv::Mat BVImageProcessor::histthresh(const cv::Mat& img, float min,float max, uchar max_val)
00167 {
00168     //cv::Mat retVal(img.size(),CV_8U,cv::Scalar(0));
00169     cv::Mat retVal(img);
00170 
00171     for(int i=1; i<img.size().width-1; ++i)
00172     {
00173         for(int j=1; j<img.size().height-1; ++j)
00174         {
00175             cv::Mat thehood = img(cv::Range(i-1,i+1),cv::Range(j-1,j+1));
00176             cv::Mat rethood = retVal(cv::Range(i-1,i+1),cv::Range(j-1,j+1));
00177 
00178             bool flag = false;
00179             for (int k=0; k<thehood.size().width; ++k)
00180             {
00181                 for (int k2=0; k2<thehood.size().height; ++k2)
00182                 {
00183                     //std::cout<<"Pixel val:"<<thehood.at<float>(k,k2)<<std::endl;
00184                     if ((flag = thehood.at<float>(k,k2) >= max)) break;
00185                 }
00186             }
00187 
00188             if (flag)
00189             {
00190                 for (int k=0; k<thehood.size().width; ++k)
00191                 {
00192                     for (int k2=0; k2<thehood.size().height; ++k2)
00193                     {
00194                         if (thehood.at<float>(k,k2) >= min)
00195                         {
00196                             rethood.at<float>(k,k2) = max_val;
00197                         }
00198                         else
00199                         {
00200                             rethood.at<float>(k,k2) = 0;
00201                         }
00202                     }
00203                 }
00204             }
00205             else
00206             {
00207                 cv::Mat(3,3,CV_8U,cv::Scalar(0)).copyTo(rethood);
00208             }
00209         }
00210     }
00211 
00212     return retVal;
00213 }
00214 
00215 boost::shared_ptr<std::vector<TrackedFeature> > BVImageProcessor::label(cv::Mat& binary)
00216 {
00217         //This will be replaced with a LABELING algorithm to save execution time
00218         cv::morphologyEx(binary, binary, cv::MORPH_OPEN,cv::Mat());
00219 
00220         std::vector< std::vector<cv::Point> > contours;
00221         std::vector<cv::Vec4i> hierarchy;
00222 
00223         cv::findContours(binary,contours,hierarchy,cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE);
00224 
00225         boost::shared_ptr<std::vector<TrackedFeature> > info(new std::vector<TrackedFeature>);
00226 
00227         for (size_t i = 0; i < contours.size(); ++i)
00228         {
00229                 std::vector<cv::Point> c_new;
00230                 cv::convexHull(cv::Mat(contours[i]),c_new,true);
00231                 contours[i] = c_new;
00232                 cv::Moments moments = cv::moments(cv::Mat(contours[i]),true);
00233 
00234                 TrackedFeature tfeature;
00235                 tfeature.perimeter = cv::arcLength(cv::Mat(contours[i]),true);
00236                 tfeature.area = moments.m00;
00237                 tfeature.pposition.x = (moments.m10)/moments.m00 + roiOffset.x;
00238                 tfeature.pposition.y = (moments.m01)/moments.m00 + roiOffset.y;
00239 
00240                 info->push_back(tfeature);
00241         }
00242 
00243         return info;
00244 }
00245 
00246 bool BVImageProcessor::associate(boost::shared_ptr<std::vector<TrackedFeature> > features)
00247 {
00248  int idx = -1;
00249  double mindiff = 10000;
00250 
00251  //When its hard to detect anything
00252  if (features->size()>10) return false;
00253 
00254  for (size_t i=0;i<features->size();++i)
00255  {
00256   TrackedFeature& tfeature = (*features)[i];
00257 
00258   double tt_dx = tfeature.pposition.x - target.pposition.x;
00259   double tt_dy = tfeature.pposition.y - target.pposition.y;
00260   double tt_distance = sqrt(tt_dx*tt_dx + tt_dy*tt_dy);
00261    
00262   double dx = (tfeature.pposition.x) - tracklet.pposition.x;
00263   double dy = (tfeature.pposition.y) - tracklet.pposition.y;
00264   double distance = sqrt(dx*dx + dy*dy);
00265   
00266   if (!hasTarget) tt_distance = 100;
00267 
00268   //if (tt_distance > distance)
00269   //{
00270    if (hasTarget) std::cout<<"Distance to target:"<<tt_distance*resolution<<std::endl;
00271 
00272    if (distance<mindiff)
00273    {
00274     mindiff = distance;
00275     idx = i;
00276    }
00277   //}
00278   //else
00279   //{
00280     //std::cout<<"Skipped target."<<std::endl;
00281   //}
00282  }
00283 
00284  if (idx != -1)
00285  {
00286   tracklet.pposition = (*features)[idx].pposition;
00287   foundVehicle = true;
00288   hasVehicle = true;
00289   return true;
00290  }
00291  else
00292  {
00293          return false;
00294  }
00295 }
00296 
00297 void BVImageProcessor::setPosition(const SonarHead& cnt)
00298 {
00299   //Calculate the position in the sonar image
00300 
00301   tracklet.position.x = cnt.range*cos((cnt.bearing - cnt.panAngle)*M_PI/180);
00302   tracklet.position.y = cnt.range*sin((cnt.bearing - cnt.panAngle)*M_PI/180);
00303 
00304   //Calculate the world LAT-LON-Z position for the tracklet
00305   xy2llz(cnt,tracklet);
00306 
00307   //Testing code
00308   //std::cout<<"Meter position: ("<<tracklet.position.x<<","<<tracklet.position.y<<")"<<std::endl;
00309 
00310   std::cout<<"Contact:"<<tracklet.position.x<<","<<tracklet.position.y<<std::endl;
00311   std::cout<<"R-b-h-p:"<<cnt.range<<","<<cnt.bearing<<","<<cnt.heading<<","<<cnt.panAngle<<std::endl;
00312 
00313   foundVehicle = hasVehicle = true;
00314 }
00315 
00316 void BVImageProcessor::setTarget(const SonarHead& cnt)
00317 {
00318   //Calculate the position in the sonar image
00319 
00320   target.position.x = cnt.range*cos((cnt.bearing - cnt.panAngle)*M_PI/180);
00321   target.position.y = cnt.range*sin((cnt.bearing - cnt.panAngle)*M_PI/180);
00322 
00323   //Calculate the world LAT-LON-Z position for the tracklet
00324   xy2llz(cnt,target);
00325 
00326   //Testing code
00327   //std::cout<<"Meter position: ("<<tracklet.position.x<<","<<tracklet.position.y<<")"<<std::endl;
00328 
00329   std::cout<<"Target contact:"<<target.position.x<<","<<target.position.y<<std::endl;
00330   std::cout<<"R-b-h-p:"<<cnt.range<<","<<cnt.bearing<<","<<cnt.heading<<","<<cnt.panAngle<<std::endl;
00331 
00332   hasTarget = true;
00333 }
00334 
00335 void BVImageProcessor::llz2xy(const SonarHead& head, TrackedFeature& tracklet)
00336 {
00337   //Remember to update the rotation matrix
00338   _update(head);
00339 
00340   //Calculate the X-Y offset
00341   std::pair<double,double>
00342    value = deg2meter(tracklet.latlon.x - head.latlon.x,tracklet.latlon.y - head.latlon.y,head.latlon.x);
00343 
00344   //Testing code
00345   //std::cout<<"Move:"<<value.first<<","<<value.second<<std::endl;
00346 
00347   enum {x = 0,y,z};
00348   cv::Mat pos_n(3,1,CV_64F);
00349   pos_n.at<double>(x,0) = value.first;
00350   pos_n.at<double>(y,0) = value.second;
00351   //We assume its in the middle of the beam (A BIG ASSUMPTION)
00352   pos_n.at<double>(z,0) = tracklet.latlon.z;
00353 
00354   std::cout<<"debug point 0"<<std::endl;
00355   //Calculate to head X-Y-Z coordinates
00356   using namespace boost::numeric;
00357   ublas::matrix<double> uR(3,3);
00358   ublas::vector<double> uPos(3);
00359 
00360   uPos(0) = pos_n.at<double>(0,0);
00361   uPos(1) = pos_n.at<double>(1,0);
00362   uPos(2) = pos_n.at<double>(2,0);
00363   
00364   uR(0,0) = R.at<double>(0,0);
00365   uR(0,1) = R.at<double>(0,1);
00366   uR(0,2) = R.at<double>(0,2);
00367   uR(1,0) = R.at<double>(1,0);
00368   uR(1,1) = R.at<double>(1,1);
00369   uR(1,2) = R.at<double>(1,2);
00370   uR(2,0) = R.at<double>(2,0);
00371   uR(2,1) = R.at<double>(2,1);
00372   uR(2,2) = R.at<double>(2,2);
00373 
00374   ublas::matrix<double> invR(ublas::zero_matrix<double>(3));
00375   labust::math::gjinverse(uR,invR);
00376   ublas::vector<double> upos = prod(invR,uPos);
00377 
00378   cv::Mat pos(3,1,CV_64F);
00379   pos.at<double>(0,0) = upos(0);
00380   pos.at<double>(1,0) = upos(1);
00381   pos.at<double>(2,0) = upos(2);
00382   std::cout<<"debug point 1"<<std::endl;
00383 
00384   //Update tracklet X-Y position
00385   tracklet.position.x = pos.at<double>(x,0);
00386   tracklet.position.y = pos.at<double>(y,0);
00387 }
00388 
00389 void BVImageProcessor::xy2llz(const SonarHead& head, TrackedFeature& tracklet)
00390 {
00391   //Remember to update the rotation matrix
00392   _update(head);
00393 
00394   enum {x = 0,y,z};
00395   cv::Mat pos(3,1,CV_64F);
00396   pos.at<double>(x,0) = tracklet.position.x;
00397   pos.at<double>(y,0) = tracklet.position.y;
00398   //We assume its in the middle of the beam (A BIG ASSUMPTION)
00399   pos.at<double>(z,0) = 0;
00400 
00401   //Calculate to world X-Y-Z coordinates
00402   cv::Mat pos_n = R*pos;
00403   std::pair<double,double>
00404   value = meter2deg(pos_n.at<double>(x,0), pos_n.at<double>(y,0), head.latlon.x);
00405 
00406   //Testing code
00407   //std::cout<<"Meter2Degree"<<value.first<<","<<value.second<<std::endl;
00408 
00409   //Convert to latlon
00410   tracklet.latlon.x = head.latlon.x + value.first;
00411   tracklet.latlon.y = head.latlon.y + value.second;
00412   tracklet.latlon.z = head.latlon.z + pos_n.at<double>(z,0);
00413 }
00414 
00415 void BVImageProcessor::meter2pixel(TrackerROI& roi, TrackedFeature& tracklet)
00416 {
00417   //std::cout<<"ROI origin:"<<roi->origin.x<<","<<roi->origin.y<<std::endl;
00418   std::cout<<"Meter position:"<<(tracklet.position.x)<<","<<(tracklet.position.y)<<std::endl;
00419 
00420   double pxrange = sqrt(tracklet.position.y*tracklet.position.y + tracklet.position.x*tracklet.position.x) / roi.headData.resolution;
00421   double bearing = atan2(tracklet.position.y,tracklet.position.x);
00422 
00423   tracklet.pposition.y = roi.origin.y - int(pxrange*cos(bearing));
00424   tracklet.pposition.x = roi.origin.x + int(pxrange*sin(bearing));
00425 
00426   std::cout<<"Pixel position: ("<<tracklet.pposition.x<<","<<tracklet.pposition.y<<")"<<std::endl;
00427   //std::cout<<"R-b pixel:"<<pxrange<<","<<bearing<<std::endl;
00428 }
00429 
00430 inline void BVImageProcessor::pixel2meter(TrackerROI& roi, TrackedFeature& tracklet)
00431 {
00432   //std::cout<<"ROI origin:"<<roi->origin.x<<","<<roi->origin.y<<std::endl;
00433   //std::cout<<"Pixel position 2:"<<(tracklet.pposition.x)<<","<<(tracklet.pposition.y)<<std::endl;
00434 
00435   tracklet.position.y = (-roi.origin.x + tracklet.pposition.x)*roi.headData.resolution;
00436   tracklet.position.x = (roi.origin.y - tracklet.pposition.y)*roi.headData.resolution;
00437 
00438   //std::cout<<"Meter position: ("<<tracklet.position.x<<","<<tracklet.position.y<<")"<<std::endl;
00439 
00440   //std::cout<<"Before:"<<tracklet.position.x<<","<<tracklet.position.y<<std::endl;
00441 }
00442 
00443 void BVImageProcessor::_update(const SonarHead& head)
00444 {
00445   double c1 = 1, s1 = 0;
00446   double c2 = cos(head.tiltAngle*M_PI/180), s2 = sin(head.tiltAngle*M_PI/180);
00447   double c3 = cos(head.panAngle*M_PI/180), s3 = sin(head.panAngle*M_PI/180);
00448 
00449   cv::Mat R_hb(3,3,CV_64F);
00450   R_hb.at<double>(0,0) = c3*c2;
00451   R_hb.at<double>(0,1) = c3*s2*s1-s3*c1;
00452   R_hb.at<double>(0,2) = s3*s1+c3*c1*s2;
00453   R_hb.at<double>(1,0) = s3*c2;
00454   R_hb.at<double>(1,1) = c1*c3+s1*s2*s3;
00455   R_hb.at<double>(1,2) = c1*s2*s3-c3*s1;
00456   R_hb.at<double>(2,0) = -s2;
00457   R_hb.at<double>(2,1) = c2*s1;
00458   R_hb.at<double>(2,2) = c1*c2;
00459 
00460   c1 = 1, s1 = 0;
00461   c2 = 1, s2 = 0;
00462   c3 = cos(head.heading*M_PI/180), s3 = sin(head.heading*M_PI/180);
00463 
00464   cv::Mat R_bn(3,3,CV_64F);
00465   R_bn.at<double>(0,0) = c3;
00466   R_bn.at<double>(0,1) = -s3;
00467   R_bn.at<double>(0,2) = 0;
00468   R_bn.at<double>(1,0) = s3;
00469   R_bn.at<double>(1,1) = c3;
00470   R_bn.at<double>(1,2) = 0;
00471   R_bn.at<double>(2,0) = 0;
00472   R_bn.at<double>(2,1) = 0;
00473   R_bn.at<double>(2,2) = 1;
00474 
00475   R = R_bn*R_hb;
00476 
00477  // Testing code
00478  // for (int i=0;i<3;++i)
00479  //  for (int j=0; j<3;++j)
00480  //   std::cout<<R_bn.at<float>(i,j)<<",";
00481 }
00482 
00483 std::pair<double,double> BVImageProcessor::meter2deg(double x, double y, double lat)
00484 {
00485   static const double radius = 6378137;
00486   static const double ratio = 0.99664719;
00491   double mpdlat = 111132.954 - 559.822*cos(2*lat*M_PI/180) + 1.175*cos(4*lat*M_PI/180);
00492   double mpdlon = M_PI*radius*cos(atan(ratio*tan(lat*M_PI/180)))/180;
00493 
00494   return std::pair<double,double>(x/mpdlat,y/mpdlon);
00495 }
00496 
00497 std::pair<double,double> BVImageProcessor::deg2meter(double difflat, double difflon, double lat)
00498 {
00499   static const double radius = 6378137;
00500   static const double ratio = 0.99664719;
00505   double mpdlat = 111132.954 - 559.822*cos(2*lat*M_PI/180) + 1.175*cos(4*lat*M_PI/180);
00506   double mpdlon = M_PI*radius*cos(atan(ratio*tan(lat*M_PI/180)))/180;
00507 
00508   return std::pair<double,double>(difflat*mpdlat,difflon*mpdlon);
00509 }


target_detector
Author(s): Gyula Nagy
autogenerated on Fri Feb 7 2014 11:36:05