Mask.cpp

Go to the documentation of this file.
00001 // -*- c-basic-offset: 4 -*-
00002 
00011 /*  This program is free software; you can redistribute it and/or
00012  *  modify it under the terms of the GNU General Public
00013  *  License as published by the Free Software Foundation; either
00014  *  version 2 of the License, or (at your option) any later version.
00015  *
00016  *  This software is distributed in the hope that it will be useful,
00017  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00018  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00019  *  General Public License for more details.
00020  *
00021  *  You should have received a copy of the GNU General Public
00022  *  License along with this software. If not, see
00023  *  <http://www.gnu.org/licenses/>.
00024  *
00025  */
00026 
00027 // for debugging
00028 #include <iostream>
00029 #include <stdio.h>
00030 
00031 #include "Mask.h"
00032 
00033 #include <iostream>
00034 #include <vector>
00035 #include <panotools/PanoToolsInterface.h>
00036 #include <panodata/PTScriptParsing.h>
00037 
00038 namespace HuginBase {
00039 
00040 bool MaskPolygon::isInside(const hugin_utils::FDiff2D p) const
00041 {
00042     if(m_polygon.size()<3)
00043         return false;
00044     if(!m_boundingBox.contains(vigra::Point2D(p.x,p.y)))
00045         return false;
00046     int wind=getWindingNumber(p);
00047     if(m_invert)
00048         return wind==0;
00049     else
00050         return wind!=0;
00051 };
00052 
00053 int MaskPolygon::getWindingNumber(const hugin_utils::FDiff2D p) const
00054 {
00055     // algorithm is modified version of winding number method
00056     // described at http://www.softsurfer.com/Archive/algorithm_0103/algorithm_0103.htm
00057     // Copyright 2001, softSurfer (www.softsurfer.com)
00058     // This code may be freely used and modified for any purpose
00059     // providing that this copyright notice is included with it.
00060     if(m_polygon.size()<3)
00061         return 0;
00062     int wind=0;
00063     hugin_utils::FDiff2D a=m_polygon[m_polygon.size()-1];
00064     for(unsigned int i=0;i<m_polygon.size();i++)
00065     {
00066         hugin_utils::FDiff2D b=m_polygon[i];
00067         if(a.y<=p.y)
00068         {
00069             if(b.y>p.y)
00070                 if((b.x-a.x)*(p.y-a.y)<(p.x-a.x)*(b.y-a.y))
00071                     wind++;
00072         }
00073         else
00074         {
00075             if(b.y<=p.y)
00076                 if((b.x-a.x)*(p.y-a.y)>(p.x-a.x)*(b.y-a.y))
00077                     wind--;
00078         };
00079         a=b;
00080     };
00081     return wind;
00082 };
00083 
00084 int MaskPolygon::getTotalWindingNumber() const
00085 {
00086     if(m_polygon.size()<2)
00087         return 0;
00088     MaskPolygon diffPoly;
00089     unsigned int count=m_polygon.size();
00090     for(unsigned int i=0;i<count;i++)
00091     {
00092         diffPoly.addPoint(m_polygon[(i+1)%count]-m_polygon[i]);
00093     };
00094     return diffPoly.getWindingNumber(hugin_utils::FDiff2D(0,0));
00095 };
00096 
00097 bool MaskPolygon::isPositive() const
00098 {
00099     return (m_maskType==Mask_positive) || 
00100            (m_maskType==Mask_Stack_positive);
00101 };
00102 
00103 void MaskPolygon::setMaskPolygon(const VectorPolygon& newMask)
00104 {
00105     m_polygon=newMask;
00106     calcBoundingBox();
00107 };
00108 
00109 void MaskPolygon::addPoint(const hugin_utils::FDiff2D p) 
00110 {
00111     m_polygon.push_back(p);
00112     calcBoundingBox();
00113 };
00114 
00115 void MaskPolygon::insertPoint(const unsigned int index, const hugin_utils::FDiff2D p)
00116 {
00117     if(index<=m_polygon.size())
00118     {
00119         m_polygon.insert(m_polygon.begin()+index,p);
00120         calcBoundingBox();
00121     };
00122 };
00123 
00124 
00125 void MaskPolygon::removePoint(const unsigned int index)
00126 {
00127     if(index<m_polygon.size())
00128     {
00129         m_polygon.erase(m_polygon.begin()+index);
00130         calcBoundingBox();
00131     };
00132 };
00133 
00134 void MaskPolygon::movePointTo(const unsigned int index, const hugin_utils::FDiff2D p)
00135 {
00136     if(index<m_polygon.size())
00137     {
00138         m_polygon[index].x=p.x;
00139         m_polygon[index].y=p.y;
00140         calcBoundingBox();
00141     };
00142 };
00143 
00144 void MaskPolygon::movePointBy(const unsigned int index, const hugin_utils::FDiff2D diff)
00145 {
00146     if(index<m_polygon.size())
00147     {
00148         m_polygon[index].x+=diff.x;
00149         m_polygon[index].y+=diff.y;
00150         calcBoundingBox();
00151     };
00152 };
00153 
00154 void MaskPolygon::scale(const double factorx,const double factory)
00155 {
00156     for(unsigned int i=0;i<m_polygon.size();i++)
00157     {
00158         m_polygon[i].x*=factorx;
00159         m_polygon[i].y*=factory;
00160     };
00161     calcBoundingBox();
00162 };
00163 
00164 void MaskPolygon::transformPolygon(const PTools::Transform &trans)
00165 {
00166     double xnew,ynew;
00167     VectorPolygon newPoly;
00168     for(unsigned int i=0;i<m_polygon.size();i++)
00169     {
00170         if(trans.transformImgCoord(xnew,ynew,m_polygon[i].x,m_polygon[i].y))
00171         {
00172             newPoly.push_back(hugin_utils::FDiff2D(xnew,ynew));
00173         };
00174     };
00175     m_polygon=newPoly;
00176     calcBoundingBox();
00177 };
00178 
00179 void MaskPolygon::subSample(const double max_distance)
00180 {
00181     if(m_polygon.size()<3)
00182         return;
00183     VectorPolygon oldPoly=m_polygon;
00184     unsigned int count=oldPoly.size();
00185     m_polygon.clear();
00186     for(unsigned int i=0;i<count;i++)
00187     {
00188         addPoint(oldPoly[i]);
00189         hugin_utils::FDiff2D p1=oldPoly[i];
00190         hugin_utils::FDiff2D p2=oldPoly[(i+1)%count];
00191         double distance=norm(p2-p1);
00192         if(distance>max_distance)
00193         {
00194             //add intermediate points
00195             double currentDistance=max_distance;
00196             while(currentDistance<distance)
00197             {
00198                 hugin_utils::FDiff2D p_new=p1+(p2-p1)*currentDistance/distance;
00199                 addPoint(p_new);
00200                 currentDistance+=max_distance;
00201             };
00202         };
00203     };
00204 };
00205 
00206 void MaskPolygon::calcBoundingBox()
00207 {
00208     if(m_polygon.size()>0)
00209     {
00210         m_boundingBox.setUpperLeft(vigra::Point2D(m_polygon[0].x,m_polygon[0].y));
00211         m_boundingBox.setLowerRight(vigra::Point2D(m_polygon[0].x+1,m_polygon[0].y+1));
00212         if(m_polygon.size()>1)
00213         {
00214             for(unsigned int i=1;i<m_polygon.size();i++)
00215             {
00216                 m_boundingBox|=vigra::Point2D(m_polygon[i].x,m_polygon[i].y);
00217             };
00218         };
00219         //adding a small border to get no rounding error because polygon coordinates are float
00220         //numbers, but bounding box has integer coordinates
00221         m_boundingBox.addBorder(2);
00222     };
00223 };
00224 
00225 //helper function for clipping
00226 enum clipSide
00227 {
00228     clipLeft=0,
00229     clipRight,
00230     clipTop,
00231     clipBottom
00232 };
00233 
00234 bool clip_isSide(const hugin_utils::FDiff2D p, const vigra::Rect2D r, const clipSide side)
00235 {
00236     switch(side){
00237         case clipLeft:
00238             return p.x>=r.left();
00239         case clipRight:
00240             return p.x<=r.right();
00241         case clipTop:
00242             return p.y>=r.top();
00243         case clipBottom:
00244             return p.y<=r.bottom();
00245     };
00246     //this should never happens
00247     return false;
00248 }
00249 
00250 hugin_utils::FDiff2D clip_getIntersection(const hugin_utils::FDiff2D p, const hugin_utils::FDiff2D q, const vigra::Rect2D r, const clipSide side)
00251 {
00252     double a;
00253     double b;
00254     double xnew;
00255     double ynew;
00256     if(q.x-p.x==0)
00257     {
00258         a=0;
00259         b=p.y;
00260     }
00261     else
00262     {
00263         a=(q.y-p.y)/(q.x-p.x);
00264         b=p.y-p.x*a;
00265     };
00266     switch(side){
00267         case clipLeft:
00268             xnew=r.left();
00269             ynew=xnew*a+b;
00270             break;
00271         case clipRight:
00272             xnew=r.right();
00273             ynew=xnew*a+b;
00274             break;
00275         case clipTop:
00276             ynew=r.top();
00277             if(a!=0)
00278                 xnew=(ynew-b)/a;
00279             else
00280                 xnew=p.x;
00281             break;
00282         case clipBottom:
00283             ynew=r.bottom();
00284             if(a!=0)
00285                 xnew=(ynew-b)/a;
00286             else
00287                 xnew=p.x;
00288             break;
00289     };
00290     return hugin_utils::FDiff2D(xnew,ynew);
00291 };
00292 
00293 VectorPolygon clip_onPlane(const VectorPolygon& polygon, const vigra::Rect2D r, const clipSide side)
00294 {
00295     if(polygon.size()<3)
00296     {
00297         return polygon;
00298     };
00299     hugin_utils::FDiff2D s=polygon[polygon.size()-1];
00300     hugin_utils::FDiff2D p;
00301     VectorPolygon newPolygon;
00302     for(unsigned int i=0;i<polygon.size();i++)
00303     {
00304         p=polygon[i];
00305         if(clip_isSide(p,r,side))
00306         {
00307             // point p is "inside"
00308             if(!clip_isSide(s,r,side))
00309                 // and point s is "outside"
00310                 newPolygon.push_back(clip_getIntersection(p,s,r,side));
00311             newPolygon.push_back(p);
00312         }
00313         else
00314         {
00315             //point p is "outside"
00316             if(clip_isSide(s,r,side))
00317                 //ans point s is "inside"
00318                 newPolygon.push_back(clip_getIntersection(s,p,r,side));
00319         };
00320         s=p;
00321     };
00322     return newPolygon;
00323 };
00324 
00325 bool MaskPolygon::clipPolygon(const vigra::Rect2D rect)
00326 {
00327     //clipping using Sutherland-Hodgman algorithm
00328     m_polygon=clip_onPlane(m_polygon, rect, clipLeft);
00329     m_polygon=clip_onPlane(m_polygon, rect, clipRight);
00330     m_polygon=clip_onPlane(m_polygon, rect, clipTop);
00331     m_polygon=clip_onPlane(m_polygon, rect, clipBottom);
00332     return (m_polygon.size()>2);
00333 };
00334 
00335 //helper function for clipping
00342 bool clip_insideCircle(const hugin_utils::FDiff2D p, const hugin_utils::FDiff2D center, const double radius)
00343 {
00344     return p.squareDistance(center)<=radius*radius;
00345 };
00346 
00354 std::vector<hugin_utils::FDiff2D> clip_getIntersectionCircle(const hugin_utils::FDiff2D p, const hugin_utils::FDiff2D s, const hugin_utils::FDiff2D center, const double radius)
00355 {
00356     std::vector<hugin_utils::FDiff2D> intersections;
00357     hugin_utils::FDiff2D slope=s-p;
00358     if(slope.squareLength()<1e-5)
00359     {
00360         return intersections;
00361     };
00362     hugin_utils::FDiff2D p2=p-center;
00363     double dotproduct=p2.x*slope.x+p2.y*slope.y;
00364     double root=sqrt(dotproduct*dotproduct-slope.squareLength()*(p2.squareLength()-radius*radius));
00365     double t1=(-dotproduct+root)/slope.squareLength();
00366     double t2=(-dotproduct-root)/slope.squareLength();
00367     std::set<double> t;
00368     if(t1>0 && t1<1)
00369     {
00370         t.insert(t1);
00371     };
00372     if(t2>0 && t2<1)
00373     {
00374         if(fabs(t2-t1)>1e-5)
00375         {
00376             t.insert(t2);
00377         };
00378     };
00379     if(!t.empty())
00380     {
00381         for(std::set<double>::const_iterator it=t.begin();it!=t.end();++it)
00382         {
00383             intersections.push_back(p+slope*(*it));
00384         };
00385     };
00386     return intersections;
00387 };
00388 
00390 double angle_between(const hugin_utils::FDiff2D a, const hugin_utils::FDiff2D b)
00391 {
00392     return asin((a.x*b.y-a.y*b.x)/(sqrt(a.squareLength())*sqrt(b.squareLength())));
00393 };
00394 
00402 void generateArc(VectorPolygon& poly, const hugin_utils::FDiff2D s, const hugin_utils::FDiff2D center, const double radius, const bool clockwise)
00403 {
00404     if(poly.size()==0)
00405     {
00406         return;
00407     };
00408     hugin_utils::FDiff2D p=poly[poly.size()-1];
00409     double maxDistance=5.0;
00410     if(p.squareDistance(s)<maxDistance*maxDistance)
00411     {
00412         return;
00413     };
00414     double angle=atan2(p.y-center.y,p.x-center.x);
00415     double final_angle=atan2(s.y-center.y,s.x-center.x);
00416     //step 1 degree or less, so that max distance between 2 points is smaller than maxDistance
00417     double step=std::min<double>(PI/180,atan2(maxDistance,radius));
00418     if(!clockwise)
00419     {
00420         while(final_angle<angle)
00421         {
00422             final_angle+=2*PI;
00423         };
00424         angle+=step;
00425         while(angle<final_angle)
00426         {
00427             poly.push_back(hugin_utils::FDiff2D(cos(angle)*radius+center.x,sin(angle)*radius+center.y));
00428             angle+=step;
00429         };
00430     }
00431     else
00432     {
00433         while(final_angle>angle)
00434         {
00435             final_angle-=2*PI;
00436         };
00437         angle-=step;
00438         while(angle>final_angle)
00439         {
00440             poly.push_back(hugin_utils::FDiff2D(cos(angle)*radius+center.x,sin(angle)*radius+center.y));
00441             angle-=step;
00442         };
00443     };
00444 };
00445 
00446 bool MaskPolygon::clipPolygon(const hugin_utils::FDiff2D center,const double radius)
00447 {
00448     if(radius<=0 || m_polygon.size()<3)
00449     {
00450         return false;
00451     };
00452     hugin_utils::FDiff2D s=m_polygon[m_polygon.size()-1];
00453     bool s_inside=clip_insideCircle(s,center,radius);
00454     hugin_utils::FDiff2D p;
00455     VectorPolygon newPolygon;
00456     bool needsFinalArc=false;
00457     double angleCovered=0;
00458     double angleCoveredOffset=0;
00459     for(unsigned int i=0;i<m_polygon.size();i++)
00460     {
00461         p=m_polygon[i];
00462         bool p_inside=clip_insideCircle(p,center,radius);
00463         if(p_inside)
00464         {
00465             if(s_inside)
00466             {
00467                 //both points inside
00468                 newPolygon.push_back(p);
00469             }
00470             else
00471             {
00472                 //line crosses circles from outside
00473                 std::vector<hugin_utils::FDiff2D> points=clip_getIntersectionCircle(p,s,center,radius);
00474                 DEBUG_ASSERT(points.size()==1);
00475                 angleCovered+=angle_between(s-center,points[0]-center);
00476                 if(newPolygon.size()==0)
00477                 {
00478                     needsFinalArc=true;
00479                     angleCoveredOffset=angleCovered;
00480                 }
00481                 else
00482                 {
00483                     generateArc(newPolygon,points[0],center,radius,angleCovered<0);
00484                 };
00485                 newPolygon.push_back(points[0]);
00486                 newPolygon.push_back(p);
00487             };
00488         }
00489         else
00490         {
00491             if(!s_inside)
00492             {
00493                 //both points outside of circle
00494                 std::vector<hugin_utils::FDiff2D> points=clip_getIntersectionCircle(s,p,center,radius);
00495                 //intersection can only be zero points or 2 points
00496                 if(points.size()>1)
00497                 {
00498                     angleCovered+=angle_between(s-center,points[0]-center);
00499                     if(newPolygon.size()==0)
00500                     {
00501                         needsFinalArc=true;
00502                         angleCoveredOffset=angleCovered;
00503                     }
00504                     else
00505                     {
00506                         generateArc(newPolygon,points[0],center,radius,angleCovered<0);
00507                     };
00508                     newPolygon.push_back(points[0]);
00509                     newPolygon.push_back(points[1]);
00510                     angleCovered=angle_between(points[1]-center,p-center);
00511                 }
00512                 else
00513                 {
00514                     angleCovered+=angle_between(s-center,p-center);
00515                 };
00516             }
00517             else
00518             {
00519                 //line segment intersects circle from inside
00520                 std::vector<hugin_utils::FDiff2D> points=clip_getIntersectionCircle(s,p,center,radius);
00521                 angleCovered=0;
00522                 DEBUG_ASSERT(points.size()==1);
00523                 newPolygon.push_back(points[0]);
00524             };
00525         };
00526         s=p;
00527         s_inside=p_inside;
00528     };
00529     if(needsFinalArc && newPolygon.size()>1)
00530     {
00531         generateArc(newPolygon,newPolygon[0],center,radius,(angleCovered+angleCoveredOffset)<0);
00532     };        
00533     m_polygon=newPolygon;
00534     return (m_polygon.size()>2);
00535 };
00536 
00537 void MaskPolygon::rotate90(bool clockwise,unsigned int maskWidth,unsigned int maskHeight)
00538 {
00539     for(unsigned int i=0;i<m_polygon.size();i++)
00540     {
00541         if(clockwise)
00542         {
00543             hugin_utils::FDiff2D p=m_polygon[i];
00544             m_polygon[i].x=maskHeight-p.y;
00545             m_polygon[i].y=p.x;
00546         }
00547         else
00548         {
00549             hugin_utils::FDiff2D p=m_polygon[i];
00550             m_polygon[i].x=p.y;
00551             m_polygon[i].y=maskWidth-p.x;
00552         };
00553     };
00554 };
00555 
00556 unsigned int MaskPolygon::FindPointNearPos(const hugin_utils::FDiff2D p, const double tol)
00557 {
00558     if(m_polygon.size()==0)
00559         return UINT_MAX;
00560     hugin_utils::FDiff2D p1;
00561     unsigned int j=m_polygon.size()-1;
00562     hugin_utils::FDiff2D p2=m_polygon[j];
00563     for(unsigned int i=0;i<m_polygon.size();i++)
00564     {
00565         p1=m_polygon[i];
00566         // find intersection of perpendicular through point p and line between point i and j 
00567         hugin_utils::FDiff2D diff=p2-p1;
00568         if(norm(diff)<0.001)
00569             continue;
00570         double u=((p.x-p1.x)*(p2.x-p1.x)+(p.y-p1.y)*(p2.y-p1.y))/hugin_utils::sqr(norm(diff));
00571         if((u>=0.1) && (u<=0.9))
00572         {
00573             // intersection is between p1 and p2
00574             hugin_utils::FDiff2D footpoint=p1+diff*u;
00575             // now check distance between intersection and p
00576             if(norm(p-footpoint)<tol)
00577                 return i==0 ? j+1 : i;
00578         };
00579         j=i;
00580         p2=p1;
00581     };
00582     return UINT_MAX;
00583 };
00584 
00585 MaskPolygon& MaskPolygon::operator=(const MaskPolygon& otherPoly)
00586 {
00587     if (this == &otherPoly)
00588         return *this;
00589     setMaskType(otherPoly.getMaskType());
00590     setMaskPolygon(otherPoly.getMaskPolygon());
00591     setImgNr(otherPoly.getImgNr());
00592     setInverted(otherPoly.isInverted());
00593     return *this;
00594 };
00595 
00596 const bool MaskPolygon::operator==(const MaskPolygon& otherPoly) const
00597 {
00598     return ((m_maskType == otherPoly.getMaskType()) && (m_polygon == otherPoly.getMaskPolygon()));
00599 };
00600 
00601 bool MaskPolygon::parsePolygonString(const std::string& polygonStr)
00602 {
00603     m_polygon.clear();
00604     if(polygonStr.length()==0)
00605         return false;
00606     std::stringstream is(polygonStr);
00607     while(is.good())
00608     {
00609         double x;
00610         if (is >> x)
00611         {
00612             double y;
00613             if (is >> y)
00614             {
00615                 m_polygon.push_back(hugin_utils::FDiff2D(x, y));
00616             };
00617         };
00618     };
00619     return m_polygon.size()>2;
00620 };
00621 
00622 void MaskPolygon::printPolygonLine(std::ostream &o, const unsigned int newImgNr) const
00623 {
00624     o<<"k i"<<newImgNr<<" ";
00625     o<<"t"<<(int)m_maskType<<" ";
00626     o<<"p\"";
00627     for(unsigned int i=0; i<m_polygon.size(); i++)
00628     {
00629         o<<m_polygon[i].x<<" "<<m_polygon[i].y;
00630         if((i+1)!=m_polygon.size())
00631             o<<" ";
00632     };
00633     o<<"\""<<std::endl;
00634 };
00635 
00636 void LoadMaskFromStream(std::istream& stream,vigra::Size2D& imageSize, MaskPolygonVector &newMasks, size_t imgNr)
00637 {
00638     while (stream.good()) 
00639     {
00640         std::string line;
00641         std::getline(stream,line);
00642         switch (line[0]) 
00643         {
00644             case '#':
00645             {
00646                 unsigned int w;
00647                 if (PTScriptParsing::getIntParam(w, line, "w"))
00648                     imageSize.setWidth(w);
00649                 unsigned int h;
00650                 if (PTScriptParsing::getIntParam(h, line, "h"))
00651                     imageSize.setHeight(h);
00652                 break;
00653             }
00654             case 'k':
00655             {
00656                 HuginBase::MaskPolygon newPolygon;
00657                 //Ignore image number set in mask
00658                 newPolygon.setImgNr(imgNr);
00659                 unsigned int param;
00660                 if (PTScriptParsing::getIntParam(param,line,"t"))
00661                 {
00662                     newPolygon.setMaskType((HuginBase::MaskPolygon::MaskType)param);
00663                 }
00664                 std::string format;
00665                 if (PTScriptParsing::getPTParam(format,line,"p"))
00666                 {
00667                     if(newPolygon.parsePolygonString(format)) {
00668                         newMasks.push_back(newPolygon);
00669                     } 
00670                 }
00671                 break;
00672             }
00673             default:
00674             {
00675                 break;
00676             }
00677         }
00678     }
00679 };
00680 
00681 void SaveMaskToStream(std::ostream& stream, vigra::Size2D imageSize, MaskPolygon &maskToWrite, size_t imgNr)
00682 {
00683     stream << "# w" << imageSize.width() << " h" << imageSize.height() << std::endl;
00684     maskToWrite.printPolygonLine(stream, imgNr);
00685 };
00686 
00687 } // namespace

Generated on 24 Jul 2016 for Hugintrunk by  doxygen 1.4.7