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