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