Hugin trunk 0.1
Loading...
Searching...
No Matches
Mask.cpp
Go to the documentation of this file.
1// -*- c-basic-offset: 4 -*-
2
11/* This program is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU General Public
13 * License as published by the Free Software Foundation; either
14 * version 2 of the License, or (at your option) any later version.
15 *
16 * This software is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 * General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public
22 * License along with this software. If not, see
23 * <http://www.gnu.org/licenses/>.
24 *
25 */
26
27// for debugging
28#include <iostream>
29#include <stdio.h>
30
31#include "Mask.h"
32
33#include <iostream>
34#include <vector>
37
38namespace HuginBase {
39
41{
42 if(m_polygon.size()<3)
43 return false;
44 if(!m_boundingBox.contains(vigra::Point2D(p.x,p.y)))
45 return false;
47 if(m_invert)
48 return wind==0;
49 else
50 return wind!=0;
51};
52
54{
55 // algorithm is modified version of winding number method
56 // described at http://www.softsurfer.com/Archive/algorithm_0103/algorithm_0103.htm
57 // Copyright 2001, softSurfer (www.softsurfer.com)
58 // This code may be freely used and modified for any purpose
59 // providing that this copyright notice is included with it.
60 if(m_polygon.size()<3)
61 return 0;
62 int wind=0;
64 for(unsigned int i=0;i<m_polygon.size();i++)
65 {
67 if(a.y<=p.y)
68 {
69 if(b.y>p.y)
70 if((b.x-a.x)*(p.y-a.y)<(p.x-a.x)*(b.y-a.y))
71 wind++;
72 }
73 else
74 {
75 if(b.y<=p.y)
76 if((b.x-a.x)*(p.y-a.y)>(p.x-a.x)*(b.y-a.y))
77 wind--;
78 };
79 a=b;
80 };
81 return wind;
82};
83
85{
86 if(m_polygon.size()<2)
87 return 0;
89 unsigned int count=m_polygon.size();
90 for(unsigned int i=0;i<count;i++)
91 {
93 };
94 return diffPoly.getWindingNumber(hugin_utils::FDiff2D(0,0));
95};
96
98{
99 return (m_maskType==Mask_positive) ||
101};
102
108
110{
111 m_polygon.push_back(p);
113};
114
115void MaskPolygon::insertPoint(const unsigned int index, const hugin_utils::FDiff2D p)
116{
117 if(index<=m_polygon.size())
118 {
119 m_polygon.insert(m_polygon.begin()+index,p);
121 };
122};
123
124void MaskPolygon::removePoint(const unsigned int index)
125{
126 if(index<m_polygon.size())
127 {
128 m_polygon.erase(m_polygon.begin()+index);
130 };
131};
132
133void MaskPolygon::movePointTo(const unsigned int index, const hugin_utils::FDiff2D p)
134{
135 if(index<m_polygon.size())
136 {
137 m_polygon[index] = p;
139 };
140};
141
142void MaskPolygon::movePointBy(const unsigned int index, const hugin_utils::FDiff2D diff)
143{
144 if(index<m_polygon.size())
145 {
146 m_polygon[index] += diff;
148 };
149};
150
151void MaskPolygon::scale(const double factorx,const double factory)
152{
153 for(unsigned int i=0;i<m_polygon.size();i++)
154 {
155 m_polygon[i].x*=factorx;
156 m_polygon[i].y*=factory;
157 };
159};
160
161void MaskPolygon::scale(const double factor, const hugin_utils::FDiff2D& p)
162{
163 for (unsigned int i = 0; i < m_polygon.size(); i++)
164 {
165 m_polygon[i] = (m_polygon[i] - p) * factor + p;
166 };
168}
169
171{
172 double xnew,ynew;
174 for(unsigned int i=0;i<m_polygon.size();i++)
175 {
176 if(trans.transformImgCoord(xnew,ynew,m_polygon[i].x,m_polygon[i].y))
177 {
179 };
180 };
183};
184
186{
187 if(m_polygon.size()<3)
188 return;
190 unsigned int count=oldPoly.size();
191 m_polygon.clear();
192 for(unsigned int i=0;i<count;i++)
193 {
196 hugin_utils::FDiff2D p2=oldPoly[(i+1)%count];
197 double distance=norm(p2-p1);
198 if(distance>max_distance)
199 {
200 //add intermediate points
202 while(currentDistance<distance)
203 {
204 hugin_utils::FDiff2D p_new=p1+(p2-p1)*currentDistance/distance;
207 };
208 };
209 };
210};
211
213{
214 if(!m_polygon.empty())
215 {
216 m_boundingBox = vigra::Rect2D(vigra::Point2D(m_polygon[0].x, m_polygon[0].y), vigra::Size2D(1, 1));
217 if(m_polygon.size()>1)
218 {
219 for(unsigned int i=1;i<m_polygon.size();i++)
220 {
221 m_boundingBox|=vigra::Point2D(m_polygon[i].x,m_polygon[i].y);
222 };
223 };
224 //adding a small border to get no rounding error because polygon coordinates are float
225 //numbers, but bounding box has integer coordinates
226 m_boundingBox.addBorder(2);
227 };
228};
229
230//helper function for clipping
238
239bool clip_isSide(const hugin_utils::FDiff2D p, const vigra::Rect2D r, const clipSide side)
240{
241 switch(side){
242 case clipLeft:
243 return p.x>=r.left();
244 case clipRight:
245 return p.x<=r.right();
246 case clipTop:
247 return p.y>=r.top();
248 case clipBottom:
249 return p.y<=r.bottom();
250 };
251 //this should never happens
252 return false;
253}
254
256{
257 double a;
258 double b;
259 double xnew;
260 double ynew;
261 if(q.x-p.x==0)
262 {
263 a=0;
264 b=p.y;
265 }
266 else
267 {
268 a=(q.y-p.y)/(q.x-p.x);
269 b=p.y-p.x*a;
270 };
271 switch(side){
272 case clipLeft:
273 xnew=r.left();
274 ynew=xnew*a+b;
275 break;
276 case clipRight:
277 xnew=r.right();
278 ynew=xnew*a+b;
279 break;
280 case clipTop:
281 ynew=r.top();
282 if(a!=0)
283 xnew=(ynew-b)/a;
284 else
285 xnew=p.x;
286 break;
287 case clipBottom:
288 ynew=r.bottom();
289 if(a!=0)
290 xnew=(ynew-b)/a;
291 else
292 xnew=p.x;
293 break;
294 };
296};
297
298VectorPolygon clip_onPlane(const VectorPolygon& polygon, const vigra::Rect2D r, const clipSide side)
299{
300 if(polygon.size()<3)
301 {
302 return polygon;
303 };
307 for(unsigned int i=0;i<polygon.size();i++)
308 {
309 p=polygon[i];
310 if(clip_isSide(p,r,side))
311 {
312 // point p is "inside"
313 if(!clip_isSide(s,r,side))
314 // and point s is "outside"
315 newPolygon.push_back(clip_getIntersection(p,s,r,side));
316 newPolygon.push_back(p);
317 }
318 else
319 {
320 //point p is "outside"
321 if(clip_isSide(s,r,side))
322 //ans point s is "inside"
323 newPolygon.push_back(clip_getIntersection(s,p,r,side));
324 };
325 s=p;
326 };
327 return newPolygon;
328};
329
330bool MaskPolygon::clipPolygon(const vigra::Rect2D rect)
331{
332 //clipping using Sutherland-Hodgman algorithm
338 return (m_polygon.size()>2);
339};
340
341//helper function for clipping
348bool clip_insideCircle(const hugin_utils::FDiff2D p, const hugin_utils::FDiff2D center, const double radius)
349{
350 return p.squareDistance(center)<=radius*radius;
351};
352
360std::vector<hugin_utils::FDiff2D> clip_getIntersectionCircle(const hugin_utils::FDiff2D p, const hugin_utils::FDiff2D s, const hugin_utils::FDiff2D center, const double radius)
361{
362 std::vector<hugin_utils::FDiff2D> intersections;
364 if(slope.squareLength()<1e-5)
365 {
366 return intersections;
367 };
368 hugin_utils::FDiff2D p2=p-center;
369 double dotproduct=p2.x*slope.x+p2.y*slope.y;
370 double root=sqrt(dotproduct*dotproduct-slope.squareLength()*(p2.squareLength()-radius*radius));
371 double t1=(-dotproduct+root)/slope.squareLength();
372 double t2=(-dotproduct-root)/slope.squareLength();
373 std::set<double> t;
374 if(t1>0 && t1<1)
375 {
376 t.insert(t1);
377 };
378 if(t2>0 && t2<1)
379 {
380 if(fabs(t2-t1)>1e-5)
381 {
382 t.insert(t2);
383 };
384 };
385 if(!t.empty())
386 {
387 for(std::set<double>::const_iterator it=t.begin();it!=t.end();++it)
388 {
389 intersections.push_back(p+slope*(*it));
390 };
391 };
392 return intersections;
393};
394
397{
398 return asin((a.x*b.y-a.y*b.x)/(sqrt(a.squareLength())*sqrt(b.squareLength())));
399};
400
408void generateArc(VectorPolygon& poly, const hugin_utils::FDiff2D s, const hugin_utils::FDiff2D center, const double radius, const bool clockwise)
409{
410 if(poly.empty())
411 {
412 return;
413 };
414 hugin_utils::FDiff2D p=poly[poly.size()-1];
415 double maxDistance=5.0;
417 {
418 return;
419 };
420 double angle=atan2(p.y-center.y,p.x-center.x);
421 double final_angle=atan2(s.y-center.y,s.x-center.x);
422 //step 1 degree or less, so that max distance between 2 points is smaller than maxDistance
423 double step=std::min<double>(PI/180,atan2(maxDistance,radius));
424 if(!clockwise)
425 {
426 while(final_angle<angle)
427 {
428 final_angle+=2*PI;
429 };
430 angle+=step;
431 while(angle<final_angle)
432 {
433 poly.push_back(hugin_utils::FDiff2D(cos(angle)*radius+center.x,sin(angle)*radius+center.y));
434 angle+=step;
435 };
436 }
437 else
438 {
439 while(final_angle>angle)
440 {
441 final_angle-=2*PI;
442 };
443 angle-=step;
444 while(angle>final_angle)
445 {
446 poly.push_back(hugin_utils::FDiff2D(cos(angle)*radius+center.x,sin(angle)*radius+center.y));
447 angle-=step;
448 };
449 };
450};
451
452bool MaskPolygon::clipPolygon(const hugin_utils::FDiff2D center,const double radius)
453{
454 if(radius<=0 || m_polygon.size()<3)
455 {
456 return false;
457 };
459 bool s_inside=clip_insideCircle(s,center,radius);
462 bool needsFinalArc=false;
463 double angleCovered=0;
464 double angleCoveredOffset=0;
465 for(unsigned int i=0;i<m_polygon.size();i++)
466 {
467 p=m_polygon[i];
468 bool p_inside=clip_insideCircle(p,center,radius);
469 if(p_inside)
470 {
471 if(s_inside)
472 {
473 //both points inside
474 newPolygon.push_back(p);
475 }
476 else
477 {
478 //line crosses circles from outside
479 std::vector<hugin_utils::FDiff2D> points=clip_getIntersectionCircle(p,s,center,radius);
480 DEBUG_ASSERT(points.size()==1);
481 angleCovered+=angle_between(s-center,points[0]-center);
482 if(newPolygon.empty())
483 {
484 needsFinalArc=true;
486 }
487 else
488 {
489 generateArc(newPolygon,points[0],center,radius,angleCovered<0);
490 };
491 newPolygon.push_back(points[0]);
492 newPolygon.push_back(p);
493 };
494 }
495 else
496 {
497 if(!s_inside)
498 {
499 //both points outside of circle
500 std::vector<hugin_utils::FDiff2D> points=clip_getIntersectionCircle(s,p,center,radius);
501 //intersection can only be zero points or 2 points
502 if(points.size()>1)
503 {
504 angleCovered+=angle_between(s-center,points[0]-center);
505 if(newPolygon.empty())
506 {
507 needsFinalArc=true;
509 }
510 else
511 {
512 generateArc(newPolygon,points[0],center,radius,angleCovered<0);
513 };
514 newPolygon.push_back(points[0]);
515 newPolygon.push_back(points[1]);
516 angleCovered=angle_between(points[1]-center,p-center);
517 }
518 else
519 {
520 angleCovered+=angle_between(s-center,p-center);
521 };
522 }
523 else
524 {
525 //line segment intersects circle from inside
526 std::vector<hugin_utils::FDiff2D> points=clip_getIntersectionCircle(s,p,center,radius);
527 angleCovered=0;
528 DEBUG_ASSERT(points.size()==1);
529 newPolygon.push_back(points[0]);
530 };
531 };
532 s=p;
534 };
535 if(needsFinalArc && newPolygon.size()>1)
536 {
538 };
541 return (m_polygon.size()>2);
542};
543
544void MaskPolygon::rotate90(bool clockwise,unsigned int maskWidth,unsigned int maskHeight)
545{
546 for(unsigned int i=0;i<m_polygon.size();i++)
547 {
548 if(clockwise)
549 {
552 m_polygon[i].y=p.x;
553 }
554 else
555 {
557 m_polygon[i].x=p.y;
558 m_polygon[i].y=maskWidth-p.x;
559 };
560 };
562};
563
564unsigned int MaskPolygon::FindPointNearPos(const hugin_utils::FDiff2D p, const double tol) const
565{
566 if(m_polygon.empty())
567 return UINT_MAX;
569 unsigned int j=m_polygon.size()-1;
571 for(unsigned int i=0;i<m_polygon.size();i++)
572 {
573 p1=m_polygon[i];
574 // find intersection of perpendicular through point p and line between point i and j
576 if(norm(diff)<0.001)
577 continue;
578 double u=((p.x-p1.x)*(p2.x-p1.x)+(p.y-p1.y)*(p2.y-p1.y))/hugin_utils::sqr(norm(diff));
579 if((u>=0.1) && (u<=0.9))
580 {
581 // intersection is between p1 and p2
583 // now check distance between intersection and p
584 if(norm(p-footpoint)<tol)
585 return i==0 ? j+1 : i;
586 };
587 j=i;
588 p2=p1;
589 };
590 return UINT_MAX;
591};
592
594{
595 if (this == &otherPoly)
596 return *this;
597 setMaskType(otherPoly.getMaskType());
598 setMaskPolygon(otherPoly.getMaskPolygon());
599 setImgNr(otherPoly.getImgNr());
600 setInverted(otherPoly.isInverted());
601 return *this;
602};
603
605{
606 return ((m_maskType == otherPoly.getMaskType()) && (m_polygon == otherPoly.getMaskPolygon()));
607};
608
610{
611 m_polygon.clear();
612 if(polygonStr.length()==0)
613 return false;
614 std::stringstream is(polygonStr);
615 while(is.good())
616 {
617 double x;
618 if (is >> x)
619 {
620 double y;
621 if (is >> y)
622 {
623 m_polygon.push_back(hugin_utils::FDiff2D(x, y));
624 };
625 };
626 };
628 return m_polygon.size()>2;
629};
630
631void MaskPolygon::printPolygonLine(std::ostream &o, const unsigned int newImgNr) const
632{
633 o<<"k i"<<newImgNr<<" ";
634 o<<"t"<<(int)m_maskType<<" ";
635 o<<"p\"";
636 for(unsigned int i=0; i<m_polygon.size(); i++)
637 {
638 o<<m_polygon[i].x<<" "<<m_polygon[i].y;
639 if((i+1)!=m_polygon.size())
640 o<<" ";
641 };
642 o<<"\""<<std::endl;
643};
644
646{
647 if (m_polygon.empty())
648 {
649 return hugin_utils::FDiff2D();
650 }
651 else
652 {
654 for (size_t i = 0; i < m_polygon.size(); ++i)
655 {
656 center += m_polygon[i];
657 }
658 return center / (double)m_polygon.size();
659 };
660}
661
662void LoadMaskFromStream(std::istream& stream,vigra::Size2D& imageSize, MaskPolygonVector &newMasks, size_t imgNr)
663{
664 while (stream.good())
665 {
666 std::string line;
667 std::getline(stream,line);
668 switch (line[0])
669 {
670 case '#':
671 {
672 unsigned int w;
673 if (PTScriptParsing::getIntParam(w, line, "w"))
674 imageSize.setWidth(w);
675 unsigned int h;
676 if (PTScriptParsing::getIntParam(h, line, "h"))
677 imageSize.setHeight(h);
678 break;
679 }
680 case 'k':
681 {
683 //Ignore image number set in mask
684 newPolygon.setImgNr(imgNr);
685 unsigned int param;
686 if (PTScriptParsing::getIntParam(param,line,"t"))
687 {
689 }
690 std::string format;
692 {
693 if(newPolygon.parsePolygonString(format)) {
694 newMasks.push_back(newPolygon);
695 }
696 }
697 break;
698 }
699 default:
700 {
701 break;
702 }
703 }
704 }
705};
706
707void SaveMaskToStream(std::ostream& stream, vigra::Size2D imageSize, MaskPolygon &maskToWrite, size_t imgNr)
708{
709 stream << "# w" << imageSize.width() << " h" << imageSize.height() << std::endl;
710 maskToWrite.printPolygonLine(stream, imgNr);
711};
712
713} // namespace
declaration of classes to work with mask
base class, which stores one mask polygon
Definition Mask.h:53
bool isPositive() const
returns true, if mask type is positive
Definition Mask.cpp:97
void movePointBy(const unsigned int index, const hugin_utils::FDiff2D diff)
relativ moves the point at position index by diff
Definition Mask.cpp:142
void movePointTo(const unsigned int index, const hugin_utils::FDiff2D p)
moves the point at position index to the new absolute position p
Definition Mask.cpp:133
void removePoint(const unsigned int index)
removes point at the position index from the polygon
Definition Mask.cpp:124
void setImgNr(const unsigned int newImgNr)
sets the associated image number, only used when loading a project, otherwise discarded
Definition Mask.h:87
void setMaskPolygon(const VectorPolygon &newMask)
set complete vector with all corrdinates of the polygon
Definition Mask.cpp:103
vigra::Rect2D m_boundingBox
Definition Mask.h:147
void rotate90(bool clockwise, unsigned int maskWidth, unsigned int maskHeight)
rotate the polygon by 90 degrees
Definition Mask.cpp:544
unsigned int FindPointNearPos(const hugin_utils::FDiff2D p, const double tol) const
search a point which lies near the polygon line and return the index for inserting the new point
Definition Mask.cpp:564
void calcBoundingBox()
calculates the bounding box of the polygon to speed up tests
Definition Mask.cpp:212
void addPoint(const hugin_utils::FDiff2D p)
adds point at the end to the polygon
Definition Mask.cpp:109
void subSample(const double max_distance)
subsamples the polygon, so that the longest distance between 2 points is max_distance
Definition Mask.cpp:185
int getWindingNumber(const hugin_utils::FDiff2D p) const
returns the winding number of the polygon around point p
Definition Mask.cpp:53
bool isInside(const hugin_utils::FDiff2D p) const
checks if given point is inside of the stored polygon
Definition Mask.cpp:40
void scale(const double factorx, const double factory)
scales all polygon coordinates by factorx for x position and factory for y position
Definition Mask.cpp:151
bool clipPolygon(const vigra::Rect2D rect)
clips the polygon to the given rectangle
Definition Mask.cpp:330
MaskPolygon & operator=(const MaskPolygon &otherPoly)
assign operator
Definition Mask.cpp:593
bool parsePolygonString(const std::string &polygonStr)
parses the x and y positions from the given string
Definition Mask.cpp:609
VectorPolygon m_polygon
Definition Mask.h:144
const bool operator==(const MaskPolygon &otherPoly) const
comparision operator
Definition Mask.cpp:604
void setMaskType(const MaskType newType)
sets mask type
Definition Mask.h:77
void printPolygonLine(std::ostream &o, const unsigned int newImgNr) const
writes the complete k line which describes the mask to the stream, using the given newImgNr for the i...
Definition Mask.cpp:631
hugin_utils::FDiff2D getCenter() const
return the center point of the polygon
Definition Mask.cpp:645
int getTotalWindingNumber() const
returns the total winding number of the polygon
Definition Mask.cpp:84
void insertPoint(const unsigned int index, const hugin_utils::FDiff2D p)
insert point at the position index into the polygon
Definition Mask.cpp:115
void setInverted(const bool inverted)
set mask to normal or inverted
Definition Mask.h:89
MaskType m_maskType
Definition Mask.h:143
MaskType
enumeration with type of possible masks
Definition Mask.h:57
void transformPolygon(const PTools::Transform &trans)
transforms the polygon coordinates by the given transformation
Definition Mask.cpp:170
Holds transformations for Image -> Pano and the other way.
#define DEBUG_ASSERT(cond)
Definition utils.h:80
#define PI
Header file for Khan's deghosting algorithm Copyright (C) 2009 Lukáš Jirkovský l.jirkovsky@gmail....
Definition khan.h:43
bool getIntParam(T &value, const std::string &line, const std::string &name)
bool getPTParam(std::string &output, const std::string &line, const std::string &parameter)
helper functions for parsing of a script line
mainly consists of wrapper around the pano tools library, to assist in ressource management and to pr...
Definition wxcms.cpp:39
bool clip_isSide(const hugin_utils::FDiff2D p, const vigra::Rect2D r, const clipSide side)
Definition Mask.cpp:239
std::vector< MaskPolygon > MaskPolygonVector
Definition Mask.h:150
@ clipTop
Definition Mask.cpp:235
@ clipBottom
Definition Mask.cpp:236
@ clipLeft
Definition Mask.cpp:233
@ clipRight
Definition Mask.cpp:234
hugin_utils::FDiff2D clip_getIntersection(const hugin_utils::FDiff2D p, const hugin_utils::FDiff2D q, const vigra::Rect2D r, const clipSide side)
Definition Mask.cpp:255
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)
returns intersection of line and circle
Definition Mask.cpp:360
VectorPolygon clip_onPlane(const VectorPolygon &polygon, const vigra::Rect2D r, const clipSide side)
Definition Mask.cpp:298
double angle_between(const hugin_utils::FDiff2D a, const hugin_utils::FDiff2D b)
calculates angle between vector a and b in radians
Definition Mask.cpp:396
void generateArc(VectorPolygon &poly, const hugin_utils::FDiff2D s, const hugin_utils::FDiff2D center, const double radius, const bool clockwise)
adds an arc with given radius at the end of the polygon, the point is not added to the arc
Definition Mask.cpp:408
void LoadMaskFromStream(std::istream &stream, vigra::Size2D &imageSize, MaskPolygonVector &newMasks, size_t imgNr)
load the mask from stream
Definition Mask.cpp:662
void SaveMaskToStream(std::ostream &stream, vigra::Size2D imageSize, MaskPolygon &maskToWrite, size_t imgNr)
save the mask into stream
Definition Mask.cpp:707
bool clip_insideCircle(const hugin_utils::FDiff2D p, const hugin_utils::FDiff2D center, const double radius)
check if point is inside circle
Definition Mask.cpp:348
std::vector< hugin_utils::FDiff2D > VectorPolygon
vector, which stores coordinates of one polygon
Definition Mask.h:39
TDiff2D< double > FDiff2D
Definition hugin_math.h:162
T squareLength() const
Return the square of the length of the vector.
Definition hugin_math.h:153
T squareDistance(TDiff2D< T > other) const
Return square of the distance to another point.
Definition hugin_math.h:147
std::vector< deghosting::BImagePtr > threshold(const std::vector< deghosting::FImagePtr > &inputImages, const double threshold, const uint16_t flags)
Threshold function used for creating alpha masks for images.
Definition threshold.h:41