00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040 #include "Channels/ContourChannel.H"
00041
00042 #include "Channels/ChannelBase.H"
00043 #include "Channels/ChannelOpts.H"
00044 #include "Channels/ComplexChannel.H"
00045 #include "Component/ModelOptionDef.H"
00046 #include "Component/OptionManager.H"
00047 #include "Image/ColorOps.H"
00048 #include "Image/DrawOps.H"
00049 #include "Image/FilterOps.H"
00050 #include "Image/ImageSet.H"
00051 #include "Image/ImageSetOps.H"
00052 #include "Image/LevelSpec.H"
00053 #include "Image/MathOps.H"
00054 #include "Image/PyramidOps.H"
00055 #include "Image/Range.H"
00056 #include "Image/ShapeOps.H"
00057 #include "Image/fancynorm.H"
00058 #include "Raster/GenericFrame.H"
00059 #include "Transport/FrameOstream.H"
00060 #include "Util/Assert.H"
00061 #include "Util/CpuTimer.H"
00062 #include "Util/StringConversions.H"
00063 #include "Util/StringUtil.H"
00064 #include "Util/Types.H"
00065 #include "Util/log.H"
00066 #include "Util/sformat.H"
00067 #include "rutz/shared_ptr.h"
00068
00069 #include <cmath>
00070 #include <map>
00071 #include <iostream>
00072 #include <fstream>
00073
00074 namespace dummy_namespace_to_avoid_gcc411_bug_ContourChannel_C
00075 {
00076
00077
00078
00079 float angDist(float ang1, float ang2)
00080 {
00081 while (ang1 < 0.0f) ang1 += 180.0f;
00082 while (ang2 < 0.0f) ang2 += 180.0f;
00083 while (ang1 >= 180.0f) ang1 -= 180.0f;
00084 while (ang2 >= 180.0f) ang2 -= 180.0f;
00085
00086 float diff = fabs(ang1 - ang2);
00087
00088 if (diff > 90.0f) diff = 180.0f - diff;
00089
00090 return diff;
00091 }
00092
00093
00094
00095
00096
00097 float sigmoid(float v, float thresh)
00098 {
00099 return 1.0f / ( 1.0f + exp(2.0f - 4.0f*v/thresh) );
00100 }
00101
00102 Image<float> sigmoid(const Image<float>& input, float thresh)
00103 {
00104 Image<float> result(input.getDims(), NO_INIT);
00105
00106 Image<float>::iterator dptr = result.beginw();
00107 Image<float>::iterator stop = result.endw();
00108 Image<float>::const_iterator sptr = input.begin();
00109
00110 while (dptr != stop)
00111 {
00112 *dptr++ = sigmoid(*sptr++, thresh);
00113 }
00114
00115 return result;
00116 }
00117
00118 struct Accum
00119 {
00120 double s;
00121 int c;
00122
00123 Accum() : s(0.0), c(0) {}
00124
00125 void add(double d) { s+=d; ++c; }
00126
00127 int count() const { return c; }
00128 double sum() const { return s; }
00129 double mean() const { return c != 0.0 ? s/c : 0.0; }
00130 };
00131
00132 inline double sigmoid2(double x)
00133 {
00134 return 1.0 / (1.0 + exp(-x));
00135 }
00136
00137 inline double excitFiring(double x)
00138 {
00139 double result = 2.0*sigmoid2(0.2 * x) - 1;
00140 return (result < 0.0) ? 0.0 : result;
00141 }
00142
00143 inline double PHI(double x)
00144 {
00145 return sigmoid2(10.0 * x);
00146 }
00147
00148 inline double inhibFiring(double x)
00149 {
00150 const double R_THR = 0.5;
00151 const double R_CUT = 0.2;
00152 double result = PHI(x - R_THR) - PHI(R_CUT - R_THR);
00153 return (result < 0.0) ? 0.0 : result;
00154 }
00155
00156 float getScaleBias(int scaleNumber)
00157 {
00158 switch(scaleNumber)
00159 {
00160 case 0: return 1.0f;
00161 case 1: return 0.9f;
00162 case 2: return 0.7f;
00163 }
00164 return 0.7f;
00165 }
00166
00167 void printStats(const Image<float>& img, const char* name)
00168 {
00169 float mi, ma, me;
00170 getMinMaxAvg(img, mi, ma, me);
00171
00172 LINFO("%s: min %.2f, max %.2f, mean %.2f", name, mi, ma, me);
00173 }
00174
00175 Image<float> boxDownSizeClean(const Image<float>& src,
00176 const Dims& new_dims,
00177 const int filterWidth)
00178 {
00179 if (src.getDims() == new_dims) return src;
00180
00181 ASSERT(new_dims.isNonEmpty());
00182
00183 Image<float> result = src;
00184
00185 Image<float> filt(filterWidth, 1, ZEROS);
00186 filt.clear(1.0f / filterWidth);
00187
00188 while (result.getWidth() >= new_dims.w() * 2 &&
00189 result.getHeight() >= new_dims.h() * 2)
00190 {
00191 result = decX(sepFilter(result, filt, Image<float>(),
00192 CONV_BOUNDARY_CLEAN));
00193 result = decY(sepFilter(result, Image<float>(), filt,
00194 CONV_BOUNDARY_CLEAN));
00195 }
00196
00197 return rescaleBilinear(result, new_dims);
00198 }
00199
00200 struct SaveSet
00201 {
00202 private:
00203 typedef std::map<std::string, GenericFrame> map_type;
00204 map_type imgmap;
00205
00206 public:
00207 void clear()
00208 {
00209 map_type().swap(imgmap);
00210 }
00211
00212 void add(const std::string& nm, const GenericFrame& f)
00213 {
00214 imgmap.insert(map_type::value_type(nm, f));
00215 }
00216
00217 void saveAll(FrameOstream& ofs, const char* savePfx) const
00218 {
00219 for (map_type::const_iterator itr = imgmap.begin(), stop = imgmap.end();
00220 itr != stop; ++itr)
00221 ofs.writeFrame((*itr).second,
00222 sformat("%s.%s", savePfx, (*itr).first.c_str()));
00223 }
00224 };
00225 }
00226
00227 using namespace dummy_namespace_to_avoid_gcc411_bug_ContourChannel_C;
00228
00229
00230
00231
00232
00233
00234
00235 enum ContourConnectionType
00236 {
00237 CC_CXN_CINNIC = 1,
00238 CC_CXN_BRAUN = 2,
00239 CC_CXN_BRAUN_NORM = 3
00240 };
00241
00242 std::string convertToString(const ContourConnectionType val)
00243 {
00244 switch (val)
00245 {
00246 case CC_CXN_CINNIC: return "Cinnic"; break;
00247 case CC_CXN_BRAUN: return "Braun"; break;
00248 case CC_CXN_BRAUN_NORM: return "BraunNorm"; break;
00249 }
00250
00251 LFATAL("invalid ContourConnectionType '%d'", int(val));
00252 return std::string();
00253 }
00254
00255 void convertFromString(const std::string& str1, ContourConnectionType& val)
00256 {
00257 const std::string str = toLowerCase(str1);
00258
00259 if (str.compare("cinnic") == 0) { val = CC_CXN_CINNIC; }
00260 else if (str.compare("braun") == 0) { val = CC_CXN_BRAUN; }
00261 else if (str.compare("braunnorm") == 0) { val = CC_CXN_BRAUN_NORM; }
00262 else
00263 conversion_error::raise<ContourConnectionType>(str1);
00264 }
00265
00266
00267 class ContourConnection : public ModelComponent
00268 {
00269 public:
00270 ContourConnection(OptionManager& manager);
00271
00272 virtual ~ContourConnection();
00273
00274 float weight(int a, int b, int x, int y) const
00275 {
00276 return itsWeights[index(a,b,x,y)];
00277 }
00278
00279 const float* weightAXY(int a, int x, int y) const
00280 {
00281 return itsWeights + index(a, 0, x, y);
00282 }
00283
00284 const float* excitAXY(int a, int x, int y) const
00285 {
00286 return itsExcit + index(a, 0, x, y);
00287 }
00288
00289 const float* inhibAXY(int a, int x, int y) const
00290 {
00291 return itsInhib + index(a, 0, x, y);
00292 }
00293
00294 int numAngles() const { return itsNumAngles.getVal(); }
00295
00296 Dims getDims() const { return itsDims.getVal(); }
00297
00298 Point2D<int> getCenter() const
00299 {
00300 return Point2D<int>((itsDims.getVal().w()-1) / 2,
00301 (itsDims.getVal().h()-1) / 2);
00302 }
00303
00304 float angle(int i) const { return itsAngles[i]; }
00305
00306 const float* angles() const { return itsAngles; }
00307
00308 Image<byte> getSummaryImage() const;
00309
00310 private:
00311 ContourConnection(const ContourConnection&);
00312 ContourConnection& operator=(const ContourConnection&);
00313
00314 virtual void start2();
00315
00316 virtual void stop1();
00317
00318 virtual void setupConnectionWeights() = 0;
00319
00320 void printStats() const;
00321
00322 protected:
00323 int index(int a, int b, int x, int y) const
00324 {
00325 return b + itsNA*(y + itsH*(x + a*itsW));
00326 }
00327
00328 float& weight(int a, int b, int x, int y)
00329 {
00330 return itsWeights[index(a,b,x,y)];
00331 }
00332
00333 OModelParam<Dims> itsDims;
00334 OModelParam<int> itsNumAngles;
00335
00336 int itsW;
00337 int itsH;
00338 int itsNA;
00339 int itsNumWeights;
00340 float* itsAngles;
00341 float* itsWeights;
00342 float* itsExcit;
00343 float* itsInhib;
00344 };
00345
00346
00347
00348
00349
00350
00351
00352
00353 static const ModelOptionDef OPT_ContourConnectionDims =
00354 { MODOPT_ARG(Dims), "ContourConnectionDims", &MOC_CHANNEL, OPTEXP_CORE,
00355 "Dimensions of the individual connection kernels in the contour channel "
00356 "(corresponds to old ParamMap parameters 'sizeX=13' and 'sizeY=13')",
00357 "contour-cxn-dims", '\0', "<w>x<h>", "13x13" };
00358
00359
00360 static const ModelOptionDef OPT_ContourConnectionNumOrient =
00361 { MODOPT_ARG(int), "ContourConnectionNumOrient", &MOC_CHANNEL, OPTEXP_CORE,
00362 "Number of orientations to use in the contour channel's connection kernel "
00363 "(corresponds to old ParamMap parameter 'numAngles=12')",
00364 "contour-num-orient", '\0', "<int>", "12" };
00365
00366 ContourConnection::ContourConnection(OptionManager& mgr)
00367 :
00368 ModelComponent(mgr, "Contour Connection Kernel", "ContourConnection"),
00369 itsDims ( &OPT_ContourConnectionDims, this ),
00370 itsNumAngles ( &OPT_ContourConnectionNumOrient, this),
00371 itsW ( -1 ),
00372 itsH ( -1 ),
00373 itsNA ( -1 ),
00374 itsNumWeights ( -1 ),
00375 itsAngles ( 0 ),
00376 itsWeights ( 0 ),
00377 itsExcit ( 0 ),
00378 itsInhib ( 0 )
00379 {}
00380
00381 ContourConnection::~ContourConnection()
00382 {}
00383
00384 void ContourConnection::start2()
00385 {
00386
00387 if ((itsDims.getVal().w() % 2) == 0)
00388 LFATAL("Expected --%s to have odd width, but got --%s=%dx%d",
00389 itsDims.getOptionDef()->longoptname,
00390 itsDims.getOptionDef()->longoptname,
00391 itsDims.getVal().w(), itsDims.getVal().h());
00392
00393 if ((itsDims.getVal().h() % 2) == 0)
00394 LFATAL("Expected --%s to have odd height, but got --%s=%dx%d",
00395 itsDims.getOptionDef()->longoptname,
00396 itsDims.getOptionDef()->longoptname,
00397 itsDims.getVal().w(), itsDims.getVal().h());
00398
00399 if (itsNumAngles.getVal() <= 0)
00400 LFATAL("Expected a positive value for --%s, but got --%s=%d",
00401 itsNumAngles.getOptionDef()->longoptname,
00402 itsNumAngles.getOptionDef()->longoptname,
00403 itsNumAngles.getVal());
00404
00405 itsW = itsDims.getVal().w();
00406 itsH = itsDims.getVal().h();
00407 itsNA = itsNumAngles.getVal();
00408 itsNumWeights = itsNA*itsNA*itsW*itsH;
00409
00410 ASSERT(itsAngles == 0); itsAngles = new float[itsNA];
00411 ASSERT(itsWeights == 0); itsWeights = new float[itsNumWeights];
00412 ASSERT(itsExcit == 0); itsExcit = new float[itsNumWeights];
00413 ASSERT(itsInhib == 0); itsInhib = new float[itsNumWeights];
00414
00415 for (int i = 0; i < itsNA; ++i)
00416 {
00417 itsAngles[i] = i * (180.0 / itsNA);
00418 }
00419
00420 for (int i = 0; i < itsNumWeights; ++i)
00421 {
00422 itsWeights[i] = 0.0f;
00423 }
00424
00425 this->setupConnectionWeights();
00426
00427 printStats();
00428
00429 ModelComponent::start2();
00430 }
00431
00432 void ContourConnection::stop1()
00433 {
00434 itsW = -1;
00435 itsH = -1;
00436 itsNumWeights = -1;
00437 delete [] itsAngles; itsAngles = 0;
00438 delete [] itsWeights; itsWeights = 0;
00439 delete [] itsExcit; itsExcit = 0;
00440 delete [] itsInhib; itsInhib = 0;
00441 }
00442
00443 void ContourConnection::printStats() const
00444 {
00445 int countAll = 0;
00446 int countSuppression = 0;
00447 int countExcitation = 0;
00448 double sumAll = 0;
00449 double sumSuppression = 0;
00450 double sumExcitation = 0;
00451
00452 ASSERT(itsNumWeights > 0);
00453
00454 for (int i = 0; i < itsNumWeights; ++i)
00455 {
00456 const float w = itsWeights[i];
00457
00458 ++countAll;
00459 sumAll += w;
00460
00461 if (w < 0.0)
00462 {
00463 ++countSuppression;
00464 sumSuppression += w;
00465 }
00466 else if (w > 0.0)
00467 {
00468 ++countExcitation;
00469 sumExcitation += w;
00470 }
00471 }
00472
00473 LINFO("%+8.2f (%5d) all", sumAll, countAll);
00474 LINFO("%+8.2f (%5d) suppression", sumSuppression, countSuppression);
00475 LINFO("%+8.2f (%5d) excitation", sumExcitation, countExcitation);
00476 }
00477
00478 Image<byte> ContourConnection::getSummaryImage() const
00479 {
00480 const int nori = itsNumAngles.getVal();
00481
00482 Image<float> weights[(nori+1)*(nori+1)];
00483
00484 weights[0] = Image<float>(itsDims.getVal() * 4, ZEROS);
00485
00486 int c = 1;
00487
00488 for (int b = 0; b < nori; ++b)
00489 {
00490 Image<float> tmp(itsDims.getVal() * 4, ZEROS);
00491 writeText(tmp, Point2D<int>(0,0),
00492 sformat("%.1f", angle(b)).c_str());
00493 inplaceNormalize(tmp, 0.0f, 1.0f);
00494 weights[c++] = tmp;
00495 }
00496
00497 for (int angle_a = 0; angle_a < nori; ++angle_a)
00498 {
00499 Image<float> foo(itsDims.getVal() * 4, ZEROS);
00500 writeText(foo, Point2D<int>(0,0),
00501 sformat("%.1f", angle(angle_a)).c_str());
00502 inplaceNormalize(foo, 0.0f, 1.0f);
00503 weights[c++] = foo;
00504
00505 for (int angle_b = 0; angle_b < nori; ++angle_b)
00506 {
00507 Image<float> tmp(itsDims.getVal(), NO_INIT);
00508
00509 for (int y = 0; y < itsH; ++y)
00510 for (int x = 0; x < itsW; ++x)
00511 tmp.setVal(x, y, weight(angle_a, angle_b, x, y));
00512
00513 weights[c++] = intXY(intXY(tmp, true), true);
00514 }
00515 }
00516
00517 Image<float> img = concatArray(&weights[0],
00518 (nori+1)*(nori+1),
00519 nori+1);
00520 inplaceNormalize(img, 0.0f, 255.0f);
00521
00522 return Image<byte>(img);
00523 }
00524
00525
00526
00527
00528
00529
00530
00531
00532 class ContourConnectionCinnic : public ContourConnection
00533 {
00534 public:
00535 ContourConnectionCinnic(OptionManager& manager);
00536
00537 virtual ~ContourConnectionCinnic() {}
00538
00539 private:
00540 virtual void setupConnectionWeights();
00541
00542 OModelParam<float> itsAngleDropOff;
00543 OModelParam<float> itsAngleSuppress;
00544 OModelParam<float> itsSupMult;
00545 OModelParam<float> itsOrthMult;
00546 OModelParam<float> itsOrthA;
00547 OModelParam<float> itsOrthB;
00548 OModelParam<float> itsNeuronExSize;
00549 OModelParam<float> itsNeuronSupSize;
00550 OModelParam<float> itsNeuronSupStart;
00551 OModelParam<float> itsNeuronOrthSize;
00552 OModelParam<float> itsValueCutoff;
00553 OModelParam<float> itsColinearDiff;
00554 };
00555
00556
00557 static const ModelOptionDef OPT_CinnicAngleDropOff =
00558 { MODOPT_ARG(float), "CinnicAngleDropOff", &MOC_CHANNEL, OPTEXP_CORE,
00559 "AngleDropOff parameter for the cinnic kernel in the contour channel "
00560 "(corresponds to old ParamMap parameter 'AngleDropOff=35.0')",
00561 "cinnic-angle-dropoff", '\0', "<float>", "35.0" };
00562
00563
00564 static const ModelOptionDef OPT_CinnicAngleSuppress =
00565 { MODOPT_ARG(float), "CinnicAngleSuppress", &MOC_CHANNEL, OPTEXP_CORE,
00566 "AngleSuppress parameter for the cinnic kernel in the contour channel "
00567 "(corresponds to old ParamMap parameter 'AngleSuppress=60.0')",
00568 "cinnic-angle-suppress", '\0', "<float>", "60.0" };
00569
00570
00571 static const ModelOptionDef OPT_CinnicSupMult =
00572 { MODOPT_ARG(float), "CinnicSupMult", &MOC_CHANNEL, OPTEXP_CORE,
00573 "SupMult parameter for the cinnic kernel in the contour channel "
00574 "(corresponds to old ParamMap parameter 'SupMult=1.5')",
00575 "cinnic-sup-mult", '\0', "<float>", "1.5" };
00576
00577
00578 static const ModelOptionDef OPT_CinnicOrthMult =
00579 { MODOPT_ARG(float), "CinnicOrthMult", &MOC_CHANNEL, OPTEXP_CORE,
00580 "OrthMult parameter for the cinnic kernel in the contour channel "
00581 "(corresponds to old ParamMap parameter 'OrthMult=0.0')",
00582 "cinnic-orth-mult", '\0', "<float>", "0.0" };
00583
00584
00585 static const ModelOptionDef OPT_CinnicAngleOrthRangeA =
00586 { MODOPT_ARG(float), "CinnicAngleOrthRangeA", &MOC_CHANNEL, OPTEXP_CORE,
00587 "AngleOrthRangeA parameter for the cinnic kernel in the contour channel "
00588 "(corresponds to old ParamMap parameter 'AngleOrthRangeA=45.0')",
00589 "cinnic-angle-orth-range-a", '\0', "<float>", "45.0" };
00590
00591
00592 static const ModelOptionDef OPT_CinnicAngleOrthRangeB =
00593 { MODOPT_ARG(float), "CinnicAngleOrthRangeB", &MOC_CHANNEL, OPTEXP_CORE,
00594 "AngleOrthRangeB parameter for the cinnic kernel in the contour channel "
00595 "(corresponds to old ParamMap parameter 'AngleOrthRangeB=35.0')",
00596 "cinnic-angle-orth-range-b", '\0', "<float>", "35.0" };
00597
00598
00599 static const ModelOptionDef OPT_CinnicNeuronExSize =
00600 { MODOPT_ARG(float), "CinnicNeuronExSize", &MOC_CHANNEL, OPTEXP_CORE,
00601 "NeuronExSize parameter for the cinnic kernel in the contour channel "
00602 "(corresponds to old ParamMap parameter 'NeuronExSize=12.0')",
00603 "cinnic-neuron-ex-size", '\0', "<float>", "12.0" };
00604
00605
00606 static const ModelOptionDef OPT_CinnicNeuronSupSize =
00607 { MODOPT_ARG(float), "CinnicNeuronSupSize", &MOC_CHANNEL, OPTEXP_CORE,
00608 "NeuronSupSize parameter for the cinnic kernel in the contour channel "
00609 "(corresponds to old ParamMap parameter 'NeuronSupSize=10.0')",
00610 "cinnic-neuron-sup-size", '\0', "<float>", "10.0" };
00611
00612
00613 static const ModelOptionDef OPT_CinnicNeuronSupStart =
00614 { MODOPT_ARG(float), "CinnicNeuronSupStart", &MOC_CHANNEL, OPTEXP_CORE,
00615 "NeuronSupStart parameter for the cinnic kernel in the contour channel "
00616 "(corresponds to old ParamMap parameter 'NeuronSupStart=1.0')",
00617 "cinnic-neuron-sup-start", '\0', "<float>", "1.0" };
00618
00619
00620 static const ModelOptionDef OPT_CinnicNeuronOrthSize =
00621 { MODOPT_ARG(float), "CinnicNeuronOrthSize", &MOC_CHANNEL, OPTEXP_CORE,
00622 "NeuronOrthSize parameter for the cinnic kernel in the contour channel "
00623 "(corresponds to old ParamMap parameter 'NeuronOrthSize=12.0')",
00624 "cinnic-neuron-orth-size", '\0', "<float>", "12.0" };
00625
00626
00627 static const ModelOptionDef OPT_CinnicValueCutoff =
00628 { MODOPT_ARG(float), "CinnicValueCutoff", &MOC_CHANNEL, OPTEXP_CORE,
00629 "valueCutOff parameter for the cinnic kernel in the contour channel "
00630 "(corresponds to old ParamMap parameter 'valueCutOff=0.0001')",
00631 "cinnic-value-cutoff", '\0', "<float>", "0.0001" };
00632
00633
00634 static const ModelOptionDef OPT_CinnicColinearDiff =
00635 { MODOPT_ARG(float), "CinnicColinearDiff", &MOC_CHANNEL, OPTEXP_CORE,
00636 "ColinearDiff parameter for the cinnic kernel in the contour channel "
00637 "(corresponds to old ParamMap parameter 'CoLinearDiff=16.0')",
00638 "cinnic-colinear-diff", '\0', "<float>", "16.0" };
00639
00640 ContourConnectionCinnic::
00641 ContourConnectionCinnic(OptionManager& manager)
00642 :
00643 ContourConnection(manager),
00644 itsAngleDropOff (&OPT_CinnicAngleDropOff, this),
00645 itsAngleSuppress (&OPT_CinnicAngleSuppress, this),
00646 itsSupMult (&OPT_CinnicSupMult, this),
00647 itsOrthMult (&OPT_CinnicOrthMult, this),
00648 itsOrthA (&OPT_CinnicAngleOrthRangeA, this),
00649 itsOrthB (&OPT_CinnicAngleOrthRangeB, this),
00650 itsNeuronExSize (&OPT_CinnicNeuronExSize, this),
00651 itsNeuronSupSize (&OPT_CinnicNeuronSupSize, this),
00652 itsNeuronSupStart (&OPT_CinnicNeuronSupStart, this),
00653 itsNeuronOrthSize (&OPT_CinnicNeuronOrthSize, this),
00654 itsValueCutoff (&OPT_CinnicValueCutoff, this),
00655 itsColinearDiff (&OPT_CinnicColinearDiff, this)
00656 {}
00657
00658 void ContourConnectionCinnic::setupConnectionWeights()
00659 {
00660 ASSERT(itsValueCutoff.getVal() >= 0.0f);
00661
00662 ASSERT(itsW > 0);
00663 ASSERT(itsH > 0);
00664
00665 const int nori = itsNumAngles.getVal();
00666
00667 const Point2D<int> center = this->getCenter();
00668
00669 for (int x = 0; x < itsW; ++x) {
00670 for (int y = 0; y < itsH; ++y) {
00671
00672 const float distance =
00673 sqrt( (center.i-x)*(center.i-x) + (center.j-y)*(center.j-y) );
00674
00675 if (distance == 0)
00676 continue;
00677
00678
00679
00680
00681 const float connectingAngle = (180/M_PI) * atan2(center.j-y, x-center.i);
00682
00683 for (int a = 0; a < nori; ++a) {
00684
00685
00686
00687 const float angDistAC = angDist(connectingAngle, angle(a));
00688
00689 for (int b = 0; b < nori; ++b) {
00690
00691 const float angDistAB = angDist(angle(a), angle(b));
00692
00693
00694
00695 const bool is_orth =
00696 (angDistAC > 90.0f-itsOrthA.getVal())
00697 &&
00698 (angDistAB > 90.0f-itsOrthB.getVal());
00699
00700 if (is_orth)
00701 {
00702 if (itsOrthMult.getVal() <= 0.0f)
00703 continue;
00704
00705 const float alphaAngleMod = 1.0f - (90.0f-angDistAC)/itsOrthA.getVal();
00706 ASSERT(alphaAngleMod >= 0);
00707
00708 const float distanceMod = 1.0f - distance/(itsNeuronOrthSize.getVal()/2);
00709
00710 if (distanceMod <= 0.0f)
00711 continue;
00712
00713 weight(a, b, x, y) =
00714 itsOrthMult.getVal() * alphaAngleMod * distanceMod;
00715 }
00716 else if (angDistAC > itsAngleDropOff.getVal())
00717 {
00718 ASSERT(itsAngleDropOff.getVal() > (90.0f - itsAngleSuppress.getVal()));
00719
00720
00721 if (angDistAB >= itsColinearDiff.getVal())
00722 continue;
00723
00724
00725
00726 if ((distance < itsNeuronSupStart.getVal()) ||
00727 (distance >= (itsNeuronSupSize.getVal()/2)))
00728 continue;
00729
00730 const float distanceMod = 1.0f - distance/(itsNeuronSupSize.getVal()/2);
00731
00732 ASSERT(distanceMod > 0.0f);
00733
00734 const float alphaAngleMod =
00735 1.0f - (90.0f-angDistAC)/itsAngleSuppress.getVal();
00736
00737 ASSERT(alphaAngleMod >= 0.0f);
00738
00739 const float angleMod =
00740 alphaAngleMod * (1.0f - angDistAB / itsColinearDiff.getVal());
00741
00742 weight(a, b, x, y) =
00743 -1.0f * itsSupMult.getVal() * ((angleMod + distanceMod)/2);
00744 }
00745 else
00746 {
00747
00748
00749 const float angDistBC = angDist(connectingAngle, angle(b));
00750
00751 const float betaAngleMod = 1.0f - angDistBC/itsAngleDropOff.getVal();
00752
00753 if (betaAngleMod <= itsValueCutoff.getVal())
00754 continue;
00755
00756 const float alphaAngleMod = 1.0f - angDistAC/itsAngleDropOff.getVal();
00757 ASSERT(alphaAngleMod >= 0.0f);
00758
00759
00760 const float angleMod = alphaAngleMod * betaAngleMod;
00761
00762 if (angleMod <= itsValueCutoff.getVal())
00763 continue;
00764
00765 const float distanceMod = 1.0f - distance/(itsNeuronExSize.getVal()/2);
00766
00767 if (distanceMod <= itsValueCutoff.getVal())
00768 continue;
00769
00770 weight(a, b, x, y) = (angleMod + distanceMod)/2;
00771 }
00772 }
00773 }
00774 }
00775 }
00776 }
00777
00778
00779
00780
00781
00782
00783
00784
00785
00786 class ContourConnectionBraun : public ContourConnection
00787 {
00788 public:
00789 ContourConnectionBraun(OptionManager& manager,
00790 bool doOneNormalize);
00791
00792 virtual ~ContourConnectionBraun() {}
00793
00794 private:
00795 virtual void setupConnectionWeights();
00796
00797 OModelParam<float> itsPeakDistance;
00798 OModelParam<float> itsExcitStrength;
00799 OModelParam<float> itsInhibStrength;
00800 OModelParam<float> itsBeta;
00801 OModelParam<bool> itsOrthoIntxn;
00802
00803 const bool itsDoOneNormalize;
00804 };
00805
00806
00807 static const ModelOptionDef OPT_ContourConnectionPeakDistance =
00808 { MODOPT_ARG(float), "ContourConnectionPeakDistance", &MOC_CHANNEL, OPTEXP_CORE,
00809 "PeakDistance parameter for the Braun kernel in the contour channel "
00810 "(corresponds to old ParamMap parameter 'peakDistance=2.0')",
00811 "contour-cxn-peak-distance", '\0', "<float>", "2.0" };
00812
00813
00814 static const ModelOptionDef OPT_ContourConnectionExcitStrength =
00815 { MODOPT_ARG(float), "ContourConnectionExcitStrength", &MOC_CHANNEL, OPTEXP_CORE,
00816 "ExcitStrength parameter for the Braun kernel in the contour channel "
00817 "(corresponds to old ParamMap parameter 'excitStrength=1.7')",
00818 "contour-cxn-excit-strength", '\0', "<float>", "1.7" };
00819
00820
00821 static const ModelOptionDef OPT_ContourConnectionInhibStrength =
00822 { MODOPT_ARG(float), "ContourConnectionInhibStrength", &MOC_CHANNEL, OPTEXP_CORE,
00823 "InhibStrength parameter for the Braun kernel in the contour channel "
00824 "(corresponds to old ParamMap parameter 'inhibStrength=-0.3')",
00825 "contour-cxn-inhib-strength", '\0', "<float>", "-0.3" };
00826
00827
00828 static const ModelOptionDef OPT_ContourConnectionBeta =
00829 { MODOPT_ARG(float), "ContourConnectionBeta", &MOC_CHANNEL, OPTEXP_CORE,
00830 "Beta parameter for the Braun kernel in the contour channel "
00831 "(corresponds to old ParamMap parameter 'beta=0.07')",
00832 "contour-cxn-beta", '\0', "<float>", "0.07" };
00833
00834
00835 static const ModelOptionDef OPT_ContourConnectionOrthoIntxn =
00836 { MODOPT_FLAG, "ContourConnectionOrthoIntxn", &MOC_CHANNEL, OPTEXP_CORE,
00837 "Whether to include (inhibitory) interactions between (near-)orthogonal "
00838 "orientations in the contour channel "
00839 "(corresponds to old ParamMap parameter 'orthoIntxn=0')",
00840 "contour-ortho-interaction", '\0', "", "false" };
00841
00842 ContourConnectionBraun::
00843 ContourConnectionBraun(OptionManager& manager,
00844 bool doOneNormalize)
00845 :
00846 ContourConnection(manager),
00847 itsPeakDistance(&OPT_ContourConnectionPeakDistance, this),
00848 itsExcitStrength(&OPT_ContourConnectionExcitStrength, this),
00849 itsInhibStrength(&OPT_ContourConnectionInhibStrength, this),
00850 itsBeta(&OPT_ContourConnectionBeta, this),
00851 itsOrthoIntxn(&OPT_ContourConnectionOrthoIntxn, this),
00852 itsDoOneNormalize(doOneNormalize)
00853 {}
00854
00855 void ContourConnectionBraun::setupConnectionWeights()
00856 {
00857 ASSERT(itsW > 0);
00858 ASSERT(itsH > 0);
00859
00860 const int nori = itsNumAngles.getVal();
00861 const Point2D<int> center = this->getCenter();
00862
00863 for (int x = 0; x < itsW; ++x) {
00864 for (int y = 0; y < itsH; ++y) {
00865
00866 const float distance =
00867 sqrt( (center.i-x)*(center.i-x) + (center.j-y)*(center.j-y) );
00868
00869 const float distRatio = distance/itsPeakDistance.getVal();
00870
00871 const float rho = pow(distRatio * exp(1.0 - distRatio), 2.0);
00872
00873 const float connectingAngle = (180/M_PI) * atan2(center.j-y, x-center.i);
00874
00875 for (int a = 0; a < nori; ++a) {
00876
00877 const float angDistAC = angDist(angle(a), connectingAngle);
00878
00879 for (int b = 0; b < nori; ++b) {
00880
00881 const float angDistAB = angDist(angle(a), angle(b));
00882 const float angDistBC = angDist(angle(b), connectingAngle);
00883
00884 const float avgAngle = fabs(angle(a)-angle(b)) > 90.0f
00885 ? (angle(a)+angle(b)-180.0f) / 2.0f
00886 : (angle(a)+angle(b)) / 2.0f;
00887
00888 const bool colinear =
00889 angDistAB < 45.0f && angDistAC < 45.0f && angDistBC < 45.0f;
00890
00891 const float psi = colinear
00892 ? exp(-itsBeta.getVal() * angDist(avgAngle, connectingAngle))
00893 : 0.0f;
00894
00895 const float excitPart = rho * psi * itsExcitStrength.getVal();
00896
00897 const bool haveInhib = (angDistAB < 45.0f || itsOrthoIntxn.getVal());
00898
00899 const float inhibPart =
00900 haveInhib
00901 ? rho * itsInhibStrength.getVal()
00902 : 0.0f;
00903
00904 weight(a, b, x, y) = excitPart + inhibPart;
00905 itsInhib[index(a,b,x,y)] = inhibPart;
00906 itsExcit[index(a,b,x,y)] = excitPart;
00907 }
00908 }
00909 }
00910 }
00911
00912 if (itsDoOneNormalize)
00913 {
00914 float maxw = 0.0f;
00915
00916 ASSERT(itsNumWeights > 0);
00917
00918 for (int i = 0; i < itsNumWeights; ++i)
00919 {
00920 if (fabs(itsWeights[i]) > maxw) maxw = fabs(itsWeights[i]);
00921 }
00922
00923 if (maxw > 0.0f)
00924 {
00925 for (int i = 0; i < itsNumWeights; ++i)
00926 {
00927 itsWeights[i] /= maxw;
00928 }
00929 }
00930 }
00931 }
00932
00933
00934
00935
00936
00937
00938
00939
00940 class ContourLayer : public ModelComponent
00941 {
00942 public:
00943 ContourLayer(OptionManager& mgr,
00944 const std::string& descrName,
00945 const std::string& tagName);
00946
00947 virtual ~ContourLayer();
00948
00949 Image<float> compute(const ImageSet<float>& input,
00950 const ContourConnection& connection,
00951 const std::string& saveSuffix,
00952 int normFlags,
00953 SaveSet& save,
00954 const unsigned int niter,
00955 const Dims& origdims);
00956
00957 protected:
00958 OModelParam<bool> itsSaveLayerOutput;
00959 OModelParam<bool> itsSaveLayerDetails;
00960
00961 private:
00962 virtual void startNewInput(const ImageSet<float>& input,
00963 const ContourConnection& connection) = 0;
00964
00965
00966 virtual Image<float> iterate(const ContourConnection& connection,
00967 const char* saveSuffix,
00968 int normFlags,
00969 SaveSet& save) = 0;
00970 };
00971
00972
00973 static const ModelOptionDef OPT_ContourLayerSaveOutput =
00974 { MODOPT_FLAG, "ContourLayerSaveOutput", &MOC_CHANNEL, OPTEXP_SAVE,
00975 "Whether to save per-iteration output from individual scale bands "
00976 "in the contour channel",
00977 "save-contour-layer-output", '\0', "", "false" };
00978
00979
00980 static const ModelOptionDef OPT_ContourLayerSaveDetails =
00981 { MODOPT_FLAG, "ContourLayerSaveDetails", &MOC_CHANNEL, OPTEXP_SAVE,
00982 "Whether to save detailed intermediate maps from individual scale "
00983 "bands in the contour channel",
00984 "save-contour-layer-details", '\0', "", "false" };
00985
00986 ContourLayer::ContourLayer(OptionManager& mgr,
00987 const std::string& descrName,
00988 const std::string& tagName)
00989 :
00990 ModelComponent(mgr, descrName, tagName),
00991 itsSaveLayerOutput(&OPT_ContourLayerSaveOutput, this),
00992 itsSaveLayerDetails(&OPT_ContourLayerSaveDetails, this)
00993 {}
00994
00995 ContourLayer::~ContourLayer() {}
00996
00997 Image<float> ContourLayer::compute(const ImageSet<float>& input,
00998 const ContourConnection& connection,
00999 const std::string& saveSuffix,
01000 int normFlags,
01001 SaveSet& save,
01002 const unsigned int niter,
01003 const Dims& origdims)
01004 {
01005 this->startNewInput(input, connection);
01006
01007 Image<float> salmap;
01008
01009 for (unsigned int iter = 0; iter < niter; ++iter)
01010 {
01011 LINFO("scale %s, iteration %u / %u ... ",
01012 saveSuffix.c_str(), iter+1, niter);
01013
01014 const std::string sfx2 =
01015 sformat("%s.i-%02u", saveSuffix.c_str(), iter);
01016
01017 salmap = this->iterate(connection, sfx2.c_str(), normFlags,
01018 save);
01019
01020 if (itsSaveLayerOutput.getVal())
01021 save.add(sformat("potential.%s", sfx2.c_str()),
01022 GenericFrame(rescale(salmap, origdims),
01023 normFlags));
01024 }
01025
01026 return salmap;
01027 }
01028
01029
01030
01031
01032
01033
01034
01035
01036 class ContourLayerDynamic : public ContourLayer
01037 {
01038 private:
01039 OModelParam<float> itsTimestep;
01040 OModelParam<float> itsFilterThresh;
01041
01042 ImageSet<float> itsInput;
01043 ImageSet<float> itsNeuronsCur;
01044 ImageSet<float> itsNeuronsTempSpace;
01045
01046 public:
01047 ContourLayerDynamic(OptionManager& mgr);
01048
01049 virtual ~ContourLayerDynamic();
01050
01051 private:
01052 virtual void startNewInput(const ImageSet<float>& input,
01053 const ContourConnection& connection);
01054
01055 virtual Image<float> iterate(const ContourConnection& connection,
01056 const char* saveSuffix,
01057 int normFlags,
01058 SaveSet& save);
01059 };
01060
01061
01062
01063
01064
01065
01066
01067
01068 static const ModelOptionDef OPT_ContourLayerDynamicTimestep =
01069 { MODOPT_ARG(float), "ContourLayerDynamicTimestep", &MOC_CHANNEL, OPTEXP_CORE,
01070 "Timestep for the contour channel with --contour-dynamics-type=Dynamic "
01071 "(corresponds to old ParamMap parameter 'timestep=0.5')",
01072 "contour-layer-timestep", '\0', "<float>", "0.5" };
01073
01074
01075 static const ModelOptionDef OPT_ContourLayerDynamicFilterThresh =
01076 { MODOPT_ARG(float), "ContourLayerDynamicFilterThresh", &MOC_CHANNEL, OPTEXP_CORE,
01077 "Timestep for the contour channel with --contour-dynamics-type=Dynamic "
01078 "(corresponds to old ParamMap parameter 'filterThresh=0.0001')",
01079 "contour-layer-filter-thresh", '\0', "<float>", "0.0001" };
01080
01081 ContourLayerDynamic::ContourLayerDynamic(OptionManager& mgr)
01082 :
01083 ContourLayer(mgr, "Dynamic Contour Layer", "ContourLayerDynamic"),
01084 itsTimestep ( &OPT_ContourLayerDynamicTimestep, this),
01085 itsFilterThresh ( &OPT_ContourLayerDynamicFilterThresh, this),
01086 itsInput ( ),
01087 itsNeuronsCur ( ),
01088 itsNeuronsTempSpace ( )
01089 {}
01090
01091 ContourLayerDynamic::~ContourLayerDynamic()
01092 {}
01093
01094 void ContourLayerDynamic::startNewInput(const ImageSet<float>& input,
01095 const ContourConnection& cxn)
01096 {
01097 ASSERT(rangeOf(input).max() == 1.0f);
01098
01099 itsInput = input;
01100 itsNeuronsCur = ImageSet<float>(cxn.numAngles(), input[0].getDims());
01101 itsNeuronsTempSpace = ImageSet<float>();
01102 }
01103
01104 Image<float> ContourLayerDynamic::iterate(const ContourConnection& cxn,
01105 const char* saveSuffix,
01106 int normFlags,
01107 SaveSet& save)
01108 {
01109 ASSERT(cxn.started());
01110
01111 const Dims dims = itsInput[0].getDims();
01112
01113
01114
01115 const int nori = cxn.numAngles();
01116 const float timestep = itsTimestep.getVal();
01117 const float fthresh = itsFilterThresh.getVal();
01118 const Dims kerndims = cxn.getDims();
01119 const Point2D<int> kerncenter = cxn.getCenter();
01120
01121 itsNeuronsTempSpace = itsNeuronsCur;
01122 doAddWeighted(itsNeuronsTempSpace, itsInput, timestep);
01123
01124 itsNeuronsTempSpace *= (1.0f - timestep);
01125
01126
01127
01128 Image<float>::const_iterator input_itrs[nori];
01129 Image<float>::const_iterator prev_charge_itrs[nori];
01130 Image<float>::iterator charge_itrs[nori];
01131
01132 Image<float> exc[nori];
01133 Image<float> inh[nori];
01134 Image<float> chg[nori];
01135
01136 for (int b = 0; b < nori; ++b)
01137 {
01138 input_itrs[b] = itsInput[b].begin();
01139 prev_charge_itrs[b] = itsNeuronsCur[b].begin();
01140 charge_itrs[b] = itsNeuronsTempSpace[b].beginw();
01141
01142 exc[b] = Image<float>(itsInput[b].getDims(), ZEROS);
01143 inh[b] = Image<float>(itsInput[b].getDims(), ZEROS);
01144 chg[b] = Image<float>(itsInput[b].getDims(), ZEROS);
01145 }
01146
01147
01148
01149 for (int angle_a = 0; angle_a < nori; ++angle_a)
01150 {
01151 Image<float>& charges_a = itsNeuronsCur[angle_a];
01152
01153 Image<float>::const_iterator input_a_itr = itsInput[angle_a].begin();
01154
01155 Image<float>::const_iterator charges_a_itr = charges_a.begin();
01156
01157 for (int a_y = 0; a_y < dims.h(); ++a_y)
01158 {
01159 for (int a_x = 0;
01160 a_x < dims.w();
01161 ++a_x, ++input_a_itr, ++charges_a_itr)
01162 {
01163 const float input_a = *input_a_itr;
01164
01165 if (input_a <= fthresh)
01166 continue;
01167
01168 const float charge_a = *charges_a_itr;
01169
01170 const float excit_a = excitFiring(charge_a);
01171
01172 const float inhib_a = inhibFiring(excit_a);
01173
01174 chg[angle_a].setVal(a_x, a_y, charge_a);
01175 exc[angle_a].setVal(a_x, a_y, excit_a);
01176 inh[angle_a].setVal(a_x, a_y, inhib_a);
01177
01178 for (int rf_x = 0; rf_x < kerndims.w(); ++rf_x)
01179 {
01180
01181 const int b_x = a_x + (rf_x-kerncenter.i);
01182
01183 if (b_x < 0 || b_x >= dims.w())
01184 continue;
01185
01186 for (int rf_y = 0; rf_y < kerndims.h(); ++rf_y)
01187 {
01188 const int b_y = a_y - (rf_y-kerncenter.j);
01189
01190 if (b_y < 0 || b_y >= dims.h())
01191 continue;
01192
01193 const int offset = b_x + b_y*dims.w();
01194
01195 const float* weights_ptr =
01196 cxn.weightAXY(angle_a, rf_x, rf_y);
01197
01198 const float* const w_excit_ptr =
01199 cxn.excitAXY(angle_a, rf_x, rf_y);
01200
01201 const float* const w_inhib_ptr =
01202 cxn.inhibAXY(angle_a, rf_x, rf_y);
01203
01204 for (int angle_b = 0; angle_b < nori; ++angle_b)
01205 {
01206 const float cxn_strength = *weights_ptr++;
01207
01208 if (cxn_strength == 0.0f)
01209 continue;
01210
01211 const float input_b = input_itrs[angle_b][offset];
01212
01213 const float charge_b = prev_charge_itrs[angle_b][offset];
01214
01215
01216 const float excit =
01217 excit_a * input_b * w_excit_ptr[angle_b];
01218
01219
01220 const float inhib =
01221 inhib_a * charge_b * w_inhib_ptr[angle_b];
01222
01223
01224 charge_itrs[angle_b][offset] +=
01225 timestep * (excit + inhib);
01226 }
01227 }
01228 }
01229 }
01230 }
01231 }
01232
01233 if (itsSaveLayerDetails.getVal())
01234 {
01235 const Image<float> all_chg = concatArray(&chg[0], nori, 4);
01236 const Image<float> all_exc = concatArray(&exc[0], nori, 4);
01237 const Image<float> all_inh = concatArray(&inh[0], nori, 4);
01238
01239 save.add(sformat("all_chg.%s", saveSuffix), GenericFrame(all_chg, normFlags));
01240 save.add(sformat("all_exc.%s", saveSuffix), GenericFrame(all_exc, normFlags));
01241 save.add(sformat("all_inh.%s", saveSuffix), GenericFrame(all_inh, normFlags));
01242 }
01243
01244 itsNeuronsCur.swap(itsNeuronsTempSpace);
01245
01246 const Image<float> activation = sum(itsNeuronsCur);
01247
01248 printStats(activation, "activation");
01249
01250 return activation;
01251 }
01252
01253
01254
01255
01256
01257
01258
01259
01260 class ContourLayerStatic : public ContourLayer
01261 {
01262 private:
01263 OModelParam<float> itsFastPlast;
01264 OModelParam<float> itsGroupBottom;
01265 OModelParam<float> itsGroupTop;
01266 OModelParam<int> itsGroupSize;
01267 OModelParam<float> itsLeak;
01268 OModelParam<float> itsSuppressionAdd;
01269 OModelParam<float> itsSuppressionSub;
01270 OModelParam<float> itsSigmoidThresh;
01271 OModelParam<float> itsFilterThresh;
01272
01273 ImageSet<float> itsInput;
01274
01275 Image<float> itsSalmap;
01276
01277 Image<float> itsEnergyMap;
01278
01279 ImageSet<float> itsNeuronsCur;
01280 ImageSet<float> itsNeuronsTempSpace;
01281
01282 Image<float> itsGroupModMap;
01283
01284 void convolveAll(const ContourConnection& cxn);
01285
01286 public:
01287 ContourLayerStatic(OptionManager& mgr);
01288
01289 virtual ~ContourLayerStatic();
01290
01291 private:
01292 virtual void startNewInput(const ImageSet<float>& input,
01293 const ContourConnection& connection);
01294
01295 virtual Image<float> iterate(const ContourConnection& connection,
01296 const char* saveSuffix,
01297 int normFlags,
01298 SaveSet& save);
01299 };
01300
01301
01302
01303
01304
01305
01306
01307
01308 static const ModelOptionDef OPT_CinnicLayerFastPlast =
01309 { MODOPT_ARG(float), "CinnicLayerFastPlast", &MOC_CHANNEL, OPTEXP_CORE,
01310 "Fast-plasticity strength under --contour-dynamics-type=Static "
01311 "(corresponds to old ParamMap parameter 'fastPlast=7.2077')",
01312 "cinnic-fast-plast", '\0', "<float>", "7.2077" };
01313
01314
01315 static const ModelOptionDef OPT_CinnicLayerGroupBottom =
01316 { MODOPT_ARG(float), "CinnicLayerGroupBottom", &MOC_CHANNEL, OPTEXP_CORE,
01317 "Threshold beneath which group suppression is removed, under "
01318 "--contour-dynamics-type=Static "
01319 "(corresponds to old ParamMap parameter 'groupAvgBottom=0.0')",
01320 "cinnic-group-bottom", '\0', "<float>", "0.0" };
01321
01322
01323 static const ModelOptionDef OPT_CinnicLayerGroupTop =
01324 { MODOPT_ARG(float), "CinnicLayerGroupTop", &MOC_CHANNEL, OPTEXP_CORE,
01325 "Threshold above which group suppression is added, under "
01326 "--contour-dynamics-type=Static "
01327 "(corresponds to old ParamMap parameter 'groupAvgTop=0.03125')",
01328 "cinnic-group-top", '\0', "<float>", "0.03125" };
01329
01330
01331 static const ModelOptionDef OPT_CinnicLayerGroupSize =
01332 { MODOPT_ARG(int), "CinnicLayerGroupSize", &MOC_CHANNEL, OPTEXP_CORE,
01333 "Width of low-pass kernel used to compute group suppression, under "
01334 "--contour-dynamics-type=Static "
01335 "(corresponds to old ParamMap parameter 'groupSize=15')",
01336 "cinnic-group-size", '\0', "<int>", "15" };
01337
01338
01339 static const ModelOptionDef OPT_CinnicLayerLeak =
01340 { MODOPT_ARG(float), "CinnicLayerLeak", &MOC_CHANNEL, OPTEXP_CORE,
01341 "Amount of global leak during each iteration under "
01342 "--contour-dynamics-type=Static "
01343 "(corresponds to old ParamMap parameter 'leakFactor=0.5')",
01344 "cinnic-leak", '\0', "<float>", "0.5" };
01345
01346
01347 static const ModelOptionDef OPT_CinnicLayerSuppressionAdd =
01348 { MODOPT_ARG(float), "CinnicLayerSuppressionAdd", &MOC_CHANNEL, OPTEXP_CORE,
01349 "Scale factor for group suppression added where the rate of change "
01350 "exceeds --cinnic-group-top, --contour-dynamics-type=Static "
01351 "(corresponds to old ParamMap parameter 'suppressionAdd=1.0')",
01352 "cinnic-suppression-add", '\0', "<float>", "1.0" };
01353
01354
01355 static const ModelOptionDef OPT_CinnicLayerSuppressionSub =
01356 { MODOPT_ARG(float), "CinnicLayerSuppressionSub", &MOC_CHANNEL, OPTEXP_CORE,
01357 "Scale factor for group suppression removed where the rate of change "
01358 "is less than --cinnic-group-bottom, --contour-dynamics-type=Static "
01359 "(corresponds to old ParamMap parameter 'suppressionSub=1.0')",
01360 "cinnic-suppression-sub", '\0', "<float>", "1.0" };
01361
01362
01363 static const ModelOptionDef OPT_CinnicLayerSigmoidThresh =
01364 { MODOPT_ARG(float), "CinnicLayerSigmoidThresh", &MOC_CHANNEL, OPTEXP_CORE,
01365 "Threshold for the output sigmoid nonlinearity, under "
01366 "--contour-dynamics-type=Static "
01367 "(corresponds to old ParamMap parameter 'sigmoidThresh=9.0')",
01368 "cinnic-sigmoid-thresh", '\0', "<float>", "9.0" };
01369
01370
01371 static const ModelOptionDef OPT_CinnicLayerFilterThresh =
01372 { MODOPT_ARG(float), "CinnicLayerFilterThresh", &MOC_CHANNEL, OPTEXP_CORE,
01373 "Filter values less than this threshold will be forced to 0, under "
01374 "--contour-dynamics-type=Static "
01375 "(corresponds to old ParamMap parameter 'filterThresh=0.0001')",
01376 "cinnic-filter-thresh", '\0', "<float>", "0.0001" };
01377
01378 ContourLayerStatic::ContourLayerStatic(OptionManager& mgr)
01379 :
01380 ContourLayer(mgr, "Static Contour Layer", "ContourLayerStatic"),
01381 itsFastPlast ( &OPT_CinnicLayerFastPlast, this ),
01382 itsGroupBottom ( &OPT_CinnicLayerGroupBottom, this ),
01383 itsGroupTop ( &OPT_CinnicLayerGroupTop, this ),
01384 itsGroupSize ( &OPT_CinnicLayerGroupSize, this ),
01385 itsLeak ( &OPT_CinnicLayerLeak, this ),
01386 itsSuppressionAdd ( &OPT_CinnicLayerSuppressionAdd, this ),
01387 itsSuppressionSub ( &OPT_CinnicLayerSuppressionSub, this ),
01388 itsSigmoidThresh ( &OPT_CinnicLayerSigmoidThresh, this ),
01389 itsFilterThresh ( &OPT_CinnicLayerFilterThresh, this ),
01390 itsInput ( ),
01391 itsSalmap ( ),
01392 itsEnergyMap ( ),
01393 itsNeuronsCur ( ),
01394 itsNeuronsTempSpace ( ),
01395 itsGroupModMap ( )
01396 {}
01397
01398 ContourLayerStatic::~ContourLayerStatic()
01399 {}
01400
01401 void ContourLayerStatic::startNewInput(const ImageSet<float>& input,
01402 const ContourConnection& cxn)
01403 {
01404 ASSERT(rangeOf(input).max() == 1.0f);
01405
01406 itsInput = input;
01407 itsSalmap = Image<float>(input[0].getDims(), ZEROS);
01408 itsEnergyMap = Image<float>(input[0].getDims(), ZEROS);
01409 itsNeuronsCur = ImageSet<float>(cxn.numAngles(), input[0].getDims());
01410 itsNeuronsTempSpace = ImageSet<float>(cxn.numAngles(), input[0].getDims());
01411 itsGroupModMap = Image<float>(input[0].getDims(), ZEROS);
01412 itsGroupModMap.clear(1.0f);
01413 }
01414
01415 void ContourLayerStatic::convolveAll(const ContourConnection& cxn)
01416 {
01417 ASSERT(cxn.started());
01418
01419 const Dims dims = itsInput[0].getDims();
01420
01421 doClear(itsNeuronsTempSpace, 0.0f);
01422
01423
01424 Accum fpTotal;
01425 Accum fpHi;
01426 Accum fpLo;
01427
01428
01429
01430 const int nori = cxn.numAngles();
01431 const float cfgFastPlast = itsFastPlast.getVal();
01432 const float cfgFilterThresh = itsFilterThresh.getVal();
01433 const Dims kerndims = cxn.getDims();
01434 const Point2D<int> kerncenter = cxn.getCenter();
01435
01436
01437
01438 Image<float>::iterator charges_b_itrs[nori];
01439 Image<float>::const_iterator input_b_itrs[nori];
01440
01441 for (int b = 0; b < nori; ++b)
01442 {
01443 charges_b_itrs[b] = itsNeuronsTempSpace[b].beginw();
01444 input_b_itrs[b] = itsInput[b].begin();
01445 }
01446
01447
01448 CpuTimer t1, t2;
01449
01450
01451 int c = 0;
01452 const int maxc = nori * dims.h() * dims.w();
01453
01454
01455
01456 for (int angle_a = 0; angle_a < nori; ++angle_a)
01457 {
01458 Image<float>& charges_a = itsNeuronsCur[angle_a];
01459
01460 Image<float>::const_iterator input_a_itr = itsInput[angle_a].begin();
01461
01462 Image<float>::const_iterator charges_a_itr = charges_a.begin();
01463
01464 for (int a_y = 0; a_y < dims.h(); ++a_y)
01465 {
01466 for (int a_x = 0;
01467 a_x < dims.w();
01468 ++a_x, ++input_a_itr, ++charges_a_itr)
01469 {
01470 const float input_a = *input_a_itr;
01471
01472 if (input_a <= cfgFilterThresh)
01473 continue;
01474
01475 const float charge_a = *charges_a_itr;
01476
01477
01478 float fastPlastMod = charge_a * cfgFastPlast;
01479 fpTotal.add(fastPlastMod);
01480 if (fastPlastMod < 1.0f) { fpLo.add(fastPlastMod); fastPlastMod = 1.0f; }
01481 else if (fastPlastMod > 5.0f) { fpHi.add(fastPlastMod); fastPlastMod = 5.0f; }
01482
01483 const float groupMod = itsGroupModMap.getVal(a_x, a_y);
01484
01485 for (int rf_x = 0; rf_x < kerndims.w(); ++rf_x)
01486 {
01487
01488 const int b_x = a_x + (rf_x-kerncenter.i);
01489
01490 if (b_x < 0 || b_x >= dims.w())
01491 continue;
01492
01493 for (int rf_y = 0; rf_y < kerndims.h(); ++rf_y)
01494 {
01495 const int b_y = a_y - (rf_y-kerncenter.j);
01496
01497 if (b_y < 0 || b_y >= dims.h())
01498 continue;
01499
01500 const int offset = b_x + b_y*dims.w();
01501
01502 const float* weights_ptr =
01503 cxn.weightAXY(angle_a, rf_x, rf_y);
01504
01505 for (int angle_b = 0; angle_b < nori; ++angle_b)
01506 {
01507 const float cxn_strength = *weights_ptr++;
01508
01509 if (cxn_strength == 0.0f)
01510 continue;
01511
01512 const float input_b = input_b_itrs[angle_b][offset];
01513
01514 if (input_b <= cfgFilterThresh)
01515 continue;
01516
01517
01518
01519 float charge =
01520 input_a * input_b
01521 * cxn_strength * fastPlastMod;
01522
01523 if (cxn_strength < 0)
01524 charge *= groupMod;
01525
01526
01527 charges_b_itrs[angle_b][offset] += charge;
01528 }
01529 }
01530 }
01531
01532 ++c;
01533
01534 t2.mark();
01535
01536 if (t2.user_secs() > 20.0)
01537 {
01538 t1.mark();
01539 t1.report("convolve");
01540 t2.reset();
01541 LINFO("%.2f%% complete", (100.0*c)/maxc);
01542 }
01543 }
01544 }
01545 }
01546
01547 itsNeuronsCur.swap(itsNeuronsTempSpace);
01548
01549 LINFO("fast plast: total N (%d), avg (%.2f)",
01550 fpTotal.count(), fpTotal.mean());
01551
01552 LINFO("fast plast: hi N (%d), avg (%.2f)",
01553 fpHi.count(), fpHi.mean());
01554 LINFO("fast plast: lo N (%d), avg (%.2f)",
01555 fpLo.count(), fpLo.mean());
01556 }
01557
01558 Image<float> ContourLayerStatic::iterate(const ContourConnection& cxn,
01559 const char* saveSuffix,
01560 int normFlags,
01561 SaveSet& save)
01562 {
01563
01564
01565 const float cfgGroupBottom = itsGroupBottom.getVal();
01566 const float cfgGroupTop = itsGroupTop.getVal();
01567 const float cfgSuppressionAdd = itsSuppressionAdd.getVal();
01568 const float cfgSuppressionSub = itsSuppressionSub.getVal();
01569
01570 convolveAll(cxn);
01571
01572 doRectify(itsNeuronsCur);
01573
01574 const Image<float> activation = sum(itsNeuronsCur);
01575
01576 printStats(activation, "activation");
01577
01578
01579
01580 itsSalmap -= itsLeak.getVal();
01581 itsSalmap += activation;
01582
01583 printStats(itsSalmap, "salmap");
01584
01585 inplaceRectify(itsSalmap);
01586
01587
01588
01589 const Image<float> prevEnergyMap = itsEnergyMap;
01590
01591
01592 itsEnergyMap = sigmoid(itsSalmap, itsSigmoidThresh.getVal());
01593
01594 printStats(itsEnergyMap, "energy");
01595
01596
01597
01598 Image<float> delta = lowPass(itsGroupSize.getVal(),
01599 itsEnergyMap - prevEnergyMap);
01600
01601 Image<float>::const_iterator delta_itr = delta.begin();
01602 Image<float>::iterator gmod_itr = itsGroupModMap.beginw();
01603 Image<float>::iterator stop = itsGroupModMap.endw();
01604
01605 int numTooHigh = 0;
01606 int numTooLow = 0;
01607
01608 while (gmod_itr != stop)
01609 {
01610 const float pctChange = *delta_itr;
01611
01612
01613 if (pctChange > cfgGroupTop)
01614 {
01615 *gmod_itr += cfgSuppressionAdd * (pctChange - cfgGroupTop);
01616 ++numTooHigh;
01617 }
01618
01619
01620 else if (pctChange < cfgGroupBottom)
01621 {
01622 *gmod_itr += cfgSuppressionSub * (pctChange - cfgGroupTop);
01623 ++numTooLow;
01624 }
01625
01626 ++gmod_itr;
01627 ++delta_itr;
01628 }
01629
01630 inplaceRectify(itsGroupModMap);
01631
01632 LINFO("suppression: N (%d), + (%d), - (%d)",
01633 delta.getSize(), numTooHigh, numTooLow);
01634
01635 if (itsSaveLayerDetails.getVal())
01636 save.add(sformat("gmod.%s", saveSuffix),
01637 GenericFrame(itsGroupModMap, normFlags));
01638
01639 return itsEnergyMap;
01640 }
01641
01642
01643
01644
01645
01646
01647
01648 enum ContourChannelFilterStyle
01649 {
01650 CC_FILTER_STEERABLE = 1,
01651 CC_FILTER_GABOR = 2,
01652 CC_FILTER_GAUSSIAN_STEERABLE = 3,
01653 };
01654
01655 std::string convertToString(const ContourChannelFilterStyle val)
01656 {
01657 switch (val)
01658 {
01659 case CC_FILTER_STEERABLE: return "Steerable"; break;
01660 case CC_FILTER_GABOR: return "Gabor"; break;
01661 case CC_FILTER_GAUSSIAN_STEERABLE: return "GaussianSteerable"; break;
01662 }
01663
01664 LFATAL("invalid ContourChannelFilterStyle '%d'", int(val));
01665 return std::string();
01666 }
01667
01668 void convertFromString(const std::string& str1, ContourChannelFilterStyle& val)
01669 {
01670 const std::string str = toLowerCase(str1);
01671
01672 if (str.compare("steerable") == 0) { val = CC_FILTER_STEERABLE; }
01673 else if (str.compare("gabor") == 0) { val = CC_FILTER_GABOR; }
01674 else if (str.compare("gaussiansteerable") == 0) { val = CC_FILTER_GAUSSIAN_STEERABLE; }
01675 else
01676 conversion_error::raise<ContourChannelFilterStyle>(str1);
01677 }
01678
01679 enum ContourChannelDynamicsType
01680 {
01681 CC_STATIC = 1,
01682 CC_DYNAMIC = 2
01683 };
01684
01685 std::string convertToString(const ContourChannelDynamicsType val)
01686 {
01687 switch (val)
01688 {
01689 case CC_STATIC: return "Static"; break;
01690 case CC_DYNAMIC: return "Dynamic"; break;
01691 }
01692
01693 LFATAL("invalid ContourChannelDynamicsType '%d'", int(val));
01694 return std::string();
01695 }
01696
01697 void convertFromString(const std::string& str1, ContourChannelDynamicsType& val)
01698 {
01699 const std::string str = toLowerCase(str1);
01700
01701 if (str.compare("static") == 0) { val = CC_STATIC; }
01702 else if (str.compare("dynamic") == 0) { val = CC_DYNAMIC; }
01703 else
01704 conversion_error::raise<ContourChannelDynamicsType>(str1);
01705 }
01706
01707 class ContourChannel : public ChannelBase
01708 {
01709 public:
01710 ContourChannel(OptionManager& mgr,
01711 const std::string& saveprefix = "contourout");
01712 virtual ~ContourChannel();
01713
01714 virtual bool outputAvailable() const;
01715
01716 virtual Dims getMapDims() const;
01717
01718 virtual uint numSubmaps() const;
01719
01720
01721 virtual Image<float> getSubmap(unsigned int index) const
01722 {
01723 LFATAL("getSubmap() not implemented for ContourChannel");
01724 return Image<float>();
01725 }
01726
01727 virtual std::string getSubmapName(unsigned int index) const;
01728
01729 virtual std::string getSubmapNameShort(unsigned int index) const;
01730
01731
01732 virtual void getFeatures(const Point2D<int>& locn,
01733 std::vector<float>& mean) const
01734 {
01735 LFATAL("getFeatures() not implemented for ContourChannel");
01736 }
01737
01738 virtual void getFeaturesBatch(std::vector<Point2D<int>*> *locn,
01739 std::vector<std::vector<float> > *mean,
01740 int *count) const
01741 {
01742 LFATAL("getFeatures() not implemented for ContourChannel");
01743 }
01744
01745 virtual Image<float> getOutput();
01746
01747
01748 virtual void saveResults(const nub::ref<FrameOstream>& ofs);
01749
01750 protected:
01751 virtual void paramChanged(ModelParamBase* param,
01752 const bool valueChanged,
01753 ParamClient::ChangeStatus* status);
01754
01755 virtual void start1()
01756 {
01757 ChannelBase::start1();
01758 itsFrameIdx = 0;
01759 if (itsNumIterations.getVal() <= 0)
01760 LFATAL("expected positive value for --%s, but got %u",
01761 itsNumIterations.getOptionDef()->longoptname,
01762 itsNumIterations.getVal());
01763
01764 if (itsNumScales.getVal() <= 0)
01765 LFATAL("expected positive value for --%s, but got %u",
01766 itsNumScales.getOptionDef()->longoptname,
01767 itsNumScales.getVal());
01768 }
01769
01770 virtual void doInput(const InputFrame& inframe);
01771
01772 virtual void killCaches();
01773
01774 virtual void saveStats(const Image<float> img, const short idx);
01775
01776 private:
01777 ContourChannel(const ContourChannel&);
01778 ContourChannel& operator=(const ContourChannel&);
01779
01780 OModelParam<ContourConnectionType> itsCxnType;
01781 OModelParam<unsigned int> itsNumIterations;
01782 OModelParam<unsigned int> itsLowPassWidth;
01783 OModelParam<unsigned int> itsFirstScale;
01784 OModelParam<unsigned int> itsNumScales;
01785 OModelParam<Dims> itsMaxInputDims;
01786 OModelParam<ContourChannelFilterStyle> itsFilterStyle;
01787 OModelParam<float> itsGaborPeriod;
01788 OModelParam<unsigned int> itsFilterReduction;
01789 OModelParam<ContourChannelDynamicsType> itsDynamicsType;
01790 OModelParam<MaxNormType> itsNormType;
01791 OModelParam<int> itsNormFlags;
01792 OModelParam<LevelSpec> itsLevelSpec;
01793 OModelParam<int> itsOutputBlur;
01794 OModelParam<bool> itsSaveWeights;
01795 OModelParam<bool> itsSaveFilterOutput;
01796 OModelParam<bool> itsSaveOutput;
01797 const std::string itsSavePrefix;
01798
01799
01800 OModelParam<bool> itsGetSingleChannelStats;
01801
01802
01803 OModelParam<bool> itsSaveStatsPerChannel;
01804
01805
01806 OModelParam<std::string> itsGetSingleChannelStatsFile;
01807
01808
01809 OModelParam<std::string> itsGetSingleChannelStatsTag;
01810
01811 nub::ref<ContourConnection> itsConnection;
01812 nub::ref<ContourLayer> itsScaleBand;
01813
01814 Image<float> itsRawInput;
01815 Image<float> itsFloatInput;
01816 Image<float> itsOutput;
01817
01818 SaveSet itsSaveSet;
01819 uint itsFrameIdx;
01820 };
01821
01822
01823
01824
01825
01826
01827
01828
01829 static const ModelOptionDef OPT_ContourConnectionType =
01830 { MODOPT_ARG(ContourConnectionType), "ContourConnectionType", &MOC_CHANNEL, OPTEXP_CORE,
01831 "Type of connection kernel to use in the contour channel "
01832 "(corresponds to old ParamMap parameter 'cxnType=3')",
01833 "contour-cxn-type", '\0', "<Cinnic|Braun|BraunNorm>", "BraunNorm" };
01834
01835
01836 static const ModelOptionDef OPT_ContourChannelIterations =
01837 { MODOPT_ARG(unsigned int), "ContourChannelIterations", &MOC_CHANNEL, OPTEXP_CORE,
01838 "Number of iterations to run in the contour channel "
01839 "(corresponds to old ParamMap parameter 'iterations=3')",
01840 "contour-num-iterations", '\0', "<uint>", "3" };
01841
01842
01843 static const ModelOptionDef OPT_ContourChannelLowPassWidth =
01844 { MODOPT_ARG(unsigned int), "ContourChannelLowPassWidth", &MOC_CHANNEL, OPTEXP_CORE,
01845 "Width of low-pass filter to use in the contour channel "
01846 "(corresponds to old ParamMap parameter 'lowPassWidth=9')",
01847 "contour-lowpass-width", '\0', "<uint>", "9" };
01848
01849
01850 static const ModelOptionDef OPT_ContourChannelFirstScale =
01851 { MODOPT_ARG(unsigned int), "ContourChannelFirstScale", &MOC_CHANNEL, OPTEXP_CORE,
01852 "Lowest pyramid scale to use in the contour channel "
01853 "(corresponds to old ParamMap parameter 'firstScale=0')",
01854 "contour-first-scale", '\0', "<uint>", "0" };
01855
01856
01857 static const ModelOptionDef OPT_ContourChannelNumScales =
01858 { MODOPT_ARG(unsigned int), "ContourChannelNumScales", &MOC_CHANNEL, OPTEXP_CORE,
01859 "Number of pyramid scale to use in the contour channel "
01860 "(corresponds to old ParamMap parameter 'scalesNumber=3')",
01861 "contour-num-scales", '\0', "<uint>", "3" };
01862
01863
01864 static const ModelOptionDef OPT_ContourChannelMaxInputDims =
01865 { MODOPT_ARG(Dims), "ContourChannelMaxInputDims", &MOC_CHANNEL, OPTEXP_CORE,
01866 "If input to the contour channel is larger than these dims, then "
01867 "it will be rescaled down to fit "
01868 "(corresponds to old ParamMap parameters 'maxInputW=10000' and 'maxInputH=10000')",
01869 "contour-max-input-dims", '\0', "<w>x<h>", "10000x10000" };
01870
01871
01872 static const ModelOptionDef OPT_ContourChannelFilterStyle =
01873 { MODOPT_ARG(ContourChannelFilterStyle), "ContourChannelFilterStyle", &MOC_CHANNEL, OPTEXP_CORE,
01874 "Type of orientation filtering to use in the contour channel "
01875 "(corresponds to old ParamMap parameter 'filterStyle=2')",
01876 "contour-filter-style", '\0', "<Steerable|Gabor|GaussianSteerable>", "Gabor" };
01877
01878
01879 static const ModelOptionDef OPT_ContourChannelGaborPeriod =
01880 { MODOPT_ARG(float), "ContourChannelGaborPeriod", &MOC_CHANNEL, OPTEXP_CORE,
01881 "Period of the sine-wave component of the gabor filter used in the "
01882 "contour channel "
01883 "(corresponds to old ParamMap parameter 'gaborPeriod=5.0')",
01884 "contour-gabor-width", '\0', "<float>", "5.0" };
01885
01886
01887 static const ModelOptionDef OPT_ContourChannelFilterReduction =
01888 { MODOPT_ARG(unsigned int), "ContourChannelFilterReduction", &MOC_CHANNEL, OPTEXP_CORE,
01889 "Number of octaves by which to downsize the oriented filter outputs "
01890 "in the contour channel "
01891 "(corresponds to old ParamMap parameter 'filterReduction=1')",
01892 "contour-filter-reduction", '\0', "<uint>", "1" };
01893
01894
01895 static const ModelOptionDef OPT_ContourChannelDynamicsType =
01896 { MODOPT_ARG(ContourChannelDynamicsType), "ContourChannelDynamicsType", &MOC_CHANNEL, OPTEXP_CORE,
01897 "The type of iterative dynamics to use in the contour channel "
01898 "(corresponds to old ParamMap parameter 'dynamicsType=1')",
01899 "contour-dynamics-type", '\0', "<Static|Dynamic>", "Static" };
01900
01901
01902 static const ModelOptionDef OPT_ContourChannelNormFlags =
01903 { MODOPT_ARG(int), "ContourChannelNormFlags", &MOC_CHANNEL, OPTEXP_CORE,
01904 "Bitwise-OR'ed combination of Flags to use when normaling float images "
01905 "in the contour channel (FLOAT_NORM_0_255=1, FLOAT_NORM_WITH_SCALE=2, "
01906 "FLOAT_NORM_PRESERVE=4) "
01907 "(corresponds to old ParamMap parameter 'normFlags=3')",
01908 "contour-norm-flags", '\0', "<int>", "3" };
01909
01910
01911 static const ModelOptionDef OPT_ContourChannelOutputBlur =
01912 { MODOPT_ARG(int), "ContourChannelOutputBlur", &MOC_CHANNEL, OPTEXP_CORE,
01913 "Box-filter width to use when blurring+decimating to rescale "
01914 "the output of the contour channel",
01915 "contour-output-blur", '\0', "<int>", "4" };
01916
01917
01918 static const ModelOptionDef OPT_ContourChannelSaveWeights =
01919 { MODOPT_FLAG, "ContourChannelSaveWeights", &MOC_CHANNEL, OPTEXP_SAVE,
01920 "Whether to save a summary image of the contour channel's kernel "
01921 "weights",
01922 "save-contour-weights", '\0', "", "false" };
01923
01924
01925 static const ModelOptionDef OPT_ContourChannelSaveFilterOutput =
01926 { MODOPT_FLAG, "ContourChannelSaveFilterOutput", &MOC_CHANNEL, OPTEXP_SAVE,
01927 "Whether to save summary images of the contour channel's oriented "
01928 "filter outputs",
01929 "save-contour-filter-output", '\0', "", "false" };
01930
01931
01932 static const ModelOptionDef OPT_ContourChannelSaveOutput =
01933 { MODOPT_FLAG, "ContourChannelSaveOutput", &MOC_CHANNEL, OPTEXP_SAVE,
01934 "Whether to save output maps from the contour channel",
01935 "save-contour-output", '\0', "", "false" };
01936
01937
01938
01939 static const ModelOptionDef OPT_ALIAScontourModel0027 =
01940 { MODOPT_ALIAS, "ALIAScontourModel0027", &MOC_ALIAS, OPTEXP_CORE,
01941 "Contour model #0027",
01942 "contour-model-0027", '\0', "",
01943 "--contour-cxn-type=BraunNorm "
01944 "--contour-cxn-beta=0.07 "
01945 "--contour-cxn-excit-strength=1.7 "
01946 "--contour-cxn-inhib-strength=-0.3 "
01947 "--contour-cxn-dims=25x25 "
01948 "--contour-cxn-peak-distance=2.5 "
01949 "--contour-dynamics-type=Static "
01950 "--cinnic-fast-plast=5.405775 "
01951 "--cinnic-group-top=0.0048828125 "
01952 "--cinnic-leak=0.0003662109375 "
01953 "--cinnic-sigmoid-thresh=35.15625 "
01954 "--cinnic-suppression-add=1.6875 "
01955 "--cinnic-suppression-sub=2.0 "
01956 "--contour-filter-reduction=1 "
01957 "--contour-first-scale=2 "
01958 "--contour-gabor-width=5.0 "
01959 "--contour-num-iterations=3 "
01960 "--contour-num-scales=1 "
01961 };
01962
01963
01964 static const ModelOptionDef OPT_ALIAScontourModel0032 =
01965 { MODOPT_ALIAS, "ALIAScontourModel0032", &MOC_ALIAS, OPTEXP_CORE,
01966 "Contour model #0032",
01967 "contour-model-0032", '\0', "",
01968 "--contour-cxn-type=BraunNorm "
01969 "--contour-cxn-beta=0.000720977783205 "
01970 "--contour-cxn-dims=31x31 "
01971 "--contour-cxn-excit-strength=2.33459472656 "
01972 "--contour-cxn-inhib-strength=-0.0030920982361 "
01973 "--contour-cxn-peak-distance=54.931640625 "
01974 "--contour-dynamics-type=Static "
01975 "--cinnic-fast-plast=0.0791861572262 "
01976 "--cinnic-group-size=11 "
01977 "--cinnic-group-top=0.15625 "
01978 "--cinnic-leak=1.318359375 "
01979 "--cinnic-sigmoid-thresh=474.609375 "
01980 "--cinnic-suppression-add=0.75 "
01981 "--cinnic-suppression-sub=2.0 "
01982 "--contour-filter-reduction=2 "
01983 "--contour-first-scale=1 "
01984 "--contour-gabor-width=2.02549743327 "
01985 "--contour-num-iterations=1 "
01986 "--contour-num-scales=1 "
01987 };
01988
01989
01990 ContourChannel::ContourChannel(OptionManager& mgr,
01991 const std::string& saveprefix)
01992 :
01993 ChannelBase(mgr, "Contours", "contours", UNKNOWN),
01994
01995 itsCxnType ( &OPT_ContourConnectionType, this ),
01996 itsNumIterations ( &OPT_ContourChannelIterations, this ),
01997 itsLowPassWidth ( &OPT_ContourChannelLowPassWidth, this ),
01998 itsFirstScale ( &OPT_ContourChannelFirstScale, this ),
01999 itsNumScales ( &OPT_ContourChannelNumScales, this ),
02000 itsMaxInputDims ( &OPT_ContourChannelMaxInputDims, this ),
02001 itsFilterStyle ( &OPT_ContourChannelFilterStyle, this ),
02002 itsGaborPeriod ( &OPT_ContourChannelGaborPeriod, this ),
02003 itsFilterReduction ( &OPT_ContourChannelFilterReduction, this ),
02004 itsDynamicsType ( &OPT_ContourChannelDynamicsType, this ),
02005 itsNormType ( &OPT_MaxNormType, this ),
02006 itsNormFlags ( &OPT_ContourChannelNormFlags, this ),
02007 itsLevelSpec ( &OPT_LevelSpec, this ),
02008 itsOutputBlur ( &OPT_ContourChannelOutputBlur, this ),
02009 itsSaveWeights ( &OPT_ContourChannelSaveWeights, this ),
02010 itsSaveFilterOutput ( &OPT_ContourChannelSaveFilterOutput, this ),
02011 itsSaveOutput ( &OPT_ContourChannelSaveOutput, this ),
02012 itsSavePrefix ( saveprefix ),
02013 itsGetSingleChannelStats(&OPT_GetSingleChannelStats, this),
02014 itsSaveStatsPerChannel(&OPT_SaveStatsPerChannel, this),
02015 itsGetSingleChannelStatsFile(&OPT_GetSingleChannelStatsFile, this),
02016 itsGetSingleChannelStatsTag(&OPT_GetSingleChannelStatsTag, this),
02017 itsConnection ( new ContourConnectionBraun(mgr, true) ),
02018 itsScaleBand ( new ContourLayerStatic(mgr) ),
02019
02020 itsRawInput ( ),
02021 itsFloatInput ( ),
02022 itsOutput ( )
02023 {
02024 mgr.requestOptionAlias(&OPT_ALIAScontourModel0027);
02025 mgr.requestOptionAlias(&OPT_ALIAScontourModel0032);
02026
02027 this->addSubComponent(itsConnection);
02028 this->addSubComponent(itsScaleBand);
02029 }
02030
02031
02032 ContourChannel::~ContourChannel()
02033 {}
02034
02035
02036 bool ContourChannel::outputAvailable() const
02037 {
02038 return itsOutput.initialized();
02039 }
02040
02041
02042 Dims ContourChannel::getMapDims() const
02043 {
02044 const int lev = itsLevelSpec.getVal().mapLevel();
02045
02046 return this->getInputDims() / (1 << lev);
02047 }
02048
02049
02050 uint ContourChannel::numSubmaps() const
02051 {
02052 return itsNumScales.getVal() * itsConnection->numAngles();
02053 }
02054
02055
02056 std::string ContourChannel::getSubmapName(unsigned int index) const
02057 {
02058 const unsigned int scale = index / itsNumScales.getVal();
02059 const unsigned int angle = index % itsNumScales.getVal();
02060
02061 return sformat("%s scale: %u, angle: %u", descriptiveName().c_str(), scale, angle);
02062 }
02063
02064
02065 std::string ContourChannel::getSubmapNameShort(unsigned int index) const
02066 {
02067 const unsigned int scale = index / itsNumScales.getVal();
02068 const unsigned int angle = index % itsNumScales.getVal();
02069
02070 return sformat("%s(%u,%u)", tagName().c_str(), scale, angle);
02071 }
02072
02073
02074 Image<float> ContourChannel::getOutput()
02075 {
02076 if(itsGetSingleChannelStats.getVal())
02077 saveStats(itsOutput, -1);
02078
02079 return itsOutput;
02080 }
02081
02082
02083 void ContourChannel::saveStats(const Image<float> img, const short idx)
02084 {
02085 std::string fileName;
02086
02087 if(itsSaveStatsPerChannel.getVal())
02088 {
02089 std::string dot = ".";
02090 std::string txt = ".txt";
02091 fileName = itsGetSingleChannelStatsFile.getVal()+ dot + tagName() + txt;
02092 }
02093 else
02094 {
02095 fileName = itsGetSingleChannelStatsFile.getVal();
02096 }
02097
02098
02099
02100 ushort minx = 0, miny = 0, maxx = 0, maxy = 0;
02101 float min, max, avg, std;
02102 uint N;
02103
02104 getMinMaxAvgEtc(img, min, max, avg, std, minx, miny, maxx, maxy, N);
02105
02106 std::ofstream statsFile(fileName.c_str(), std::ios::app);
02107
02108 statsFile << itsGetSingleChannelStatsTag.getVal() << "\t";
02109
02110 statsFile << itsFrameIdx << "\t";
02111
02112
02113
02114 if(idx == -1)
02115 {
02116 statsFile << "COMBINED\t-1\t";
02117 itsFrameIdx++;
02118 }
02119 else
02120 statsFile << "SCALE\t" << idx << "\t";
02121
02122 statsFile << tagName().c_str() << "\t" << descriptiveName().c_str() << "\t";
02123 statsFile << min << "\t" << max << "\t" << avg << "\t" << std << "\t"
02124 << minx << "\t" << miny << "\t" << maxx << "\t" << maxy << "\t"
02125 << N << "\n";
02126 statsFile.close();
02127 }
02128
02129
02130 void ContourChannel::saveResults(const nub::ref<FrameOstream>& ofs)
02131 {
02132 itsSaveSet.saveAll(*ofs, itsSavePrefix.c_str());
02133 }
02134
02135
02136 void ContourChannel::paramChanged(ModelParamBase* param,
02137 const bool valueChanged,
02138 ParamClient::ChangeStatus* status)
02139 {
02140 if (param == &itsCxnType)
02141 {
02142 this->removeSubComponent(*itsConnection);
02143
02144 switch (itsCxnType.getVal())
02145 {
02146 case CC_CXN_CINNIC: itsConnection.reset(new ContourConnectionCinnic(getManager())); break;
02147 case CC_CXN_BRAUN: itsConnection.reset(new ContourConnectionBraun(getManager(), false)); break;
02148 case CC_CXN_BRAUN_NORM: itsConnection.reset(new ContourConnectionBraun(getManager(), true)); break;
02149 default:
02150 LFATAL("unknown connection type '%d'", int(itsCxnType.getVal()));
02151 }
02152
02153 this->addSubComponent(itsConnection);
02154 itsConnection->exportOptions(MC_RECURSE);
02155 }
02156 else if (param == &itsDynamicsType)
02157 {
02158 this->removeSubComponent(*itsScaleBand);
02159
02160 switch (itsDynamicsType.getVal())
02161 {
02162 case CC_STATIC: itsScaleBand.reset(new ContourLayerStatic(getManager())); break;
02163 case CC_DYNAMIC: itsScaleBand.reset(new ContourLayerDynamic(getManager())); break;
02164 default:
02165 LFATAL("unknown dynamics type '%d'", int(itsDynamicsType.getVal()));
02166 }
02167
02168 this->addSubComponent(itsScaleBand);
02169 itsScaleBand->exportOptions(MC_RECURSE);
02170 }
02171 }
02172
02173
02174 void ContourChannel::doInput(const InputFrame& inframe)
02175 {
02176 ASSERT(inframe.grayFloat().initialized());
02177
02178 CpuTimer t;
02179
02180 itsSaveSet.clear();
02181
02182 if (itsSaveWeights.getVal())
02183 itsSaveSet.add("weights", GenericFrame(itsConnection->getSummaryImage()));
02184
02185 itsRawInput = inframe.grayFloat();
02186
02187 itsFloatInput =
02188 (inframe.getWidth() > itsMaxInputDims.getVal().w() ||
02189 inframe.getHeight() > itsMaxInputDims.getVal().h())
02190 ? rescale(inframe.grayFloat(), itsMaxInputDims.getVal())
02191 : inframe.grayFloat();
02192
02193 ImageSet<float> filterSets[itsNumScales.getVal()];
02194
02195 switch (itsFilterStyle.getVal())
02196 {
02197 case CC_FILTER_STEERABLE:
02198 {
02199 const ImageSet<float> lpyr =
02200 buildPyrLaplacian(itsFloatInput,
02201 0, itsFirstScale.getVal()+itsNumScales.getVal(),
02202 itsLowPassWidth.getVal());
02203
02204 for (unsigned int s = 0; s < itsNumScales.getVal(); ++s)
02205 {
02206 filterSets[s] =
02207 orientedFilterSet(lpyr[itsFirstScale.getVal()+s],
02208 itsGaborPeriod.getVal(),
02209 itsConnection->angles(),
02210 itsConnection->numAngles());
02211 doMeanNormalize(filterSets[s]);
02212 doRectify(filterSets[s]);
02213 }
02214 }
02215 break;
02216 case CC_FILTER_GABOR:
02217 case CC_FILTER_GAUSSIAN_STEERABLE:
02218 {
02219 ImageSet<float> gausspyr =
02220 buildPyrGaussian(itsFloatInput,
02221 0, itsFirstScale.getVal()+itsNumScales.getVal(),
02222 itsLowPassWidth.getVal());
02223
02224 doEnergyNorm(gausspyr);
02225
02226 if (itsFilterStyle.getVal() == CC_FILTER_GABOR)
02227 {
02228 ImageSet<float> gpyr[itsConnection->numAngles()];
02229
02230 for (int i = 0; i < itsConnection->numAngles(); ++i)
02231 {
02232
02233
02234
02235 gpyr[i] = buildPyrGabor(gausspyr,
02236 itsConnection->angle(i),
02237 itsGaborPeriod.getVal(),
02238 1.0 ,
02239 -1 ,
02240 0 );
02241
02242
02243
02244
02245
02246
02247
02248 for (unsigned int n = 0; n < itsFirstScale.getVal(); ++n)
02249 gpyr[i].getImageMut(n) = Image<float>();
02250 }
02251
02252 for (unsigned int s = 0; s < itsNumScales.getVal(); ++s)
02253 {
02254 filterSets[s] = takeSlice(gpyr,
02255 itsConnection->numAngles(),
02256 itsFirstScale.getVal()+s);
02257 doMeanNormalize(filterSets[s]);
02258 doRectify(filterSets[s]);
02259 }
02260 }
02261 else
02262 {
02263 for (unsigned int s = 0; s < itsNumScales.getVal(); ++s)
02264 {
02265 filterSets[s] =
02266 orientedFilterSet(gausspyr[itsFirstScale.getVal()+s],
02267 itsGaborPeriod.getVal(),
02268 itsConnection->angles(),
02269 itsConnection->numAngles());
02270 doMeanNormalize(filterSets[s]);
02271 doRectify(filterSets[s]);
02272 }
02273 }
02274 }
02275 break;
02276 default:
02277 LFATAL("invalid filterStyle '%d'", int(itsFilterStyle.getVal()));
02278 }
02279
02280 t.mark();
02281 t.report();
02282
02283 const Dims imgDims = itsFloatInput.getDims();
02284
02285 itsOutput = Image<float>(imgDims, ZEROS);
02286 Image<float> maxout(imgDims, ZEROS);
02287
02288 for (unsigned int s = 0; s < itsNumScales.getVal(); ++s)
02289 {
02290 filterSets[s] = reduce(filterSets[s], itsFilterReduction.getVal());
02291
02292 if (itsSaveFilterOutput.getVal())
02293 itsSaveSet.add(sformat("filter.s-%u", s),
02294 GenericFrame(makeImageArray(filterSets[s]),
02295 itsNormFlags.getVal()));
02296
02297 doOneNormalize(filterSets[s]);
02298
02299 const std::string saveSuffix = sformat("s-%u", s);
02300
02301 const Image<float> salmap =
02302 itsScaleBand->compute(filterSets[s],
02303 *itsConnection,
02304 saveSuffix,
02305 itsNormFlags.getVal(),
02306 itsSaveSet,
02307 itsNumIterations.getVal(),
02308 imgDims);
02309
02310 const Image<float> scale_sm = rescale(salmap, imgDims);
02311
02312 const float bias = getScaleBias(s);
02313 itsOutput += (scale_sm * bias);
02314 maxout = takeMax(maxout, scale_sm);
02315
02316 t.mark();
02317 t.report();
02318 }
02319
02320 if (itsSaveOutput.getVal())
02321 {
02322 itsSaveSet.add("avg",
02323 GenericFrame(itsOutput, itsNormFlags.getVal()));
02324
02325 itsSaveSet.add("max",
02326 GenericFrame(maxout, itsNormFlags.getVal()));
02327 }
02328
02329 itsOutput = boxDownSizeClean(itsOutput, this->getMapDims(),
02330 itsOutputBlur.getVal());
02331
02332 itsOutput = maxNormalize(itsOutput,
02333 MAXNORMMIN, MAXNORMMAX,
02334 itsNormType.getVal());
02335
02336 if (itsSaveOutput.getVal())
02337 {
02338 itsSaveSet.add("final",
02339 GenericFrame(itsOutput, itsNormFlags.getVal()));
02340 }
02341 }
02342
02343
02344 void ContourChannel::killCaches()
02345 {
02346 ChannelBase::killCaches();
02347 }
02348
02349
02350
02351
02352
02353
02354
02355 nub::ref<ChannelBase> makeContourChannel(OptionManager& mgr,
02356 const std::string& saveprefix)
02357 {
02358 return makeSharedComp(new ContourChannel(mgr, saveprefix));
02359 }
02360
02361
02362
02363
02364
02365