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: */