00001
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
00033
00034 #ifndef GROOVX_VISX_GABORARRAY_CC_UTC20050626084015_DEFINED
00035 #define GROOVX_VISX_GABORARRAY_CC_UTC20050626084015_DEFINED
00036
00037 #include "gaborarray.h"
00038
00039 #include "geom/geom.h"
00040 #include "geom/rect.h"
00041
00042 #include "gfx/bbox.h"
00043 #include "gfx/canvas.h"
00044 #include "gfx/gxaligner.h"
00045
00046 #include "io/ioproxy.h"
00047 #include "io/reader.h"
00048 #include "io/writer.h"
00049
00050 #include "media/bmapdata.h"
00051 #include "media/imgfile.h"
00052
00053 #include "rutz/error.h"
00054 #include "rutz/rand.h"
00055 #include "rutz/sfmt.h"
00056
00057 #include "visx/gaborpatch.h"
00058 #include "visx/snake.h"
00059
00060 #include <cstdio>
00061
00062 #include "rutz/debug.h"
00063 GVX_DBG_REGISTER
00064 #include "rutz/trace.h"
00065
00066 using geom::vec2i;
00067 using geom::vec2d;
00068 using geom::vec3d;
00069
00070 using media::bmap_data;
00071
00072 using rutz::shared_ptr;
00073
00074 using Gfx::Bbox;
00075 using Gfx::Canvas;
00076
00077 namespace
00078 {
00079 const int MAX_GABOR_NUMBER = 1800;
00080
00081 void dumpFrame(shared_ptr<media::bmap_data> bmap)
00082 {
00083 GVX_TRACE("<gaborarray.cc>::dumpFrame");
00084
00085 static int framecount = 0;
00086
00087 char fname[256];
00088 snprintf(fname, 256, "frame_%06d.png", framecount++);
00089
00090 media::save_image(fname, *bmap);
00091 }
00092
00093 const int GABORARRAY_SVID = 0;
00094 }
00095
00096 GaborArray::GaborArray(double gaborPeriod, double gaborSigma,
00097 int foregNumber, double foregSpacing,
00098 int sizeX, int sizeY,
00099 double gridSpacing,
00100 double minSpacing)
00101 :
00102 itsForegSeed(0),
00103 itsForegNumber(foregNumber),
00104 itsForegSpacing(foregSpacing),
00105 itsForegPosX(0),
00106 itsForegPosY(0),
00107
00108 itsBackgSeed(0),
00109 itsSizeX(sizeX),
00110 itsSizeY(sizeY),
00111 itsGridSpacing(gridSpacing),
00112 itsMinSpacing(minSpacing),
00113 itsFillResolution(15.0),
00114
00115 itsThetaSeed(0),
00116 itsPhaseSeed(0),
00117 itsThetaJitter(0.0),
00118 itsGaborPeriod(gaborPeriod),
00119 itsGaborSigma(gaborSigma),
00120 itsContrastSeed(0),
00121 itsContrastJitter(0.0),
00122
00123 itsTotalNumber(0),
00124 itsArray(MAX_GABOR_NUMBER),
00125 itsBmap(),
00126
00127 itsDumpingFrames(false),
00128 itsFrameDumpPeriod(20)
00129 {
00130 GVX_TRACE("GaborArray::GaborArray");
00131
00132 setAlignmentMode(GxAligner::CENTER_ON_CENTER);
00133 setPercentBorder(0);
00134 }
00135
00136 GaborArray::~GaborArray() throw() {}
00137
00138 const FieldMap& GaborArray::classFields()
00139 {
00140 static const Field FIELD_ARRAY[] =
00141 {
00142 Field("foregSeed", &GaborArray::itsForegSeed, 0, 0, 20000, 1,
00143 Field::NEW_GROUP),
00144 Field("foregNumber", &GaborArray::itsForegNumber, 24, 1, 100, 1),
00145 Field("foregSpacing", &GaborArray::itsForegSpacing,
00146 45.0, 1.0, 100.0, 1.0),
00147 Field("foregPosX", &GaborArray::itsForegPosX, 0, -2048, 2048, 1),
00148 Field("foregPosY", &GaborArray::itsForegPosY, 0, -2048, 2048, 1),
00149
00150 Field("backgSeed", &GaborArray::itsBackgSeed, 0, 0, 20000, 1),
00151 Field("sizeX", &GaborArray::itsSizeX, 512, 16, 2048, 16),
00152 Field("sizeY", &GaborArray::itsSizeY, 512, 16, 2048, 16),
00153 Field("gridSpacing", &GaborArray::itsGridSpacing, 48., 1., 200., 1.),
00154 Field("minSpacing", &GaborArray::itsMinSpacing, 36., 1., 200., 1.),
00155 Field("fillResolution", &GaborArray::itsFillResolution, 15., 1., 50., 1.),
00156
00157 Field("thetaSeed", &GaborArray::itsThetaSeed, 0, 0, 20000, 1),
00158 Field("phaseSeed", &GaborArray::itsPhaseSeed, 0, 0, 20000, 1),
00159 Field("thetaJitter", &GaborArray::itsThetaJitter, 0.0, 0.0, 1.0, 0.01),
00160 Field("gaborPeriod", &GaborArray::itsGaborPeriod, 15.0, 1.0, 50.0, 1.0),
00161 Field("gaborSigma", &GaborArray::itsGaborSigma, 7.5, 0.5, 25.0, 0.5),
00162 Field("contrastSeed", &GaborArray::itsContrastSeed, 0, 0, 20000, 1),
00163 Field("contrastJitter", &GaborArray::itsContrastJitter, 0.0, 0.0, 2.0, 0.01),
00164
00165 Field("dumpingFrames", &GaborArray::itsDumpingFrames,
00166 false, false, true, true, Field::BOOLEAN | Field::TRANSIENT),
00167 Field("frameDumpPeriod", &GaborArray::itsFrameDumpPeriod,
00168 20, 1, 100, 1, Field::TRANSIENT),
00169 };
00170
00171 static FieldMap GABORARRAY_FIELDS(FIELD_ARRAY, &GxShapeKit::classFields());
00172
00173 return GABORARRAY_FIELDS;
00174 }
00175
00176 io::version_id GaborArray::class_version_id() const
00177 {
00178 GVX_TRACE("GaborArray::class_version_id");
00179 return GABORARRAY_SVID;
00180 }
00181
00182 void GaborArray::read_from(io::reader& reader)
00183 {
00184 GVX_TRACE("GaborArray::read_from");
00185
00186 readFieldsFrom(reader, classFields());
00187
00188 reader.read_base_class("GxShapeKit", io::make_proxy<GxShapeKit>(this));
00189 }
00190
00191 void GaborArray::write_to(io::writer& writer) const
00192 {
00193 GVX_TRACE("GaborArray::write_to");
00194
00195 writeFieldsTo(writer, classFields(), GABORARRAY_SVID);
00196
00197 writer.write_base_class("GxShapeKit", io::make_const_proxy<GxShapeKit>(this));
00198 }
00199
00200 void GaborArray::saveImage(const char* filename) const
00201 {
00202 GVX_TRACE("GaborArray::saveImage");
00203
00204 update();
00205
00206 media::save_image(filename, *itsBmap);
00207 }
00208
00209 void GaborArray::saveContourOnlyImage(const char* filename) const
00210 {
00211 GVX_TRACE("GaborArray::saveContourOnlyImage");
00212
00213 updateForeg();
00214
00215 const int npix = itsSizeX*itsSizeY;
00216
00217 rutz::fixed_block<double> win(npix);
00218
00219 for (int i = 0; i < npix; ++i)
00220 win[i] = 0.0;
00221
00222 for (int i = 0; i < itsTotalNumber; ++i)
00223 {
00224 if (itsArray[i].type != GaborArrayElement::CONTOUR)
00225 continue;
00226
00227 const double theta = geom::rad_0_2pi(itsArray[i].theta + M_PI_2);
00228
00229 const int xcenter = int(itsArray[i].pos.x() + itsSizeX / 2.0 + 0.5);
00230 const int ycenter = int(itsArray[i].pos.y() + itsSizeY / 2.0 + 0.5);
00231
00232 const double period = 1.5*itsForegSpacing;
00233
00234 const GaborPatch& p =
00235 GaborPatch::lookup(itsForegSpacing/2.0, 2*M_PI/period,
00236 theta, 0.0);
00237
00238
00239 const int x0 = xcenter - p.size() / 2;
00240 const int y0 = ycenter - p.size() / 2;
00241
00242 const int x1 = x0 + p.size();
00243 const int y1 = y0 + p.size();
00244
00245 for (int y = y0; y < y1; ++y)
00246 for (int x = x0; x < x1; ++x)
00247 {
00248 if (x >= 0 && x < itsSizeX && y >=0 && y < itsSizeY)
00249 win[x+y*itsSizeX] += p.at(x-x0, y-y0);
00250 }
00251 }
00252
00253 shared_ptr<media::bmap_data> result
00254 (new media::bmap_data(vec2i(itsSizeX, itsSizeY), 8, 1));
00255
00256 unsigned char* bytes = result->bytes_ptr();
00257
00258 for (int k = 0; k < npix; ++k)
00259 {
00260 int val = int(win[k]*255);
00261
00262
00263
00264 if (val < 128) { val = 0; }
00265 else if (val > 255) { val = 255; }
00266
00267 *bytes++ = val;
00268 }
00269
00270 media::save_image(filename, *result);
00271 }
00272
00273 void GaborArray::grGetBoundingBox(Bbox& bbox) const
00274 {
00275 GVX_TRACE("GaborArray::grGetBoundingBox");
00276
00277 bbox.drawScreenRect(vec3d::zeros(),
00278 vec2i(itsSizeX, itsSizeY),
00279 vec2d::ones());
00280 }
00281
00282 void GaborArray::grRender(Canvas& canvas) const
00283 {
00284 GVX_TRACE("GaborArray::grRender");
00285
00286 update();
00287
00288 canvas.drawPixels(*itsBmap, vec3d::zeros(), vec2d::ones());
00289 }
00290
00291 void GaborArray::updateForeg() const
00292 {
00293 GVX_TRACE("GaborArray::updateForeg");
00294
00295 if (itsForegSeed.ok()
00296 && itsForegNumber.ok()
00297 && itsForegSpacing.ok()
00298 && itsForegPosX.ok()
00299 && itsForegPosY.ok())
00300 return;
00301
00302 itsTotalNumber = 0;
00303
00304 rutz::urand urand(itsForegSeed);
00305
00306 Snake snake(itsForegNumber, itsForegSpacing, urand);
00307
00308
00309 for (int n = 0; n < itsForegNumber; ++n)
00310 {
00311 GaborArrayElement e = snake.getElement(n);
00312 e.pos.x() += itsForegPosX;
00313 e.pos.y() += itsForegPosY;
00314
00315 if (!tryPush(e))
00316 {
00317 throw rutz::error("foreground elements were "
00318 "too close together!\n", SRC_POS);
00319 }
00320 }
00321
00322 itsForegSeed.save();
00323 itsForegNumber.save();
00324 itsForegSpacing.save();
00325 itsForegPosX.save();
00326 itsForegPosY.save();
00327
00328 itsBackgSeed.touch();
00329
00330 GVX_ASSERT(itsTotalNumber == itsForegNumber);
00331 }
00332
00333 void GaborArray::updateBackg() const
00334 {
00335 GVX_TRACE("GaborArray::updateBackg");
00336
00337 if (itsBackgSeed.ok()
00338 && itsSizeX.ok()
00339 && itsSizeY.ok()
00340 && itsGridSpacing.ok()
00341 && itsMinSpacing.ok()
00342 && itsFillResolution.ok())
00343 return;
00344
00345 itsTotalNumber = itsForegNumber;
00346
00347 backgHexGrid();
00348
00349 const int diffusionCycles = 10;
00350
00351 rutz::urand urand(itsBackgSeed);
00352
00353 for (int i = 0; i < diffusionCycles; ++i)
00354 {
00355 backgJitter(urand);
00356 backgFill();
00357 }
00358
00359 int insideNumber = itsForegNumber;
00360
00361 for (int n = 0; n < itsTotalNumber; ++n)
00362 {
00363 if (itsArray[n].type == GaborArrayElement::CONTOUR)
00364 continue;
00365
00366 bool inside = true;
00367
00368 for (int i = 0; i < itsForegNumber; ++i)
00369 {
00370 const int j = (i+1) % itsForegNumber;
00371
00372
00373
00374 const double Yij = itsArray[i].pos.x() - itsArray[j].pos.x();
00375 const double Xij = itsArray[j].pos.y() - itsArray[i].pos.y();
00376
00377
00378
00379 const double Xin = itsArray[n].pos.x() - itsArray[i].pos.x();
00380 const double Yin = itsArray[n].pos.y() - itsArray[i].pos.y();
00381
00382
00383
00384 const double vp = Xij*Xin + Yij*Yin;
00385
00386 if (vp < 0.0) { inside = false; break; }
00387 }
00388
00389 if (inside)
00390 {
00391 itsArray[n].type = GaborArrayElement::INSIDE;
00392 ++insideNumber;
00393 }
00394 }
00395
00396 printf(" FOREG_NUMBER %d PATCH_NUMBER %d TOTAL_NUMBER %d\n",
00397 itsForegNumber.val, insideNumber, itsTotalNumber);
00398
00399 itsBackgSeed.save();
00400 itsSizeX.save();
00401 itsSizeY.save();
00402 itsGridSpacing.save();
00403 itsMinSpacing.save();
00404 itsFillResolution.save();
00405
00406 itsThetaSeed.touch();
00407 }
00408
00409 shared_ptr<media::bmap_data> GaborArray::generateBmap(bool doTagLast) const
00410 {
00411 GVX_TRACE("GaborArray::generateBmap");
00412
00413 const int npix = itsSizeX*itsSizeY;
00414
00415 rutz::fixed_block<double> win(npix);
00416
00417 for (int i = 0; i < npix; ++i)
00418 win[i] = 0.0;
00419
00420 rutz::urand thetas(itsThetaSeed);
00421 rutz::urand phases(itsPhaseSeed);
00422 rutz::urand contrasts(itsContrastSeed);
00423
00424 for (int i = 0; i < itsTotalNumber; ++i)
00425 {
00426 const double phi = 2 * M_PI * phases.fdraw();
00427
00428
00429 const double rand_theta = 2*M_PI * thetas.fdraw();
00430
00431 const double theta =
00432 (itsArray[i].type == GaborArrayElement::CONTOUR)
00433 ? geom::rad_0_2pi(itsThetaJitter * (rand_theta - M_PI) +
00434 (itsArray[i].theta + M_PI_2))
00435 : rand_theta;
00436
00437 const int xcenter = int(itsArray[i].pos.x() + itsSizeX / 2.0 + 0.5);
00438 const int ycenter = int(itsArray[i].pos.y() + itsSizeY / 2.0 + 0.5);
00439
00440 const GaborPatch& p =
00441 GaborPatch::lookup(itsGaborSigma, 2*M_PI/itsGaborPeriod,
00442 theta, phi);
00443
00444
00445 const int x0 = xcenter - p.size() / 2;
00446 const int y0 = ycenter - p.size() / 2;
00447
00448 const int x1 = x0 + p.size();
00449 const int y1 = y0 + p.size();
00450
00451 const double contrast = exp(-itsContrastJitter * contrasts.fdraw());
00452
00453 for (int y = y0; y < y1; ++y)
00454 for (int x = x0; x < x1; ++x)
00455 {
00456 if (x >= 0 && x < itsSizeX && y >=0 && y < itsSizeY)
00457 win[x+y*itsSizeX] += contrast * p.at(x-x0, y-y0);
00458 }
00459
00460 if (doTagLast && i == (itsTotalNumber-1))
00461 {
00462 const double outer = 0.4 * p.size();
00463 const double inner = outer - 3;
00464
00465 for (int y = y0; y < y1; ++y)
00466 for (int x = x0; x < x1; ++x)
00467 {
00468 if (x >= 0 && x < itsSizeX && y >=0 && y < itsSizeY)
00469 {
00470 const double r = sqrt((x-xcenter)*(x-xcenter) +
00471 (y-ycenter)*(y-ycenter));
00472
00473 if (r >= inner && r <= outer)
00474 {
00475 win[x+y*itsSizeX] = 1.0;
00476 }
00477 }
00478 }
00479 }
00480 }
00481
00482 shared_ptr<media::bmap_data> result
00483 (new media::bmap_data(vec2i(itsSizeX, itsSizeY), 8, 1));
00484
00485 unsigned char* bytes = result->bytes_ptr();
00486
00487 bool clip = false;
00488
00489 for (int k = 0; k < npix; ++k)
00490 {
00491 int val = int((win[k]+1.0)/2.0*255);
00492
00493 if (val < 0) { clip = true; val = 0; }
00494 else if (val > 255) { clip = true; val = 255; }
00495
00496 GVX_ASSERT(val >= 0);
00497 GVX_ASSERT(val <= 255);
00498
00499 *bytes++ = val;
00500 }
00501
00502 if (clip)
00503 printf("warning: some values were clipped\n");
00504
00505 return result;
00506 }
00507
00508 void GaborArray::updateBmap() const
00509 {
00510 GVX_TRACE("GaborArray::updateBmap");
00511
00512 if (itsThetaSeed.ok()
00513 && itsPhaseSeed.ok()
00514 && itsThetaJitter.ok()
00515 && itsGaborPeriod.ok()
00516 && itsGaborSigma.ok()
00517 && itsContrastSeed.ok()
00518 && itsContrastJitter.ok())
00519 return;
00520
00521 itsBmap = generateBmap(false);
00522
00523 itsThetaSeed.save();
00524 itsPhaseSeed.save();
00525 itsThetaJitter.save();
00526 itsGaborPeriod.save();
00527 itsGaborSigma.save();
00528 itsContrastSeed.save();
00529 itsContrastJitter.save();
00530 }
00531
00532 void GaborArray::update() const
00533 {
00534 GVX_TRACE("GaborArray::update");
00535
00536 updateForeg();
00537
00538 updateBackg();
00539
00540 updateBmap();
00541 }
00542
00543 bool GaborArray::tryPush(const GaborArrayElement& e) const
00544 {
00545 if (tooClose(e.pos, -1)) return false;
00546
00547 if (itsTotalNumber >= MAX_GABOR_NUMBER)
00548 {
00549 throw rutz::error(rutz::sfmt(" More than %d elements!\n",
00550 MAX_GABOR_NUMBER), SRC_POS);
00551 }
00552
00553 itsArray[itsTotalNumber++] = e;
00554
00555
00556
00557 if (itsDumpingFrames)
00558 {
00559 shared_ptr<media::bmap_data> bmap = generateBmap(true);
00560
00561 dumpFrame(bmap);
00562 dumpFrame(bmap);
00563 }
00564
00565 return true;
00566 }
00567
00568 bool GaborArray::tooClose(const vec2d& v, int except) const
00569 {
00570 double minSpacingSqr = itsMinSpacing*itsMinSpacing;
00571
00572 for (int n = 0; n < itsTotalNumber; ++n)
00573 {
00574 const double dx = itsArray[n].pos.x() - v.x();
00575 const double dy = itsArray[n].pos.y() - v.y();
00576
00577 if (dx*dx+dy*dy <= minSpacingSqr && n != except)
00578 return true;
00579 }
00580
00581 return false;
00582 }
00583
00584 void GaborArray::backgHexGrid() const
00585 {
00586 GVX_TRACE("GaborArray::backgHexGrid");
00587
00588
00589
00590 const double dx = itsGridSpacing;
00591 const double dy = sqrt(3.0) * itsGridSpacing / 2.0;
00592
00593 const int nx = int((itsSizeX - itsMinSpacing) / dx - 0.5);
00594 const int ny = int((itsSizeY - itsMinSpacing) / dy);
00595
00596 double y = -0.5 * (ny-1) * dy;
00597
00598 for (int j = 0; j < ny; ++j, y += dy)
00599 {
00600 double x = -0.5 * (nx-1) * dx - 0.25 * dx;
00601
00602
00603
00604 if (j%2) x += 0.5*dx;
00605
00606 for (int i = 0; i < nx; ++i, x += dx)
00607 {
00608 tryPush(GaborArrayElement(x, y, 0.0, GaborArrayElement::OUTSIDE));
00609 }
00610 }
00611 }
00612
00613 void GaborArray::backgFill() const
00614 {
00615 GVX_TRACE("GaborArray::backgFill");
00616
00617 const double dx = itsMinSpacing / itsFillResolution;
00618
00619 const double halfX = 0.5 * itsSizeX;
00620 const double halfY = 0.5 * itsSizeY;
00621
00622 for (double x = -halfX; x <= halfX; x += dx)
00623 for (double y = -halfY; y <= halfY; y += dx)
00624 {
00625 tryPush(GaborArrayElement(x, y, 0.0, GaborArrayElement::OUTSIDE));
00626 }
00627
00628 const double spacing = sqrt(2.0*itsSizeX*itsSizeY/(sqrt(3.0)*itsTotalNumber));
00629 printf(" %d elements, ave spacing %f\n", itsTotalNumber, spacing);
00630 }
00631
00632 void GaborArray::backgJitter(rutz::urand& urand) const
00633 {
00634 GVX_TRACE("GaborArray::backgJitter");
00635
00636 const double jitter = (itsMinSpacing/16.0);
00637
00638 const int backgroundIters = 1000;
00639
00640 const double halfX = 0.5 * itsSizeX;
00641 const double halfY = 0.5 * itsSizeY;
00642
00643 for (int niter = 0; niter < backgroundIters; ++niter)
00644 {
00645 for (int n = 0; n < itsTotalNumber; ++n)
00646 {
00647 if (itsArray[n].type == GaborArrayElement::CONTOUR)
00648 continue;
00649
00650 vec2d v;
00651 v.x() = itsArray[n].pos.x() + jitter*(urand.fdraw_range(-1.0, 1.0));
00652 v.y() = itsArray[n].pos.y() + jitter*(urand.fdraw_range(-1.0, 1.0));
00653
00654 if (v.x() < -halfX) v.x() += itsSizeX;
00655 if (v.x() > halfX) v.x() -= itsSizeX;
00656 if (v.y() < -halfY) v.y() += itsSizeY;
00657 if (v.y() > halfY) v.y() -= itsSizeY;
00658
00659 if (!tooClose(v, n))
00660 {
00661 itsArray[n].pos = v;
00662 }
00663 }
00664
00665 if (itsDumpingFrames &&
00666 niter % itsFrameDumpPeriod == 0)
00667 {
00668 shared_ptr<media::bmap_data> bmap = generateBmap(true);
00669 dumpFrame(bmap);
00670 }
00671 }
00672 }
00673
00674 static const char __attribute__((used)) vcid_groovx_visx_gaborarray_cc_utc20050626084015[] = "$Id: gaborarray.cc 10065 2007-04-12 05:54:56Z rjpeters $ $HeadURL: file:
00675 #endif // !GROOVX_VISX_GABORARRAY_CC_UTC20050626084015_DEFINED