1 // This file is part of OpenMVG, an Open Multiple View Geometry C++ library.
2
3 // Copyright (c) 2012, 2013 Pierre MOULON.
4
5 // This Source Code Form is subject to the terms of the Mozilla Public
6 // License, v. 2.0. If a copy of the MPL was not distributed with this
7 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
8
9 #ifndef OPENMVG_IMAGE_IMAGE_DRAWING_HPP
10 #define OPENMVG_IMAGE_IMAGE_DRAWING_HPP
11
12 #include "openMVG/image/image_container.hpp"
13
14 namespace openMVG
15 {
16 namespace image
17 {
18
19 /**
20 * @brief Put the pixel in the image to the given color only if the point (xc,yc)
21 * is inside the image.
22 * /!\ Be careful at the order (Y,X).
23 * @param yc Position on Y-axis
24 * @param xc Position on X-axis
25 * @param col Color to set
26 * @param[out] pim Output image where color is set
27 */
28 template <typename Image, typename Color>
SafePutPixel(int yc,int xc,const Color & col,Image * pim)29 inline void SafePutPixel( int yc, int xc, const Color& col, Image *pim )
30 {
31 if ( pim )
32 {
33 if ( pim->Contains( yc, xc ) )
34 {
35 ( *pim )( yc, xc ) = col;
36 }
37 }
38 }
39
40 /**
41 * @brief Bresenham approach to draw ellipse.
42 * @ref http://raphaello.univ-fcomte.fr/ig/algorithme/ExemplesGLUt/BresenhamEllipse.htm
43 * Add the rotation of the ellipse.
44 * As the algo. use symmetry we must use 4 rotations.
45 * @param xc Ellipse center on X-axis
46 * @param yx Ellipse center on Y-axis
47 * @param radiusA Main radius of ellipse
48 * @param radiusB Secondary radius of ellipse
49 * @param col Color of the ellipse
50 * @param[out] pim Output image
51 * @param angle Rotation angle of the Ellipse
52 */
53 template <typename Image, typename Color>
DrawEllipse(int xc,int yc,int radiusA,int radiusB,const Color & col,Image * pim,double angle=0.0)54 void DrawEllipse( int xc, int yc, int radiusA, int radiusB,
55 const Color& col, Image *pim, double angle = 0.0 )
56 {
57 int a = radiusA, b = radiusB;
58
59 // Counter Clockwise rotation matrix.
60 double matXY[4] = { cos( angle ), sin( angle ),
61 -sin( angle ), cos( angle )
62 };
63 int x, y;
64 double d1, d2;
65 x = 0;
66 y = b;
67 d1 = b * b - a * a * b + a * a / 4;
68
69 int rotX = ceil( matXY[0] * x + matXY[1] * y );
70 int rotY = ceil( matXY[2] * x + matXY[3] * y );
71 SafePutPixel( yc + rotY, xc + rotX, col, pim );
72 rotX = matXY[0] * x - matXY[1] * y;
73 rotY = matXY[2] * x - matXY[3] * y;
74 SafePutPixel( yc + rotY, xc + rotX, col, pim );
75 rotX = -matXY[0] * x - matXY[1] * y;
76 rotY = -matXY[2] * x - matXY[3] * y;
77 SafePutPixel( yc + rotY, xc + rotX, col, pim );
78 rotX = -matXY[0] * x + matXY[1] * y;
79 rotY = -matXY[2] * x + matXY[3] * y;
80 SafePutPixel( yc + rotY, xc + rotX, col, pim );
81
82 while ( a * a * ( y - .5 ) > b * b * ( x + 1 ) )
83 {
84 if ( d1 < 0 )
85 {
86 d1 += b * b * ( 2 * x + 3 );
87 ++x;
88 }
89 else
90 {
91 d1 += b * b * ( 2 * x + 3 ) + a * a * ( -2 * y + 2 );
92 ++x;
93 --y;
94 }
95 rotX = matXY[0] * x + matXY[1] * y;
96 rotY = matXY[2] * x + matXY[3] * y;
97 SafePutPixel( yc + rotY, xc + rotX, col, pim );
98 rotX = matXY[0] * x - matXY[1] * y;
99 rotY = matXY[2] * x - matXY[3] * y;
100 SafePutPixel( yc + rotY, xc + rotX, col, pim );
101 rotX = -matXY[0] * x - matXY[1] * y;
102 rotY = -matXY[2] * x - matXY[3] * y;
103 SafePutPixel( yc + rotY, xc + rotX, col, pim );
104 rotX = -matXY[0] * x + matXY[1] * y;
105 rotY = -matXY[2] * x + matXY[3] * y;
106 SafePutPixel( yc + rotY, xc + rotX, col, pim );
107 }
108 d2 = b * b * ( x + .5 ) * ( x + .5 ) + a * a * ( y - 1 ) * ( y - 1 ) - a * a * b * b;
109 while ( y > 0 )
110 {
111 if ( d2 < 0 )
112 {
113 d2 += b * b * ( 2 * x + 2 ) + a * a * ( -2 * y + 3 );
114 --y;
115 ++x;
116 }
117 else
118 {
119 d2 += a * a * ( -2 * y + 3 );
120 --y;
121 }
122 rotX = matXY[0] * x + matXY[1] * y;
123 rotY = matXY[2] * x + matXY[3] * y;
124 SafePutPixel( yc + rotY, xc + rotX, col, pim );
125 rotX = matXY[0] * x - matXY[1] * y;
126 rotY = matXY[2] * x - matXY[3] * y;
127 SafePutPixel( yc + rotY, xc + rotX, col, pim );
128 rotX = -matXY[0] * x - matXY[1] * y;
129 rotY = -matXY[2] * x - matXY[3] * y;
130 SafePutPixel( yc + rotY, xc + rotX, col, pim );
131 rotX = -matXY[0] * x + matXY[1] * y;
132 rotY = -matXY[2] * x + matXY[3] * y;
133 SafePutPixel( yc + rotY, xc + rotX, col, pim );
134 }
135 }
136
137
138 /**
139 * @brief Bresenham approach do not allow to draw concentric circle without holes.
140 * So it's better the use the Andres method.
141 * @ref http://fr.wikipedia.org/wiki/Algorithme_de_tracé_de_cercle_d'Andres.
142 * @param x Circle center on X-axis
143 * @param y Circle center on Y-axis
144 * @param col Color of the circle
145 * @param[out] pim Output image
146 */
147 template <typename Image, typename Color>
DrawCircle(int x,int y,int radius,const Color & col,Image * pim)148 void DrawCircle( int x, int y, int radius, const Color& col, Image *pim )
149 {
150 Image &im = *pim;
151 if ( im.Contains( y + radius, x + radius )
152 || im.Contains( y + radius, x - radius )
153 || im.Contains( y - radius, x + radius )
154 || im.Contains( y - radius, x - radius ) )
155 {
156 int x1 = 0;
157 int y1 = radius;
158 int d = radius - 1;
159 while ( y1 >= x1 )
160 {
161 // Draw the point for each octant.
162 SafePutPixel( y1 + y, x1 + x, col, pim );
163 SafePutPixel( x1 + y, y1 + x, col, pim );
164 SafePutPixel( y1 + y, -x1 + x, col, pim );
165 SafePutPixel( x1 + y, -y1 + x, col, pim );
166 SafePutPixel( -y1 + y, x1 + x, col, pim );
167 SafePutPixel( -x1 + y, y1 + x, col, pim );
168 SafePutPixel( -y1 + y, -x1 + x, col, pim );
169 SafePutPixel( -x1 + y, -y1 + x, col, pim );
170 if ( d >= 2 * x1 )
171 {
172 d = d - 2 * x1 - 1;
173 x1 += 1;
174 }
175 else
176 {
177 if ( d <= 2 * ( radius - y1 ) )
178 {
179 d = d + 2 * y1 - 1;
180 y1 -= 1;
181 }
182 else
183 {
184 d = d + 2 * ( y1 - x1 - 1 );
185 y1 -= 1;
186 x1 += 1;
187 }
188 }
189 }
190 }
191 }
192
193
194 /**
195 * @brief Bresenham algorithm used to draw a line
196 * @param xa Start of the line on X-axis
197 * @param ya Start of the line on Y-axis
198 * @param xb End of the line on X-axis
199 * @param yb End of the line on Y-axis
200 * @param col Color of the line
201 * @param[out] pim Output image
202 */
203 template <typename Image, typename Color>
DrawLine(int xa,int ya,int xb,int yb,const Color & col,Image * pim)204 void DrawLine( int xa, int ya, int xb, int yb, const Color& col, Image *pim )
205 {
206 Image &im = *pim;
207
208 // If one point is outside the image
209 // Replace the outside point by the intersection of the line and
210 // the limit (either x=width or y=height).
211 if ( !im.Contains( ya, xa ) || !im.Contains( yb, xb ) )
212 {
213
214 int width = pim->Width(), height = pim->Height();
215 const bool xdir = xa < xb, ydir = ya < yb;
216 float nx0 = float( xa ), nx1 = float( xb ), ny0 = float( ya ), ny1 = float( yb ),
217 &xleft = xdir ? nx0 : nx1, &yleft = xdir ? ny0 : ny1,
218 &xright = xdir ? nx1 : nx0, &yright = xdir ? ny1 : ny0,
219 &xup = ydir ? nx0 : nx1, &yup = ydir ? ny0 : ny1,
220 &xdown = ydir ? nx1 : nx0, &ydown = ydir ? ny1 : ny0;
221
222 if ( xright < 0 || xleft >= width )
223 {
224 return;
225 }
226 if ( xleft < 0 )
227 {
228 yleft -= xleft * ( yright - yleft ) / ( xright - xleft );
229 xleft = 0;
230 }
231 if ( xright >= width )
232 {
233 yright -= ( xright - width ) * ( yright - yleft ) / ( xright - xleft );
234 xright = float( width ) - 1;
235 }
236 if ( ydown < 0 || yup >= height )
237 {
238 return;
239 }
240 if ( yup < 0 )
241 {
242 xup -= yup * ( xdown - xup ) / ( ydown - yup );
243 yup = 0;
244 }
245 if ( ydown >= height )
246 {
247 xdown -= ( ydown - height ) * ( xdown - xup ) / ( ydown - yup );
248 ydown = float( height ) - 1;
249 }
250
251 xa = ( int ) xleft;
252 xb = ( int ) xright;
253 ya = ( int ) yleft;
254 yb = ( int ) yright;
255 }
256
257 int xbas, xhaut, ybas, yhaut;
258 // Check the condition ybas < yhaut.
259 if ( ya <= yb )
260 {
261 xbas = xa;
262 ybas = ya;
263 xhaut = xb;
264 yhaut = yb;
265 }
266 else
267 {
268 xbas = xb;
269 ybas = yb;
270 xhaut = xa;
271 yhaut = ya;
272 }
273 // Initialize slope.
274 int x, y, dx, dy, incrmX, incrmY, dp, N, S;
275 dx = xhaut - xbas;
276 dy = yhaut - ybas;
277 if ( dx > 0 ) // If xhaut > xbas we will increment X.
278 {
279 incrmX = 1;
280 }
281 else
282 {
283 incrmX = -1; // else we will decrement X.
284 dx *= -1;
285 }
286 if ( dy > 0 ) // Positive slope will increment X.
287 {
288 incrmY = 1;
289 }
290 else // Negative slope.
291 {
292 incrmY = -1;
293 }
294 if ( dx >= dy )
295 {
296 dp = 2 * dy - dx;
297 S = 2 * dy;
298 N = 2 * ( dy - dx );
299 y = ybas;
300 x = xbas;
301 while ( x != xhaut )
302 {
303 SafePutPixel( y, x, col, pim );
304 x += incrmX;
305 if ( dp <= 0 ) // Go in direction of the South Pixel.
306 {
307 dp += S;
308 }
309 else // Go to the North.
310 {
311 dp += N;
312 y += incrmY;
313 }
314 }
315 }
316 else
317 {
318 dp = 2 * dx - dy;
319 S = 2 * dx;
320 N = 2 * ( dx - dy );
321 x = xbas;
322 y = ybas;
323 while ( y < yhaut )
324 {
325 SafePutPixel( y, x, col, pim );
326 y += incrmY;
327 if ( dp <= 0 ) // Go in direction of the South Pixel.
328 {
329 dp += S;
330 }
331 else // Go to the North.
332 {
333 dp += N;
334 x += incrmX;
335 }
336 }
337 }
338 SafePutPixel( y, x, col, pim );
339 }
340
341
342 /**
343 * @brief Draw filled circle
344 * Exterior point computed with bresenham approach
345 * @see DrawCircle
346 * @param x Circle center on X-axis
347 * @param y Circle center on Y-axis
348 * @param col Color of the circle
349 * @param[out] pim Output image
350 */
351 template <typename Image, typename Color>
FilledCircle(int x,int y,int radius,const Color & col,Image * pim)352 void FilledCircle( int x, int y, int radius, const Color& col, Image *pim )
353 {
354 Image &im = *pim;
355 if ( im.Contains( y + radius, x + radius )
356 || im.Contains( y + radius, x - radius )
357 || im.Contains( y - radius, x + radius )
358 || im.Contains( y - radius, x - radius ) )
359 {
360 int x1 = 0;
361 int y1 = radius;
362 int d = radius - 1;
363 while ( y1 >= x1 )
364 {
365 DrawLine( x1 + x, y1 + y, x1 + x, -y1 + y, col, pim );
366 DrawLine( y1 + x, x1 + y, y1 + x, -x1 + y, col, pim );
367 DrawLine( -x1 + x, y1 + y, -x1 + x, -y1 + y, col, pim );
368 DrawLine( -y1 + x, x1 + y, -y1 + x, -x1 + y, col, pim );
369 if ( d >= 2 * x1 )
370 {
371 d = d - 2 * x1 - 1;
372 x1 += 1;
373 }
374 else
375 {
376 if ( d <= 2 * ( radius - y1 ) )
377 {
378 d = d + 2 * y1 - 1;
379 y1 -= 1;
380 }
381 else
382 {
383 d = d + 2 * ( y1 - x1 - 1 );
384 y1 -= 1;
385 x1 += 1;
386 }
387 }
388 }
389 }
390 }
391
392
393 /**
394 * @brief Draw a serie of circles along the line, the algorithm is slow but accurate
395 * @param xa Start of the line on X-axis
396 * @param ya Start of the line on Y-axis
397 * @param xb End of the line on X-axis
398 * @param yb End of the line on Y-axis
399 * @param col Color of the line
400 * @param thickness Thiness of the line to draw
401 * @param[out] pim Output image
402 */
403 template <typename Image, typename Color>
DrawLineThickness(int xa,int ya,int xb,int yb,const Color & col,int thickness,Image * pim)404 void DrawLineThickness( int xa, int ya, int xb, int yb, const Color& col, int thickness, Image *pim )
405 {
406 Image &im = *pim;
407 int halfThickness = ( thickness + 1 ) / 2;
408
409 // If one point is outside the image
410 // Replace the outside point by the intersection of the line and
411 // the limit (either x=width or y=height).
412 if ( !im.Contains( ya, xa ) || !im.Contains( yb, xb ) )
413 {
414
415 int width = pim->Width(), height = pim->Height();
416 const bool xdir = xa < xb, ydir = ya < yb;
417 float nx0 = float( xa ), nx1 = float( xb ), ny0 = float( ya ), ny1 = float( yb ),
418 &xleft = xdir ? nx0 : nx1, &yleft = xdir ? ny0 : ny1,
419 &xright = xdir ? nx1 : nx0, &yright = xdir ? ny1 : ny0,
420 &xup = ydir ? nx0 : nx1, &yup = ydir ? ny0 : ny1,
421 &xdown = ydir ? nx1 : nx0, &ydown = ydir ? ny1 : ny0;
422
423 if ( xright < 0 || xleft >= width )
424 {
425 return;
426 }
427 if ( xleft < 0 )
428 {
429 yleft -= xleft * ( yright - yleft ) / ( xright - xleft );
430 xleft = 0;
431 }
432 if ( xright >= width )
433 {
434 yright -= ( xright - width ) * ( yright - yleft ) / ( xright - xleft );
435 xright = float( width ) - 1;
436 }
437 if ( ydown < 0 || yup >= height )
438 {
439 return;
440 }
441 if ( yup < 0 )
442 {
443 xup -= yup * ( xdown - xup ) / ( ydown - yup );
444 yup = 0;
445 }
446 if ( ydown >= height )
447 {
448 xdown -= ( ydown - height ) * ( xdown - xup ) / ( ydown - yup );
449 ydown = float( height ) - 1;
450 }
451
452 xa = ( int ) xleft;
453 xb = ( int ) xright;
454 ya = ( int ) yleft;
455 yb = ( int ) yright;
456 }
457
458 int xbas, xhaut, ybas, yhaut;
459 // Check the condition ybas < yhaut.
460 if ( ya <= yb )
461 {
462 xbas = xa;
463 ybas = ya;
464 xhaut = xb;
465 yhaut = yb;
466 }
467 else
468 {
469 xbas = xb;
470 ybas = yb;
471 xhaut = xa;
472 yhaut = ya;
473 }
474 // Initialize slope.
475 int x, y, dx, dy, incrmX, incrmY, dp, N, S;
476 dx = xhaut - xbas;
477 dy = yhaut - ybas;
478 if ( dx > 0 ) // If xhaut > xbas we will increment X.
479 {
480 incrmX = 1;
481 }
482 else
483 {
484 incrmX = -1; // else we will decrement X.
485 dx *= -1;
486 }
487 if ( dy > 0 ) // Positive slope will increment X.
488 {
489 incrmY = 1;
490 }
491 else // Negative slope.
492 {
493 incrmY = -1;
494 }
495 if ( dx >= dy )
496 {
497 dp = 2 * dy - dx;
498 S = 2 * dy;
499 N = 2 * ( dy - dx );
500 y = ybas;
501 x = xbas;
502 while ( x != xhaut )
503 {
504 DrawCircle( x, y, halfThickness, col, pim );
505 x += incrmX;
506 if ( dp <= 0 ) // Go in direction of the South Pixel.
507 {
508 dp += S;
509 }
510 else // Go to the North.
511 {
512 dp += N;
513 y += incrmY;
514 }
515 }
516 }
517 else
518 {
519 dp = 2 * dx - dy;
520 S = 2 * dx;
521 N = 2 * ( dx - dy );
522 x = xbas;
523 y = ybas;
524 while ( y < yhaut )
525 {
526 DrawCircle( x, y, halfThickness, col, pim );
527 y += incrmY;
528 if ( dp <= 0 ) // Go in direction of the South Pixel.
529 {
530 dp += S;
531 }
532 else // Go to the North.
533 {
534 dp += N;
535 x += incrmX;
536 }
537 }
538 }
539 DrawCircle( x, y, halfThickness, col, pim );
540 }
541
542 } // namespace image
543 } // namespace openMVG
544
545 #endif // OPENMVG_IMAGE_IMAGE_DRAWING_HPP
546