Basic Image AlgorithmS Library  2.8.0
 All Classes Namespaces Functions Variables Typedefs Enumerations Enumerator Friends Groups Pages
ImageDraw.cpp
1 /* This file is part of the BIAS library (Basic ImageAlgorithmS).
2 
3  Copyright (C) 2003-2009 (see file CONTACT for details)
4  Multimediale Systeme der Informationsverarbeitung
5  Institut fuer Informatik
6  Christian-Albrechts-Universitaet Kiel
7 
8  BIAS is free software; you can redistribute it and/or modify
9  it under the terms of the GNU Lesser General Public License as published by
10  the Free Software Foundation; either version 2.1 of the License, or
11  (at your option) any later version.
12 
13  BIAS is distributed in the hope that it will be useful,
14  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  GNU Lesser General Public License for more details.
17 
18  You should have received a copy of the GNU Lesser General Public License
19  along with BIAS; if not, write to the Free Software
20  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21  */
22 
23 #include <Base/Common/W32Compat.hh>
24 #include "ImageDraw.hh"
25 #include <cmath>
26 #include <Base/ImageUtils/Bresenham.hh>
27 #include <Base/ImageUtils/BresenhamCircle.hh>
28 #include <Base/ImageUtils/BresenhamCircleEighth.hh>
29 #include <Base/Image/ColourRGB.hh>
30 #include <Base/Image/ImageConvert.hh>
31 
32 #ifdef BIAS_HAVE_OPENCV
33 # include <Base/Image/WrapBias2Ipl.hh>
34 #endif
35 
36 // ImageMagick support
37 #ifdef BIAS_HAVE_IMAGEMAGICKLIB
38 # include <Base/Common/BIASpragmaStart.hh>
39 # include <Magick++.h>
40 # include <Base/Common/BIASpragmaEnd.hh>
41 # if (MagickLibVersion >= 0x650)
42 # define MagickLib MagickCore
43 # endif
44 #endif
45 
46 //replace old ImageMagick-namespace with new one
47 #ifdef BIAS_IMAGEMAGICKLIB_V2
48 # define MagickLib MagickCore
49 #endif //BIAS_IMAGEMAGICKLIB_V2
50 
51 
52 using namespace BIAS;
53 using namespace std;
54 
55 namespace BIAS {
56 
57  template <class StorageType>
58  StorageType ImageDraw<StorageType>::
59  GetContrastValue(const StorageType t)
60  {
61  BIASERR("Don't know what would be a good contrast value for "
62  <<(double)t<<" for StorageType "<<PRINTTYPE(StorageType));
63  StorageType thresh ;
64  if (numeric_limits<StorageType>::is_signed){
65  thresh = numeric_limits<StorageType>::min();
66  } else {
67  thresh = (StorageType)(( numeric_limits<StorageType>::max()-
68  numeric_limits<StorageType>::min() )/(StorageType)2.0);
69  }
70  return (t>thresh)?(numeric_limits<StorageType>::min()):
71  (numeric_limits<StorageType>::max());
72  }
73 
74  template <>
75  float ImageDraw<float>::
76  GetContrastValue(const float t)
77  {
78  return float((t>0.0)?-1.0:1.0);
79  }
80 
81  template <>
82  unsigned char ImageDraw<unsigned char>::
83  GetContrastValue(const unsigned char t)
84  {
85  return (unsigned char)((t > 127) ? 0 : 255);
86  }
87 
88  ////////////////////////////////////////////////////////////////////
89  // rectangles
90  ////////////////////////////////////////////////////////////////////
91 
92  template <class StorageType>
94  RectangleCorners(Image<StorageType>& im, const int aminx, const int aminy,
95  const int amaxx, const int amaxy,
96  const StorageType Value[])
97  {
98  const int minx = (aminx<amaxx)?(aminx):(amaxx);
99  const int maxx = (amaxx>aminx)?(amaxx):(aminx);
100  const int miny = (aminy<amaxy)?(aminy):(amaxy);
101  const int maxy = (amaxy>aminy)?(amaxy):(aminy);
102  const int w = im.GetWidth(), h=im.GetHeight(),
103  cc = im.GetChannelCount();
104  register StorageType **ida = im.GetImageDataArray();
105  register int x, y, c, xp1, xp2;
106 
107  // top and bottom lines
108  for (x = minx; x <= maxx; x++){
109  if (x>=0 && x<w){
110  xp1 = x * im.GetChannelCount();
111  for (c=0; c<cc; c++){
112  if (miny>=0 && miny<h) { ida[miny][xp1+c]=Value[c]; }
113  if (maxy>=0 && maxy<h) { ida[maxy][xp1+c]=Value[c]; }
114  }
115  }
116  }
117  // left and right lines
118  xp1=minx*im.GetChannelCount();
119  xp2=maxx*im.GetChannelCount();
120  for (y = miny; y <= maxy; y++){
121  if (y>=0 && y<h) {
122  for (c=0; c<cc; c++){
123  if (minx>=0 && minx<w) { ida[y][xp1+c]=Value[c]; }
124  if (maxx>=0 && maxx<w) { ida[y][xp2+c]=Value[c]; }
125  }
126  }
127  }
128  return (minx>=0 && miny>=0 && maxx<w && maxy<h)?(0):(-1);
129  }
130 
131 
132  template <class StorageType>
134  RectangleCorners(Image<StorageType>& im, const int minx, const int miny,
135  const int maxx, const int maxy, const StorageType value)
136  {
137  StorageType *val = new StorageType[im.GetChannelCount()];
138  for (register int i=0; i<(int)im.GetChannelCount(); i++)
139  val[i]=value;
140  int res = RectangleCorners(im, minx, miny, maxx, maxy, val);
141  delete[] val;
142  return res;
143  }
144 
145 
146  template <class StorageType>
149  const int aminy, const int amaxx, const int amaxy)
150  {
151  const int minx = (aminx<amaxx)?(aminx):(amaxx);
152  const int maxx = (amaxx>aminx)?(amaxx):(aminx);
153  const int miny = (aminy<amaxy)?(aminy):(amaxy);
154  const int maxy = (amaxy>aminy)?(amaxy):(aminy);
155  const int w = im.GetWidth(), h=im.GetHeight(),
156  cc = im.GetChannelCount();
157  register StorageType **ida = im.GetImageDataArray();
158  register int x, y, c, xp1, xp2;
159 
160  // top and bottom lines
161  for (x = minx; x <= maxx; x++){
162  if (x>=0 && x<w){
163  xp1 = x * cc;
164  for (c=0; c< cc; c++){
165  if (miny>=0 && miny<h) {
166  ida[miny][xp1+c]=GetContrastValue(ida[miny][xp1+c]);
167  }
168  if (maxy>=0 && maxy<h) {
169  ida[maxy][xp1+c]=GetContrastValue(ida[maxy][xp1+c]);
170  }
171  }
172  }
173  }
174  // left and right lines
175  xp1=minx*im.GetChannelCount();
176  xp2=maxx*im.GetChannelCount();
177  for (y = miny; y <= maxy; y++){
178  if (y>=0 && y<h) {
179  for (c=0; c< cc; c++){
180  if (minx>=0 && minx<w) {
181  ida[y][xp1+c]=GetContrastValue(ida[y][xp1+c]);
182  }
183  if (maxx>=0 && maxx<w) {
184  ida[y][xp2+c]=GetContrastValue(ida[y][xp2+c]);
185  }
186  }
187  }
188  }
189  return (minx>=0 && miny>=0 && maxx<w && maxy<h)?(0):(-1);
190  }
191 
192  template <class StorageType>
194  RectangleCornersFill(Image<StorageType>& im, const int aminx, const int aminy,
195  const int amaxx, const int amaxy,
196  const StorageType Value[])
197  {
198  const int cc = im.GetChannelCount();
199  const int minx = (aminx<amaxx)?(aminx):(amaxx);
200  const int maxx = ((amaxx>aminx)?(amaxx):(aminx)) * cc;
201  const int miny = (aminy<amaxy)?(aminy):(amaxy);
202  const int maxy = (amaxy>aminy)?(amaxy):(aminy);
203  const int w = im.GetWidth(), h=im.GetHeight();
204 
205  register StorageType **ida = im.GetImageDataArray();
206  register int x, y, c;
207 
208  // top and bottom lines
209 
210  for (x = minx*cc; x <= maxx; x+=cc)
211  for (y = miny; y <= maxy; y++)
212  for (c = 0; c < cc; c++)
213  ida[y][x+c] = Value[c];
214 
215  return (minx>=0 && miny>=0 && maxx<w && maxy<h)?(0):(-1);
216  }
217 
218 
219  template <class StorageType>
222  const int aminy, const int amaxx,
223  const int amaxy, const StorageType GreyValue)
224  {
225  if (im.GetChannelCount()!=1){
226  BEXCEPTION("ImageDraw::RectangleCornersGrey() is only for single "
227  <<"channel images");
228  }
229  const int minx = (aminx<amaxx)?(aminx):(amaxx);
230  const int maxx = (amaxx>aminx)?(amaxx):(aminx);
231  const int miny = (aminy<amaxy)?(aminy):(amaxy);
232  const int maxy = (amaxy>aminy)?(amaxy):(aminy);
233  const int w = im.GetWidth(), h=im.GetHeight();
234  register StorageType **ida = im.GetImageDataArray();
235  register int x, y;
236 
237  // top and bottom lines
238  for (x = minx; x <= maxx; x++){
239  if (x>=0 && x<w){
240  if (miny>=0 && miny<h) { ida[miny][x]=GreyValue; }
241  if (maxy>=0 && maxy<h) { ida[maxy][x]=GreyValue; }
242  }
243  }
244  // left and right lines
245  for (y = miny; y <= maxy; y++){
246  if (y>=0 && y<h) {
247  if (minx>=0 && minx<w) { ida[y][x]=GreyValue; }
248  if (maxx>=0 && maxx<w) { ida[y][x]=GreyValue; }
249  }
250  }
251  return (minx>=0 && miny>=0 && maxx<w && maxy<h)?(0):(-1);
252  }
253 
254 
255  template <class StorageType>
258  const int aminy, const int amaxx,
259  const int amaxy,const StorageType GreyValue)
260  {
261  const int minx = (aminx<amaxx)?(aminx):(amaxx);
262  const int maxx = (amaxx>aminx)?(amaxx):(aminx);
263  const int miny = (aminy<amaxy)?(aminy):(amaxy);
264  const int maxy = (amaxy>aminy)?(amaxy):(aminy);
265  const int w = im.GetWidth(), h=im.GetHeight();
266  const int mmaxx = (maxx>w)?(w):(maxx);
267  const int mmaxy = (maxy>h)?(h):(maxy);
268 
269  register StorageType **ida = im.GetImageDataArray();
270  register int x, y;
271 
272  for (y=miny; y<mmaxy; y++){
273  for (x=minx; x<mmaxx; x++){
274  ida[y][x] = GreyValue;
275  }
276  }
277 
278  return (minx>=0 && miny>=0 && maxx<w && maxy<h)?(0):(-1);
279  }
280 
281 
282  template <class StorageType>
285  const int Y, const int Size,
286  const StorageType Value[])
287  {
288  const int Halfsize = Size >> 1;
289  if (im.GetChannelCount()==1){
290  return RectangleCornersGrey(im, (X>Halfsize)?X-Halfsize:0,
291  (Y>Halfsize)?Y-Halfsize:0,
292  (X<(int)im.GetWidth()-Halfsize)?
293  (X+Halfsize):((int)im.GetWidth()-1),
294  (Y<(int)im.GetHeight()-Halfsize)?
295  (Y+Halfsize):((int)im.GetHeight()-1),
296  Value[0]);
297  } else {
298  return RectangleCorners(im, (X>Halfsize)?X-Halfsize:0,
299  (Y>Halfsize)?Y-Halfsize:0,
300  (X<(int)im.GetWidth()-Halfsize)?
301  (X+Halfsize):((int)im.GetWidth()-1),
302  (Y<(int)im.GetHeight()-Halfsize)?
303  (Y+Halfsize):((int)im.GetHeight()-1),
304  Value);
305  }
306  }
307 
308 
309  template <class StorageType>
312  const int Y, const int Size,
313  const StorageType Value)
314  {
315  const int Halfsize = Size >> 1;
316  return RectangleCornersGrey(im, (X>Halfsize)?X-Halfsize:0,
317  (Y>Halfsize)?Y-Halfsize:0,
318  (X<(int)im.GetWidth()-Halfsize)?
319  (X+Halfsize):((int)im.GetWidth()-1),
320  (Y<(int)im.GetHeight()-Halfsize)?
321  (Y+Halfsize):((int)im.GetHeight()-1),
322  Value);
323  }
324 
325 
326  template <class StorageType>
329  const int Y, const int Size,
330  const StorageType Value)
331  {
332  const int Halfsize = Size >> 1;
333  return RectangleCornersGreyFill(im, (X>Halfsize)?X-Halfsize:0,
334  (Y>Halfsize)?Y-Halfsize:0,
335  (X<(int)im.GetWidth()-Halfsize)?
336  (X+Halfsize):((int)im.GetWidth()-1),
337  (Y<(int)im.GetHeight()-Halfsize)?
338  (Y+Halfsize):((int)im.GetHeight()-1),
339  Value);
340  }
341 
342 
343  template <class StorageType>
346  const int y, const int size)
347  {
348  const int Halfsize = size >> 1;
349  return RectangleCorners(im, (x>Halfsize)?x-Halfsize:0,
350  (y>Halfsize)?y-Halfsize:0,
351  (x<(int)im.GetWidth()-Halfsize)?
352  (x+Halfsize):((int)im.GetWidth()-1),
353  (y<(int)im.GetHeight()-Halfsize)?
354  (y+Halfsize):((int)im.GetHeight()-1));
355  }
356 
357 
358  template <class StorageType>
360  RectangleCenter(Image<StorageType>& im, const int coo[2],
361  const int size)
362  { return RectangleCenter(im, coo[0], coo[1], size); }
363 
364 
365 
366  /////////////////////////////////////////////////////////////////////////
367  // lines
368  /////////////////////////////////////////////////////////////////////////
369 
370  template <class StorageType>
372  Line(Image<StorageType>& im, const int start[2],
373  const int end[2], const StorageType value[])
374  {
375  int res = 0;
376  Bresenham bres(start, end);
377  int next[2];
378  next[0] = start[0];
379  next[1] = start[1];
380  const int w = im.GetWidth(), h = im.GetHeight();
381  const unsigned cc = im.GetChannelCount();
382  unsigned xcc, c;
383  StorageType **ida = im.GetImageDataArray();
384  do {
385  if (next[0]<0 || next[0]>=w || next[1]<0 || next[1]>=h){
386  res = -1;
387  continue;
388  }
389  xcc = next[0]*cc;
390  for (c=0; c<im.GetChannelCount(); c++)
391  ida[next[1]][xcc+c] = value[c];
392  } while (bres.GetNext(next));
393 
394  if (res!=0){
395  BIASWARN("Line is (partially) outside of image: (" << start[0] << ", "
396  << start[1] << ") - (" << end[0] << ", " << end[1] << ")!");
397  }
398  return res;
399  }
400 
401 
402  template <class StorageType>
404  Line(Image<StorageType>& im, const unsigned int start[2],
405  const unsigned int end[2], const StorageType value[])
406  {
407 #ifdef BIAS_DEBUG
408  // check if conversion from unsigned to int is valid
409  if (start[0]>=(unsigned)numeric_limits<int>::max() ||
410  start[1]>=(unsigned)numeric_limits<int>::max() ||
411  end[0]>=(unsigned)numeric_limits<int>::max() ||
412  end[1]>=(unsigned)numeric_limits<int>::max() ) {
413  BIASWARN("Conversion from int to unsigned int failed!");
414  return -2;
415  }
416 #endif
417  int start0 = start[0], start1 = start[1];
418  int istart[] = { start0, start1 };
419  int end0 = end[0], end1 = end[1];
420  int iend[] = { end0, end1 };
421  return Line(im, istart, iend, value);
422  }
423 
424 
425  template <class StorageType>
427  const int start[2],
428  const int end[2],
429  const StorageType value[],
430  int width)
431  {
432  width -= 1;
433  float m = fabs(static_cast<float>(end[1]-start[1]))/fabs(static_cast<float>(end[0]-start[0]));
434  int offset[2] = {0,0};
435  if(m<=1.0)
436  offset[1] = 1;
437  else
438  offset[0] = 1;
439 
440 
441  int startUsed[2];
442  int endUsed[2];
443 
444  int next[2];
445  for(int i=-width; i<=width; i++) {
446 
447  startUsed[0] = start[0]+ i*offset[0];
448  startUsed[1] = start[1]+ i*offset[1];
449  endUsed[0] = end[0]+ i*offset[0];
450  endUsed[1] = end[1]+ i*offset[1];
451 // cout<<" start "<<startUsed[0]<<", "<<startUsed[1]<<endl;
452 // cout<<" end "<<endUsed[0]<<", "<<endUsed[1]<<endl;
453  Bresenham bres(startUsed, endUsed);
454 
455  next[0]=startUsed[0];
456  next[1]=startUsed[1];
457 
458  StorageType **ida=im.GetImageDataArray();
459  do {
460  if (!(next[0]<0 || next[0]>=int(im.GetWidth()) ||
461  next[1]<0 || next[1]>=int(im.GetHeight()))) {
462  for (unsigned int c=0; c<im.GetChannelCount(); c++)
463  ida[next[1]][next[0]*im.GetChannelCount()+c]=value[c];
464  }
465  } while (bres.GetNext(next));
466  }
467  return 0;
468  }
469 
470  template <class StorageType>
472  const unsigned int start[2],
473  const unsigned int end[2], StorageType value)
474  {
475 #ifdef BIAS_DEBUG
476  if (start[0]<0 || start[0]>=im.GetWidth() ||
477  start[1]<0 || start[1]>=im.GetHeight() ||
478  end[0]<0 || end[0]>=im.GetWidth() ||
479  end[1]<0 || end[1]>=im.GetHeight()){
480  BIASWARN("Line is (partially) outside of image: (" << start[0] << ", "
481  << start[1] << ") - (" << end[0] << ", " << end[1] << ")!");
482  return -1;
483  }
484 #endif
485  unsigned int next[2];
486  Bresenham bres(start, end);
487 
488  next[0]=start[0];
489  next[1]=start[1];
490 
491  StorageType **ida=im.GetImageDataArray();
492  do {
493  for (unsigned int c=0; c<im.GetChannelCount(); c++)
494  ida[next[1]][next[0]*im.GetChannelCount()+c]=value;
495  } while (bres.GetNext(next));
496  return 0;
497  }
498 
499  template <class StorageType>
501  const unsigned int start[2],
502  const unsigned int end[2],
503  StorageType value)
504  {
505 #ifdef BIAS_DEBUG
506  if (start[0]<0 || start[0]>=im.GetWidth() ||
507  start[1]<0 || start[1]>=im.GetHeight() ||
508  end[0]<0 || end[0]>=im.GetWidth() ||
509  end[1]<0 || end[1]>=im.GetHeight()){
510  BIASWARN("Line is (partially) outside of image: (" << start[0] << ", "
511  << start[1] << ") - (" << end[0] << ", " << end[1] << ")!");
512  return -1;
513  }
514  if (im.GetChannelCount() !=1){
515  BIASWARN("Only implemented for one channel images!");
516  return -1;
517  }
518 #endif
519  unsigned int next[2];
520  Bresenham bres(start, end);
521 
522  next[0]=start[0];
523  next[1]=start[1];
524 
525  StorageType **ida=im.GetImageDataArray();
526  do {
527  ida[next[1]][next[0]]=value;
528  } while (bres.GetNext(next));
529  return 0;
530  }
531 
532 
533  template <class StorageType>
535  const unsigned int start[2],
536  const unsigned int end[2])
537  {
538 #ifdef BIAS_DEBUG
539  if (start[0]<0 || start[0]>=im.GetWidth() ||
540  start[1]<0 || start[1]>=im.GetHeight() ||
541  end[0]<0 || end[0]>=im.GetWidth() ||
542  end[1]<0 || end[1]>=im.GetHeight()){
543  BIASWARN("Line is (partially) outside of image: (" << start[0] << ", "
544  << start[1] << ") - (" << end[0] << ", " << end[1] << ")!");
545  return -1;
546  }
547 #endif
548  unsigned int next[2];
549  Bresenham bres(start, end);
550 
551  next[0]=start[0];
552  next[1]=start[1];
553 
554  StorageType **ida=im.GetImageDataArray();
555  do {
556  for (unsigned int c=0; c<im.GetChannelCount(); c++)
557  ida[next[1]][next[0]*im.GetChannelCount()+c]=
558  GetContrastValue(ida[next[1]][next[0]*im.GetChannelCount()+c]);
559  } while (bres.GetNext(next));
560  return 0;
561  }
562 
563 
564  template <class StorageType>
566  const unsigned int start[2],
567  const unsigned int end[2])
568  {
569 #ifdef BIAS_DEBUG
570  if (start[0]<0 || start[0]>=im.GetWidth() ||
571  start[1]<0 || start[1]>=im.GetHeight() ||
572  end[0]<0 || end[0]>=im.GetWidth() ||
573  end[1]<0 || end[1]>=im.GetHeight()){
574  BIASWARN("Line is (partially) outside of image: (" << start[0] << ", "
575  << start[1] << ") - (" << end[0] << ", " << end[1] << ")!");
576  return -1;
577  }
578  if (im.GetChannelCount() !=1){
579  BIASWARN("Only implemented for one channel images!");
580  return -1;
581  }
582 #endif
583  unsigned int next[2];
584  Bresenham bres(start, end);
585 
586  next[0]=start[0];
587  next[1]=start[1];
588 
589  StorageType **ida=im.GetImageDataArray();
590  do {
591  ida[next[1]][next[0]]=GetContrastValue(ida[next[1]][next[0]]);
592  } while (bres.GetNext(next));
593  return 0;
594  }
595 
596 
597 
598  template <class StorageType>
600  const unsigned int StartX,
601  const unsigned int StartY,
602  const unsigned int EndX,
603  const unsigned int EndY,
604  const StorageType Value[])
605  {
606  unsigned int start[]={StartX, StartY}, end[]={EndX, EndY};
607  return Line(im, start, end, Value);
608  }
609 
610 
611  template <class StorageType>
613  const unsigned int StartX,
614  const unsigned int StartY,
615  const unsigned int EndX,
616  const unsigned int EndY,
617  const StorageType Value)
618  {
619  unsigned int start[]={StartX, StartY}, end[]={EndX, EndY};
620  return Line(im, start, end, Value);
621  }
622 
623  template <class StorageType>
625  const unsigned int StartX,
626  const unsigned int StartY,
627  const unsigned int EndX,
628  const unsigned int EndY)
629  {
630  unsigned int start[]={StartX, StartY}, end[]={EndX, EndY};
631  return Line(im, start, end);
632  }
633 
634 
635 
636  template <class StorageType>
638  const unsigned int StartX,
639  const unsigned int StartY,
640  const unsigned int EndX,
641  const unsigned int EndY,
642  const StorageType Value)
643  {
644  unsigned int start[]={StartX, StartY}, end[]={EndX, EndY};
645  return LineGrey(im, start, end, Value);
646  }
647 
648 
649 #define BIAS_DRAW_CROSS_PRODUCT(a, b, res) \
650  res[0] = a[1] * b[2] - a[2] * b[1]; \
651  res[1] = a[2] * b[0] - a[0] * b[2]; \
652  res[2] = a[0] * b[1] - a[1] * b[0];
653 
654 #define BIAS_DRAW_PRECISION 1e-8
655 
656  template <class StorageType>
658  Line(Image<StorageType>& im, const double start[2],
659  const double end[2],
660  const StorageType value[])
661  {
662  int w=im.GetWidth();
663  int h=im.GetHeight();
664  double wm=(double)(w-1);
665  double hm=(double)(h-1);
666  unsigned uistart[2], uiend[2];
667  double s[]={start[0], start[1], 1.0}, e[]={end[0], end[1], 1.0};
668 
669  // the line is build by the cross product of the homogenous
670  // start and end points
671  double line[3];
672  BIAS_DRAW_CROSS_PRODUCT(s, e, line);
673 
674  // now intersect the line with the image border lines
675  // again by using the cross product
676  double top[]={0, -1.0, 0.0}, bottom[]={0.0, -1.0, hm};
677  double left[]={-1.0, 0.0, 0.0}, right[]={-1.0, 0.0, wm};
678  double intersections[4][3]; // 0=left, 1=right, 2=top, 3=bottom
679  BIAS_DRAW_CROSS_PRODUCT(line, left, intersections[0]);
680  BIAS_DRAW_CROSS_PRODUCT(line, right, intersections[1]);
681  BIAS_DRAW_CROSS_PRODUCT(line, top, intersections[2]);
682  BIAS_DRAW_CROSS_PRODUCT(line, bottom, intersections[3]);
683 
684  unsigned coo[4]={0,0,0,0};
685  bool intersect[4];
686  int intersectioncount=0;
687  // deal with compiler precision
688  for (unsigned int i=0; i<4; i++){
689  if (fabs(intersections[i][0]) < BIAS_DRAW_PRECISION)
690  intersections[i][0] = 0.0;
691  if (fabs(intersections[i][1]) < BIAS_DRAW_PRECISION)
692  intersections[i][1] = 0.0;
693  if (fabs(intersections[i][0]-wm) < BIAS_DRAW_PRECISION)
694  intersections[i][0] = wm;
695  if (fabs(intersections[i][1]-hm) < BIAS_DRAW_PRECISION)
696  intersections[i][1] = hm;
697  if (fabs(intersections[i][2]) < BIAS_DRAW_PRECISION){
698  intersections[i][2] = 0.0;
699  intersect[i]=false;
700  } else { // homogenize
701  intersections[i][0]/=intersections[i][2];
702  intersections[i][1]/=intersections[i][2];
703  intersect[i]=true;
704  }
705  }
706 
707  // extract the intersection points into coo[]
708  // left
709  if (intersect[0] && intersections[0][1]>=0 && intersections[0][1]<=hm){
710  intersectioncount++;
711  coo[0] = 0;
712  coo[1] = (unsigned int)rint(intersections[0][1]);
713  }
714  // right
715  if (intersect[1] && intersections[1][1]>=0 && intersections[1][1]<=hm){
716  if (intersectioncount==0){
717  coo[0] = w- 1;
718  coo[1] = (unsigned int)rint(intersections[1][1]);
719  } else {
720  coo[2] = w - 1;
721  coo[3] = (unsigned int)rint(intersections[1][1]);
722  }
723  intersectioncount++;
724  }
725  // top
726  if (intersect[2] && intersections[2][0]>=0 && intersections[2][0]<=wm){
727  switch (intersectioncount){
728  case 0:
729  coo[0] = (unsigned int)rint(intersections[2][0]);
730  coo[1] = 0;
731  break;
732  case 1:
733  coo[2] = (unsigned int)rint(intersections[2][0]);
734  coo[3] = 0;
735  break;
736  default:
737  // do nothing, all intersections correctly calulated
738  // because left and right intersection can not have a common point
739  // cout << "found intersections "<<coo[0]<<", "<<coo[1]<<" and "<<coo[2]<<", "<<coo[3]<<"\nnew intersection "<<(unsigned int)rint(intersections[2][0])<<", 0\n";
740  break;
741  }
742  intersectioncount++;
743  }
744  // bottom
745  if (intersect[3] && intersections[3][0]>=0 && intersections[3][0]<=wm){
746  switch (intersectioncount){
747  case 0:
748  coo[0] = (unsigned int)rint(intersections[3][0]);
749  coo[1] = h-1;
750  break;
751  case 1:
752  coo[2] = (unsigned int)rint(intersections[3][0]);
753  coo[3] = h-1;
754  break;
755  case 2:
756  // this can only happen if upper left or upper right corner of image
757  // is intersection
758  // It follows that the top corner is always stored in coo[2/3], because
759  // interscetioncount was 1 when checking for interscetions with top edge
760  coo[0] = (unsigned int)rint(intersections[3][0]);
761  coo[1] = h-1;
762  break;
763  default:
764  // do nothing, all intersections correctly calulated
765  // cout << "found intersections "<<coo[0]<<", "<<coo[1]<<" and "<<coo[2]<<", "<<coo[3]<<"\nnew intersection "<<(unsigned int)rint(intersections[2][0])<<", 0\n";
766  break;
767  }
768  intersectioncount++;
769  }
770 
771  if (intersectioncount>1){
772 
773  // now draw the line
774  if (start[0]>=0.0 && start[0]<=wm && start[1]>=0.0 && start[1]<=hm){
775  // easy, start[0] in image and start[1] in image
776  uistart[0]=(unsigned)rint(start[0]);
777  uistart[1]=(unsigned)rint(start[1]);
778  } else {
779  // replace start point
780 
781  // take coo[0/1] if (end-coo[0/1]).ScalarProduct(end-start)==1.0
782  double dex = end[0]-coo[0], dey = end[1]-coo[1];
783  double dx = end[0]-start[0], dy = end[1]-start[1];
784  if (dx*dex+dy*dey>0.0){ // angle < 90 degree take coo[0/1]
785  uistart[0]=coo[0];
786  uistart[1]=coo[1];
787  } else {
788  uistart[0]=coo[2];
789  uistart[1]=coo[3];
790  }
791 
792  }
793  if (start[0]>=0.0 && start[0]<=wm && start[1]>=0.0 && start[1]<=hm){
794  // easy, end[0] in image and end[1] in image
795  uiend[0]=(unsigned)rint(end[0]);
796  uiend[1]=(unsigned)rint(end[1]);
797  } else {
798  // replace end point
799 
800  // take coo[0/1] if (start-coo[0/1]).ScalarProduct(start-end)==1.0
801  double dex = start[0]-coo[0], dey = start[1]-coo[1];
802  double dx = start[0]-end[0], dy = start[1]-end[1];
803  if (dx*dex+dy*dey>0.0){ // angle < 90 degree take coo[0/1]
804  uistart[0]=coo[0];
805  uistart[1]=coo[1];
806  } else {
807  uistart[0]=coo[2];
808  uistart[1]=coo[3];
809  }
810  }
811  return Line(im, uistart, uiend, value);
812  }
813 
814  //cerr << "nothing to draw\n";
815  return 0;
816  }
817 
818 #undef BIAS_DRAW_CROSS_PRODUCT
819 #undef BIAS_DRAW_PRECISION
820 
821  ////////////////////////////////////////////////////
822  // interpolated lines
823  ///////////////////////////////////////////////////////
824 
825  template <class StorageType>
827  const int StartX,
828  const int StartY,
829  const int EndX,
830  const int EndY,
831  const StorageType Value[],
832  const float Thickness,
833  const float Opacity)
834  {
835  return InterpolatedLine(im,HomgPoint2D((float)StartX,(float)StartY,1.0),
836  HomgPoint2D((float)EndX,(float)EndY,1.0), Value,
837  Thickness,Opacity);
838  }
839 
840 
841  template <class StorageType>
843  const HomgPoint2D Start,
844  const HomgPoint2D End,
845  const StorageType Value[],
846  const float Thickness,
847  const float Opacity)
848  {
849 
850 
851  StorageType **ida = im.GetImageDataArray();
852  unsigned int cc=im.GetChannelCount();
853  float opac = min(fabs(Opacity),1.0f);
854  int returnValue = 0;
855 
856  // Set left and right line point for a better handling
857  float LeftX, RightX, LeftY,RightY;
858 
859  if (Start[0] < End[0]){
860  LeftX = (float)Start[0]; LeftY = (float)Start[1];
861  RightX = (float)End[0]; RightY = (float)End[1];
862  } else {
863  LeftX = (float)End[0]; LeftY = (float)End[1];
864  RightX = (float)Start[0]; RightY = (float)Start[1];
865  }
866 
867  // Set the borders the loop has to clip to
868  float MinY = (float)min(Start[1],End[1]) - Thickness;
869  float MaxY = (float)max(Start[1],End[1]) + Thickness;
870 
871  //Set the the borders the rendering has to clip to:
872  int ClipLeft = max((int)floor(LeftX-Thickness),0);
873  int ClipRight = min((int)ceil(RightX+Thickness),(int)im.GetWidth()-1);
874 
875  float slope;
876  if (RightX != LeftX)
877  slope = (float)(RightY-LeftY) / (float)(RightX - LeftX);
878  else
879  slope = -10000.0; // This is a little bit dirty...
880 
881  float length = sqrt(((float)End[0]-(float)Start[0])*((float)End[0]-(float)Start[0])
882  +((float)End[1]-(float)Start[1])*((float)End[1]-(float)Start[1]));
883 
884  // First rotate the pixels by alpha (we just need y)
885  float angle = atan2(RightY - LeftY,RightX-LeftX);
886  float sinAngle = sin(angle);
887  float cosAngle = cos(angle);
888 
889  for (int xDrawPos = ClipLeft ; xDrawPos <= ClipRight ; xDrawPos++ ){
890  // Set the y range to draw on
891  float Y1 = LeftY +
892  ((float)xDrawPos-LeftX-Thickness)*slope;
893  float Y2 = LeftY +
894  ((float)xDrawPos-LeftX+Thickness)*slope;
895  float LowerY, UpperY;
896  if (Y1 < Y2){
897  LowerY = Y1 -Thickness;
898  UpperY = Y2 + Thickness;
899  } else {
900  LowerY = Y2 -Thickness;
901  UpperY = Y1 + Thickness;
902  }
903 
904 
905  LowerY = max(ceil(LowerY),ceil(MinY));
906  UpperY = min(floor(UpperY),floor(MaxY));
907 
908  //Set the the borders the rendering has to clip to:
909  int ClipUp = max((int)floor(LowerY-1),0);
910  int ClipLow = min((int)ceil(UpperY)+1,(int)im.GetHeight()-1);
911 
912 
913  for (int yDrawPos = ClipUp; yDrawPos <= ClipLow; yDrawPos++){
914  // Rotate the point with the rotation that puts the line onto the y-axis:
915  float xRotated = sinAngle*((float)yDrawPos-LeftY) + cosAngle*((float)xDrawPos-LeftX);
916  float yRotated = -sinAngle*((float)xDrawPos-LeftX) + cosAngle*((float)yDrawPos-LeftY);
917  float dist = fabs(yRotated);
918 
919  if (xRotated<0)
920  dist = sqrt(yRotated*yRotated + xRotated*xRotated);
921 
922  if (fabs(xRotated)>=length)
923  dist = sqrt(yRotated*yRotated + (fabs(xRotated)-length)*(fabs(xRotated)-length));
924 
925  if (dist < Thickness){
926  dist = max(dist - Thickness + 1.0f,0.0f);
927  float ratio = (1.0f - dist) * opac;
928  for (unsigned int c = 0; c < cc; c++)
929  ida[yDrawPos][xDrawPos*cc+c] = (StorageType)((1.0 - ratio)*(double)ida[yDrawPos][xDrawPos*cc+c] +
930  ratio * (double)Value[c]);
931  }
932  }
933  }
934 
935  return returnValue;
936  }
937 
938  template <class StorageType>
940  const HomgPoint2D Start,
941  const HomgPoint2D End,
942  const StorageType GreyValue,
943  const float Thickness ,
944  const float Opacity )
945  {
946  int channels = im.GetChannelCount();
947  StorageType* color = new StorageType[channels];
948  for (int c = 0; c < channels; c++)
949  color[c] = GreyValue;
950  int returnValue = InterpolatedLine(im, Start,End,color,Thickness,Opacity);
951  delete[] color;
952  return returnValue;
953  }
954 
955  template <class StorageType>
957  const int StartX,
958  const int StartY,
959  const int EndX,
960  const int EndY,
961  const StorageType GreyValue,
962  const float Thickness,
963  const float Opacity)
964  {
965  return InterpolatedLineGrey(im, HomgPoint2D((float)StartX,(float)StartY,1.0),
966  HomgPoint2D((float)EndX,(float)EndY,1.0),
967  GreyValue,Thickness,Opacity);
968 
969 
970  }
971 
972  ///////////////////////////////////////////////////////////
973  // circles
974  //////////////////////////////////////////////////////////
975 
976  template <class StorageType>
978  unsigned int CenterX,
979  unsigned int CenterY,
980  unsigned int Radius,
981  const StorageType Value[])
982  {
983  int res=0;
984  BresenhamCircle bc;
985  int center[2], next[]={0,0}, radius=(int)Radius;
986  StorageType **ida=im.GetImageDataArray();
987  unsigned int cc=im.GetChannelCount();
988  const int height = (int)im.GetHeight();
989  const int width = (int)im.GetWidth();
990 
991  register unsigned int c;
992 
993  center[0]=(int)CenterX;
994  center[1]=(int)CenterY;
995 
996  bc.Init(center, radius);
997 
998  if (Value==NULL) {
999  // no color supplied, use black and white dep. on background
1000  while (bc.GetNext(next)){
1001  if (next[1]>=0 && next[1]<height &&
1002  next[0]>=0 && next[0]<width) {
1003  for (c=0; c<cc; c++) {
1004  ida[next[1]][next[0]*cc+c]=
1005  GetContrastValue(ida[next[1]][next[0]*cc+c]);
1006  }
1007  }
1008  }
1009  } else {
1010  while (bc.GetNext(next)){
1011  if (next[1]>=0 && next[1]<height &&
1012  next[0]>=0 && next[0]<width) {
1013  for (c=0; c<cc; c++) {
1014  ida[next[1]][next[0]*cc+c]=Value[c];
1015  }
1016  }
1017  }
1018  }
1019  return res;
1020  }
1021 
1022  template <class StorageType>
1024  unsigned int CenterX,
1025  unsigned int CenterY,
1026  unsigned int Radius,
1027  const StorageType Value[])
1028  {
1029  int res=0;
1031  int zerocenter[]={0,0};
1032  int center[2], next[2], radius=(int)Radius;
1033  unsigned int start[2], end[2];
1034  const int width = (int)im.GetWidth();
1035  const int height = (int)im.GetHeight();
1036  center[0]=(int)CenterX;
1037  center[1]=(int)CenterY;
1038 
1039  if (center[0]<=radius || center[1]<=radius ||
1040  center[0]+radius+1>=width || center[1]+radius+1>=height){
1041  return -1;
1042  }
1043 
1044  bc.Init(zerocenter, radius);
1045 
1046  while (bc.GetNext(next)){
1047  start[0]=center[0]+next[0];
1048  start[1]=center[1]+next[1];
1049  end[0]=center[0]-next[0];
1050  end[1]=center[1]+next[1];
1051  Line(im, start, end, Value);
1052  start[0]=center[0]+next[1];
1053  start[1]=center[1]+next[0];
1054  end[0]=center[0]-next[1];
1055  end[1]=center[1]+next[0];
1056  Line(im, start, end, Value);
1057  start[0]=center[0]+next[0];
1058  start[1]=center[1]-next[1];
1059  end[0]=center[0]-next[0];
1060  end[1]=center[1]-next[1];
1061  Line(im, start, end, Value);
1062  start[0]=center[0]+next[1];
1063  start[1]=center[1]-next[0];
1064  end[0]=center[0]-next[1];
1065  end[1]=center[1]-next[0];
1066  Line(im, start, end, Value);
1067  }
1068 
1069  return res;
1070  }
1071 
1072 
1073  template <class StorageType>
1075  HomgPoint2D Center,
1076  float Radius,
1077  const StorageType Value[],
1078  const float Thickness ,
1079  const float Opacity )
1080  {
1081  int res=0;
1082  StorageType **ida=im.GetImageDataArray();
1083  unsigned int cc=im.GetChannelCount();
1084  float opac = min(fabs(Opacity),1.0f);
1085  int leftBorder = (int)floor(Center[0] - Radius - Thickness);
1086  int rightBorder = (int)ceil(Center[0] + Radius+Thickness);
1087  int upperBorder = (int)floor(Center[1] - Radius - Thickness);
1088  int lowerBorder = (int)ceil(Center[1] + Radius + Thickness);
1089 
1090  // Clip to image borders
1091  leftBorder = max(0,leftBorder);
1092  rightBorder = min((int)im.GetWidth()-1,rightBorder);
1093  upperBorder = max(0,upperBorder);
1094  lowerBorder = min((int)im.GetHeight()-1,lowerBorder);
1095 
1096  for (int x = leftBorder; x <= rightBorder; x++)
1097  for (int y = upperBorder; y <= lowerBorder; y++){
1098  float dist = sqrt(((float)x-(float)Center[0])*((float)x-(float)Center[0])
1099  +((float)y-(float)Center[1])*((float)y-(float)Center[1]));
1100  if (fabs(dist-Radius) < Thickness){
1101  dist = max(fabs(dist - Radius)- Thickness + 1.0f,0.0f);
1102  float ratio = (1.0f - dist) * opac;
1103  for (unsigned int c = 0; c < cc; c++)
1104  ida[y][x*cc+c] = (StorageType)((1.0 - ratio)*(double)ida[y][x*cc+c] +
1105  ratio * (double)Value[c]);
1106  }
1107  }
1108 
1109  return res;
1110  }
1111 
1112 
1113  template <class StorageType>
1115  unsigned int CenterX,
1116  unsigned int CenterY,
1117  unsigned int Radius,
1118  const StorageType Value[],
1119  const float Thickness,
1120  const float Opacity)
1121  {
1122  return InterpolatedCircleCenter(im,HomgPoint2D(CenterX,CenterY,1.0),(float)Radius,Value,Thickness,Opacity);
1123  }
1124 
1125 
1126  template <class StorageType>
1128  const unsigned start[2],
1129  const unsigned mend[2],
1130  const unsigned length,
1131  const unsigned width,
1132  const StorageType value[])
1133  {
1134  int dx=(int)mend[0]-(int)start[0], dy=(int)mend[1]-(int)start[1];
1135  unsigned end[]={mend[0], mend[1]};
1136  double l = sqrt((double)dx*(double)dx+(double)dy*(double)dy);
1137  if (l<(double)length){
1138  unsigned num = (unsigned)ceil( (double)length / l );
1139  end[0] += dx * num;
1140  end[1] += dy * num;
1141  }
1142 
1143  float thick=2.0;
1144 
1145  //if (Line(im, start, end, value)!=0){
1146  //cout << "line: "<<start[0]<<" "<<start[1]<<" "<<end[0]<<" "<<end[1]<<endl;
1147  if (Line(im, start[0], start[1], end[0], end[1], value, thick)!=0){
1148  return -1;
1149  }
1150  if (dx==0 && dy==0){
1151  // do not draw arrow, zero length and hence direction indetermined
1152  return -2;
1153  }
1154 
1155  int next[2]={0,0};
1156  Bresenham bres(end, start), bres2;
1157 
1158  for (unsigned i=0; i<length; i++)
1159  bres.GetNext(next);
1160 
1161  int left[]={0,0}, right[]={0,0};
1162  int lend[2], rend[2];
1163  rend[0]=(int)next[0]-dy;
1164  rend[1]=(int)next[1]+dx;
1165  lend[0]=(int)next[0]+dy;
1166  lend[1]=(int)next[1]-dx;
1167  //cerr << "next: ("<<next[0]<<", "<<next[1]<<") lend: ("<<lend[0]<<", "
1168  // <<lend[1]<<") rend: ("<<rend[0]<<", "<<rend[1]<<")\n";
1169  bres.Init(next, rend);
1170  bres2.Init(next, lend);
1171  for (unsigned i=0; i<width; i++){
1172  bres.GetNext(right);
1173  bres2.GetNext(left);
1174  }
1175 
1176  int res = 0;
1177  if (right[0]>=0 && right[1]<(int)im.GetWidth()){
1178  //cout << "right: "<<end[0]<<" "<<end[1]<<" "<<right[0]<<" "<<right[1]<<endl;
1179  if (Line(im, end[0], end[1], (unsigned)(right[0]), (unsigned)(right[1]),
1180  value, thick)!=0){
1181  //if (Line(im, end, (unsigned *)right, value)!=0){
1182  return -1;
1183  }
1184  } else { res = -1; }
1185 
1186  if (left[0]>=0 && left[1]<(int)im.GetWidth()){
1187  //cout << "left: "<<end[0]<<" "<<end[1]<<" "<<left[0]<<" "<<left[1]<<endl;
1188  if (Line(im, end[0], end[1], (unsigned)(left[0]), (unsigned)(left[1]),
1189  value, thick)!=0){
1190  //if (Line(im, end, (unsigned *)left, value)!=0){
1191  return -1;
1192  }
1193  } else { res = -1; }
1194  return res;
1195  }
1196 
1197  template <class StorageType>
1199  const unsigned int StartX,
1200  const unsigned int StartY,
1201  const unsigned int EndX,
1202  const unsigned int EndY,
1203  const StorageType color[],
1204  const float thickness )
1205  {
1206 #ifdef BIAS_HAVE_OPENCV
1207  int i_thickness=int(thickness);
1208  Line(im,
1209  StartX, StartY,
1210  EndX, EndY,
1211  ColourRGB<StorageType>(color[0],color[1], color[2]),
1212  i_thickness
1213  );
1214 #else
1215  BIASWARNONCE("Compile BIAS with OpenCV to draw thick lines! Using slow workaround.");
1216  // workaround : draw multiple lines to get a thicker one
1217 
1218  // direction of (original) line
1219  double dir[2]; //< direction od the line
1220  double ortho[2]; //< perpendicalur vector of line displacement for thickness
1221  dir[0] = EndX - StartX; //< dx
1222  dir[1] = EndY - StartY; //< dy
1223 
1224  // orthogonal offset is perpendicular to line direction:
1225  ortho[0] = -dir[1];
1226  ortho[1] = dir[0]; //< y, x as perpendicular of dir
1227  double norm = sqrt(ortho[0]*ortho[0] + ortho[1]*ortho[1]);
1228  if (norm ==0) {
1229  ortho[0] = 1.0; ortho[1] = 0.0;
1230  } else {
1231  ortho[0] /= norm; // length one
1232  ortho[1] /= norm; // length one
1233  };
1234 
1235  // draw multiple parallel lines to emulate thickness
1236  // QUCKHACK. \todo implement Bresenham for thicked lines or even better with antialised lines.
1237  // \todo the line may actually be shifted for fractional part of thickness
1238  // it is not enogh to use 1.0 step...
1239  for (float l= float(-thickness/2.0); l <= float(thickness/2.0); l+=1.0) {
1240  //ImageDraw<StorageType::
1241  Line(im,
1242  (unsigned int)(StartX +l * ortho[0]),
1243  (unsigned int)(StartY +l * ortho[1]),
1244 
1245  (unsigned int)(EndX +l * ortho[0] ),
1246  (unsigned int)(EndY +l * ortho[1] ),
1247  color
1248  );
1249  };
1250 #endif // OpenCV
1251  return 0;
1252  }
1253 
1254  template <class StorageType>
1256  double center[2], double a[2],
1257  double b[2],
1258  const StorageType Value[])
1259  {
1260  // points on ellipse can be parametrized by angle t
1261  // (a*cos(t), b*sin(t)) if the ellipse-axes are parallel to coordinate axes
1262 
1263  const int w = im.GetWidth();
1264  const int h = im.GetHeight();
1265  StorageType **ida=im.GetImageDataArray();
1266  const int cc=im.GetChannelCount();
1267 
1268  double length_a = sqrt(a[0]*a[0]+a[1]*a[1]);
1269  double length_b = sqrt(b[0]*b[0]+b[1]*b[1]);
1270 
1271  // test for degenerate cases, where the ellipse
1272  // consists only of a single line
1273  if (length_a<1e-8){
1274  double start[2], end[2];
1275  start[0]=center[0]-b[0];
1276  start[1]=center[1]-b[1];
1277  end[0]=center[0]+b[0];
1278  end[1]=center[1]+b[1];
1279  return ImageDraw<StorageType>::Line(im, start, end, Value);
1280  }
1281  if (length_b<1e-8){
1282  double start[2], end[2];
1283  start[0]=center[0]-a[0];
1284  start[1]=center[1]-a[1];
1285  end[0]=center[0]+a[0];
1286  end[1]=center[1]+a[1];
1287  return ImageDraw<StorageType>::Line(im, start, end, Value);
1288  }
1289 
1290 #ifdef BIAS_DEBUG
1291  // assume axes are perpendicular
1292  if ((a[0]*b[0]+a[1]*b[1])/(length_a*length_b)>1e-8){
1293  BIASERR("Half axes of ellipse are not perpendicular: ("<<a[0]<<", "<<a[1]
1294  <<"), ("<<b[0]<<", "<<b[1]<<")");
1295  BIASASSERT((a[0]*b[0]+a[1]*b[1])/(length_a*length_b)<1e-8);
1296  }
1297 #endif
1298 
1299  double dt;
1300  if (length_a>length_b){
1301  dt = atan2(1.0, length_a);
1302  } else {
1303  dt = atan2(1.0, length_b);
1304  }
1305  //cout << "dt: "<<dt<<endl;
1306 
1307  // angel of axis a with x axis
1308  double ang=atan2(a[1], a[0]);
1309  //cout << "ang: "<<ang*180.0/M_PI<<" deg"<<endl;
1310  // rotation with respect to coordinate system
1311  double ca=cos(ang), sa=sin(ang);
1312 
1313  double p[2], rp[2], ap[2];
1314  double t=0.0;
1315  int ix, iy;
1316 
1317  while (t<=M_PI_2){
1318  //cout << "t: "<<t<<endl;
1319  p[0] = length_a * cos(t);
1320  p[1] = length_b * sin(t);
1321  //cout << "p : "<<p[0]<<", "<<p[1]<<endl;
1322 
1323  ap[0] = p[0];
1324  ap[1] = p[1];
1325  // now rotate and translate the point
1326  rp[0] = ap[0]*ca - ap[1]*sa + center[0];
1327  rp[1] = ap[1]*ca + ap[0]*sa + center[1];
1328  //cout << "1 - rp : "<<rp[0]<<", "<<rp[1]<<endl;
1329  ix = (int)rint(rp[0]);
1330  iy = (int)rint(rp[1]);
1331  if (ix>=0 && ix<w && iy>=0 && iy<h){
1332  for (int i=0; i<cc; i++)
1333  ida[iy][ix*cc+i]=Value[i];
1334  }
1335 
1336  // use 4-ways symmetry of ellipse
1337  ap[0] = -p[0];
1338  ap[1] = -p[1];
1339  // now rotate and translate the point
1340  rp[0] = ap[0]*ca - ap[1]*sa + center[0];
1341  rp[1] = ap[1]*ca + ap[0]*sa + center[1];
1342  //cout << "2 - rp : "<<rp[0]<<", "<<rp[1]<<endl;
1343  ix = (int)rint(rp[0]);
1344  iy = (int)rint(rp[1]);
1345  if (ix>=0 && ix<w && iy>=0 && iy<h){
1346  for (int i=0; i<cc; i++)
1347  ida[iy][ix*cc+i]=Value[i];
1348  }
1349 
1350  // use 4-ways symmetry of ellipse
1351  ap[0] = p[0];
1352  ap[1] = -p[1];
1353  // now rotate and translate the point
1354  rp[0] = ap[0]*ca - ap[1]*sa + center[0];
1355  rp[1] = ap[1]*ca + ap[0]*sa + center[1];
1356  //cout << "3 - rp : "<<rp[0]<<", "<<rp[1]<<endl;
1357  ix = (int)rint(rp[0]);
1358  iy = (int)rint(rp[1]);
1359  if (ix>=0 && ix<w && iy>=0 && iy<h){
1360  for (int i=0; i<cc; i++)
1361  ida[iy][ix*cc+i]=Value[i];
1362  }
1363 
1364  // use 4-ways symmetry of ellipse
1365  ap[0] = -p[0];
1366  ap[1] = p[1];
1367  // now rotate and translate the point
1368  rp[0] = ap[0]*ca - ap[1]*sa + center[0];
1369  rp[1] = ap[1]*ca + ap[0]*sa + center[1];
1370  //cout << "4 - rp : "<<rp[0]<<", "<<rp[1]<<endl;
1371  ix = (int)rint(rp[0]);
1372  iy = (int)rint(rp[1]);
1373  if (ix>=0 && ix<w && iy>=0 && iy<h){
1374  for (int i=0; i<cc; i++)
1375  ida[iy][ix*cc+i]=Value[i];
1376  }
1377 
1378  t+=dt;
1379  }
1380  return 0;
1381  }
1382 
1383  template <class StorageType>
1385  const std::string & message,
1386  const int & posX,
1387  const int & posY,
1388  const BIAS::ColourRGB<StorageType> &colorRGB,
1389  const int fontface,
1390  const double hscale,
1391  const double vscale,
1392  const double shear,
1393  const int thickness,
1394  const int linetype
1395  )
1396  {
1397 #ifdef BIAS_HAVE_OPENCV
1398  WrapBias2Ipl wrap;
1399  int ret = wrap.Bind( &dstImg );
1400  if(ret !=0) {
1401  BIASWARN("Failed to wrap image to IPL to draw text with OpenCV!");
1402  return;
1403  }
1404  CvFont font;
1405  cvInitFont( &font,
1406  fontface,
1407  hscale,
1408  vscale,
1409  shear,
1410  thickness,
1411  linetype
1412  );
1413  cvPutText( wrap.p_imgIpl,
1414  message.c_str(),
1415  cvPoint(posX, posY),
1416  &font,
1417  cvScalar(colorRGB[0], colorRGB[1], colorRGB[2]) // no swap!
1418  );
1419 #else
1420  BIASWARNONCE("Recompile BIAS with OpenCV to enable drawing text!");
1421 #endif
1422  }
1423 
1424 
1425 template <class StorageType>
1427  const std::string & message,
1428  const int & posX,
1429  const int & posY,
1430  const BIAS::ColourRGB<StorageType> &colorRGB,
1431  const double hscale,
1432  const double vscale,
1433  const double shear,
1434  const int thickness,
1435  const int linetype)
1436  {
1437 #ifdef BIAS_HAVE_IMAGEMAGICKLIB
1438  #ifdef WIN32
1439  //initialize DLL loaded library (not required on Linux but once on Windows)
1440  Magick::InitializeMagick(NULL);
1441  #endif
1442  Magick::Image image;
1443  ImageConvert::BIAS2ImageMagick(dstImg,image);
1444  list<Magick::Drawable> drawables;
1445 
1446  // Example of setting up a DrawableFont object
1447  Magick::DrawableFont fontIM("-*-helvetica-medium-r-normal-*-*-120-*-*-*-*-iso8859-1");
1448  drawables.push_back(fontIM);
1449  Magick::ColorRGB color(colorRGB[0], colorRGB[1], colorRGB[2]);
1450  Magick::DrawableStrokeColor stroke(color);
1451  drawables.push_back(stroke);
1452  Magick::DrawableText text(posX,posY,message);
1453  drawables.push_back(text);
1454 
1455 
1456 #ifdef _GLIBCXX_DEBUG //ImageMagick has incompatible list<> implementation when useing stl debuggin
1457  for (auto it = drawables.begin(); it != drawables.end(); it++)
1458  image.draw(*it);
1459 #else
1460  image.draw(drawables);
1461 #endif
1462 
1463  ImageConvert::ImageMagick2BIAS(image,dstImg);
1464 #else
1465  BIASWARNONCE("Recompile BIAS with ImageMagick to enable drawing text!");
1466 #endif
1467  }
1468 
1469 //////////////////////////////////////////////////////////////////////
1470  template <class StorageType>
1472  Arrow(Image<StorageType>& im, const int startX,
1473  const int startY, const int endX, const int endY,
1474  const BIAS::ColourRGB<StorageType> &colorRGB,
1475  const int thickness)
1476  {
1477 #ifdef BIAS_HAVE_OPENCV
1478  WrapBias2Ipl wrap( &im );
1479  int dx=endX-startX, dy=endY-startY;
1480  if (dx==0 && dy==0){
1481  // do not draw arrow, zero length and hence direction indetermined
1482  return;
1483  }
1484  CvPoint start, end;
1485  start.x = startX;
1486  start.y = startY;
1487  end.x = endX;
1488  end.y = endY;
1489 
1490  CvScalar color = cvScalar(colorRGB[0], colorRGB[1], colorRGB[2]);
1491  double angle = atan2( (double)(start.y - end.y), (double)(start.x - end.x) );
1492  int shift = 0;
1493 
1494  // main line
1495  cvLine(wrap.p_imgIpl, start, end, color, thickness, CV_AA, shift);
1496 
1497  /* Now draw the tips of the arrow. I do some scaling so that the
1498  * tips look proportional to the main line of the arrow. */
1499  start.x = (int) (end.x + 6 * cos(angle + M_PI / 4));
1500  start.y = (int) (end.y + 6 * sin(angle + M_PI / 4));
1501  cvLine(wrap.p_imgIpl, start, end, color, thickness, CV_AA, shift);
1502 
1503  start.x = (int) (end.x + 6 * cos(angle - M_PI / 4));
1504  start.y = (int) (end.y + 6 * sin(angle - M_PI / 4));
1505  cvLine(wrap.p_imgIpl, start, end, color, thickness, CV_AA, shift);
1506 #else
1507  BIASWARNONCE("Recompile BIAS with OpenCV to enable drawing arrows!");
1508 #endif
1509  }
1510 
1511  template <class StorageType>
1513  const int & centerX,
1514  const int & centerY,
1515  const int & radius,
1516  const BIAS::ColourRGB<StorageType> &colorRGB,
1517  const int & thickness,
1518  const int & linetype,
1519  const int & shift
1520  )
1521  {
1522 #ifdef BIAS_HAVE_OPENCV
1523  WrapBias2Ipl wrap( &dstImg );
1524  cvCircle( wrap.p_imgIpl,
1525  cvPoint (centerX, centerY),
1526  radius,
1527  cvScalar(colorRGB[0], colorRGB[1], colorRGB[2] ),
1528  thickness,
1529  linetype,
1530  shift
1531  );
1532 #else
1533  StorageType color[3] = {colorRGB[0],colorRGB[1],colorRGB[2]};
1534  CircleCenter(dstImg,centerX,centerY,radius,color);
1535 #endif
1536  }
1537 
1538  template <class StorageType>
1540  const int & pt1X,
1541  const int & pt1Y,
1542  const int & pt2X,
1543  const int & pt2Y,
1544  const BIAS::ColourRGB<StorageType> &colorRGB,
1545  const int thickness,
1546  const int linetype,
1547  const int shift
1548  )
1549  {
1550 #ifdef BIAS_HAVE_OPENCV
1551  WrapBias2Ipl wrap( &dstImg );
1552  cvLine( wrap.p_imgIpl,
1553  cvPoint (pt1X, pt1Y),
1554  cvPoint (pt2X, pt2Y),
1555  cvScalar(colorRGB[0], colorRGB[1], colorRGB[2] ),
1556  thickness,
1557  linetype,
1558  shift
1559  );
1560 #else
1561  StorageType color[3] = {colorRGB[0],colorRGB[1],colorRGB[2]};
1562  InterpolatedLine(dstImg,pt1X,pt1Y,pt2X,pt2Y,color,thickness);
1563 #endif
1564  }
1565 
1566  template <class StorageType>
1568  const int & pt1X,
1569  const int & pt1Y,
1570  const int & pt2X,
1571  const int & pt2Y,
1572  const BIAS::ColourRGB<StorageType> &colorRGB,
1573  const int thickness,
1574  const int linetype,
1575  const int shift
1576  )
1577  {
1578 #ifdef BIAS_HAVE_OPENCV
1579  WrapBias2Ipl wrap( &dstImg );
1580  cvRectangle(wrap.p_imgIpl,
1581  cvPoint (pt1X, pt1Y),
1582  cvPoint (pt2X, pt2Y),
1583  cvScalar(colorRGB[0], colorRGB[1], colorRGB[2] ),
1584  thickness,
1585  linetype,
1586  shift
1587  );
1588 #else
1589  StorageType color[3] = {colorRGB[0],colorRGB[1],colorRGB[2]};
1590  RectangleCorners(dstImg,pt1X,pt1Y,pt2X,pt2Y,color);
1591 #endif
1592  }
1593 
1594  template <class StorageType>
1596  const int & centerX,
1597  const int & centerY,
1598  const int & axesX,
1599  const int & axesY,
1600  const double & angle,
1601  const double & start_angle,
1602  const double & end_angle,
1603  const BIAS::ColourRGB<StorageType> &colorRGB,
1604  const int & thickness,
1605  const int & linetype,
1606  const int & shift
1607  )
1608  {
1609 #ifdef BIAS_HAVE_OPENCV
1610  WrapBias2Ipl wrap( &dstImg );
1611  cvEllipse(wrap.p_imgIpl,
1612  cvPoint(centerX, centerY),
1613  cvSize (axesX, axesY),
1614  angle,
1615  start_angle,
1616  end_angle,
1617  cvScalar(colorRGB[0], colorRGB[1], colorRGB[2] ),
1618  thickness,
1619  linetype,
1620  shift );
1621 #else
1622  BIASWARNONCE("Recompile BIAS with OpenCV to enable drawing ellipses!");
1623 #endif
1624  }
1625 
1626 } // namespace BIAS
1627 
1628 
1629 //
1630 // solve explicit instantiation
1631 //
1632 #define INSTANCE_ImageDraw(type)\
1633 template class BIASImageUtilsBase_EXPORT ImageDraw<type>;
1634 
1635 
1636 // create instances
1637 namespace BIAS{
1638 INSTANCE_ImageDraw(unsigned char)
1639 INSTANCE_ImageDraw(float)
1640 #ifdef BUILD_IMAGE_INT
1641 INSTANCE_ImageDraw(int)
1642 #endif
1643 #ifdef BUILD_IMAGE_CHAR
1644 INSTANCE_ImageDraw(char)
1645 #endif
1646 #ifdef BUILD_IMAGE_SHORT
1647 INSTANCE_ImageDraw(short)
1648 #endif
1649 #if defined(BUILD_IMAGE_USHORT)
1650 INSTANCE_ImageDraw(unsigned short)
1651 #endif
1652 #ifdef BUILD_IMAGE_DOUBLE
1653 INSTANCE_ImageDraw(double)
1654 #endif
1655 #ifdef BUILD_IMAGE_UINT
1656 INSTANCE_ImageDraw(unsigned int)
1657 #endif
1658 }
static int InterpolatedLineGrey(Image< StorageType > &im, const int StartX, const int StartY, const int EndX, const int EndY, const StorageType Value, const float thickness=1.0, const float Opacity=1.0)
draws an anti-aliased line that automatically clips to the image borders.
Definition: ImageDraw.cpp:956
static int Arrow(Image< StorageType > &im, const unsigned start[2], const unsigned end[2], const unsigned length, const unsigned width, const StorageType value[])
draws an arrow from start to end, the tips of the head are length pixel back on the line and width pi...
Definition: ImageDraw.cpp:1127
void Init(int center[2], int radius)
Initialises this BresenhamCircleEighth with new center and radius.
class HomgPoint2D describes a point with 2 degrees of freedom in projective coordinates.
Definition: HomgPoint2D.hh:67
static int RectangleCornersGreyFill(Image< StorageType > &im, const int minx, const int miny, const int maxx, const int maxy, const StorageType GreyValue)
faster for one channel images
Definition: ImageDraw.cpp:257
static int InterpolatedLine(Image< StorageType > &im, const int StartX, const int StartY, const int EndX, const int EndY, const StorageType Value[], const float thickness=1.0, const float Opacity=1.0)
draws an anti-aliased line that automatically clips to the image borders.
Definition: ImageDraw.cpp:826
Scans a circle using Bresenham&#39;s integer arithmetic algorithm.
static void Circle(BIAS::Image< StorageType > &dstImg, const int &centerX, const int &centerY, const int &radius=3, const BIAS::ColourRGB< StorageType > &colorRGB=ColourRGB< StorageType >(255, 255, 255), const int &thickness=1, const int &linetype=8, const int &shift=0)
OpenCV: Draws a circle.
Definition: ImageDraw.cpp:1512
interface class used to ease handover in function calls
Definition: ColourRGB.hh:34
static int CircleCenterFilled(Image< StorageType > &im, unsigned int CenterX, unsigned int CenterY, unsigned int Radius, const StorageType Value[])
draws a filled circle using Value
Definition: ImageDraw.cpp:1023
unsigned int GetWidth() const
Definition: ImageBase.hh:312
static int RectangleCorners(Image< StorageType > &im, const int minx, const int miny, const int maxx, const int maxy, const StorageType value[])
rectangles
Definition: ImageDraw.cpp:94
static StorageType GetContrastValue(StorageType t)
Definition: ImageDraw.cpp:59
static int CircleCenter(Image< StorageType > &im, unsigned int CenterX, unsigned int CenterY, unsigned int Radius, const StorageType Value[]=NULL)
draws a circular line, either using Value or a good contrast value
Definition: ImageDraw.cpp:977
static int Line(Image< StorageType > &im, const unsigned int start[2], const unsigned int end[2], const StorageType value[])
lines
Definition: ImageDraw.cpp:404
static int RectangleCenterGreyFill(Image< StorageType > &im, const int x, const int y, const int size, const StorageType value)
Definition: ImageDraw.cpp:328
static int Ellipse(Image< StorageType > &im, double center[2], double a[2], double b[2], const StorageType Value[])
draws an ellipse at center with half axes a and b
Definition: ImageDraw.cpp:1255
unsigned int GetChannelCount() const
returns the number of Color channels, e.g.
Definition: ImageBase.hh:382
bool GetNext(int next[2])
Returns the coordinate of the next point on the circle.
unsigned int GetHeight() const
Definition: ImageBase.hh:319
bool GetNext(int next[2])
Definition: Bresenham.hh:185
static int LineGrey(Image< StorageType > &im, const unsigned int start[2], const unsigned int end[2], const StorageType value)
faster algorithm for 1 channel images
Definition: ImageDraw.cpp:500
The image template class for specific storage types.
Definition: Image.hh:78
static int RectangleCenter(Image< StorageType > &im, const int x, const int y, const int size, const StorageType value[])
Draws the rectangle around X, Y with Size and Value[i] in channel i.
Definition: ImageDraw.cpp:284
bool GetNext(int next[2])
Returns the coordinate of the next point on the 1/8 circle.
void Init(int center[2], int radius)
Initialises this BresenhamCircle with new center and radius.
static int BIAS2ImageMagick(const BIAS::ImageBase &input, Magick::Image &dest)
Returns a new, separate ImageMagick Image for use with ImageMagick created from the source BIAS Image...
Scans a line using Bresenhams integer arithmetic algorithm.
Definition: Bresenham.hh:42
static void Rectangle(BIAS::Image< StorageType > &dstImg, const int &pt1X, const int &pt1Y, const int &pt2X, const int &pt2Y, const BIAS::ColourRGB< StorageType > &colorRGB=ColourRGB< StorageType >(255, 255, 255), const int thickness=1, const int linetype=8, const int shift=0)
OpenCV: Draws a simple, thick or filled rectangle.
Definition: ImageDraw.cpp:1567
static int InterpolatedCircleCenter(Image< StorageType > &im, unsigned int CenterX, unsigned int CenterY, unsigned int Radius, const StorageType Value[], const float Thickness=1.0, const float Opacity=1.0)
draws an anti-aliased circular line that clips automatically to the image borders.
Definition: ImageDraw.cpp:1114
static void TextIM(BIAS::Image< StorageType > &dstImg, const std::string &message, const int &posX=0, const int &posY=20, const ColourRGB< StorageType > &colorRGB=ColourRGB< StorageType >(255, 255, 255), const double hscale=1.0, const double vscale=1.0, const double shear=0, const int thickness=1, const int linetype=8)
ImageMagick: Draw Text into image.
Definition: ImageDraw.cpp:1426
static int ImageMagick2BIAS(Magick::Image &image, BIAS::ImageBase &result)
Returns a new, separate BIAS Image created from the source ImageMagick Image.
wrapper around a BIAS image to be used as an OpenCv IPlimage with shared data area.
Definition: WrapBias2Ipl.hh:26
Just like BresenhamCircle but only computes 1/8 of the circle.
static int RectangleCenterGrey(Image< StorageType > &im, const int x, const int y, const int size, const StorageType value)
fills the channels with value
Definition: ImageDraw.cpp:311
void Init(const int start[2], const int end[2])
Definition: Bresenham.hh:125
static void Text(BIAS::Image< StorageType > &dstImg, const std::string &message, const int &posX=0, const int &posY=20, const ColourRGB< StorageType > &colorRGB=ColourRGB< StorageType >(255, 255, 255), const int fontface=1, const double hscale=1.0, const double vscale=1.0, const double shear=0, const int thickness=1, const int linetype=8)
OpenCV: Draw Text into image.
Definition: ImageDraw.cpp:1384
class BIASGeometryBase_EXPORT HomgPoint2D
static int RectangleCornersGrey(Image< StorageType > &im, const int minx, const int miny, const int maxx, const int maxy, const StorageType GreyValue)
faster for one channel images
Definition: ImageDraw.cpp:221
static int RectangleCornersFill(Image< StorageType > &im, const int minx, const int miny, const int maxx, const int maxy, const StorageType value[])
Draws the filled rectangle defined by upper left and bottom right corner with value[i] in channel i...
Definition: ImageDraw.cpp:194
const StorageType ** GetImageDataArray() const
overloaded GetImageDataArray() from ImageBase
Definition: Image.hh:153
int Bind(const BIAS::ImageBase *p_src)
create the internal IPL image which shares the data area with the src Bias image. ...