DrawOps.C

Go to the documentation of this file.
00001 /*!@file Image/DrawOps.C functions for drawing on images
00002  */
00003 // //////////////////////////////////////////////////////////////////// //
00004 // The iLab Neuromorphic Vision C++ Toolkit - Copyright (C) 2000-2002   //
00005 // by the University of Southern California (USC) and the iLab at USC.  //
00006 // See http://iLab.usc.edu for information about this project.          //
00007 // //////////////////////////////////////////////////////////////////// //
00008 // Major portions of the iLab Neuromorphic Vision Toolkit are protected //
00009 // under the U.S. patent ``Computation of Intrinsic Perceptual Saliency //
00010 // in Visual Environments, and Applications'' by Christof Koch and      //
00011 // Laurent Itti, California Institute of Technology, 2001 (patent       //
00012 // pending; filed July 23, 2001, following provisional applications     //
00013 // No. 60/274,674 filed March 8, 2001 and 60/288,724 filed May 4, 2001).//
00014 // //////////////////////////////////////////////////////////////////// //
00015 // This file is part of the iLab Neuromorphic Vision C++ Toolkit.       //
00016 //                                                                      //
00017 // The iLab Neuromorphic Vision C++ Toolkit is free software; you can   //
00018 // redistribute it and/or modify it under the terms of the GNU General  //
00019 // Public License as published by the Free Software Foundation; either  //
00020 // version 2 of the License, or (at your option) any later version.     //
00021 //                                                                      //
00022 // The iLab Neuromorphic Vision C++ Toolkit is distributed in the hope  //
00023 // that it will be useful, but WITHOUT ANY WARRANTY; without even the   //
00024 // implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR      //
00025 // PURPOSE.  See the GNU General Public License for more details.       //
00026 //                                                                      //
00027 // You should have received a copy of the GNU General Public License    //
00028 // along with the iLab Neuromorphic Vision C++ Toolkit; if not, write   //
00029 // to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,   //
00030 // Boston, MA 02111-1307 USA.                                           //
00031 // //////////////////////////////////////////////////////////////////// //
00032 //
00033 // Primary maintainer for this file: Dirk Walther <walther@caltech.edu>
00034 // $HeadURL: svn://isvn.usc.edu/software/invt/trunk/saliency/src/Image/DrawOps.C $
00035 // $Id: DrawOps.C 14654 2011-03-30 04:10:14Z dberg $
00036 //
00037 
00038 #include <deque>
00039 #include "Image/DrawOps.H"
00040 
00041 #include "Image/ColorOps.H"    // for makeRGB()
00042 #include "Image/ColorMap.H"
00043 #include "Image/CutPaste.H"    // for concatX() etc.
00044 #include "Image/FilterOps.H"   // for lowPass9()
00045 #include "Image/Image.H"
00046 #include "Image/MathOps.H"     // for inplaceNormalize() etc.
00047 #include "Image/Pixels.H"
00048 #include "Image/Point2D.H"
00049 #include "Image/Range.H"
00050 #include "Image/ShapeOps.H"    // for rescale() etc.
00051 #include "Image/Transforms.H"  // for chamfer34() etc.
00052 #include "Image/MatrixOps.H" //for transpose
00053 #include "Util/Assert.H"
00054 #include "Util/MathFunctions.H"
00055 #include "Util/sformat.H"
00056 #include "rutz/trace.h"
00057 #include <cmath>
00058 // ######################################################################
00059 void drawTraj(Image< PixRGB<byte> >& img,
00060               const Point2D<int>* foa, const Point2D<int>* foas_end)
00061 {
00062 GVX_TRACE(__PRETTY_FUNCTION__);
00063   const Point2D<int>* foa_prev = foa;
00064   for (; foa != foas_end; ++foa)
00065     {
00066       drawCircle(img, *foa, 10, PixRGB<byte>(255,255,0), 1);
00067       if (foa != foa_prev)
00068         drawArrow(img, *foa_prev, *foa, PixRGB<byte>(255,0,0), 1);
00069       foa_prev = foa;
00070     }
00071 }
00072 
00073 // ######################################################################
00074 Image< PixRGB<byte> > colGreyCombo(const Image< PixRGB<byte> >& colimg,
00075                                    const Image<float>& bwimg,
00076                                    const bool xcombo, const bool interp)
00077 {
00078 GVX_TRACE(__PRETTY_FUNCTION__);
00079 
00080   ASSERT(colimg.initialized());
00081 
00082   // will convert and clamp to byte value range as necessary:
00083   const Image<byte> tmp =
00084     bwimg.initialized()
00085     ? Image<byte>(rescaleOpt(bwimg, colimg.getDims(), interp))
00086     : Image<byte>(colimg.getDims(), ZEROS);
00087 
00088   const Image< PixRGB<byte> > tmp2 = makeRGB(tmp, tmp, tmp);
00089 
00090   if (xcombo) return concatX(colimg, tmp2);
00091   else        return concatY(colimg, tmp2);
00092 }
00093 
00094 // ######################################################################
00095 Image< PixRGB<byte> > colColCombo(const Image< PixRGB<byte> >& colimg1,
00096                                    const Image< PixRGB<byte> >& colimg2,
00097                                    const bool xcombo, const bool interp)
00098 {
00099 GVX_TRACE(__PRETTY_FUNCTION__);
00100 
00101   ASSERT(colimg1.initialized());
00102   ASSERT(colimg2.initialized());
00103 
00104   Image< PixRGB<byte> > tmp;
00105   if (xcombo)
00106     {
00107       if (colimg1.getHeight() != colimg2.getHeight())
00108         tmp = rescaleOpt(colimg2, colimg1.getWidth() / 2,
00109                       colimg1.getHeight(), interp);
00110       else tmp = colimg2;
00111       return concatX(colimg1, tmp);
00112     }
00113   else
00114     {
00115       if (colimg1.getWidth() != colimg2.getWidth())
00116         tmp = rescaleOpt(colimg2, colimg1.getWidth(),
00117                       colimg1.getHeight() / 2, interp);
00118       else tmp = colimg2;
00119       return concatY(colimg1, tmp);
00120     }
00121 }
00122 
00123 // ######################################################################
00124 Image< PixRGB<byte> > highlightRegions(const Image< PixRGB<byte> >& img,
00125                                        const Image<byte>& mask,
00126                                        const int maxval)
00127 {
00128 GVX_TRACE(__PRETTY_FUNCTION__);
00129   Image<float> tmp = chamfer34(mask);
00130   tmp = binaryReverse(tmp, float(maxval));
00131   tmp.setVal(0, 0, 0.0);
00132   inplaceNormalize(tmp, 0.0f, 1.0f);
00133   return img * tmp;
00134 }
00135 
00136 // ######################################################################
00137 Image< PixRGB<byte> > warp3Dmap(const Image< PixRGB<byte> >& img,
00138                                 const Image<float>& hmap,
00139                                 const float pitch, const float yaw,
00140                                 Dims& imdims)
00141 {
00142 GVX_TRACE(__PRETTY_FUNCTION__);
00143     // smooth linear interpolation of the height map:
00144     Image<float> ftmp = lowPass9(rescale(hmap,img.getWidth(),img.getHeight()));
00145     inplaceClamp(ftmp, 0.0f, 255.0f);
00146 
00147     // first call to this fct will compute size of output:
00148     return warp3D(img, ftmp, pitch, yaw, 255.0, imdims);
00149 }
00150 
00151 // ######################################################################
00152 void inplaceSetValMask(Image<float>& dest,
00153                        const Image<byte>& mask, const float val)
00154 {
00155 GVX_TRACE(__PRETTY_FUNCTION__);
00156   ASSERT(dest.isSameSize(mask));
00157 
00158   Image<byte>::const_iterator mptr = mask.begin();
00159 
00160   Image<float>::iterator
00161     dptr = dest.beginw(),
00162     endptr = dest.endw();
00163 
00164   while (dptr != endptr)
00165     {
00166       if (*mptr) *dptr = val;
00167       ++mptr;
00168       ++dptr;
00169     }
00170 }
00171 
00172 // ######################################################################
00173 template <class T>
00174 void drawDisk(Image<T>& dst, const Point2D<int>& center,
00175               const int radius, const T value)
00176 {
00177 GVX_TRACE(__PRETTY_FUNCTION__);
00178   ASSERT(dst.initialized());
00179   if (radius == 1)
00180     {
00181       if (dst.coordsOk(center)) dst.setVal(center.i + dst.getWidth() * center.j, value);
00182       return;
00183     }
00184 
00185   for (int y = -radius; y <= radius; ++y)
00186     {
00187       int bound = int(sqrtf(float(squareOf(radius) - squareOf(y))));
00188       for (int x = -bound; x <= bound; ++x)
00189         if (dst.coordsOk(x + center.i, y + center.j))
00190           dst.setVal(x + center.i, y + center.j, value);
00191     }
00192 }
00193 
00194 // ######################################################################
00195 template <class T>
00196 void drawLine(Image<T>& dst,
00197     const Point2D<int>& pos, float ori, float len, const T col,
00198     const int rad = 1)
00199 {
00200 
00201 GVX_TRACE(__PRETTY_FUNCTION__);
00202   ASSERT(dst.initialized());
00203 
00204   int x1 = int(cos(ori)*len/2);
00205   int y1 = int(sin(ori)*len/2);
00206 
00207   Point2D<int> p1(pos.i-x1, pos.j+y1);
00208   Point2D<int> p2(pos.i+x1, pos.j-y1);
00209 
00210   drawLine(dst, p1, p2, col, rad);
00211 
00212 }
00213 
00214 
00215 // ######################################################################
00216 template <class T>
00217 void drawLine(Image<T>& dst, const Point2D<int>& p1, const Point2D<int>& p2,
00218               const T col, const int rad)
00219 {
00220 GVX_TRACE(__PRETTY_FUNCTION__);
00221   ASSERT(dst.initialized());
00222   // from Graphics Gems / Paul Heckbert
00223   int dx = p2.i - p1.i, ax = abs(dx) << 1, sx = signOf(dx);
00224   int dy = p2.j - p1.j, ay = abs(dy) << 1, sy = signOf(dy);
00225   int x = p1.i, y = p1.j;
00226 
00227   const int w = dst.getWidth();
00228   const int h = dst.getHeight();
00229 
00230   T* const dptr = dst.getArrayPtr();
00231 
00232   if (ax > ay)
00233     {
00234       int d = ay - (ax >> 1);
00235       for (;;)
00236         {
00237           if (rad == 1)
00238             {
00239               // avoid a function call to drawDisk() if rad==1
00240               if (x >= 0 && x < w && y >= 0 && y < h)
00241                 dptr[x + w * y] = col;
00242             }
00243           else
00244             drawDisk(dst, Point2D<int>(x,y), rad, col);
00245           if (x == p2.i) return;
00246           if (d >= 0) { y += sy; d -= ax; }
00247           x += sx; d += ay;
00248         }
00249     }
00250   else
00251     {
00252       int d = ax - (ay >> 1);
00253       for (;;)
00254         {
00255           if (rad == 1)
00256             {
00257               // avoid a function call to drawDisk() if rad==1
00258               if (x >= 0 && x < w && y >= 0 && y < h)
00259                 dptr[x + w * y] = col;
00260             }
00261           else
00262             drawDisk(dst, Point2D<int>(x,y), rad, col);
00263           if (y == p2.j) return;
00264           if (d >= 0) { x += sx; d -= ay; }
00265           y += sy; d += ax;
00266         }
00267     }
00268 }
00269 
00270 // ######################################################################
00271 template <class T>
00272 void drawCorner(Image<T>& dst,
00273     const Point2D<int>& pos, float ori, float ang, float len, const T col,
00274     const int rad = 1)
00275 {
00276 
00277 GVX_TRACE(__PRETTY_FUNCTION__);
00278   ASSERT(dst.initialized());
00279 
00280   int x1 = int(cos(ori - ang/2)*len/2);
00281   int y1 = int(sin(ori - ang/2)*len/2);
00282 
00283   int x2 = int(cos(ori + ang/2)*len/2);
00284   int y2 = int(sin(ori + ang/2)*len/2);
00285 
00286   Point2D<int> p1(pos.i, pos.j);
00287   Point2D<int> p2(pos.i+x1, pos.j-y1);
00288   Point2D<int> p3(pos.i+x2, pos.j-y2);
00289 
00290   drawLine(dst, p1, p2, col, rad);
00291   drawLine(dst, p1, p3, col, rad);
00292 
00293 }
00294 
00295 // ######################################################################
00296 template <class T>
00297 void drawRect(Image<T>& dst,
00298               const Rectangle& r, const T color, const int rad)
00299 {
00300 GVX_TRACE(__PRETTY_FUNCTION__);
00301   ASSERT(dst.initialized());
00302   ASSERT(r.isValid());
00303   ASSERT(dst.rectangleOk(r));
00304 
00305   Point2D<int> p1(r.left(), r.top());
00306   Point2D<int> p2(r.rightI(), r.top());
00307   Point2D<int> p3(r.left(), r.bottomI());
00308   Point2D<int> p4(r.rightI(), r.bottomI());
00309 
00310   drawLine(dst, p1, p2, color, rad);
00311   drawLine(dst, p1, p3, color, rad);
00312   drawLine(dst, p3, p4, color, rad);
00313   drawLine(dst, p2, p4, color, rad);
00314 }
00315 
00316 // ######################################################################
00317 template <class T>
00318 void drawRectSquareCorners(Image<T>& dst, const Rectangle& r1,
00319                            const T color, const int linewidth)
00320 {
00321 GVX_TRACE(__PRETTY_FUNCTION__);
00322   ASSERT(dst.initialized());
00323   ASSERT(r1.isValid());
00324 
00325   const Rectangle r = r1.getOverlap(dst.getBounds());
00326 
00327   ASSERT(dst.rectangleOk(r));
00328 
00329   const int minrad = (linewidth - 1) / 2;
00330 
00331   for (int i = 0; i < linewidth; ++i)
00332     {
00333       const int offset = i - minrad;
00334 
00335       const Point2D<int> p1(r.left() + offset, r.top() + offset);
00336       const Point2D<int> p2(r.rightI() - offset, r.top() + offset);
00337       const Point2D<int> p3(r.left() + offset, r.bottomI() - offset);
00338       const Point2D<int> p4(r.rightI() - offset, r.bottomI() - offset);
00339 
00340       drawLine(dst, p1, p2, color, 1);
00341       drawLine(dst, p1, p3, color, 1);
00342       drawLine(dst, p3, p4, color, 1);
00343       drawLine(dst, p2, p4, color, 1);
00344     }
00345 }
00346 
00347 // ######################################################################
00348 template <class T>
00349 void drawRectOR(Image<T>& dst,
00350               const Rectangle& r, const T color, const int rad,
00351               const float ori)
00352 {
00353 GVX_TRACE(__PRETTY_FUNCTION__);
00354   ASSERT(dst.initialized());
00355   //ASSERT(dst.rectangleOk(r));
00356 
00357   // compute the center of the rect
00358   const float centerX = r.rightI()  - r.left();
00359   const float centerY = r.bottomI() - r.top();
00360 
00361   // compute the distance from the center to the corners
00362   //const float distX  = r.rightI()  - centerX;
00363   //const float distY  = r.bottomI() - centerY;
00364   const float distX  = centerX/2;
00365   const float distY  = centerY/2;
00366   const float radius = sqrt(pow(distX,2) + pow(distY,2));
00367 
00368   // compute the transformed coordinates
00369   const float thetaO  = atan(distX/distY);
00370   const float thetaN1 = thetaO + ori;
00371   const float thetaN2 = ori - thetaO;
00372   const float Xnew1   = radius * sin(thetaN1);
00373   const float Ynew1   = radius * cos(thetaN1);
00374   const float Xnew2   = radius * sin(thetaN2);
00375   const float Ynew2   = radius * cos(thetaN2);
00376 
00377   // compute new coords based on rotation
00378   const float Xbr    = r.left() + distX + Xnew1; // bottom-right
00379   const float Ybr    = r.top()  + distY + Ynew1;
00380   const float Xtl    = r.left() + distX - Xnew1; // top-left
00381   const float Ytl    = r.top()  + distY - Ynew1;
00382   const float Xbl    = r.left() + distX + Xnew2; // bottom-left
00383   const float Ybl    = r.top()  + distY + Ynew2;
00384   const float Xtr    = r.left() + distX - Xnew2; // top-right
00385   const float Ytr    = r.top()  + distY - Ynew2;
00386 
00387   // set up points
00388   Point2D<int> p1((int)round(Xbr), (int)round(Ybr));
00389   Point2D<int> p2((int)round(Xtl), (int)round(Ytl));
00390   Point2D<int> p3((int)round(Xbl), (int)round(Ybl));
00391   Point2D<int> p4((int)round(Xtr), (int)round(Ytr));
00392 
00393   // draw lines
00394   drawLine(dst, p1, p3, color, rad);
00395   drawLine(dst, p1, p4, color, rad);
00396   drawLine(dst, p2, p3, color, rad);
00397   drawLine(dst, p2, p4, color, rad);
00398 }
00399 
00400 // ######################################################################
00401 template <class T>
00402 void drawRectEZ(Image<T>& dst,
00403                 const Rectangle& r, const T color, const int rad)
00404 {
00405 GVX_TRACE(__PRETTY_FUNCTION__);
00406   if (!r.isValid()) return;
00407 
00408   ASSERT(dst.initialized());
00409 
00410   if (dst.rectangleOk(r))
00411   {
00412     Point2D<int> p1(r.left(), r.top());
00413     Point2D<int> p2(r.rightI(), r.top());
00414     Point2D<int> p3(r.left(), r.bottomI());
00415     Point2D<int> p4(r.rightI(), r.bottomI());
00416 
00417     drawLine(dst, p1, p2, color, rad);
00418     drawLine(dst, p1, p3, color, rad);
00419     drawLine(dst, p3, p4, color, rad);
00420     drawLine(dst, p2, p4, color, rad);
00421   }
00422   else
00423   {
00424     const Rectangle r2 = r.getOverlap(dst.getBounds());
00425 
00426     int rl = r2.left();  int rt = r2.top();
00427     int rr = r2.rightI(); int rb = r2.bottomI();
00428 
00429     Point2D<int> p1(rl, rt); Point2D<int> p2(rr, rt);
00430     Point2D<int> p3(rl, rb); Point2D<int> p4(rr, rb);
00431 
00432     drawLine(dst, p1, p2, color, rad); drawLine(dst, p1, p3, color, rad);
00433     drawLine(dst, p3, p4, color, rad); drawLine(dst, p2, p4, color, rad);
00434   }
00435 
00436 }
00437 
00438 // ######################################################################
00439 template <class T>
00440 void drawFilledRect(Image<T>& dst, const Rectangle& r, const T val)
00441 {
00442 GVX_TRACE(__PRETTY_FUNCTION__);
00443   ASSERT(dst.rectangleOk(r));
00444 
00445   const int w = dst.getWidth();
00446 
00447   const int rw = r.width();
00448   const int rh = r.height();
00449 
00450   const int rowskip = w - rw;
00451 
00452   typename Image<T>::iterator dptr = dst.beginw() + r.left() + r.top() * w;
00453 
00454   for (int y = 0; y < rh; ++y)
00455     {
00456       for (int x = 0; x < rw; ++x)
00457         *dptr++ = val;
00458       dptr += rowskip;
00459     }
00460 }
00461 
00462 // ######################################################################
00463 template <class T>
00464 Image<T> drawHistogram(std::vector<float> hist, int width, int height, T lineVal, T fillVal)
00465 {
00466   Image<T> ret = Image<T>(width,height,ZEROS);
00467   if(hist.size() == 0)
00468     return ret;
00469   float recWidth = width/float(hist.size()+1);
00470   float peak = *std::max_element(hist.begin(),hist.end());
00471   float recScale = height/float(peak);
00472   for(uint h=0; h< hist.size(); h++)
00473     {
00474       Dims d = Dims(floor(recWidth),std::max(int(floor(recScale*(hist[h]))),1));
00475       Point2D<int> p = Point2D<int>(floor(recWidth*h+recWidth/2.0),0);
00476       Rectangle rec = Rectangle(p,d);
00477       if(!ret.rectangleOk(rec))
00478         {
00479           LINFO("H[%d] failed with p %s and Dims %s",h,convertToString(p).c_str(),convertToString(d).c_str());
00480           LFATAL("Histogram rectangle invalid");
00481         }
00482       drawFilledRect(ret,rec,fillVal);
00483       drawRectSquareCorners(ret,rec,lineVal,1);
00484     }
00485   return ret;
00486 }
00487 
00488 // ######################################################################
00489 template <class T>
00490 void drawCross(Image<T>& dst,
00491                const Point2D<int>& p, const T col, const int siz,
00492                const int rad)
00493 {
00494 GVX_TRACE(__PRETTY_FUNCTION__);
00495   ASSERT(dst.initialized());
00496   Point2D<int> p1, p2;
00497 
00498   p1.i = clampValue(p.i - siz, 0, dst.getWidth() - 1);
00499   p1.j = clampValue(p.j, 0, dst.getHeight() - 1);
00500   p2.i = clampValue(p.i + siz, 0, dst.getWidth() - 1);
00501   p2.j = clampValue(p.j, 0, dst.getHeight() - 1);
00502 
00503   drawLine(dst, p1, p2, col, rad);
00504 
00505   p1.i = clampValue(p.i, 0, dst.getWidth() - 1);
00506   p1.j = clampValue(p.j - siz, 0, dst.getHeight() - 1);
00507   p2.i = clampValue(p.i, 0, dst.getWidth() - 1);
00508   p2.j = clampValue(p.j + siz, 0, dst.getHeight() - 1);
00509 
00510   drawLine(dst, p1, p2, col, rad);
00511 }
00512 
00513 // ######################################################################
00514 template <class T>
00515 void drawCrossOR(Image<T>& dst,
00516                  const Point2D<int>& p, const T col, const int siz,
00517                  const int rad, const float ori)
00518 {
00519 GVX_TRACE(__PRETTY_FUNCTION__);
00520   ASSERT(dst.initialized());
00521   Point2D<int> p1, p2;
00522 
00523   // compute new X and Y given rotation
00524   const float newX = siz * sin(ori);
00525   const float newY = siz * cos(ori);
00526 
00527   p1.i = clampValue(int(p.i - newY), 0, dst.getWidth() - 1);
00528   p1.j = clampValue(int(p.j + newX), 0, dst.getHeight() - 1);
00529 
00530   p2.i = clampValue(int(p.i + newY), 0, dst.getWidth() - 1);
00531   p2.j = clampValue(int(p.j - newX), 0, dst.getHeight() - 1);
00532 
00533   drawLine(dst, p1, p2, col, rad);
00534 
00535   p1.i = clampValue(int(p.i - newX), 0, dst.getWidth() - 1);
00536   p1.j = clampValue(int(p.j - newY), 0, dst.getHeight() - 1);
00537 
00538   p2.i = clampValue(int(p.i + newX), 0, dst.getWidth() - 1);
00539   p2.j = clampValue(int(p.j + newY), 0, dst.getHeight() - 1);
00540 
00541   drawLine(dst, p1, p2, col, rad);
00542 }
00543 
00544 // ######################################################################
00545 template <class T>
00546 void drawPatch(Image<T>& dst,
00547                const Point2D<int>& p, const int siz, const T col)
00548 {
00549 GVX_TRACE(__PRETTY_FUNCTION__);
00550   ASSERT(dst.initialized());
00551   for (int i = -siz; i <= siz; ++i)
00552     for (int j = -siz; j <= siz; ++j)
00553       {
00554         const int xx = clampValue(p.i + i, 0, dst.getWidth() - 1);
00555         const int yy = clampValue(p.j + j, 0, dst.getHeight() - 1);
00556         dst.setVal(xx, yy, col);
00557       }
00558 }
00559 
00560 // ######################################################################
00561 template <class T>
00562 void drawPatchBB(Image<T>& dst, const Point2D<int>& p, const int siz, const T col, const T bgcol)
00563 {
00564 GVX_TRACE(__PRETTY_FUNCTION__);
00565   ASSERT(dst.initialized());
00566 
00567   for (int i = -siz-2; i <= siz+2; ++i)
00568     for (int j = -siz-2; j <= siz+2; ++j)
00569       {
00570         const int xx = clampValue(p.i + i, 0, dst.getWidth() - 1);
00571         const int yy = clampValue(p.j + j, 0, dst.getHeight() - 1);
00572         if (i < -siz || i > siz || j < -siz || j > siz) dst.setVal(xx, yy, bgcol);
00573         else dst.setVal(xx, yy, col);
00574       }
00575 }
00576 
00577 // ######################################################################
00578 template <class T>
00579 void drawCircle(Image<T>& dst,
00580                 const Point2D<int>& p, const int radius,
00581                 const T col, const int rad)
00582 {
00583 GVX_TRACE(__PRETTY_FUNCTION__);
00584   ASSERT(dst.initialized());
00585   if (radius == 1)
00586     {
00587       if (dst.coordsOk(p)) dst.setVal(p.i, p.j, col);
00588       return;
00589     }
00590 
00591   Point2D<int> pp(p);
00592   pp.i -= radius; drawDisk(dst, pp, rad, col);
00593   pp.i += radius + radius; drawDisk(dst, pp, rad, col);
00594   int bound1 = radius, bound2 = radius;
00595 
00596   for (int y = 1; y <= radius; y ++)
00597     {
00598       bound2 = bound1;
00599       bound1 = int(sqrtf(float( squareOf(radius) - squareOf(y) )));
00600       for (int i = bound1; i <= bound2; i ++)
00601         {
00602           pp.j = p.j - y;
00603           pp.i = p.i - i; drawDisk(dst, pp, rad, col);
00604           pp.i = p.i + i; drawDisk(dst, pp, rad, col);
00605           pp.j = p.j + y; drawDisk(dst, pp, rad, col);
00606           pp.i = p.i - i; drawDisk(dst, pp, rad, col);
00607         }
00608     }
00609 }
00610 
00611 // ######################################################################
00612 template <class T>
00613 void drawEllipse(Image<T>& dst,
00614                 const Point2D<int>& p, const int radiusx, const int radiusy,
00615                 const T col, const int rad)
00616 {
00617 GVX_TRACE(__PRETTY_FUNCTION__);
00618   ASSERT(dst.initialized());
00619 
00620   int steps = 36;
00621   double a = double(M_PI*2.0)/double(steps);
00622   double sa = (double)sin(a);
00623   double ca = (double)cos(a);
00624 
00625   double ny=1;
00626   double nz=0;
00627   Point2D<int> lastPoint(p.i + radiusx, p.j);
00628   for(int i=1; i<=steps; i++)
00629   {
00630     double tmp = ca*ny - sa*nz;
00631     nz = sa*ny + ca*nz;
00632     ny = tmp;
00633     Point2D<int> newPoint(p.i+(ny*radiusx),p.j+(nz*radiusy));
00634     drawLine(dst, lastPoint, newPoint, col, rad);
00635     lastPoint = newPoint;
00636   }
00637 
00638 
00639 }
00640 
00641 // ######################################################################
00642 template <class T_or_RGB>
00643 void drawSuperquadric(Image<T_or_RGB>& dst,
00644                 const Point2D<int>& p,
00645                 const float a, const float b, const float e, const T_or_RGB col,
00646                 const float rot, const float k1, const float k2,
00647                 const float thetai,
00648                 const float thetaf,
00649                 const int rad,
00650                 const int nSeg)
00651 
00652 {
00653   const float dTheta = (thetaf-thetai) / (float)nSeg;
00654 
00655   for (float theta=thetai; theta < thetaf; theta += dTheta)
00656   {
00657     Point2D<float> p1 = ellipsoid(a,b, e, theta);
00658     Point2D<float> p2 = ellipsoid(a,b, e, theta + dTheta);
00659 
00660     Point2D<float> tmpPos1;
00661     Point2D<float> tmpPos2;
00662     //Sheer
00663     tmpPos1.i = p1.i + p1.j*k1;
00664     tmpPos1.j = p1.i*k2 + p1.j;
00665 
00666     tmpPos2.i = p2.i + p2.j*k1;
00667     tmpPos2.j = p2.i*k2 + p2.j;
00668 
00669     //Rotate and move to p
00670     p1.i = (cos(rot)*tmpPos1.i - sin(rot)*tmpPos1.j) + p.i;
00671     p1.j = (sin(rot)*tmpPos1.i + cos(rot)*tmpPos1.j) + p.j;
00672 
00673     p2.i = (cos(rot)*tmpPos2.i - sin(rot)*tmpPos2.j) + p.i;
00674     p2.j = (sin(rot)*tmpPos2.i + cos(rot)*tmpPos2.j) + p.j;
00675 
00676     drawLine(dst, (Point2D<int>)p1, (Point2D<int>)p2, col, rad);
00677   }
00678 
00679 }
00680 
00681 
00682 // ######################################################################
00683 template <class T>
00684 void drawArrow(Image<T>& dst,
00685                const Point2D<int>& p1, const Point2D<int>& p2,
00686                const T col, const int rad)
00687 {
00688 GVX_TRACE(__PRETTY_FUNCTION__);
00689   // first draw a line connecting p1 to p2:
00690   drawLine(dst, p1, p2, col, rad);
00691   // second, draw an arrow:
00692   Point2D<int> pm; pm.i = (p1.i + p2.i) / 2; pm.j = (p1.j + p2.j) / 2;
00693   float norm = float(std::max(dst.getWidth(), dst.getHeight())) / 30.0F; // arrow size
00694   float angle = atan2((float)(p2.j - p1.j), (float)(p2.i - p1.i));
00695   float arrow_angle = 20.0F * M_PI / 180.0F;
00696   Point2D<int> pp;
00697   pp.i = pm.i - int(norm * cos(angle + arrow_angle));
00698   pp.j = pm.j - int(norm * sin(angle + arrow_angle));
00699   drawLine(dst, pm, pp, col, rad);
00700   pp.i = pm.i - int(norm * cos(angle - arrow_angle));
00701   pp.j = pm.j - int(norm * sin(angle - arrow_angle));
00702   drawLine(dst, pm, pp, col, rad);
00703 }
00704 
00705 // ######################################################################
00706 template <class T>
00707 void drawGrid(Image<T>& dst,
00708               const int spacingX, const int spacingY,
00709               const int thickX, const int thickY,
00710               const T col)
00711 {
00712 GVX_TRACE(__PRETTY_FUNCTION__);
00713   ASSERT(dst.initialized());
00714   for (int i = thickX / 2; i < dst.getWidth() - thickX / 2; i += spacingX)
00715     for (int j = 0; j < dst.getHeight(); j ++)
00716       for (int t = -thickX / 2; t <= thickX / 2; t ++)
00717         dst.setVal(i + t, j, col);
00718 
00719   for (int j = thickY / 2; j < dst.getHeight() - thickY / 2; j += spacingY)
00720     for (int i = 0; i < dst.getWidth(); i ++)
00721       for (int t = -thickY / 2; t <= thickY / 2; t ++)
00722         dst.setVal(i, t + j, col);
00723 }
00724 
00725 // ######################################################################
00726 template <class T, class TT>
00727 void drawContour2D(const Image<T>& src, Image<TT>& dst, const TT& col, const byte rad)
00728 {
00729   // this code should be in sync with contour2D() in Image/Transforms.C
00730   ASSERT(src.initialized());
00731   ASSERT(dst.initialized());
00732   ASSERT(src.isSameSize(dst));
00733 
00734   typename Image<T>::const_iterator sptr = src.begin();
00735   T z = T(); const int h = src.getHeight(), w = src.getWidth();
00736 
00737   // This is a 4-connected contour algorithm. We are on the contour if we are not zero but at least: either 1) one of
00738   // our 4-neighbors is zero, or 2) we are on an edge of the image. Here we unfold the algo for max speed, i.e., we
00739   // treat the borders of the image explicitly and then the bulk:
00740   Point2D<int> p(0, 0);
00741 
00742   // first row:
00743   for (p.i = 0; p.i < w; ++p.i) if (*sptr++) drawDisk(dst, p, rad, col);
00744 
00745   // done if the image only has 1 row:
00746   if (h == 1) return;
00747 
00748   // bulk, will only run if image has >2 rows:
00749   for (p.j = 1; p.j < h-1; ++p.j)
00750     {
00751       // leftmost pixel of current row:
00752       p.i = 0; if (*sptr++) drawDisk(dst, p, rad, col);
00753 
00754       // done if image has only 1 column:
00755       if (w == 1) continue;
00756 
00757       // bulk of current row, will run only if image has >2 columns:
00758       for (p.i = 1; p.i < w-1; ++p.i) {
00759         if (*sptr && (sptr[-1] == z || sptr[1] == z || sptr[-w] == z || sptr[w] == z)) drawDisk(dst, p, rad, col);
00760         ++sptr;
00761       }
00762 
00763       // rightmost pixel of current row, we know it exists since image has at least 2 columns:
00764       p.i = w-1; if (*sptr++) drawDisk(dst, p, rad, col);
00765     }
00766 
00767   // last row (we know it exists since image has at least 2 rows):
00768   p.j = h-1; for (p.i = 0; p.i < w; ++p.i) if (*sptr++) drawDisk(dst, p, rad, col);
00769 }
00770 
00771 // ######################################################################
00772 template <class T>
00773 void drawGrid(Image<T>& dst, const uint nx, const uint ny, const int thick, const T col)
00774 {
00775 GVX_TRACE(__PRETTY_FUNCTION__);
00776   ASSERT(dst.initialized());
00777 
00778   const float wx = float(dst.getWidth()) / float(nx), hy = float(dst.getHeight()) / float(ny);
00779   for (uint j = 0; j < ny; ++j)
00780     {
00781       const int py1 = int(hy*(j)+0.49999F);
00782       const int py2 = int(hy*(j+1)+0.49999F);
00783       for (uint i = 0; i < nx; ++i)
00784         {
00785           const int px1 = int(wx*(i)+0.49999F);
00786           const int px2 = int(wx*(i+1)+0.49999F);
00787           drawLine(dst, Point2D<int>(px1, py2-1), Point2D<int>(px2-1, py2-1), col, thick);
00788           drawLine(dst, Point2D<int>(px2-1, py1), Point2D<int>(px2-1, py2-1), col, thick);
00789       }
00790     }
00791   drawLine(dst, Point2D<int>(0, 0), Point2D<int>(dst.getWidth()-1, 0), col, thick);
00792   drawLine(dst, Point2D<int>(0, 0), Point2D<int>(0, dst.getHeight()-1), col, thick);
00793 }
00794 
00795 // ######################################################################
00796 template <class T>
00797 void writeText(Image<T>& dst,
00798                const Point2D<int>& pt, const char* text,
00799                const T col, const T bgcol, const SimpleFont& f,
00800                const bool transparent_bg,
00801                const TextAnchor anchor)
00802 {
00803 GVX_TRACE(__PRETTY_FUNCTION__);
00804 
00805   const int textwidth = strlen(text) * f.w();
00806   const int textheight = f.h();
00807 
00808   const Point2D<int> top_left
00809     =    anchor == ANCHOR_BOTTOM_RIGHT ?    pt - Point2D<int>(textwidth, textheight)
00810     :    anchor == ANCHOR_BOTTOM_LEFT  ?    pt - Point2D<int>(0, textheight)
00811     :    anchor == ANCHOR_TOP_RIGHT    ?    pt - Point2D<int>(textwidth, 0)
00812     : /* anchor == ANCHOR_TOP_LEFT     ? */ pt;
00813 
00814   Point2D<int> p = top_left; // copy for modif
00815   const int ww = dst.getWidth(), hh = dst.getHeight();
00816   const int len = int(strlen(text));
00817 
00818   for (int i = 0; i < len; i ++)
00819     {
00820       const unsigned char *ptr = f.charptr(text[i]);
00821 
00822       for (int y = 0; y < int(f.h()); y ++)
00823         for (int x = 0; x < int(f.w()); x ++)
00824           if (p.i + x >= 0 && p.i + x < ww && p.j + y >= 0 && p.j + y < hh)
00825             {
00826               if (!ptr[y * f.w() + x])
00827                 dst.setVal(p.i + x, p.j + y, col);
00828               else if (!transparent_bg)
00829                 dst.setVal(p.i + x, p.j + y, bgcol);
00830             }
00831       p.i += f.w();
00832     }
00833 }
00834 
00835 // ######################################################################
00836 template <class T>
00837 Image<T> makeMultilineTextBox(const int w,
00838                               const std::string* lines,
00839                               const size_t nlines,
00840                               const T col,
00841                               const T bg,
00842                               const size_t max_chars_per_line_hint,
00843                               const int fontwidth)
00844 {
00845   size_t maxchars = 0;
00846 
00847   if (max_chars_per_line_hint > 0)
00848     maxchars = max_chars_per_line_hint;
00849   else
00850     for (size_t i = 0; i < nlines; ++i)
00851       if (lines[i].length() > maxchars)
00852         maxchars = lines[i].length();
00853 
00854   if (maxchars > 0)
00855     {
00856       const SimpleFont font =
00857         SimpleFont::fixedMaxWidth(fontwidth ? fontwidth : size_t(w)/maxchars);
00858 
00859       Image<T> textarea(w, 4 + nlines*(font.h()+2), ZEROS);
00860 
00861       for (size_t i = 0; i < nlines; ++i)
00862         writeText(textarea, Point2D<int>(1,1+i*(font.h()+2)),
00863                   lines[i].c_str(),
00864                   col, bg,
00865                   font);
00866 
00867       return textarea;
00868     }
00869 
00870   return Image<T>();
00871 }
00872 
00873 // ######################################################################
00874 template <class T>
00875 void drawPoint(Image<T>& dst,
00876                int X,int Y,T pix)
00877 {
00878 GVX_TRACE(__PRETTY_FUNCTION__);
00879   dst.setVal(X,Y,pix);
00880   if(X > 0) dst.setVal((X-1),Y,pix);
00881   if(Y > 0) dst.setVal(X,(Y-1),pix);
00882   if(X < (dst.getWidth()-1)) dst.setVal((X+1),Y,pix);
00883   if(Y < (dst.getHeight()-1)) dst.setVal(X,(Y+1),pix);
00884 }
00885 
00886 // ######################################################################
00887 template <class T>
00888 int drawDiskCheckTarget(Image<T>& dst,
00889                         Image<T>& mask,
00890                         const Point2D<int>& center,
00891                         const int radius,
00892                         const T value,
00893                         const T targetvalue,
00894                         const T floodvalue)
00895 {
00896 GVX_TRACE(__PRETTY_FUNCTION__);
00897   ASSERT(dst.initialized());
00898   ASSERT(floodvalue < targetvalue);
00899   int nbhit = 0; Point2D<int> hitpt;
00900   for (int y = -radius; y <= radius; y ++)
00901     {
00902       int bound = (int)(sqrt( (double)( squareOf(radius) - squareOf(y) ) ));
00903       for (int x = -bound; x <= bound; x ++)
00904         if (dst.coordsOk(x + center.i, y + center.j))
00905           {
00906             dst.setVal(x + center.i, y + center.j, value);
00907             if (mask.getVal(x + center.i, y + center.j) == targetvalue)
00908               {
00909                 nbhit ++;
00910                 hitpt.i = x + center.i, hitpt.j = y + center.j;
00911                 // flood the hit target:
00912                 Image<T> tmp = mask;
00913                 flood(tmp, mask, hitpt, targetvalue, floodvalue);
00914               }
00915           }
00916     }
00917   return nbhit;
00918 }
00919 
00920 // ######################################################################
00921 template <class T>
00922 Image<PixRGB<T> > warp3D(const Image<PixRGB<T> >& ima,
00923                          const Image<float>& zmap,
00924                          const float pitch, const float yaw,
00925                          const float zMax, Dims& dims)
00926 {
00927 GVX_TRACE(__PRETTY_FUNCTION__);
00928   ASSERT(ima.isSameSize(zmap));
00929 
00930   float cp = float(cos(double(pitch) * M_PI / 180.0));
00931   float sp = float(sin(double(pitch) * M_PI / 180.0));
00932   float cy = float(cos(double(yaw) * M_PI / 180.0));
00933   float sy = float(sin(double(yaw) * M_PI / 180.0));
00934   int iw = ima.getWidth(), ih = ima.getHeight();
00935 
00936   // compute coords of bounding box to determine 3D image size
00937   int xmin = 0, zmin = 0, xmax = 0, zmax = 0, xx, zz;
00938   // (0, 0, 0) is implicitly done: yields (0,0)
00939   // (0, 0, zMax)
00940   zz = int(-cp * zMax);
00941   zmin = std::min(zmin, zz);
00942   zmax = std::max(zmax, zz);
00943   // (0,h,0)
00944   xx = int(sy * ih);
00945   xmin = std::min(xmin, xx);
00946   xmax = std::max(xmax, xx);
00947   zz = int(- cy * sp * ih);
00948   zmin = std::min(zmin, zz);
00949   zmax = std::max(zmax, zz);
00950   // (0, h, zMax)
00951   zz = int(- cy * sp * ih - cp * zMax);
00952   zmin = std::min(zmin, zz);
00953   zmax = std::max(zmax, zz);
00954   // (w, 0, 0)
00955   xx = int(cy * iw);
00956   xmin = std::min(xmin, xx);
00957   xmax = std::max(xmax, xx);
00958   zz = int(sy * sp * iw);
00959   zmin = std::min(zmin, zz);
00960   zmax = std::max(zmax, zz);
00961   // (w, 0, zMax)
00962   zz = int(sy * sp * iw - cp * zMax);
00963   zmin = std::min(zmin, zz);
00964   zmax = std::max(zmax, zz);
00965   // (w, h, 0)
00966   xx = int(cy * iw + sy * ih);
00967   xmin = std::min(xmin, xx);
00968   xmax = std::max(xmax, xx);
00969   zz = int(sy * sp * iw - cy * sp * ih);
00970   zmin = std::min(zmin, zz);
00971   zmax = std::max(zmax, zz);
00972   // (w, h, zMax)
00973   zz = int(sy * sp * iw - cy * sp * ih - cp * zMax);
00974   zmin = std::min(zmin, zz);
00975   zmax = std::max(zmax, zz);
00976 
00977   int nw, nh;
00978   if (dims.isEmpty())  // need to compute image size
00979     {
00980       nw = ((xmax - xmin) * 12) / 10;
00981       nh = ((zmax - zmin) * 16) / 10;  // height is oversized: place_image_3D
00982       //LDEBUG("image Width=%d, Height=%d", nw, nh);
00983       dims = Dims(nw, nh);
00984     }
00985   else  // image size known; just place the 3D in it
00986     {
00987       nw = dims.w(); nh = dims.h();
00988     }
00989   Image<PixRGB<T> > result(nw, nh, ZEROS);  // set background to black
00990 
00991   int xoff = -xmin + nw / 10;  // center image
00992   int zoff = -zmin + nh / 10;
00993 
00994   // store previously drawn points, for gap filling
00995   int *prev_zz = new int[nw]; memset(prev_zz, 0, nw * sizeof(int));
00996   int *prev_xx = new int[nw]; memset(prev_xx, 0, nw * sizeof(int));
00997   int ppzz = 0, ppxx = 0;
00998   for (int j = 0; j < ih; j ++)
00999     for (int i = 0; i < iw; i ++)
01000       {
01001         float x = float(i);
01002         float y = float(j);
01003         float z = - zmap.getVal(i, j);   // z-axis pointing downwards
01004 
01005         // rotate and project
01006         xx = xoff + int(cy * x + sy * y);
01007         zz = zoff + int(sy * sp * x - cy * sp * y + cp * z);
01008         xx = clampValue(xx, 0, nw-1);
01009         zz = clampValue(zz, 0, nh-1);
01010 
01011         // check if some gap-filling is needed due to high slopes
01012         PixRGB<T> pix, col;
01013         bool drawn = false;
01014         ima.getVal(i, j, col);  // color of current pixel
01015         if (j > 0 && zz > prev_zz[i] + 1)  // we are much lower than (i, j-1)
01016           {
01017             result.getVal(prev_xx[i], prev_zz[i], pix);  // save this pixel
01018             drawLine(result, Point2D<int>(xx, zz),
01019                      Point2D<int>(prev_xx[i], prev_zz[i]),
01020                      col);
01021             result.setVal(prev_xx[i], prev_zz[i], pix);
01022             drawn = true;
01023           }
01024         if (i > 0 && j > 0 && zz > ppzz + 1) // much lower than (i-1, j-1)
01025           {
01026             result.getVal(ppxx, ppzz, pix);  // save this pixel
01027             drawLine(result, Point2D<int>(xx, zz),
01028                      Point2D<int>(ppxx, ppzz),
01029                      col);
01030             result.setVal(ppxx, ppzz, pix);
01031             drawn = true;
01032           }
01033         if (i > 0 && (zz > prev_zz[i-1] + 1||   // much lower than (i-1, j)
01034                       zz < prev_zz[i-1] - 1))    // much higher than (i-1, j)
01035           {
01036             result.getVal(prev_xx[i-1], prev_zz[i-1], pix);  // save this pixel
01037             drawLine(result, Point2D<int>(xx, zz),
01038                      Point2D<int>(prev_xx[i-1], prev_zz[i-1]),
01039                      col);
01040             result.setVal(prev_xx[i-1], prev_zz[i-1], pix);
01041             drawn = true;
01042           }
01043         if (drawn == false)     // no line to draw, only one point
01044           result.setVal(xx, zz, col);
01045 
01046         ppzz = prev_zz[i]; ppxx = prev_xx[i];
01047         prev_zz[i] = zz; prev_xx[i] = xx;
01048       }
01049   delete [] prev_zz; delete [] prev_xx;
01050 
01051   return result;
01052 }
01053 
01054 // ######################################################################
01055 Image< PixRGB<byte> >
01056 formatMapForDisplay(const Image<float>& img, const float factor,
01057                     const Dims& newdims, const bool useInterp,
01058                     const ColorMap& cmap, const bool showColorScale,
01059                     const char *label)
01060 {
01061 GVX_TRACE(__PRETTY_FUNCTION__);
01062 
01063   Image<float> ftmp = img;
01064 
01065   // if the image is empty, make it black:
01066   if (ftmp.initialized() == false) ftmp.resize(newdims, true);
01067 
01068   // start by normalizing the map values:
01069   float omi, oma;
01070   if (factor != 0.0F) { getMinMax(ftmp, omi, oma); ftmp *= factor; }
01071   else inplaceNormalize(ftmp, 0.0F, 255.0F, omi, oma);
01072 
01073   // convert to byte, which may clamp out-of-range values:
01074   Image<byte> btmp = ftmp;
01075 
01076   // colorize using the desired colormap:
01077   Image< PixRGB<byte> > ctmp = colorize(btmp, cmap);
01078 
01079   // rescale the image to the desired new dims:
01080   ctmp = rescaleOpt(ctmp, newdims, useInterp);
01081   const uint w = ctmp.getWidth(), h = ctmp.getHeight();
01082   const SimpleFont f = SimpleFont::FIXED(6);
01083   int ty = h - f.h() - 1;
01084 
01085   // draw a border:
01086   drawRect(ctmp, Rectangle::tlbrI(0, 0, h-1, w-1), PixRGB<byte>(64, 64, 255), 1);
01087 
01088   // display color scale if desired:
01089   if (showColorScale)
01090     {
01091       const float fac = float(cmap.getWidth()) / float(w);
01092       for (uint i = 0; i < w; i ++)
01093         drawLine(ctmp, Point2D<int>(i, h-4), Point2D<int>(i, h-1), cmap.getVal(int(i * fac)), 1);
01094       ty -= 4;
01095     }
01096 
01097   // Write label using large font, if we have one:
01098   if (label) writeText(ctmp, Point2D<int>(2,2), label, PixRGB<byte>(128,255,128),
01099                        PixRGB<byte>(0), SimpleFont::FIXED(10), true);
01100 
01101   // write original value range using tiny font:
01102   std::string smin = sformat("%0.02g", omi);
01103   std::string smax = sformat("%0.02g", oma);
01104 
01105   // if we used a factor, clamping may have occurred, hence let's
01106   // indicate that by adding "<" and ">" before the range values:
01107   if (factor)
01108     {
01109       smin = std::string("<") + smin;
01110       smax = std::string(">") + smax;
01111     }
01112 
01113   // add space before and after to avoid clutter:
01114   smin = std::string(" ") + smin + std::string(" ");
01115   smax = std::string(" ") + smax + std::string(" ");
01116 
01117   // do we have room to write all that?
01118   if (w >= f.w() * (smin.length() + smax.length()))
01119     {
01120       const PixRGB<byte> tcol(255, 255, 128);
01121       const PixRGB<byte> bcol(1);
01122 
01123       writeText(ctmp, Point2D<int>(1, ty), smin.c_str(), tcol, bcol, f);
01124       writeText(ctmp, Point2D<int>(w - smax.length()*f.w() - 1, ty), smax.c_str(), tcol, bcol, f);
01125     }
01126 
01127   return ctmp;
01128 }
01129 
01130 // ###############################################################
01131 template <typename T, class U>
01132 Image<PixRGB<byte> > linePlot(const U& points, const int w,const int h, 
01133                               const T& minVal, const T& maxVal,
01134                               const char *title,
01135                               const char *ylabel,
01136                               const char *xlabel,
01137                               const PixRGB<byte>& linecol,
01138                               const PixRGB<byte>& bckcol,
01139                               const int numticks,
01140                               const bool axisonright)
01141 {
01142 
01143 
01144   const int vecLength = points.size();
01145   T maxv(maxVal), minv(minVal);
01146 
01147   //find our min and max range
01148   if (minVal == 0 && maxVal == 0)
01149     {
01150       typename U::const_iterator iter = points.begin();
01151       while (iter != points.end())
01152         {
01153           if (*iter > maxv) 
01154             maxv = *iter;
01155           if (*iter < minv) 
01156             minv = *iter;
01157           ++iter;
01158         }
01159     }
01160   //fonts
01161   SimpleFont sf = SimpleFont::fixedMaxHeight(uint(0.125F*h)); //axis labels 
01162   SimpleFont sn = SimpleFont::fixedMaxHeight(uint(0.075F*h)); //tick labels
01163   std::string ymaxstr = sformat("%1.2f",(float)maxv);
01164   std::string yminstr = sformat("%1.2f",(float)minv);
01165   
01166   //border between image corner and y-axis
01167   int labelsize = ymaxstr.size() > yminstr.size() ? ymaxstr.size() : yminstr.size();
01168   
01169   //make our border has room for at least two sig figs, and four
01170   //positions for value and sign
01171   if (labelsize < 7)
01172     labelsize = 7;
01173 
01174   int hborder = (int)(labelsize * sn.w() + sf.w() * 1.5F);
01175   int vborder = (int)(sn.h() + sf.h() * 1.5F);
01176 
01177   //line thickness 
01178   const int thickness =  (h / 90) > 0 ? (h / 90) : 1;
01179 
01180   //create image with opposite axis as we will transpose after we draw
01181   //the y axis
01182   Image<PixRGB<byte> > result(h, w, ZEROS);
01183   result += bckcol;
01184 
01185   //lets make the axes
01186   Point2D<float> origin(axisonright ? w - hborder : hborder, h - vborder);
01187   Point2D<float> originraw(hborder, h - vborder);
01188   Point2D<float> xlim(!axisonright ? w - hborder : hborder, h - vborder);
01189   Point2D<float> ylim(axisonright ? w - hborder : hborder, vborder);
01190   float xpos, ypos;
01191   //write labels
01192   if (strlen(ylabel) > 0)
01193     {
01194       xpos = origin.j + (ylim.j - origin.j) / 2.0F - 
01195         sf.w() * strlen(ylabel) / 2.0F;
01196       if (axisonright)
01197         ypos = ylim.i  + sn.h() + sn.h() / 2.0F;
01198       else
01199         ypos = vborder - sn.h() - sn.h() / 2.0F;
01200       writeText(result,Point2D<int>((int)xpos, (int)ypos), ylabel, 
01201                 linecol, bckcol, sf);//ylabel
01202       result = flipVertic(transpose(result));
01203     }
01204      
01205   if (strlen(title) > 0)
01206     {
01207       xpos = origin.i + (xlim.i - origin.i)/2.0F - 
01208         sf.w() * strlen(title) / 2.0F;
01209       ypos = vborder - 1.25 * sf.h();
01210       writeText(result, Point2D<int>((int)xpos, (int)ypos),
01211                 title, linecol, bckcol, sf);//title 
01212     }
01213 
01214   if(strlen(xlabel) > 0)
01215     {
01216       xpos = origin.i + (xlim.i - origin.i) / 2.0F - 
01217         sf.w() * strlen(xlabel) / 2.0F;
01218       ypos = origin.j + sn.h() + sn.h() / 2.0F;
01219       writeText(result,Point2D<int>( (int)xpos,(int)ypos),
01220                 xlabel, linecol, bckcol, sf);//xlabel
01221     }
01222   
01223   if (numticks >= 0)  
01224     {
01225       if (axisonright)
01226         xpos = ylim.i + sn.w() * 2.0F;
01227       else
01228         xpos = hborder - (sn.w() * ymaxstr.size() + sn.w() * 2.0F);
01229       ypos = ylim.j;
01230       writeText(result, Point2D<int>((int)xpos,(int)ypos),
01231                 ymaxstr.c_str(), linecol, bckcol, sn);//ymax
01232 
01233       if (axisonright)
01234         xpos = ylim.i + sn.w() * 2.0F;
01235       else
01236         xpos = hborder - (sn.w() * yminstr.size() + sn.w() * 2.0F);
01237       ypos = ylim.j;      
01238       ypos = origin.j - sn.h();
01239       writeText(result, Point2D<int>((int)xpos, (int)ypos),
01240                 yminstr.c_str(), linecol, bckcol, sn);//ymin
01241       
01242       //Fill the x-axis with 'numticks' equally spaced tick marks. 
01243       float tspace = (xlim.i - origin.i)/(float)numticks;//tick spacing (pixel)
01244       for (int ii = 0; ii <= numticks; ++ii)
01245         {
01246           //tickmark
01247           xpos =  tspace * ii + origin.i; //xpos
01248           Point2D<int> tick((int)xpos, (int)xlim.j), 
01249             ticke((int)xpos, int(tick.j - sn.h()));
01250           drawLine(result, tick,  ticke, linecol, thickness);
01251           
01252           //draw label
01253           float xval = (float)vecLength / (float)numticks * (float)ii;
01254           std::string ticklabel(sformat("%1.2f", xval));
01255           int offset = strlen(ticklabel.c_str())*(int)sn.w()/2;
01256           tick.i -= offset; tick.j += sn.h() / 2;
01257           //time point
01258           writeText(result,tick,ticklabel.c_str(), linecol, bckcol, sn);
01259         }
01260     }
01261   //draw axis
01262   Point2D<int> origin_int((int)origin.i,(int)origin.j);
01263   Point2D<int> originraw_int((int)originraw.i,(int)originraw.j);
01264   Point2D<int> xlim_int((int)xlim.i,(int)xlim.j);
01265   Point2D<int> ylim_int((int)ylim.i,(int)ylim.j);
01266   
01267   drawLine(result, origin_int, xlim_int, linecol, thickness);
01268   drawLine(result, origin_int, ylim_int, linecol , thickness);
01269 
01270   //generate and rescale vector of points to be plotted
01271   T   newMaxY = h - vborder, newMinY = vborder;
01272   T   newMaxX = w - hborder, newMinX = hborder;
01273   int   xPoints[vecLength], yPoints[vecLength];
01274   
01275   for (int i = 0; i < vecLength; i++)
01276     {
01277       xPoints[i] = (int)((newMaxX - newMinX) * (float)i / vecLength + newMinX);
01278 
01279       T p = points[i];
01280       if (points[i] < minv) p = minv;
01281       else if (points[i] > maxv) p = maxv;
01282       
01283       yPoints[i] = (int)((newMaxY - newMinY) * (p - minv)/(maxv - minv) + newMinY + .5);
01284       
01285       if (i == 0)
01286         drawLine(result, originraw_int, 
01287                  Point2D<int>(xPoints[i],(int)(newMaxY - yPoints[i] + vborder)), linecol, thickness);
01288       else
01289         drawLine(result,
01290                  Point2D<int>(xPoints[i-1],(int)(newMaxY - yPoints[i-1] + vborder)),
01291                  Point2D<int>(xPoints[i], (int)(newMaxY - yPoints[i] + vborder)),
01292                  linecol, thickness);
01293     }
01294   return result;
01295 }
01296 
01297 // ###############################################################
01298 template <typename T>
01299 Image<PixRGB<byte> > multilinePlot(const std::vector<std::vector<T> >& lines, const int w,
01300                               const int h,T minVal,  T maxVal,
01301                               const char *title,
01302                               const char *ylabel,
01303                               const char *xlabel,
01304                               const std::vector<PixRGB<byte> >& linescolor,
01305                               const PixRGB<byte>& gridcolor,
01306                               const PixRGB<byte>& bckcolor)
01307 {
01308 
01309         //        std::vector<T> points = lines[0];
01310         //find our min and max range
01311         int defvecLength = 0;
01312 
01313         if(lines.size() !=0){
01314 
01315                 defvecLength = lines[0].size();
01316                 for(size_t i=0; i<lines.size(); i++)
01317                         defvecLength = std::min(defvecLength, (int)lines[i].size());
01318 
01319                 if (defvecLength != 0 && minVal == 0 && maxVal == 0)
01320                 {
01321                         maxVal = lines[0][0];
01322                         for(int l = 0; l < (int)lines.size();l++)
01323                         {
01324 
01325                                 std::vector<T> points = lines[l];
01326                                 const int vecLength = points.size();
01327                                 for(int i = 0; i < vecLength ; i++)
01328                                         if(points[i] > maxVal)
01329                                                 maxVal = points[i];
01330 
01331                                 minVal    = points[0];
01332                                 for(int i = 0; i < vecLength ; i++)
01333                                         if(points[i] < minVal)
01334                                                 minVal = points[i];
01335                         }
01336                 }
01337         }
01338 
01339 
01340 
01341         //fonts
01342         SimpleFont sf = SimpleFont::fixedMaxHeight(int(.1*h));
01343         SimpleFont sn = SimpleFont::fixedMaxHeight(int(.01*h));
01344         std::string ymaxstr = sformat("%1.2f",(float)maxVal);
01345         std::string yminstr = sformat("%1.2f",(float)minVal);
01346 
01347         //border between image corner and y-axis
01348         int hborder;
01349         ymaxstr.size() > yminstr.size() ?
01350                 hborder = ymaxstr.size() : hborder = yminstr.size();
01351         //make our border has room for at least two sig figs, and four
01352         //positions for value and sign
01353         if (hborder < 7)
01354                 hborder = 7;
01355         hborder = (int)(hborder * sn.w() + sf.w()*1.5);
01356         int vborder = (int)(sf.h() * 1.5);
01357 
01358         //create image with opposite axis as we will transpose after we draw
01359         //the y axis
01360         Image<PixRGB<byte> > result(h, w, ZEROS);
01361         result += bckcolor;
01362 
01363         //lets make the axes
01364         Point2D<float> origin(hborder, h - vborder);
01365         Point2D<float> xlim(w-hborder, h - vborder);
01366         Point2D<float> ylim(hborder, vborder);
01367 
01368         //write labels
01369         float pos = origin.j + (ylim.j - origin.j)/2.0F -
01370                 float(sf.w()*strlen(ylabel))/2.0F;
01371         writeText(result,Point2D<int>((int)pos, (int)(vborder - 1.25*sf.h())),
01372                         ylabel, gridcolor, bckcolor, sf);//ylabel
01373         result = flipVertic(transpose(result));
01374 
01375         writeText(result,Point2D<int>(hborder-sn.w()*ymaxstr.size()-1,
01376                                 (int)ylim.j-sn.h()),
01377                         ymaxstr.c_str(), gridcolor, bckcolor, sn);//ymax
01378 
01379         writeText(result,Point2D<int>(hborder - sn.w() * yminstr.size() -1,
01380                                 (int)origin.j),
01381                         yminstr.c_str(), gridcolor, bckcolor, sn);//ymin
01382 
01383         pos = origin.i + (xlim.i - origin.i)/2.0F - float(sf.w()*strlen(title))/2;
01384         writeText(result, Point2D<int>( (int)pos, (int)(vborder - 1.25*sf.h())),
01385                         title, gridcolor, bckcolor, sf);//title
01386 
01387         pos = origin.i + (xlim.i - origin.i)/2.0F - float(sf.w()*strlen(xlabel))/2.0F;
01388         writeText(result,Point2D<int>( (int)pos,(int)( origin.j + 1.1 * sf.h())),
01389                         xlabel, gridcolor, bckcolor, sf);//xlabel
01390 
01391         //draw axis
01392         Point2D<int> origin_int((int)origin.i,(int)origin.j),
01393                 xlim_int((int)xlim.i,(int)xlim.j),
01394                 ylim_int((int)ylim.i,(int)ylim.j);
01395 
01396         //generate and rescale vector of points to be plotted
01397         T   newMaxY = h - vborder, newMinY = vborder;
01398         T   newMaxX = w - hborder, newMinX = hborder;
01399         int   xPoints[defvecLength], yPoints[defvecLength];
01400 
01401         for(int j = 0; j < (int)lines.size(); j++)
01402         {
01403 
01404                 //Check line color vector is available
01405                 PixRGB<byte> linecolor(255,255,255) ;
01406                 if(j < (int)linescolor.size())
01407                         linecolor = linescolor[j];
01408 
01409                 for (int i = 0; i < defvecLength; i++)
01410                 {
01411                         xPoints[i] = (int)((newMaxX - newMinX) * (float)i / defvecLength + newMinX);
01412                         yPoints[i] = (int)((newMaxY - newMinY) *
01413                                         (lines[j][i] - minVal)/(maxVal - minVal) + newMinY + .5);
01414 
01415                         if (i == 0)
01416                                 drawLine(result, origin_int,
01417                                                 Point2D<int>(xPoints[i],(int)( newMaxY - yPoints[i] + vborder)),
01418                                                 linecolor, 1);
01419                         else
01420                                 drawLine(result,
01421                                                 Point2D<int>(xPoints[i-1],(int)(newMaxY - yPoints[i-1] + vborder)),
01422                                                 Point2D<int>(xPoints[i], (int)(newMaxY - yPoints[i] + vborder)),
01423                                                 linecolor, 1);
01424                 }
01425         }
01426         //draw axis
01427         drawLine(result, origin_int, xlim_int, gridcolor, 1);
01428         drawLine(result, origin_int, ylim_int, gridcolor , 1);
01429 
01430         return result;
01431 }
01432 
01433 
01434 // ######################################################################
01435 template <class T>
01436 void drawOutlinedPolygon(Image<T>& img, const std::vector<Point2D<int> >& polygon,
01437                          const T col,
01438                          const Point2D<int> trans,
01439                          const float rot,
01440                          const float scale,
01441                          const float k1,
01442                          const float k2,
01443                          const int rad)
01444 {
01445 GVX_TRACE(__PRETTY_FUNCTION__);
01446  for(uint i = 0; i<polygon.size(); i++)
01447    {
01448      Point2D<float> p1 = (Point2D<float>)polygon[i];
01449      Point2D<float> p2 = (Point2D<float>)polygon[(i+1)%polygon.size()];
01450 
01451 
01452      Point2D<float> tmpPos1;
01453      Point2D<float> tmpPos2;
01454      //Sheer
01455      tmpPos1.i = p1.i + p1.j*k1;
01456      tmpPos1.j = p1.i*k2 + p1.j;
01457 
01458      tmpPos2.i = p2.i + p2.j*k1;
01459      tmpPos2.j = p2.i*k2 + p2.j;
01460 
01461      //Rotate and move to p
01462      p1.i = scale*(cos(rot)*tmpPos1.i - sin(rot)*tmpPos1.j) + trans.i;
01463      p1.j = scale*(sin(rot)*tmpPos1.i + cos(rot)*tmpPos1.j) + trans.j;
01464 
01465      p2.i = scale*(cos(rot)*tmpPos2.i - sin(rot)*tmpPos2.j) + trans.i;
01466      p2.j = scale*(sin(rot)*tmpPos2.i + cos(rot)*tmpPos2.j) + trans.j;
01467 
01468      drawLine(img, (Point2D<int>)p1,(Point2D<int>)p2, col, rad);
01469    }
01470 }
01471 
01472 // ######################################################################
01473 template <class T>
01474 void drawFilledPolygon(Image<T>& img, const std::vector<Point2D<int> >& polygon,
01475                        const T col)
01476 {
01477   //OLD Slow Method which iterates over all pixels
01478   const int w = static_cast<int>(img.getWidth());
01479   const int h = static_cast<int>(img.getHeight());
01480   typename Image<T>::iterator iptr = img.beginw();
01481   //Point2D<int> p;
01482 
01483   //for (p.j = 0; p.j < h; p.j ++)
01484   //  for (p.i = 0; p.i < w; p.i ++)
01485   //    if (pnpoly(polygon, p)) *iptr++ = col; else iptr ++;
01486 
01487   //New method derived from pascal code by achalfin@uceng.uc.edu
01488   //http://www.bsdg.org/SWAG/EGAVGA/0170.PAS.html
01489 
01490   if (polygon.size() <= 0 ) return;
01491 
01492   int edgeCount = polygon.size();
01493   int num = polygon.size();
01494   int minY = polygon[0].j; 
01495   int startV1 = 0;
01496   for(uint c= 1; c < polygon.size(); c++) //Find Top Vertex
01497   {
01498     if (polygon[c].j < minY)
01499     {
01500       minY = polygon[c].j;
01501       startV1 = c;
01502     }
01503   }
01504   int startV2 = startV1;
01505   int endV1 = startV1 - 1;
01506   if(endV1 < 0) endV1 = (num-1);
01507 
01508   int endV2 = startV2 + 1;
01509   if (endV2 >= num) endV2 = 0;
01510 
01511   minY = polygon[startV1].j;
01512   int x1 = polygon[startV1].i; int y1 = polygon[startV1].j;
01513   int x2 = polygon[endV1].i; int y2 = polygon[endV1].j;
01514 
01515   int dx1 = ((x2 - x1) << 8) / (y2 - y1 + 1);
01516   int count1 = y2-y1;
01517   int xVal1 = x1 << 8;
01518 
01519   int x11 = polygon[startV2].i; int y11 = polygon[startV2].j;
01520   int x22 = polygon[endV2].i; int y22 = polygon[endV2].j;
01521 
01522   int dx2 = ((x22 - x11) << 8) / (y22 - y11 + 1);
01523   int count2 = y22-y11;
01524   int xVal2 = x11 << 8;
01525 
01526   while (edgeCount > 1)
01527   {
01528     while ( (count1 > 0) &&  (count2 > 0) )
01529     {
01530       //Draw the horizontal line
01531       for(int x = xVal1>>8; x <= xVal2>>8; x++)
01532         if (x >= 0 && x < w && minY >= 0 && minY < h)
01533           iptr[x + w * minY] = col;
01534 
01535       xVal1 += dx1; xVal2 += dx2;
01536       count1--; count2--;
01537       minY++;
01538     }
01539     if (count1 == 0)
01540     {
01541       edgeCount--;
01542       startV1 = endV1;
01543       endV1--;
01544       if (endV1 < 0) endV1 = num-1;
01545 
01546       minY = polygon[startV1].j;
01547       x1 = polygon[startV1].i; y1 = polygon[startV1].j;
01548       x2 = polygon[endV1].i; y2 = polygon[endV1].j;
01549       dx1 = ((x2 - x1) << 8) / (abs(y2 - y1) + 1);
01550       count1 = y2-y1;
01551       xVal1 = x1 << 8;
01552     }
01553     if (count2 == 0)
01554     {
01555       edgeCount--;
01556       startV2 = endV2;
01557       endV2++;
01558       if(endV2 >= num) endV2 = 0;
01559       minY = polygon[startV2].j;
01560       x11 = polygon[startV2].i; y11 = polygon[startV2].j;
01561       x22 = polygon[endV2].i; y22 = polygon[endV2].j;
01562       dx2 = ((x22 - x11) << 8) / (abs(y22 - y11) + 1);
01563       count2 = y22-y11;
01564       xVal2 = x11 << 8;
01565     }
01566   }
01567 }
01568 
01569 // ######################################################################
01570 Image<PixRGB<byte> > drawMeters(const MeterInfo* infos, const size_t ninfo,
01571                                 const size_t nx, const Dims& meterdims)
01572 {
01573   if (ninfo == 0) return Image<PixRGB<byte> >();
01574 
01575   ASSERT(meterdims.w() > 0 && meterdims.h() > 0);
01576 
01577   size_t maxlabelsize = infos[0].label.size();
01578   for (size_t i = 1; i < ninfo; ++i)
01579     if (infos[i].label.size() > maxlabelsize) maxlabelsize = infos[i].label.size();
01580 
01581   const SimpleFont f = SimpleFont::fixedMaxHeight(meterdims.h());
01582 
01583   const int meterx = f.w() * (maxlabelsize + 10);
01584   const int maxmeterlen = meterdims.w() - meterx;
01585 
01586   const size_t ny = (ninfo + nx-1) / nx;
01587 
01588   Image<PixRGB<byte> > result(meterdims.w() * nx, meterdims.h() * ny, ZEROS);
01589 
01590   for (size_t i = 0; i < ninfo; ++i) {
01591     const size_t ay = i % ny, ax = i / ny;
01592 
01593     const std::string txt = sformat("%*s %6.2e", int(maxlabelsize), infos[i].label.c_str(), infos[i].val);
01594 
01595     writeText(result, Point2D<int>(meterdims.w() * ax, meterdims.h() * ay),
01596               txt.c_str(), PixRGB<byte>(255), PixRGB<byte>(0), f);
01597 
01598     const int meterlen = clampValue(int(maxmeterlen * infos[i].val / infos[i].valmax), 1, maxmeterlen);
01599 
01600     const int threshlen =
01601       (infos[i].thresh < 0.0 || infos[i].thresh > infos[i].valmax) ?
01602       -1 : int(maxmeterlen * infos[i].thresh / infos[i].valmax);
01603 
01604     Image<PixRGB<byte> >::iterator itr = result.beginw()
01605       + meterdims.w()*ax + meterx + ay*meterdims.h()*result.getWidth();
01606 
01607     const int rowskip = result.getWidth() - maxmeterlen;
01608 
01609     const PixRGB<byte> c1(infos[i].color);
01610     const PixRGB<byte> c2(c1/2);
01611     const PixRGB<byte> c3(c2/3);
01612     const PixRGB<byte> c4(c3/2);
01613 
01614     for (int y = 0; y < meterdims.h()-1; ++y) {
01615       for (int x = 0; x < meterlen; ++x)
01616         *itr++ = (x == threshlen) ? PixRGB<byte>(255,255,255) : (x & 1) ? c2 : c1;
01617       for (int x = meterlen; x < maxmeterlen; ++x)
01618         *itr++ = (x == threshlen) ? PixRGB<byte>(255,255,255) : (x & 1) ? c4 : c3;
01619       itr += rowskip;
01620     }
01621   }
01622   return result;
01623 }
01624 
01625 
01626 // Include the explicit instantiations (color instantiations are now
01627 // requested by using "T_or_RGB" for the template formal parameter name in
01628 // the declarations in the .H file).
01629 #include "inst/Image/DrawOps.I"
01630 
01631 //explicit instantiations for line plot
01632 template
01633 Image<PixRGB<byte> > linePlot(const std::vector<double>& points, const int w,
01634                               const int h, const double& minVal, const double& maxVal,
01635                               const char *title,
01636                               const char *ylabel,
01637                               const char *xlabel,
01638                               const PixRGB<byte>& linecol,
01639                               const PixRGB<byte>& bckcol, 
01640                               const int numticks, const bool axisonright);
01641 
01642 template
01643 Image<PixRGB<byte> > linePlot(const std::deque<double>& points, const int w,
01644                               const int h, const double& minVal, const double& maxVal,
01645                               const char *title,
01646                               const char *ylabel,
01647                               const char *xlabel,
01648                               const PixRGB<byte>& linecol,
01649                               const PixRGB<byte>& bckcol, 
01650                               const int numticks, const bool axisonright);
01651 
01652 template
01653 Image<PixRGB<byte> > linePlot(const std::deque<float>& points, const int w,
01654                               const int h, const float& minVal, const float& maxVal,
01655                               const char *title,
01656                               const char *ylabel,
01657                               const char *xlabel,
01658                               const PixRGB<byte>& linecol,
01659                               const PixRGB<byte>& bckcol, 
01660                               const int numticks, const bool axisonright);
01661 
01662 template
01663 Image<PixRGB<byte> > linePlot(const std::vector<float>& points, const int w,
01664                               const int h, const float& minVal, const float& maxVal,
01665                               const char *title,
01666                               const char *ylabel,
01667                               const char *xlabel,
01668                               const PixRGB<byte>& linecol,
01669                               const PixRGB<byte>& bckcol, 
01670                               const int numticks, const bool axisonright);
01671 
01672 template
01673 Image<PixRGB<byte> > linePlot(const std::vector<int>& points, const int w,
01674                               const int h, const int& minVal, const int& maxVal,
01675                               const char *title,
01676                               const char *ylabel,
01677                               const char *xlabel,
01678                               const PixRGB<byte>& linecol,
01679                               const PixRGB<byte>& bckcol, 
01680                               const int numticks, const bool axisonright);
01681 
01682 // explicit instantiations for drawContour2D:
01683 template
01684 void drawContour2D(const Image<byte>& src, Image< PixRGB<byte> >& dst, const PixRGB<byte> &col, const byte rad);
01685 
01686 template
01687 void drawContour2D(const Image<byte>& src, Image<byte>& dst, const byte &col, const byte rad);
01688 
01689 template 
01690 Image<PixRGB<byte> > drawHistogram(std::vector<float> hist, int width, int height, PixRGB<byte> lineVal, PixRGB<byte> fillVal);
01691 // ######################################################################
01692 /* So things look consistent in everyone's emacs... */
01693 /* Local Variables: */
01694 /* indent-tabs-mode: nil */
01695 /* End: */
Generated on Sun May 8 08:05:07 2011 for iLab Neuromorphic Vision Toolkit by  doxygen 1.6.3