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
00041
00042
00043 #ifndef PSYCHO_SEARCHARRAY_C_DEFINED
00044 #define PSYCHO_SEARCHARRAY_C_DEFINED
00045
00046 #include "Psycho/SearchArray.H"
00047
00048 #include "Image/CutPaste.H"
00049 #include "Image/Image.H"
00050 #include "Raster/Raster.H"
00051 #include "Image/geom.h"
00052 #include "Image/Rectangle.H"
00053 #include "rutz/rand.h"
00054 #include "Util/StringConversions.H"
00055 #include <cstdio>
00056 #include <algorithm>
00057
00058 using geom::vec2d;
00059
00060 namespace
00061 {
00062 void dumpFrame(const Image<byte>& bmap)
00063 {
00064 static int framecount = 0;
00065
00066 Raster::WriteGray(bmap, sformat("frame_%06d.png", framecount++));
00067 }
00068 }
00069
00070 SearchArray::SearchArray(const Dims& dims,
00071 double gridSpacing,
00072 double minSpacing,
00073 double margin)
00074 :
00075 itsDims(dims),
00076 itsGridSpacing(gridSpacing),
00077 itsMinSpacing(minSpacing),
00078 itsFillResolution(15.0),
00079 itsMargin(margin),
00080
00081 itsArray(),
00082
00083 itsDumpingFrames(false),
00084 itsFrameDumpPeriod(20)
00085 {}
00086
00087 SearchArray::~SearchArray() throw() {}
00088
00089 void SearchArray::addElement(const rutz::shared_ptr<SearchItem>& e, const bool displace) const
00090 {
00091 if (tooClose(e->pos, -1))
00092 {
00093 if (!displace)
00094 LFATAL("element @ %g,%g is too close to an existing element",
00095 e->pos.x(), e->pos.y());
00096 else
00097 {
00098 std::vector<rutz::shared_ptr<SearchItem> > isTooClose;
00099 const double minSpacingSqr = itsMinSpacing*itsMinSpacing;
00100 for (size_t n = 0; n < itsArray.size(); ++n)
00101 {
00102 const vec2d v = e->pos;
00103 const double dx = itsArray[n]->pos.x() - v.x();
00104 const double dy = itsArray[n]->pos.y() - v.y();
00105
00106 if(dx*dx+dy*dy <= minSpacingSqr)
00107 isTooClose.push_back(itsArray[n]);
00108 }
00109 for (size_t i = 0; i < isTooClose.size(); ++i)
00110 doRemoveElement(isTooClose[i]);
00111 }
00112 }
00113 doAddElement(e);
00114 }
00115
00116 void SearchArray::replaceElementAtSamePos(size_t i, const rutz::shared_ptr<SearchItem>& e)
00117 {
00118 if (i >= itsArray.size())
00119 LFATAL("expected idx < %"ZU", got idx = %"ZU, itsArray.size(), i);
00120
00121 const rutz::shared_ptr<SearchItem> old = itsArray[i];
00122 itsArray[i] = e;
00123 itsArray[i]->pos = old->pos;
00124 }
00125
00126 void SearchArray::generateBackground(SearchItemFactory& factory,
00127 const int diffusionCycles,
00128 const bool doFinalFill,
00129 const int jitterIters,
00130 const bool doJitterForeground,
00131 const int backgSeed) const
00132 {
00133 const size_t foregSize = itsArray.size();
00134
00135 backgHexGrid(factory);
00136
00137 rutz::urand urand(backgSeed);
00138
00139 for (int i = 0; i < diffusionCycles; ++i)
00140 {
00141 backgJitter(urand, jitterIters, doJitterForeground);
00142 if (i+1 < diffusionCycles || doFinalFill)
00143 backgFill(factory);
00144
00145
00146
00147 }
00148
00149 size_t insideNumber = foregSize;
00150
00151 for (size_t n = 0; n < itsArray.size(); ++n)
00152 {
00153 if (itsArray[n]->type == SearchItem::FOREGROUND)
00154 continue;
00155
00156 bool inside = true;
00157
00158 for (size_t i = 0; i < foregSize; ++i)
00159 {
00160 const size_t j = (i+1) % foregSize;
00161
00162
00163
00164 const double Yij = itsArray[i]->pos.x() - itsArray[j]->pos.x();
00165 const double Xij = itsArray[j]->pos.y() - itsArray[i]->pos.y();
00166
00167
00168
00169 const double Xin = itsArray[n]->pos.x() - itsArray[i]->pos.x();
00170 const double Yin = itsArray[n]->pos.y() - itsArray[i]->pos.y();
00171
00172
00173
00174 const double vp = Xij*Xin + Yij*Yin;
00175
00176 if (vp < 0.0) { inside = false; break; }
00177 }
00178
00179 if (inside)
00180 {
00181 ++insideNumber;
00182 }
00183 }
00184
00185
00186
00187 }
00188
00189 Image<byte> SearchArray::getImage(const double lo, const double hi,
00190 const double bg,
00191 bool doTagLast) const
00192 {
00193 Image<double> tmpwin(itsDims, NO_INIT);
00194 tmpwin.clear(bg);
00195
00196 const Image<double>::iterator win = tmpwin.beginw();
00197
00198 for (size_t i = 0; i < itsArray.size(); ++i)
00199 {
00200 const int xcenter = int(itsArray[i]->pos.x() + itsDims.w() / 2.0 + 0.5);
00201 const int ycenter = int(itsArray[i]->pos.y() + itsDims.h() / 2.0 + 0.5);
00202
00203 const Image<double> p = itsArray[i]->getPatch();
00204
00205
00206 const int x0 = xcenter - p.getWidth() / 2;
00207 const int y0 = ycenter - p.getHeight() / 2;
00208
00209 const int x1 = x0 + p.getWidth();
00210 const int y1 = y0 + p.getHeight();
00211
00212 for (int y = y0; y < y1; ++y)
00213 for (int x = x0; x < x1; ++x)
00214 {
00215 if (x >= 0 && x < itsDims.w() && y >=0 && y < itsDims.h())
00216 win[x+y*itsDims.w()] += p.getVal(x-x0, y-y0);
00217 }
00218
00219 if (doTagLast && (i+1) == itsArray.size())
00220 {
00221 const double outer = 0.4 * p.getWidth();
00222 const double inner = outer - 3;
00223
00224 for (int y = y0; y < y1; ++y)
00225 for (int x = x0; x < x1; ++x)
00226 {
00227 if (x >= 0 && x < itsDims.w() && y >=0 && y < itsDims.h())
00228 {
00229 const double r = sqrt((x-xcenter)*(x-xcenter) +
00230 (y-ycenter)*(y-ycenter));
00231
00232 if (r >= inner && r <= outer)
00233 {
00234 win[x+y*itsDims.w()] = 1.0;
00235 }
00236 }
00237 }
00238 }
00239 }
00240
00241 Image<byte> result(tmpwin.getDims(), ZEROS);
00242
00243 unsigned char* bytes = result.beginw();
00244
00245 bool clip = false;
00246
00247 const int npix = result.getSize();
00248
00249 const double rng = hi - lo;
00250
00251 for (int k = 0; k < npix; ++k)
00252 {
00253 int val = int((win[k]-lo)/rng*255);
00254
00255 if (val < 0) { clip = true; val = 0; }
00256 else if (val > 255) { clip = true; val = 255; }
00257
00258 GVX_ASSERT(val >= 0);
00259 GVX_ASSERT(val <= 255);
00260
00261 *bytes++ = val;
00262 }
00263
00264 if (clip)
00265 LERROR("some values were clipped");
00266
00267 return result;
00268 }
00269
00270 void SearchArray::pareBackground(const uint shrunkSize) const
00271 {
00272
00273
00274 random_shuffle(itsArray.begin(),itsArray.end());
00275 while(itsArray.size() > shrunkSize)
00276 {
00277 const rutz::shared_ptr<SearchItem> last = itsArray.back();
00278 doRemoveElement(last);
00279 if (last->type == SearchItem::FOREGROUND)
00280 doAddElement(last,true);
00281 }
00282 }
00283
00284 void SearchArray::clear() const
00285 {
00286 itsArray.clear();
00287 }
00288
00289 void SearchArray::saveCoords(const std::string& filename) const
00290 {
00291 FILE* f = fopen(filename.c_str(), "w");
00292 if (f == 0)
00293 LFATAL("couldn't open %s for writing", filename.c_str());
00294
00295 fprintf(f, "%% fg? x0 y0 x1 y1\n");
00296
00297 for (size_t i = 0; i < itsArray.size(); ++i)
00298 {
00299 const int xcenter = int(itsArray[i]->pos.x() + itsDims.w() / 2.0 + 0.5);
00300 const int ycenter = int(itsArray[i]->pos.y() + itsDims.h() / 2.0 + 0.5);
00301
00302 const Image<double> p = itsArray[i]->getPatch();
00303
00304
00305 const int x0 = xcenter - p.getWidth() / 2;
00306 const int y0 = ycenter - p.getHeight() / 2;
00307
00308 const int x1 = x0 + p.getWidth();
00309 const int y1 = y0 + p.getHeight();
00310
00311 fprintf(f, " %d %d %d %d %d\n",
00312 itsArray[i]->type == SearchItem::FOREGROUND ? 1 : 0,
00313 x0, y0, x1, y1);
00314 }
00315
00316 fclose(f);
00317 }
00318
00319 Rectangle SearchArray::itemBounds() const
00320 {
00321 Rectangle a;
00322 return a.centerDims(Point2D<int>(0,0),itsDims-itsMargin*2);
00323 }
00324
00325 void SearchArray::doAddElement(const rutz::shared_ptr<SearchItem>& e, bool toFront) const
00326 {
00327 if (!toFront)
00328 itsArray.push_back(e);
00329 else
00330 itsArray.insert(itsArray.begin(),e);
00331
00332
00333
00334 if (itsDumpingFrames)
00335 {
00336 const Image<byte> bmap = getImage(true);
00337
00338 dumpFrame(bmap);
00339 dumpFrame(bmap);
00340 }
00341 }
00342
00343 bool SearchArray::tooClose(const vec2d& v, int except) const
00344 {
00345 if ((v.x() + itsDims.w()*0.5) < itsMargin
00346 || (itsDims.w()*0.5 - v.x()) <= itsMargin
00347 || (v.y() + itsDims.h()*0.5) < itsMargin
00348 || (itsDims.h()*0.5 - v.y()) <= itsMargin)
00349 return true;
00350
00351 const double minSpacingSqr = itsMinSpacing*itsMinSpacing;
00352
00353 for (size_t n = 0; n < itsArray.size(); ++n)
00354 {
00355 const double dx = itsArray[n]->pos.x() - v.x();
00356 const double dy = itsArray[n]->pos.y() - v.y();
00357
00358 if (dx*dx+dy*dy <= minSpacingSqr && int(n) != except)
00359 return true;
00360 }
00361
00362 return false;
00363 }
00364
00365 void SearchArray::backgHexGrid(SearchItemFactory& factory) const
00366 {
00367
00368
00369 const double dx = itsGridSpacing;
00370 const double dy = sqrt(3.0) * itsGridSpacing / 2.0;
00371
00372 const int nx = int((itsDims.w() - itsMinSpacing) / dx - 0.5);
00373 const int ny = int((itsDims.h() - itsMinSpacing) / dy);
00374
00375 double y = -0.5 * (ny-1) * dy;
00376
00377 for (int j = 0; j < ny; ++j, y += dy)
00378 {
00379 double x = -0.5 * (nx-1) * dx - 0.25 * dx;
00380
00381
00382
00383 if (j%2) x += 0.5*dx;
00384
00385 for (int i = 0; i < nx; ++i, x += dx)
00386 {
00387 if (!tooClose(vec2d(x, y), -1))
00388 doAddElement(factory.make(vec2d(x, y)));
00389 }
00390 }
00391 }
00392
00393 void SearchArray::backgFill(SearchItemFactory& factory) const
00394 {
00395 const double dx = itsMinSpacing / itsFillResolution;
00396
00397 const double halfX = 0.5 * itsDims.w();
00398 const double halfY = 0.5 * itsDims.h();
00399
00400 for (double x = -halfX; x <= halfX; x += dx)
00401 for (double y = -halfY; y <= halfY; y += dx)
00402 {
00403 if (!tooClose(vec2d(x, y), -1))
00404 doAddElement(factory.make(vec2d(x, y)));
00405 }
00406 }
00407
00408 void SearchArray::backgJitter(rutz::urand& urand, const int jitterIters,
00409 const bool doJitterForeground) const
00410 {
00411 const double jitter = (itsMinSpacing/16.0);
00412
00413 const double halfX = 0.5 * itsDims.w();
00414 const double halfY = 0.5 * itsDims.h();
00415
00416 for (int niter = 0; niter < jitterIters; ++niter)
00417 {
00418 for (size_t n = 0; n < itsArray.size(); ++n)
00419 {
00420 if (!doJitterForeground
00421 && itsArray[n]->type == SearchItem::FOREGROUND)
00422 continue;
00423
00424 vec2d v;
00425 v.x() = itsArray[n]->pos.x() + jitter*(urand.fdraw_range(-1.0, 1.0));
00426 v.y() = itsArray[n]->pos.y() + jitter*(urand.fdraw_range(-1.0, 1.0));
00427
00428 if (v.x() < -halfX) v.x() += itsDims.w();
00429 if (v.x() > halfX) v.x() -= itsDims.w();
00430 if (v.y() < -halfY) v.y() += itsDims.h();
00431 if (v.y() > halfY) v.y() -= itsDims.h();
00432
00433 if (!tooClose(v, n))
00434 {
00435 itsArray[n]->pos = v;
00436 }
00437 }
00438
00439 if (itsDumpingFrames &&
00440 niter % itsFrameDumpPeriod == 0)
00441 {
00442 const Image<byte> bmap = getImage(true);
00443 dumpFrame(bmap);
00444 }
00445 }
00446 }
00447
00448 void SearchArray::doRemoveElement(const rutz::shared_ptr<SearchItem>& e) const
00449 {
00450 for (size_t i = 0; i < itsArray.size(); ++i)
00451 {
00452 if (e->pos.x()==itsArray[i]->pos.x() &&
00453 e->pos.y()==itsArray[i]->pos.y())
00454 {
00455 itsArray.erase(itsArray.begin()+i);
00456 break;
00457 }
00458 }
00459
00460
00461
00462 if (itsDumpingFrames)
00463 {
00464 const Image<byte> bmap = getImage(true);
00465
00466 dumpFrame(bmap);
00467 dumpFrame(bmap);
00468 }
00469
00470 }
00471
00472
00473
00474
00475
00476
00477
00478
00479 #endif // PSYCHO_SEARCHARRAY_C_DEFINED