00001 /*!@file Image/ColorOps.C Color operations on Image 00002 */ 00003 00004 // //////////////////////////////////////////////////////////////////// // 00005 // The iLab Neuromorphic Vision C++ Toolkit - Copyright (C) 2001 by the // 00006 // University of Southern California (USC) and the iLab at USC. // 00007 // See http://iLab.usc.edu for information about this project. // 00008 // //////////////////////////////////////////////////////////////////// // 00009 // Major portions of the iLab Neuromorphic Vision Toolkit are protected // 00010 // under the U.S. patent ``Computation of Intrinsic Perceptual Saliency // 00011 // in Visual Environments, and Applications'' by Christof Koch and // 00012 // Laurent Itti, California Institute of Technology, 2001 (patent // 00013 // pending; application number 09/912,225 filed July 23, 2001; see // 00014 // http://pair.uspto.gov/cgi-bin/final/home.pl for current status). // 00015 // //////////////////////////////////////////////////////////////////// // 00016 // This file is part of the iLab Neuromorphic Vision C++ Toolkit. // 00017 // // 00018 // The iLab Neuromorphic Vision C++ Toolkit is free software; you can // 00019 // redistribute it and/or modify it under the terms of the GNU General // 00020 // Public License as published by the Free Software Foundation; either // 00021 // version 2 of the License, or (at your option) any later version. // 00022 // // 00023 // The iLab Neuromorphic Vision C++ Toolkit is distributed in the hope // 00024 // that it will be useful, but WITHOUT ANY WARRANTY; without even the // 00025 // implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // 00026 // PURPOSE. See the GNU General Public License for more details. // 00027 // // 00028 // You should have received a copy of the GNU General Public License // 00029 // along with the iLab Neuromorphic Vision C++ Toolkit; if not, write // 00030 // to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, // 00031 // Boston, MA 02111-1307 USA. // 00032 // //////////////////////////////////////////////////////////////////// // 00033 // 00034 // Primary maintainer for this file: Rob Peters <rjpeters@klab.caltech.edu> 00035 // $HeadURL: svn://isvn.usc.edu/software/invt/trunk/saliency/src/Image/ColorOps.C $ 00036 // $Id: ColorOps.C 14762 2011-05-03 01:13:16Z siagian $ 00037 // 00038 00039 #ifndef IMAGE_COLOROPS_C_DEFINED 00040 #define IMAGE_COLOROPS_C_DEFINED 00041 00042 #include "Image/ColorOps.H" 00043 00044 #include "Image/ColorMap.H" 00045 #include "Image/FilterOps.H" // for lowPass3() 00046 #include "Image/Image.H" 00047 #include "Image/MathOps.H" // for countThresh(), inplaceNormalize(), ... 00048 #include "Image/Pixels.H" 00049 #include "Image/ShapeOps.H" // for decXY() 00050 #include "Image/Transforms.H" // for dct() 00051 #include "Util/Assert.H" 00052 #include "Util/MathFunctions.H" 00053 #include "Util/log.H" 00054 #include "rutz/trace.h" 00055 00056 #include <algorithm> 00057 #include <cmath> 00058 00059 // ###################################################################### 00060 template <class T> 00061 Image<PixRGB<T> > makeRGB(const Image<T>& red, 00062 const Image<T>& green, 00063 const Image<T>& blue) 00064 { 00065 GVX_TRACE(__PRETTY_FUNCTION__); 00066 ASSERT(red.isSameSize(green) && red.isSameSize(blue)); 00067 00068 Image<PixRGB<T> > result(red.getDims(), NO_INIT); 00069 typename Image<PixRGB<T> >::iterator aptr = result.beginw(); 00070 typename Image<PixRGB<T> >::iterator stop = result.endw(); 00071 00072 typename Image<T>::const_iterator rptr = red.begin(); 00073 typename Image<T>::const_iterator gptr = green.begin(); 00074 typename Image<T>::const_iterator bptr = blue.begin(); 00075 00076 while (aptr != stop) 00077 { 00078 *aptr++ = PixRGB<T>(*rptr++, *gptr++, *bptr++); 00079 } 00080 00081 return result; 00082 } 00083 00084 // ###################################################################### 00085 Image< PixRGB<byte> > colorize(const Image<byte>& img, const ColorMap& cmap) 00086 { 00087 GVX_TRACE(__PRETTY_FUNCTION__); 00088 if (cmap.getWidth() != 256) LFATAL("Need a ColorMap with 256 entries"); 00089 00090 Image< PixRGB<byte> > result(img.getDims(), NO_INIT); 00091 Image<byte>::const_iterator src = img.begin(), stop = img.end(); 00092 Image< PixRGB<byte> >::iterator dst = result.beginw(); 00093 00094 const Image<PixRGB<byte> >::const_iterator cmapptr = cmap.begin(); 00095 00096 while (src != stop) 00097 *dst++ = cmapptr[*src++]; 00098 00099 return result; 00100 } 00101 00102 // ###################################################################### 00103 void inplaceColorSpeckleNoise(Image< PixRGB<byte> >& dest, const int num) 00104 { 00105 GVX_TRACE(__PRETTY_FUNCTION__); 00106 ASSERT(dest.initialized()); 00107 const int w = dest.getWidth(), h = dest.getHeight(); 00108 00109 for (int i = 0; i < num; ++i) 00110 { 00111 int x = randomUpToNotIncluding(w); 00112 int y = randomUpToNotIncluding(h); 00113 dest.setVal(x, y, 00114 PixRGB<byte>(randomUpToIncluding(255), 00115 randomUpToIncluding(255), 00116 randomUpToIncluding(255))); 00117 } 00118 } 00119 00120 // ###################################################################### 00121 template <class T> 00122 void getComponents(const Image<PixRGB<T> >& src, 00123 Image<T>& red, Image<T>& green, Image<T>& blue) 00124 { 00125 GVX_TRACE(__PRETTY_FUNCTION__); 00126 ASSERT(src.initialized()); 00127 00128 red = Image<T>(src.getDims(), NO_INIT); 00129 green = Image<T>(src.getDims(), NO_INIT); 00130 blue = Image<T>(src.getDims(), NO_INIT); 00131 00132 typename Image<T>::iterator rptr = red.beginw(); 00133 typename Image<T>::iterator gptr = green.beginw(); 00134 typename Image<T>::iterator bptr = blue.beginw(); 00135 00136 typename Image<PixRGB<T> >::const_iterator aptr = src.begin(); 00137 typename Image<PixRGB<T> >::const_iterator stop = src.end(); 00138 00139 while (aptr != stop) 00140 { 00141 *rptr++ = aptr->red(); 00142 *gptr++ = aptr->green(); 00143 *bptr++ = aptr->blue(); 00144 ++aptr; 00145 } 00146 } 00147 00148 // ###################################################################### 00149 Image<PixRGB<float> > normalizeRGPolar(const Image<float>& src, 00150 const float max, 00151 const float min) 00152 { 00153 GVX_TRACE(__PRETTY_FUNCTION__); 00154 return stainPosNeg(src, std::max(fabs(min), fabs(max)), 00155 PixRGB<float>(0.0f), // background 00156 PixRGB<float>(0.0f, 255.0f, 0.0f), // positive=green 00157 PixRGB<float>(255.0f, 0.0f, 0.0f));// negative=red 00158 } 00159 00160 // ###################################################################### 00161 Image<PixRGB<float> > normalizeRGPolarAuto(const Image<float>& src) 00162 { 00163 GVX_TRACE(__PRETTY_FUNCTION__); 00164 float max, min; 00165 getMinMax(src,min,max); 00166 return stainPosNeg(src, std::max(fabs(min), fabs(max)), 00167 PixRGB<float>(0.0f), // background 00168 PixRGB<float>(0.0f, 255.0f, 0.0f), // positive=green 00169 PixRGB<float>(255.0f, 0.0f, 0.0f));// negative=red 00170 } 00171 00172 // ###################################################################### 00173 Image<PixRGB<float> > normalizeWithScale(const Image<float>& src, 00174 const float min = 0.0F, 00175 const float max = 255.0F, 00176 const float clamp = 255.0F, 00177 const char baseColor = 1, 00178 const char normColor = 3) 00179 { 00180 ASSERT(src.initialized()); 00181 ASSERT(baseColor < 4); 00182 ASSERT(normColor < 4); 00183 ASSERT(normColor != baseColor); 00184 ASSERT(clamp > 0.0F); 00185 float mi,ma; 00186 getMinMax(src,mi,ma); 00187 const float scale = max - min; 00188 const float nscale = (ma - mi) / scale; 00189 Image<PixRGB<float> > outImage; 00190 outImage.resize(src.getWidth(),src.getHeight()); 00191 00192 Image<float>::const_iterator aptr = src.begin(); 00193 Image<float>::const_iterator stop = src.end(); 00194 Image<PixRGB<float> >::iterator optr = outImage.beginw(); 00195 while (aptr != stop) 00196 { 00197 if(*aptr > clamp || *aptr < 0.0F) 00198 (*optr).p[(uint)baseColor] = clamp; 00199 else 00200 (*optr).p[(uint)baseColor] = *aptr; 00201 (*optr).p[(uint)normColor] = min + ((*aptr - mi)*nscale); 00202 ++aptr; ++optr; 00203 } 00204 return outImage; 00205 } 00206 00207 // ###################################################################### 00208 Image<PixRGB<float> > normalizeScaleRainbow(const Image<float>& src, 00209 const float min = 0.0F, 00210 const float max = 255.0F) 00211 { 00212 ASSERT(src.initialized()); 00213 ASSERT(max > min); 00214 00215 Image<PixHSV<float> > scaleImage; 00216 00217 scaleImage.resize(src.getWidth(),src.getHeight()); 00218 00219 float mi, ma; 00220 getMinMax(src,mi,ma); 00221 const float interval = ma - mi; 00222 const float mult = 330.0/(max - min); 00223 00224 Image<PixHSV<float> >::iterator aptr = scaleImage.beginw(); 00225 Image<float>::const_iterator sptr = src.begin(); 00226 00227 // Set intensity as the normalized value of src 00228 // Set hue as the scale of the value of src with red as 0 and 00229 // violet as 330 00230 00231 while(sptr != src.end()) 00232 { 00233 if(*sptr < min) { aptr->setH(1); aptr->setS(1.0); aptr->setV(0); } 00234 else if(*sptr > max){ aptr->setH(1); aptr->setS(1.0); aptr->setV(255); } 00235 else 00236 { 00237 aptr->setH(mult * (*sptr - min)); 00238 aptr->setS(100.0); 00239 aptr->setV(((*sptr - mi)/interval) * 255.0); 00240 } 00241 ++aptr; ++sptr; 00242 } 00243 00244 Image<PixRGB<float> > dest; dest.resize(src.getWidth(),src.getHeight()); 00245 00246 aptr = scaleImage.beginw(); 00247 Image<PixRGB<float> >::iterator dptr = dest.beginw(); 00248 00249 while(aptr != scaleImage.end()) *dptr++ = PixRGB<float>(*aptr++); 00250 00251 return dest; 00252 } 00253 00254 // ###################################################################### 00255 template <class T> 00256 Image<PixRGB<T> > stain(const Image<T>& src, PixRGB<float> color) 00257 { 00258 GVX_TRACE(__PRETTY_FUNCTION__); 00259 Image<PixRGB<T> > result(src.getDims(), NO_INIT); 00260 00261 typename Image<T>::const_iterator sptr = src.begin(); 00262 typename Image<PixRGB<T> >::iterator dptr = result.beginw(), 00263 stop = result.endw(); 00264 00265 while (dptr != stop) 00266 { 00267 *dptr = PixRGB<T>(color * (*sptr)); 00268 ++sptr; 00269 ++dptr; 00270 } 00271 00272 return result; 00273 } 00274 00275 // ###################################################################### 00276 Image<PixRGB<float> > stainPosNeg(const Image<float>& src, 00277 const float maxval, 00278 const PixRGB<float>& background, 00279 const PixRGB<float>& pos_stain, 00280 const PixRGB<float>& neg_stain) 00281 { 00282 GVX_TRACE(__PRETTY_FUNCTION__); 00283 00284 Image<PixRGB<float> > result(src.getDims(), NO_INIT); 00285 00286 Image<PixRGB<float> >::iterator dptr = result.beginw(); 00287 Image<PixRGB<float> >::iterator stop = result.endw(); 00288 Image<float>::const_iterator sptr = src.begin(); 00289 00290 if(maxval > 0) 00291 { 00292 while (dptr != stop) 00293 { 00294 const float ratio = (*sptr++/maxval); 00295 if (ratio > 0) 00296 *dptr++ = pos_stain * ratio + background * (1.0f - ratio); 00297 else 00298 *dptr++ = neg_stain * (-ratio) + background * (1.0f + ratio); 00299 } 00300 } 00301 else 00302 { 00303 while (dptr != stop) 00304 *dptr++ = PixRGB<float>(0,0,0); 00305 } 00306 00307 return result; 00308 } 00309 00310 // ###################################################################### 00311 Image<PixRGB<float> > overlayStain(const Image<float>& top, 00312 const Image<float>& bottom, 00313 const float trans, const char channel) 00314 { 00315 GVX_TRACE(__PRETTY_FUNCTION__); 00316 // 0 to 100 percent only: 00317 ASSERT((trans >= 0.0F) && (trans <= 100.0F)); 00318 // images must be same size: 00319 ASSERT(top.isSameSize(bottom)); 00320 00321 // result is the size of the input images: 00322 Image<PixRGB<float> > result(top.getDims(), NO_INIT); 00323 00324 Image<PixRGB<float> >::iterator 00325 dptr = result.beginw(), 00326 stop = result.endw(); 00327 00328 Image<float>::const_iterator 00329 tptr = top.begin(), 00330 bptr = bottom.begin(); 00331 00332 float tfac = trans * 0.01F; 00333 00334 while (dptr != stop) 00335 { 00336 float top_val = *tptr++; 00337 00338 PixRGB<float> pix; 00339 00340 if(channel == 'r'){pix.setRed(top_val);pix.setGreen(0);pix.setBlue(0);} 00341 if(channel == 'g'){pix.setRed(0);pix.setGreen(top_val);pix.setBlue(0);} 00342 if(channel == 'b'){pix.setRed(0);pix.setGreen(0);pix.setBlue(top_val);} 00343 00344 *dptr++ = pix - ( pix - (*bptr++) ) * tfac; 00345 } 00346 00347 return result; 00348 } 00349 00350 // ###################################################################### 00351 template <class T> 00352 void getMinMaxC(const Image<PixRGB<T> >& src, T& mi, T& ma) 00353 { 00354 GVX_TRACE(__PRETTY_FUNCTION__); 00355 ASSERT(src.initialized()); 00356 typename Image<PixRGB<T> >::const_iterator aptr = src.begin(); 00357 typename Image<PixRGB<T> >::const_iterator stop = src.end(); 00358 00359 mi = aptr->red(); ma = aptr->red(); 00360 00361 while (aptr != stop) 00362 { 00363 T x = aptr->red(); 00364 if (x < mi) mi = x; else if (x > ma) ma = x; 00365 00366 x = aptr->green(); 00367 if (x < mi) mi = x; else if (x > ma) ma = x; 00368 00369 x = aptr->blue(); 00370 if (x < mi) mi = x; else if (x > ma) ma = x; 00371 00372 ++aptr; 00373 } 00374 } 00375 00376 // ###################################################################### 00377 template <class T> 00378 PixRGB<float> meanRGB(const Image<PixRGB<T> >& src) 00379 { 00380 GVX_TRACE(__PRETTY_FUNCTION__); 00381 ASSERT(src.initialized()); 00382 00383 typename Image<PixRGB<T> >::const_iterator sptr = src.begin(); 00384 typename Image<PixRGB<T> >::const_iterator stop = src.end(); 00385 00386 PixRGB<float> meanRGB; 00387 while (sptr != stop) 00388 { 00389 meanRGB[0] += sptr->p[0]; 00390 meanRGB[1] += sptr->p[1]; 00391 meanRGB[2] += sptr->p[2]; 00392 ++sptr; 00393 } 00394 00395 meanRGB[0] /= src.size(); 00396 meanRGB[1] /= src.size(); 00397 meanRGB[2] /= src.size(); 00398 00399 return meanRGB; 00400 } 00401 00402 // ###################################################################### 00403 template <> 00404 Image<byte> luminance(const Image<PixRGB<byte> >& src) 00405 { 00406 GVX_TRACE(__PRETTY_FUNCTION__); 00407 ASSERT(src.initialized()); 00408 00409 Image<byte> result(src.getDims(), NO_INIT); 00410 00411 Image<PixRGB<byte> >::const_iterator aptr = src.begin(); 00412 00413 Image<byte>::iterator dptr = result.beginw(); 00414 Image<byte>::iterator stop = result.endw(); 00415 00416 while (dptr != stop) 00417 { 00418 *dptr++ = (aptr->p[0] + aptr->p[1] + aptr->p[2]) / 3; 00419 ++aptr; 00420 } 00421 00422 return result; 00423 } 00424 00425 // ###################################################################### 00426 template <class T> 00427 Image<T> luminance(const Image<PixRGB<T> >& src) 00428 { 00429 GVX_TRACE(__PRETTY_FUNCTION__); 00430 ASSERT(src.initialized()); 00431 00432 Image<T> result(src.getDims(), NO_INIT); 00433 00434 typename Image<PixRGB<T> >::const_iterator aptr = src.begin(); 00435 00436 typename Image<T>::iterator dptr = result.beginw(); 00437 typename Image<T>::iterator stop = result.endw(); 00438 00439 while (dptr != stop) 00440 *dptr++ = (*aptr++).luminance(); 00441 00442 return result; 00443 } 00444 00445 // ###################################################################### 00446 template <class T> 00447 Image<T> luminance(const Image<T>& src) 00448 { return src; } 00449 00450 00451 // ###################################################################### 00452 template <class T> 00453 Image<T> luminanceNTSC(const Image<PixRGB<T> >& src) 00454 { 00455 GVX_TRACE(__PRETTY_FUNCTION__); 00456 ASSERT(src.initialized()); 00457 Image<T> result(src.getDims(), NO_INIT); 00458 00459 float coef1,coef2,coef3; 00460 //Taken from Matlab's rgb2gray() function 00461 // T = inv([1.0 0.956 0.621; 1.0 -0.272 -0.647; 1.0 -1.106 1.703]); 00462 // coef = T(1,:)'; 00463 coef1 = 0.298936F; coef2 = 0.587043F; coef3 = 0.114021F; 00464 00465 00466 typename Image<PixRGB<T> >::const_iterator aptr = src.begin(); 00467 00468 typename Image<T>::iterator dptr = result.beginw(); 00469 typename Image<T>::iterator stop = result.endw(); 00470 00471 while (dptr != stop){ 00472 *dptr++ = T(round(aptr->p[0]*coef1 + aptr->p[1]*coef2 + aptr->p[2]*coef3)); 00473 ++aptr; 00474 } 00475 return result; 00476 } 00477 00478 00479 // ###################################################################### 00480 template <class T> 00481 Image< PixRGB<T> > toRGB(const Image<T>& src) 00482 { 00483 GVX_TRACE(__PRETTY_FUNCTION__); 00484 ASSERT(src.initialized()); 00485 00486 Image< PixRGB<T> > result(src.getDims(), NO_INIT); 00487 typename Image<T>::const_iterator aptr = src.begin(); 00488 typename Image< PixRGB<T> >::iterator dptr = result.beginw(); 00489 typename Image< PixRGB<T> >::iterator stop = result.endw(); 00490 00491 while (dptr != stop) *dptr++ = PixRGB<T>(*aptr++); 00492 00493 return result; 00494 } 00495 00496 // ###################################################################### 00497 template <class T> 00498 Image< PixRGB<T> > toRGB(const Image< PixRGB<T> >& src) 00499 { return src; } 00500 00501 // ###################################################################### 00502 template <class T> 00503 Image<float> infoMeasure(const Image<PixRGB<T> >& src, 00504 const float eps, const int size) 00505 { 00506 GVX_TRACE(__PRETTY_FUNCTION__); 00507 ASSERT(src.initialized()); 00508 00509 Image< PixRGB<float> > float_copy(src); 00510 00511 Image<float> y, i, q; 00512 getYIQ(float_copy, y, i, q); 00513 00514 i = ::decXY(::lowPass3(i)); 00515 q = ::decXY(::lowPass3(q)); 00516 00517 // eps is given between 0 and 1; modify it according to image size 00518 // we want that eps=0.01 corresponds to a cosine with amplitude 127.5*0.01 00519 float epsy = eps * ((float)(size*size) * 127.5); 00520 float epsiq = epsy / 4.0; 00521 00522 Image<float> result(src.getWidth() / size, src.getHeight() / size, NO_INIT); 00523 00524 for (int offy = 0; offy < src.getHeight(); offy += size) 00525 for (int offx = 0; offx < src.getWidth(); offx += size) 00526 { 00527 Image<float> ydct = dct(y, offx, offy, size); 00528 Image<float> idct = dct(i, offx/2, offy/2, size/2); 00529 Image<float> qdct = dct(q, offx/2, offy/2, size/2); 00530 00531 result.setVal(offx/size, offy/size, 00532 (float(countThresh(ydct, epsy)) + 00533 4.0 * float(countThresh(idct, epsiq)) + 00534 4.0 * float(countThresh(qdct, epsiq))) 00535 / (3.0 * float(size*size))); 00536 } 00537 00538 return result; 00539 } 00540 00541 // ###################################################################### 00542 template <class T> inline 00543 void getYIQ(const Image<PixRGB<T> >& src, 00544 Image<T>& y, Image<T>& i, Image<T>& q) 00545 { 00546 GVX_TRACE(__PRETTY_FUNCTION__); 00547 ASSERT(src.initialized()); 00548 y = Image<T>(src.getDims(), NO_INIT); 00549 i = Image<T>(src.getDims(), NO_INIT); 00550 q = Image<T>(src.getDims(), NO_INIT); 00551 00552 typename Image<PixRGB<T> >::const_iterator aptr = src.begin(); 00553 typename Image<PixRGB<T> >::const_iterator stop = src.end(); 00554 00555 typename Image<T>::iterator yptr = y.beginw(); 00556 typename Image<T>::iterator iptr = i.beginw(); 00557 typename Image<T>::iterator qptr = q.beginw(); 00558 00559 while (aptr != stop) 00560 { 00561 T yy, ii, qq; 00562 PixYIQ<T>(*aptr++).getYIQ(yy, ii, qq); 00563 *yptr++ = yy; 00564 *iptr++ = ii; 00565 *qptr++ = qq; 00566 } 00567 } 00568 00569 // ###################################################################### 00570 template <class T> inline 00571 void getJpegYUV(const Image<PixRGB<T> >& src, 00572 Image<T>& y, Image<T>& u, Image<T>& v) 00573 { 00574 GVX_TRACE(__PRETTY_FUNCTION__); 00575 ASSERT(src.initialized()); 00576 y = Image<T>(src.getDims(), NO_INIT); 00577 u = Image<T>(src.getDims(), NO_INIT); 00578 v = Image<T>(src.getDims(), NO_INIT); 00579 00580 typename Image<PixRGB<T> >::const_iterator aptr = src.begin(); 00581 typename Image<PixRGB<T> >::const_iterator stop = src.end(); 00582 00583 typename Image<T>::iterator yptr = y.beginw(); 00584 typename Image<T>::iterator uptr = u.beginw(); 00585 typename Image<T>::iterator vptr = v.beginw(); 00586 00587 while (aptr != stop) 00588 { 00589 const PixJpegYUV<T> yuvpix(*aptr++); 00590 *yptr++ = yuvpix.Y(); 00591 *uptr++ = yuvpix.U(); 00592 *vptr++ = yuvpix.V(); 00593 } 00594 } 00595 00596 // ###################################################################### 00597 template <class T> 00598 Image<PixRGB<T> > luminanceNormalize(const Image<PixRGB<T> >& src, 00599 const T thresh) 00600 { 00601 GVX_TRACE(__PRETTY_FUNCTION__); 00602 ASSERT(src.initialized()); 00603 00604 Image<PixRGB<T> > result(src.getDims(), NO_INIT); 00605 00606 typename Image<PixRGB<T> >::const_iterator aptr = src.begin(); 00607 typename Image<PixRGB<T> >::const_iterator stop = src.end(); 00608 00609 typename Image<PixRGB<T> >::iterator dptr = result.beginw(); 00610 const PixRGB<T> zero(T(0)); 00611 00612 while (aptr != stop) 00613 { 00614 float v = float(aptr->luminance()); 00615 if (v >= thresh) 00616 { 00617 const float fac = 255.0 / (v * 3.0); 00618 dptr->setRed ( T(float(aptr->red()) * fac) ); 00619 dptr->setGreen( T(float(aptr->green()) * fac) ); 00620 dptr->setBlue ( T(float(aptr->blue()) * fac) ); 00621 } 00622 else 00623 *dptr = zero; 00624 00625 ++aptr; ++dptr; 00626 } 00627 00628 return result; 00629 } 00630 00631 // ###################################################################### 00632 template <class T> 00633 void getRGBY(const Image<PixRGB<T> >& src, 00634 Image<typename promote_trait<T, float>::TP>& rg, 00635 Image<typename promote_trait<T, float>::TP>& by, 00636 const typename promote_trait<T, float>::TP thresh) 00637 { 00638 GVX_TRACE(__PRETTY_FUNCTION__); 00639 // red = [r - (g+b)/2] [.] = clamp between 0 and 255 00640 // green = [g - (r+b)/2] 00641 // blue = [b - (r+g)/2] 00642 // yellow = [2*((r+g)/2 - |r-g| - b)] 00643 00644 ASSERT(src.initialized()); 00645 typedef typename promote_trait<T, float>::TP TF; 00646 rg = Image<TF>(src.getDims(), NO_INIT); 00647 by = Image<TF>(src.getDims(), NO_INIT); 00648 00649 typename Image< PixRGB<T> >::const_iterator aptr = src.begin(); 00650 typename Image< PixRGB<T> >::const_iterator stop = src.end(); 00651 00652 typename Image<TF>::iterator rgptr = rg.beginw(), byptr = by.beginw(); 00653 TF zero = TF(); TF thresh3 = 3.0F * thresh; 00654 00655 while (aptr != stop) 00656 { 00657 TF r = TF(aptr->red()), g = TF(aptr->green()), b = TF(aptr->blue()); 00658 00659 // first do the luminanceNormalization: 00660 TF lum = r + g + b; 00661 if (lum < thresh3) // too dark... no response from anybody 00662 { *rgptr++ = zero; *byptr++ = zero; ++aptr; } 00663 else 00664 { 00665 // normalize chroma by luminance: 00666 TF fac = 255.0f / lum; 00667 r *= fac; g *= fac; b *= fac; 00668 00669 // now compute color opponencies: 00670 // yellow gets a factor 2 to compensate for its previous attenuation 00671 // by luminanceNormalize(): 00672 TF red = r - 0.5f * (g + b), green = g - 0.5f * (r + b); 00673 TF blue = b - 0.5f * (r + g), yellow = -2.0f * (blue + fabs(r-g)); 00674 00675 if (red < 0.0f) red = 0.0f; 00676 else if (red > 255.0f) red = 255.0f; 00677 if (green < 0.0f) green = 0.0f; 00678 else if (green > 255.0f) green = 255.0f; 00679 if (blue < 0.0f) blue = 0.0f; 00680 else if (blue > 255.0f) blue = 255.0f; 00681 if (yellow < 0.0f) yellow=0.0f; 00682 else if (yellow > 255.0f) yellow=255.0f; 00683 00684 *rgptr++ = red - green; *byptr++ = blue - yellow; 00685 ++aptr; 00686 } 00687 } 00688 } 00689 00690 // ###################################################################### 00691 template <class T> 00692 void getRGBYsimple(const Image<PixRGB<T> >& src, 00693 Image<typename promote_trait<T, float>::TP>& rg, 00694 Image<typename promote_trait<T, float>::TP>& by, 00695 const typename promote_trait<T, float>::TP thresh) 00696 { 00697 GVX_TRACE(__PRETTY_FUNCTION__); 00698 // I = max(R,G,B) 00699 // RG = (R-G)/I 00700 // BY = (B-min(R,G))/I 00701 // Dirk Walther, September 2004 00702 ASSERT(src.initialized()); 00703 typedef typename promote_trait<T, float>::TP TF; 00704 rg = Image<TF>(src.getDims(), NO_INIT); 00705 by = Image<TF>(src.getDims(), NO_INIT); 00706 00707 typename Image< PixRGB<T> >::const_iterator aptr = src.begin(); 00708 typename Image< PixRGB<T> >::const_iterator stop = src.end(); 00709 00710 typename Image<TF>::iterator rgptr = rg.beginw(), byptr = by.beginw(); 00711 TF zero = TF(); 00712 00713 while (aptr != stop) 00714 { 00715 TF r = aptr->red(), g = aptr->green(), b = aptr->blue(); 00716 00717 // first do in = max(R,G,B): 00718 TF in = r; 00719 if (g > in) in = g; 00720 if (b > in) in = b; 00721 if (in < thresh) // too dark... no response from anybody 00722 { *rgptr++ = zero; *byptr++ = zero; ++aptr; } 00723 else 00724 { 00725 TF y = (r < g) ? r : g; 00726 *rgptr++ = (r - g) / in; 00727 *byptr++ = (b - y) / in; 00728 ++aptr; 00729 } 00730 } 00731 } 00732 00733 // ###################################################################### 00734 template <class T> 00735 void getRGBY(const Image<PixRGB<T> >& src, Image<T>& rr, Image<T>& gg, 00736 Image<T>& bb, Image<T>& yy, const T thresh) 00737 { 00738 GVX_TRACE(__PRETTY_FUNCTION__); 00739 // this is essentially the same code as the other getRGBY, just 00740 // duplicated for efficiency 00741 ASSERT(src.initialized()); 00742 rr = Image<T>(src.getDims(), NO_INIT); 00743 gg = Image<T>(src.getDims(), NO_INIT); 00744 bb = Image<T>(src.getDims(), NO_INIT); 00745 yy = Image<T>(src.getDims(), NO_INIT); 00746 00747 typename Image< PixRGB<T> >::const_iterator aptr = src.begin(); 00748 typename Image< PixRGB<T> >::const_iterator stop = src.end(); 00749 typename Image<T>::iterator rptr = rr.beginw(), gptr = gg.beginw(), 00750 bptr = bb.beginw(), yptr = yy.beginw(); 00751 00752 T zero = T(); float threshf = float(thresh); 00753 00754 while (aptr != stop) 00755 { 00756 float r = aptr->red(), g = aptr->green(), b = aptr->blue(); 00757 00758 // first do the luminanceNormalization: 00759 float lum = (r + g + b) / 3.0f; 00760 if (lum < threshf) // too dark... no response from anybody 00761 { *rptr++=zero; *gptr++=zero; *bptr++=zero; *yptr++=zero; ++aptr; } 00762 else 00763 { 00764 // normalize chroma by luminance: 00765 float fac = 255.0f / (3.0f * lum); 00766 r *= fac; g *= fac; b *= fac; 00767 00768 // now compute color opponencies: 00769 // yellow gets a factor 2 to compensate for its previous attenuation 00770 // by luminanceNormalize(): 00771 float red = r - 0.5f * (g + b), green = g - 0.5f * (r + b); 00772 float blue = b - 0.5f * (r + g), yellow = -2.0f * (blue + fabs(r-g)); 00773 00774 if (red < 0.0f) red = 0.0f; 00775 else if (red > 255.0f) red = 255.0f; 00776 if (green < 0.0f) green = 0.0f; 00777 else if (green > 255.0f) green = 255.0f; 00778 if (blue < 0.0f) blue = 0.0f; 00779 else if (blue > 255.0f) blue = 255.0f; 00780 if (yellow < 0.0f) yellow=0.0f; 00781 else if (yellow > 255.0f) yellow=255.0f; 00782 00783 *rptr++ = T(red); // no clamping necessary 00784 *gptr++ = T(green); 00785 *bptr++ = T(blue); 00786 *yptr++ = T(yellow); 00787 00788 ++aptr; 00789 } 00790 } 00791 } 00792 00793 // ###################################################################### 00794 template <class T> 00795 void getRGBY(const Image<PixRGB<T> >& src, 00796 Image<T>& rg, Image<T>& by, 00797 Image<T>& sat, Image<T>& val, 00798 const ushort H2SVtype) 00799 { 00800 GVX_TRACE(__PRETTY_FUNCTION__); 00801 ASSERT(src.initialized()); 00802 typename Image<PixRGB<T> >::const_iterator aptr = src.begin(); 00803 00804 rg = Image<T>(src.getDims(), NO_INIT); 00805 by = Image<T>(src.getDims(), NO_INIT); 00806 sat = Image<T>(src.getDims(), NO_INIT); 00807 val = Image<T>(src.getDims(), NO_INIT); 00808 00809 typename Image<T>::iterator rptr = rg.beginw(), bptr = by.beginw(), 00810 sptr = sat.beginw(), vptr = val.beginw(); 00811 00812 while(aptr != src.end()) 00813 { 00814 if(H2SVtype == 1) 00815 { 00816 PixH2SV1<T> pix = PixH2SV1<T>(*aptr++); 00817 *rptr++ = pix.H2(); *bptr++ = pix.H1(); 00818 *sptr++ = pix.S(); *vptr++ = pix.V(); 00819 } 00820 else if(H2SVtype == 2) 00821 { 00822 PixH2SV2<T> pix = PixH2SV2<T>(*aptr++); 00823 *rptr++ = pix.H2(); *bptr++ = pix.H1(); 00824 *sptr++ = pix.S(); *vptr++ = pix.V(); 00825 } 00826 else 00827 { 00828 LFATAL("This type of H2SV pixel not yet supported"); 00829 } 00830 } 00831 } 00832 00833 // ###################################################################### 00834 template <class T> 00835 void getDKL(const Image<PixRGB<T> >& src, 00836 Image<typename promote_trait<T, float>::TP>& dimg, 00837 Image<typename promote_trait<T, float>::TP>& kimg, 00838 Image<typename promote_trait<T, float>::TP>& limg) 00839 { 00840 GVX_TRACE(__PRETTY_FUNCTION__); 00841 00842 ASSERT(src.initialized()); 00843 typedef typename promote_trait<T, float>::TP TF; 00844 dimg = Image<TF>(src.getDims(), NO_INIT); 00845 kimg = Image<TF>(src.getDims(), NO_INIT); 00846 limg = Image<TF>(src.getDims(), NO_INIT); 00847 00848 typename Image< PixRGB<T> >::const_iterator aptr = src.begin(), stop = src.end(); 00849 typename Image<TF>::iterator dptr = dimg.beginw(), kptr = kimg.beginw(), lptr = limg.beginw(); 00850 00851 while (aptr != stop) 00852 { 00853 const PixDKL<TF> p(*aptr++); 00854 *dptr++ = p.D(); 00855 *kptr++ = p.K(); 00856 *lptr++ = p.L(); 00857 } 00858 } 00859 00860 // ###################################################################### 00861 template <class T> 00862 void getLAB(const Image<PixRGB<T> >& src, 00863 Image<typename promote_trait<T, float>::TP>& limg, 00864 Image<typename promote_trait<T, float>::TP>& aimg, 00865 Image<typename promote_trait<T, float>::TP>& bimg) 00866 { 00867 GVX_TRACE(__PRETTY_FUNCTION__); 00868 00869 ASSERT(src.initialized()); 00870 typedef typename promote_trait<T, float>::TP TF; 00871 limg = Image<TF>(src.getDims(), NO_INIT); 00872 aimg = Image<TF>(src.getDims(), NO_INIT); 00873 bimg = Image<TF>(src.getDims(), NO_INIT); 00874 00875 typename Image< PixRGB<T> >::const_iterator aptr = src.begin(), stop = src.end(); 00876 typename Image<TF>::iterator dptr = limg.beginw(), kptr = aimg.beginw(), lptr = bimg.beginw(); 00877 00878 while (aptr != stop) 00879 { 00880 00881 const PixRGB<byte> c(*aptr++); 00882 float X, Y, Z, fX, fY, fZ; 00883 int L,a,b; 00884 float R = c.red(); //(((rgbColor & 0xf800) >> 11) * 255 / 31); 00885 float G = c.green(); //(((rgbColor & 0x07e0) >> 5) * 255 / 63); 00886 float B = c.blue(); //((rgbColor & 0x001f) * 255 / 31); 00887 00888 X = 0.412453 * R + 0.357580 * G + 0.180423 * B; 00889 Y = 0.212671 * R + 0.715160 * G + 0.072169 * B; 00890 Z = 0.019334 * R + 0.119193 * G + 0.950227 * B; 00891 00892 X /= (255 * 0.950456); 00893 Y /= 255; 00894 Z /= (255 * 1.088754); 00895 00896 if (Y > 0.008856){ 00897 fY = pow(Y, 1.0 / 3.0); 00898 L = (int)(116.0 * fY - 16.0 + 0.5); 00899 }else{ 00900 fY = 7.787 * Y + 16.0 / 116.0; 00901 L = (int)(903.3 * Y + 0.5); 00902 } 00903 00904 00905 if (X > 0.008856) 00906 fX = pow(X, 1.0 / 3.0); 00907 else 00908 fX = 7.787 * X + 16.0 / 116.0; 00909 00910 if (Z > 0.008856) 00911 fZ = pow(Z, 1.0 / 3.0); 00912 else 00913 fZ = 7.787 * Z + 16.0 / 116.0; 00914 00915 a = (int)(500.0 * (fX - fY) + 0.5); 00916 b = (int)(200.0 * (fY - fZ) + 0.5); 00917 //-128~127 00918 // 00919 *dptr++ = L; 00920 *kptr++ = a; 00921 *lptr++ = b; 00922 00923 00924 } 00925 } 00926 00927 // ###################################################################### 00928 void normalizeLAB 00929 (Image<float>& limg, 00930 Image<float>& aimg, 00931 Image<float>& bimg) 00932 { 00933 GVX_TRACE(__PRETTY_FUNCTION__); 00934 00935 // assert that all images are of same size 00936 ASSERT(limg.isSameSize(aimg)); 00937 ASSERT(limg.isSameSize(bimg)); 00938 00939 // range for a, b channels 00940 const float ab_min = -73; 00941 const float ab_max = 95; 00942 const float ab_range = ab_max - ab_min; 00943 00944 Image<float>::iterator lptr = limg.beginw(), stop = limg.endw(); 00945 Image<float>::iterator aptr = aimg.beginw(), bptr = bimg.beginw(); 00946 00947 // normalize Lab image 00948 while (lptr != stop) 00949 { 00950 float l_val = *lptr / 100.0F; 00951 float a_val = (*aptr - ab_min) / ab_range; 00952 float b_val = (*bptr - ab_min) / ab_range; 00953 00954 if (l_val < 0) { l_val = 0.0F; } else if (l_val > 1) { l_val = 1.0F; } 00955 if (a_val < 0) { a_val = 0.0F; } else if (a_val > 1) { a_val = 1.0F; } 00956 if (b_val < 0) { b_val = 0.0F; } else if (b_val > 1) { b_val = 1.0F; } 00957 00958 *lptr++ = l_val; 00959 *aptr++ = a_val; 00960 *bptr++ = b_val; 00961 } 00962 } 00963 00964 // ###################################################################### 00965 template <class T> 00966 void getNormalizedLAB(const Image<PixRGB<T> >& src, 00967 Image<typename promote_trait<T, float>::TP>& limg, 00968 Image<typename promote_trait<T, float>::TP>& aimg, 00969 Image<typename promote_trait<T, float>::TP>& bimg) 00970 { 00971 GVX_TRACE(__PRETTY_FUNCTION__); 00972 00973 // just call get the LAB color conversion algorithm 00974 // then normalize 00975 getLAB(src, limg,aimg,bimg); 00976 normalizeLAB(limg, aimg, bimg); 00977 } 00978 00979 // ###################################################################### 00980 Image< PixRGB<byte> > contrastModulate(const Image< PixRGB<byte> >& img, 00981 const Image<float>& mask, 00982 float baseContrast, 00983 byte baseBright) 00984 { 00985 GVX_TRACE(__PRETTY_FUNCTION__); 00986 ASSERT(img.isSameSize(mask)); 00987 ASSERT((baseContrast >= 0.0) && (baseContrast <= 1.0)); 00988 00989 Image<float> contrast(mask); 00990 inplaceClamp(contrast, 0.01f, 1.0f); 00991 inplaceNormalize(contrast, 0.0f, 1.0f-baseContrast); 00992 contrast += baseContrast; 00993 Image<float> base(contrast * (-baseBright)); 00994 base += baseBright; 00995 00996 return img * contrast + base; 00997 } 00998 00999 // ###################################################################### 01000 template <class T> 01001 double pSNRcolor(const Image< PixRGB<T> >& img1, 01002 const Image< PixRGB<T> >& img2) 01003 { 01004 GVX_TRACE(__PRETTY_FUNCTION__); 01005 ASSERT(img1.isSameSize(img2)); 01006 Image<T> r1, g1, b1, r2, g2, b2; int siz = img1.getSize(); 01007 getComponents(img1, r1, g1, b1); getComponents(img2, r2, g2, b2); 01008 double mser = distance(r1, r2); mser = mser * mser / double(siz); 01009 double mseg = distance(g1, g2); mseg = mseg * mseg / double(siz); 01010 double mseb = distance(b1, b2); mseb = mseb * mseb / double(siz); 01011 01012 double mse = (mser + mseg + mseb) / 3.0; 01013 01014 return 10.0 * log10(255.0 * 255.0 / mse); 01015 } 01016 01017 // ###################################################################### 01018 template <class T> 01019 double pSNRcolor(const Image< PixRGB<T> >& img1, 01020 const Image< PixRGB<T> >& img2, 01021 const Image<float>& weight) 01022 { 01023 GVX_TRACE(__PRETTY_FUNCTION__); 01024 ASSERT(img1.isSameSize(img2)); 01025 Image<T> r1, g1, b1, r2, g2, b2; int siz = img1.getSize(); 01026 getComponents(img1, r1, g1, b1); getComponents(img2, r2, g2, b2); 01027 double mser = distance(r1, r2, weight); mser = mser * mser / double(siz); 01028 double mseg = distance(g1, g2, weight); mseg = mseg * mseg / double(siz); 01029 double mseb = distance(b1, b2, weight); mseb = mseb * mseb / double(siz); 01030 01031 double mse = (mser + mseg + mseb) / 3.0; 01032 01033 return 10.0 * log10(255.0 * 255.0 / mse); 01034 } 01035 01036 // ###################################################################### 01037 template <class T> 01038 Image< PixRGB<T> > normalizeRGB(const Image< PixRGB<T> >& img, 01039 PixRGB<T> min, 01040 PixRGB<T> max) 01041 { 01042 GVX_TRACE(__PRETTY_FUNCTION__); 01043 Image<T> r,g,b; 01044 getComponents(img, r, g, b); 01045 inplaceNormalize(r, min.red(), max.red()); 01046 inplaceNormalize(g, min.green(), max.green()); 01047 inplaceNormalize(b, min.blue(), max.blue()); 01048 return makeRGB(r, g, b); 01049 } 01050 01051 // ###################################################################### 01052 template <class T> 01053 Image<T> maxRGB(const Image< PixRGB<T> >& img) 01054 { 01055 GVX_TRACE(__PRETTY_FUNCTION__); 01056 Image<T> r,g,b; 01057 getComponents(img, r, g, b); 01058 return takeMax(r, takeMax(g, b)); 01059 } 01060 01061 // ###################################################################### 01062 template <class T> 01063 Image< PixRGB<T> > colorStain (const Image<T>& src, 01064 const T& min, const T& max, 01065 const PixRGB<T>& color) 01066 { 01067 GVX_TRACE(__PRETTY_FUNCTION__); 01068 float fmin = (float)min; 01069 float fdiff = (float)(max - min); 01070 ASSERT (fdiff != 0.0F); 01071 01072 Image < PixRGB<T> > result(src.getDims(), NO_INIT); 01073 typename Image<T>::const_iterator sptr, stop = src.end(); 01074 //typename Image< PixRGB<T> >::const_iterator stop = src.end(); 01075 typename Image< PixRGB<T> >::iterator rptr = result.beginw(); 01076 01077 for (sptr = src.begin(); sptr != stop; ++sptr, ++rptr) 01078 { 01079 *rptr = color; 01080 *rptr *= ((float)(*sptr) - fmin) / fdiff; 01081 } 01082 return result; 01083 } 01084 01085 // ###################################################################### 01086 void RGBtoCIE(const PixRGB<byte>& rgbColor, 01087 float& cr, float& cg, float& intens) 01088 { 01089 GVX_TRACE(__PRETTY_FUNCTION__); 01090 PixRGB<float> fpix(rgbColor); 01091 intens = (fpix.red() + fpix.green() + fpix.blue()); 01092 if (intens > 0.0f) 01093 { 01094 cr = fpix.red() / intens; 01095 cg = fpix.green() / intens; 01096 } 01097 else 01098 { 01099 cr = 0.0f; 01100 cg = 0.0f; 01101 } 01102 } 01103 01104 // ###################################################################### 01105 Image<float> hueDistance(const Image< PixRGB<byte> >& img, 01106 float muR, float muG, 01107 float sigR, float sigG, float rho) 01108 { 01109 GVX_TRACE(__PRETTY_FUNCTION__); 01110 Image<float> result(img.getDims(), NO_INIT); 01111 Image< PixRGB<byte> >::const_iterator iptr, stop = img.end(); 01112 Image<float>::iterator rptr = result.beginw(); 01113 01114 float sR2 = 1/sigR/sigR; 01115 float sG2 = 1/sigG/sigG; 01116 float sRG = 2*rho/sigG/sigR; 01117 01118 for (iptr = img.begin(); iptr != stop; ++iptr, ++rptr) 01119 { 01120 float cr, cg, intens; 01121 RGBtoCIE(*iptr,cr,cg,intens); 01122 *rptr = exp(-0.5f*((cr-muR)*(cr-muR)*sR2 + (cg-muG)*(cg-muG)*sG2 01123 - (cr-muR)*(cg-muG)*sRG)); 01124 } 01125 return result; 01126 } 01127 01128 template void getMinMaxC(const Image<PixRGB<int> >& src, int& mi, int& ma); 01129 template void getMinMaxC(const Image<PixRGB<double> >& src, double& mi, double& ma); 01130 01131 // Include the explicit instantiations 01132 #include "inst/Image/ColorOps.I" 01133 01134 // ###################################################################### 01135 /* So things look consistent in everyone's emacs... */ 01136 /* Local Variables: */ 01137 /* indent-tabs-mode: nil */ 01138 /* End: */ 01139 01140 #endif // !IMAGE_COLOROPS_C_DEFINED