DGtalTools  1.5.beta
2dSimplePolygonDigitizer.cpp
1 
30 #include <iostream>
31 #include <vector>
32 #include <string>
33 #include <iterator>
34 #include <fstream>
35 
36 #include "CLI11.hpp"
37 
38 #include "DGtal/base/Common.h"
39 #include "DGtal/helpers/StdDefs.h"
40 
41 //image
42 #include "DGtal/io/writers/GenericWriter.h"
43 #include "DGtal/images/ImageSelector.h"
44 #include "DGtal/io/boards/Board2D.h"
45 
46 //contour
47 #include "DGtal/io/readers/PointListReader.h"
48 
49 using namespace DGtal;
50 
51 
101 std::pair<Z2i::Point, Z2i::Point > getBoundingBox(const std::vector<Z2i::Point>& vecPts);
102 std::vector<Z2i::Point> GaussDigization(const std::vector<Z2i::Point>& polygon);
103 
104 int main( int argc, char** argv )
105 {
106 
107  // parse command line using CLI ----------------------------------------------
108  CLI::App app;
109  std::string inputFileName;
110  std::string outputFileName="result.pgm";
111 
112  app.description("compute the set of integer points inside the input polyline.\n Basic example:\n \t 2dSimplePolygonDigitizer -i inputPolyline.sdp -o contourDisplay.pgm");
113  app.add_option("-i,--input,1", inputFileName, "Input filename (freeman chain of sdp)." )
114  ->required()
115  ->check(CLI::ExistingFile);
116  app.add_option("-o,--output,2", outputFileName, "Output filename", true);
117 
118  app.get_formatter()->column_width(40);
119  CLI11_PARSE(app, argc, argv);
120  // END parse command line using CLI ----------------------------------------------
121 
122  std::string inputExt = inputFileName.substr(inputFileName.find_last_of(".")+1);
123  if(inputExt != "sdp"){
124  trace.error() << "input file should be sdp file" << std::endl;
125  return EXIT_FAILURE;
126  }
127 
128  std::vector<Z2i::Point> poly = PointListReader<Z2i::Point>::getPointsFromFile(inputFileName);
129  std::vector<Z2i::Point> gd = GaussDigization(poly);
130 
131  std::string outputExt = outputFileName.substr(outputFileName.find_last_of(".")+1);
132  std::string outputBaseName = outputFileName.substr(0, outputFileName.find_last_of("."));
133 
134  if(outputExt == "pgm") {
135  //Write results to pgm image
136  std::pair<Z2i::Point, Z2i::Point> bb = getBoundingBox(gd);
138  Z2i::Point p1 = bb.first-Z2i::Point(1,1);
139  Z2i::Point p2 = bb.second+Z2i::Point(1,1);
140  Image image(Z2i::Domain(p1,p2));
141  for(std::vector<Z2i::Point>::const_iterator it=gd.begin(); it!=gd.end(); it++) {
142  Z2i::Point p ((*it)[0],(*it)[1]);
143  image.setValue(p,255);
144  }
145  PGMWriter<Image>::exportPGM(outputFileName,image);
146  }
147  else {
148  //Write results to svg image
149  outputFileName = outputBaseName + ".svg";
150  DGtal::Board2D board;
151  board << DGtal::SetMode("PointVector", "Both");
152  for(std::vector<Z2i::Point>::const_iterator it=gd.begin(); it!=gd.end(); it++) {
153  board << *it;
154  }
156  board.setLineWidth(2);
157  for(size_t it=0; it<poly.size()-1; it++) {
158  DGtal::Z2i::RealPoint p1 (poly.at(it)[0],poly.at(it)[1]);
159  DGtal::Z2i::RealPoint p2 (poly.at(it+1)[0],poly.at(it+1)[1]);
160  board.drawLine(p1[0],p1[1],p2[0],p2[1]);
161  }
162  DGtal::Z2i::RealPoint p1 (poly.front()[0],poly.front()[1]);
163  DGtal::Z2i::RealPoint p2 (poly.back()[0],poly.back()[1]);
164  board.drawLine(p1[0],p1[1],p2[0],p2[1]);
165 
166  board.saveSVG(outputFileName.c_str());
167  }
168  return EXIT_SUCCESS;
169 }
170 
171 // Auxiliaires functions
172 // Compute the bounding box of the polyline
173 std::pair<Z2i::Point, Z2i::Point > getBoundingBox(const std::vector<Z2i::Point>& vecPts) {
174  int minX = vecPts.at(0)[0];
175  int maxX = vecPts.at(0)[0];
176  int minY = vecPts.at(0)[1];
177  int maxY = vecPts.at(0)[1];
178 
179  for(size_t it=1; it<vecPts.size(); it++) {
180  int x = vecPts.at(it)[0];
181  int y = vecPts.at(it)[1];
182  if(minX > x) minX = x;
183  if(maxX < x) maxX = x;
184  if(minY > y) minY = y;
185  if(maxY < y) maxY = y;
186  }
187  Z2i::Point tl (minX,minY);
188  Z2i::Point rb (maxX,maxY);
189  return std::make_pair(tl,rb);
190 }
191 
192 /* Source adapted from : https://www.geeksforgeeks.org/how-to-check-if-a-given-point-lies-inside-a-polygon/ */
193 // Given three colinear points p, q, r, the function checks if
194 // point q lies on line segment 'pr'
195 template<typename T>
196 T max(T v1, T v2) {
197  return v1 > v2 ? v1 : v2;
198 }
199 template<typename T>
200 T min(T v1, T v2) {
201  return v1 > v2 ? v2 : v1;
202 }
203 template<typename TPoint>
204 bool onSegment(TPoint p, TPoint q, TPoint r)
205 {
206  if (q[0] <= max(p[0], r[0]) && q[0] >= min(p[0], r[0]) && q[1] <= max(p[1], r[1]) && q[1] >= min(p[1], r[1]))
207  return true;
208  return false;
209 }
210 
211 // To find orientation of ordered triplet (p, q, r).
212 // The function returns following values
213 // 0 --> p, q and r are colinear
214 // 1 --> Clockwise
215 // 2 --> Counterclockwise
216 template<typename TPoint>
217 int orientation(TPoint p, TPoint q, TPoint r)
218 {
219  int val = (q[1] - p[1]) * (r[0] - q[0]) - (q[0] - p[0]) * (r[1] - q[1]);
220  if (val == 0) return 0; // colinear
221  return (val > 0)? 1: 2; // clock or counterclock wise
222 }
223 
224 // The function that returns true if line segment 'p1q1'
225 // and 'p2q2' intersect.
226 template<typename TPoint>
227 bool doIntersect(TPoint p1, TPoint q1, TPoint p2, TPoint q2)
228 {
229  // Find the four orientations needed for general and
230  // special cases
231  int o1 = orientation(p1, q1, p2);
232  int o2 = orientation(p1, q1, q2);
233  int o3 = orientation(p2, q2, p1);
234  int o4 = orientation(p2, q2, q1);
235 
236  // General case
237  if (o1 != o2 && o3 != o4)
238  return true;
239 
240  // Special Cases
241  // p1, q1 and p2 are colinear and p2 lies on segment p1q1
242  if (o1 == 0 && onSegment(p1, p2, q1)) return true;
243 
244  // p1, q1 and p2 are colinear and q2 lies on segment p1q1
245  if (o2 == 0 && onSegment(p1, q2, q1)) return true;
246 
247  // p2, q2 and p1 are colinear and p1 lies on segment p2q2
248  if (o3 == 0 && onSegment(p2, p1, q2)) return true;
249 
250  // p2, q2 and q1 are colinear and q1 lies on segment p2q2
251  if (o4 == 0 && onSegment(p2, q1, q2)) return true;
252 
253  return false; // Doesn't fall in any of the above cases
254 }
255 
256 //Find direction that does not pass through any vertex of the polygon
257 template<typename TPoint>
258 TPoint findDirection(const std::vector<TPoint>& polygon, TPoint p, TPoint d1, TPoint d2 )
259 {
260  TPoint d = d1;
261  bool belong = true;
262  //Find the ray that doest pass by any vertices of polygon
263  do {
264  belong = false;
265  for(size_t it=0; it<polygon.size(); it++) {
266  // Check p is a vertex of polygon, then it is inside
267  if(polygon.at(it)==p)
268  return p;
269  else {
270  // Check if 'polygon[i]' belongs to the line segment from 'p' to 'extreme',
271  // if it is then launch another ray using dichotomy search
272  if(orientation(p, polygon.at(it), d)==0) {
273  d[0] = (d[0] + d2[0])/2;
274  d[1] = (d[1] + d2[1])/2;
275  belong = true;
276  }
277  }
278  }
279  } while (belong);
280  return d;
281 }
282 
283 // Returns true if the point p lies inside the polygon[] with n vertices
284 template<typename TPoint>
285 bool isInsidePolygon(const std::vector<TPoint>& polygon, TPoint p)
286 {
287  int n = polygon.size();
288  // There must be at least 3 vertices in polygon[]
289  if (n < 3) return false;
290 
291  // Create a point for line segment from p to infinite
292  // Find the direction without containing any polygon vertices
293  TPoint extreme1 (p[0], INT_MAX/1e6);
294  TPoint extreme2 (INT_MAX/1e6, p[1]);
295  TPoint extreme = findDirection(polygon, p, extreme1, extreme2);
296 
297  // Check p is a vertex of polygon, then it is inside
298  if(extreme==p)
299  return true;
300  // Count intersections of the above line with sides of polygon
301  int count = 0, i = 0;
302  do
303  {
304  int next = (i+1)%n;
305 
306  // Check if the line segment from 'p' to 'extreme' intersects
307  // with the line segment from 'polygon[i]' to 'polygon[next]'
308  if (doIntersect(polygon[i], polygon[next], p, extreme))
309  {
310  // If the point 'p' is colinear with line segment 'i-next',
311  // then check if it lies on segment. If it lies, return true,
312  // otherwise false
313  if (orientation(polygon[i], p, polygon[next]) == 0)
314  return onSegment(polygon[i], p, polygon[next]);
315 
316  count++;
317  }
318  i = next;
319  } while (i != 0);
320 
321  // Return true if count is odd, false otherwise
322  return count&1; // Same as (count%2 == 1)
323 }
324 
325 // Gauss digitizer
326 std::vector<Z2i::Point> GaussDigization(const std::vector<Z2i::Point>& polygon)
327 {
328  typedef Z2i::Point TPoint;
329  std::vector<TPoint> gd;
330  std::pair<TPoint, TPoint> bb = getBoundingBox(polygon);
331  int minx = int(bb.first[0])-1;
332  int miny = int(bb.first[1])-1;
333  int maxx = int(bb.second[0])+1;
334  int maxy = int(bb.second[1])+1;
335  for(int x = minx; x<maxx; x++) {
336  for(int y = miny; y<maxy; y++) {
337  TPoint p(x,y);
338  bool ok = isInsidePolygon<TPoint>(polygon,p);
339  if(ok) {
340  TPoint pi(p[0],p[1]);
341  gd.push_back(pi);
342  }
343  }
344  }
345  return gd;
346 }
int main(int argc, char **argv)
static const Color Red
std::ostream & error()
Board & setPenColor(const DGtal::Color &color)
void drawLine(double x1, double y1, double x2, double y2, int depthValue=-1)
Board & setLineWidth(double width)
void saveSVG(const char *filename, PageSize size=Board::BoundingBox, double margin=10.0) const
Space::Point Point
Trace trace(traceWriterTerm)
static bool exportPGM(const std::string &filename, const Image &aImage, const Functor &aFunctor=Functor(), bool saveASCII=false, bool topbotomOrder=true)
static std::vector< TPoint > getPointsFromFile(const std::string &filename, std::vector< unsigned int > aVectPosition=std::vector< unsigned int >())