PetaVision  Alpha
InputLayer.cpp
1 /*
2  * InputLayer.cpp
3  * Formerly InputLayer.cpp
4  */
5 
6 #include "InputLayer.hpp"
7 #include "columns/RandomSeed.hpp"
8 #include "utils/BufferUtilsMPI.hpp"
9 
10 #include <cfloat>
11 
12 namespace PV {
13 
14 InputLayer::InputLayer(const char *name, HyPerCol *hc) { initialize(name, hc); }
15 
16 InputLayer::~InputLayer() {
17  delete mBorderExchanger;
18  delete mTimestampStream;
19 }
20 
21 int InputLayer::initialize(const char *name, HyPerCol *hc) {
22  int status = HyPerLayer::initialize(name, hc);
23  return status;
24 }
25 
26 Response::Status InputLayer::allocateDataStructures() {
27  auto status = HyPerLayer::allocateDataStructures();
28  if (!Response::completed(status)) {
29  return status;
30  }
31  if (mNeedInputRegionsPointer) {
32  mInputRegionsAllBatchElements.resize(getNumExtendedAllBatches());
33  }
34  return Response::SUCCESS;
35 }
36 
37 void InputLayer::initializeBatchIndexer() {
38  // TODO: move check of size of mStartFrameIndex and mSkipFrameIndex here.
39  pvAssert(getMPIBlock());
40  pvAssert(getMPIBlock()->getRank() == 0);
41  int localBatchCount = getLayerLoc()->nbatch;
42  int mpiBatchCount = getMPIBlock()->getBatchDimension();
43  int mpiGlobalCount = getMPIBlock()->getGlobalBatchDimension();
44  int globalBatchCount = localBatchCount * mpiGlobalCount;
45  int batchOffset = localBatchCount * getMPIBlock()->getStartBatch();
46  int blockBatchCount = localBatchCount * getMPIBlock()->getBatchDimension();
47  int fileCount = countInputImages();
48  mBatchIndexer = std::unique_ptr<BatchIndexer>(
49  new BatchIndexer(
50  std::string(name),
51  globalBatchCount,
52  batchOffset,
53  blockBatchCount,
54  fileCount,
55  mBatchMethod,
56  initializeFromCheckpointFlag));
57  for (int b = 0; b < blockBatchCount; ++b) {
58  mBatchIndexer->specifyBatching(
59  b, mStartFrameIndex.at(batchOffset + b), mSkipFrameIndex.at(batchOffset + b));
60  mBatchIndexer->initializeBatch(b);
61  }
62  mBatchIndexer->setRandomSeed(RandomSeed::instance()->getInitialSeed() + mRandomSeed);
63 }
64 
65 // Virtual method used to spend multiple display periods on one file.
66 // Can be used to implement lists of collections or modifications to
67 // the loaded file, such as streaming audio or video.
68 bool InputLayer::readyForNextFile() {
69  // A display period <= 0 means we never change files
70  return mDisplayPeriod > 0;
71 }
72 
73 Response::Status InputLayer::updateState(double time, double dt) {
74  if (readyForNextFile()) {
75 
76  // Write file path to timestamp file
77  if (mTimestampStream) {
78  std::ostringstream outStrStream;
79  outStrStream.precision(15);
80  int kb0 = getLayerLoc()->kb0;
81  int blockBatchCount = getLayerLoc()->nbatch * getMPIBlock()->getBatchDimension();
82  for (int b = 0; b < blockBatchCount; ++b) {
83  int index = mBatchIndexer->getIndex(b);
84  outStrStream << "[" << getName() << "] time: " << time << ", batch element: " << b + kb0
85  << ", index: " << mBatchIndexer->getIndex(b) << ","
86  << describeInput(mBatchIndexer->getIndex(b)) << "\n";
87  }
88  size_t len = outStrStream.str().length();
89  mTimestampStream->write(outStrStream.str().c_str(), len);
90  mTimestampStream->flush();
91  }
92 
93  // Read in the next file
95  }
96  return Response::SUCCESS;
97 }
98 
99 void InputLayer::retrieveInput(double timef, double dt) {
100  if (getMPIBlock()->getRank() == 0) {
101  int displayPeriodIndex = std::floor(timef / (mDisplayPeriod * dt));
102  if (displayPeriodIndex % mJitterChangeInterval == 0) {
103  for (int b = 0; b < mRandomShiftX.size(); b++) {
104  mRandomShiftX[b] = -mMaxShiftX + (mRNG() % (2 * mMaxShiftX + 1));
105  mRandomShiftY[b] = -mMaxShiftY + (mRNG() % (2 * mMaxShiftY + 1));
106  if (mXFlipEnabled) {
107  mMirrorFlipX[b] = mXFlipToggle ? !mMirrorFlipX[b] : (mRNG() % 100) > 50;
108  }
109  if (mYFlipEnabled) {
110  mMirrorFlipY[b] = mYFlipToggle ? !mMirrorFlipY[b] : (mRNG() % 100) > 50;
111  }
112  }
113  }
114  }
115 
116  int localNBatch = getLayerLoc()->nbatch;
117  for (int m = 0; m < getMPIBlock()->getBatchDimension(); m++) {
118  for (int b = 0; b < localNBatch; b++) {
119  if (getMPIBlock()->getRank() == 0) {
120  int blockBatchElement = b + localNBatch * m;
121  int inputIndex = mBatchIndexer->getIndex(blockBatchElement);
122  mInputData.at(b) = retrieveData(inputIndex);
123  int width = mInputData.at(b).getWidth();
124  int height = mInputData.at(b).getHeight();
125  int features = mInputData.at(b).getFeatures();
126  mInputRegion.at(b) = Buffer<float>(width, height, features);
127  int const N = mInputRegion.at(b).getTotalElements();
128  for (int k = 0; k < N; k++) {
129  mInputRegion.at(b).set(k, 1.0f);
130  }
131  fitBufferToGlobalLayer(mInputData.at(b), blockBatchElement);
132  fitBufferToGlobalLayer(mInputRegion.at(b), blockBatchElement);
133  // Now dataBuffer has input over the global layer. Apply normalizeLuminanceFlag, etc.
134  normalizePixels(b);
135  // Finally, crop to the part of the image covered by the MPIBlock.
136  cropToMPIBlock(mInputData.at(b));
137  cropToMPIBlock(mInputRegion.at(b));
138  }
139  // Each MPIBlock sends the local portions.
140  scatterInput(b, m);
141  }
142  }
143 }
144 
145 // Note: we call retrieveInput and then nextIndex because we update on the
146 // first timestep (even though we initialized in initializeActivity).
147 // If we could skip the update on the first timestep, we could call
148 // nextIndex first, and then call retrieveInput, which seems more natural.
149 void InputLayer::retrieveInputAndAdvanceIndex(double timef, double dt) {
150  retrieveInput(timef, dt);
151  if (mBatchIndexer) {
152  int blockBatchCount = getLayerLoc()->nbatch * getMPIBlock()->getBatchDimension();
153  for (int b = 0; b < blockBatchCount; b++) {
154  mBatchIndexer->nextIndex(b);
155  }
156  }
157 }
158 
159 int InputLayer::scatterInput(int localBatchIndex, int mpiBatchIndex) {
160  int const procBatchIndex = getMPIBlock()->getBatchIndex();
161  if (procBatchIndex != 0 and procBatchIndex != mpiBatchIndex) {
162  return PV_SUCCESS;
163  }
164  PVLayerLoc const *loc = getLayerLoc();
165  PVHalo const *halo = &loc->halo;
166  int activityWidth, activityHeight, activityLeft, activityTop;
167  if (mUseInputBCflag) {
168  activityWidth = loc->nx + halo->lt + halo->rt;
169  activityHeight = loc->ny + halo->up + halo->dn;
170  activityLeft = 0;
171  activityTop = 0;
172  }
173  else {
174  activityWidth = loc->nx;
175  activityHeight = loc->ny;
176  activityLeft = halo->lt;
177  activityTop = halo->up;
178  }
179  Buffer<float> dataBuffer;
180  Buffer<float> regionBuffer;
181 
182  if (getMPIBlock()->getRank() == 0) {
183  dataBuffer = mInputData.at(localBatchIndex);
184  regionBuffer = mInputRegion.at(localBatchIndex);
185  }
186  else {
187  dataBuffer.resize(activityWidth, activityHeight, loc->nf);
188  regionBuffer.resize(activityWidth, activityHeight, loc->nf);
189  }
190  BufferUtils::scatter<float>(getMPIBlock(), dataBuffer, loc->nx, loc->ny, mpiBatchIndex, 0);
191  BufferUtils::scatter<float>(getMPIBlock(), regionBuffer, loc->nx, loc->ny, mpiBatchIndex, 0);
192  if (procBatchIndex != mpiBatchIndex) {
193  return PV_SUCCESS;
194  }
195 
196  // All processes that make it to this point have the indicated MPI batch index,
197  // and dataBuffer has the correct data for the indicated batch index.
198  // Clear the current activity for this batch element; then copy the input data over row by row.
199  float *activityBuffer = &getActivity()[localBatchIndex * getNumExtended()];
200  for (int n = 0; n < getNumExtended(); ++n) {
201  activityBuffer[n] = mPadValue;
202  }
203 
204  for (int y = 0; y < activityHeight; ++y) {
205  for (int x = 0; x < activityWidth; ++x) {
206  for (int f = 0; f < numFeatures; ++f) {
207  int activityIndex = kIndex(
208  activityLeft + x,
209  activityTop + y,
210  f,
211  loc->nx + halo->lt + halo->rt,
212  loc->ny + halo->up + halo->dn,
213  numFeatures);
214  if (regionBuffer.at(x, y, f) > 0.0f) {
215  activityBuffer[activityIndex] = dataBuffer.at(x, y, f);
216  }
217  }
218  }
219  }
220  if (mNeedInputRegionsPointer) {
221  float *inputRegionBuffer =
222  &getInputRegionsAllBatchElements()[localBatchIndex * getNumExtended()];
223  for (int y = 0; y < activityHeight; ++y) {
224  for (int x = 0; x < activityWidth; ++x) {
225  for (int f = 0; f < numFeatures; ++f) {
226  int activityIndex = kIndex(
227  activityLeft + x,
228  activityTop + y,
229  f,
230  loc->nx + halo->lt + halo->rt,
231  loc->ny + halo->up + halo->dn,
232  numFeatures);
233  if (regionBuffer.at(x, y, f) > 0.0f) {
234  inputRegionBuffer[activityIndex] = regionBuffer.at(x, y, f);
235  }
236  }
237  }
238  }
239  }
240 
241  return PV_SUCCESS;
242 }
243 
244 void InputLayer::fitBufferToGlobalLayer(Buffer<float> &buffer, int blockBatchElement) {
245  pvAssert(getMPIBlock()->getRank() == 0);
246  const PVLayerLoc *loc = getLayerLoc();
247  int const xMargins = mUseInputBCflag ? loc->halo.lt + loc->halo.rt : 0;
248  int const yMargins = mUseInputBCflag ? loc->halo.dn + loc->halo.up : 0;
249  const int targetWidth = loc->nxGlobal + xMargins;
250  const int targetHeight = loc->nyGlobal + yMargins;
251 
252  FatalIf(
253  buffer.getFeatures() != loc->nf,
254  "ERROR: Input for layer %s has %d features, but layer has %d.\n",
255  getName(),
256  buffer.getFeatures(),
257  loc->nf);
258 
259  if (mAutoResizeFlag) {
260  BufferUtils::rescale(
261  buffer, targetWidth, targetHeight, mRescaleMethod, mInterpolationMethod, mAnchor);
262  buffer.translate(
263  -mOffsetX + mRandomShiftX[blockBatchElement],
264  -mOffsetY + mRandomShiftY[blockBatchElement]);
265  }
266  else {
267  buffer.grow(targetWidth, targetHeight, mAnchor);
268  buffer.translate(
269  -mOffsetX + mRandomShiftX[blockBatchElement],
270  -mOffsetY + mRandomShiftY[blockBatchElement]);
271  buffer.crop(targetWidth, targetHeight, mAnchor);
272  }
273 
274  if (mMirrorFlipX[blockBatchElement] || mMirrorFlipY[blockBatchElement]) {
275  buffer.flip(mMirrorFlipX[blockBatchElement], mMirrorFlipY[blockBatchElement]);
276  }
277 }
278 
279 void InputLayer::normalizePixels(int batchElement) {
280  Buffer<float> &dataBuffer = mInputData.at(batchElement);
281  Buffer<float> const &regionBuffer = mInputRegion.at(batchElement);
282  int const totalElements = dataBuffer.getTotalElements();
283  pvAssert(totalElements == regionBuffer.getTotalElements());
284  int validRegionCount = 0;
285  for (int k = 0; k < totalElements; k++) {
286  if (regionBuffer.at(k) > 0.0f) {
287  validRegionCount++;
288  }
289  }
290  if (validRegionCount == 0) {
291  return;
292  }
293  if (mNormalizeLuminanceFlag) {
294  if (mNormalizeStdDev) {
295  float imageSum = 0.0f;
296  float imageSumSq = 0.0f;
297  for (int k = 0; k < totalElements; k++) {
298  if (regionBuffer.at(k) > 0.0f) {
299  float const v = dataBuffer.at(k);
300  imageSum += v;
301  imageSumSq += v * v;
302  }
303  }
304 
305  // set mean to zero
306  float imageAverage = imageSum / validRegionCount;
307  for (int k = 0; k < totalElements; k++) {
308  if (regionBuffer.at(k) > 0.0f) {
309  float const v = dataBuffer.at(k);
310  dataBuffer.set(k, v - imageAverage);
311  }
312  }
313 
314  // set std dev to 1
315  float imageVariance = imageSumSq / validRegionCount - imageAverage * imageAverage;
316  pvAssert(imageVariance >= 0);
317  if (imageVariance > 0) {
318  float imageStdDev = std::sqrt(imageVariance);
319  for (int k = 0; k < totalElements; k++) {
320  if (regionBuffer.at(k) > 0.0f) {
321  float const v = dataBuffer.at(k) / imageStdDev;
322  dataBuffer.set(k, v);
323  }
324  }
325  }
326  else {
327  // Image is flat; set to identically zero.
328  // This may not be necessary since we subtracted the mean,
329  // but maybe there could be roundoff issues?
330  for (int k = 0; k < totalElements; k++) {
331  if (regionBuffer.at(k) > 0.0f) {
332  dataBuffer.set(k, 0.0f);
333  }
334  }
335  }
336  }
337  else { // mNormalizeStdDev is false; normalize so max is one and min is zero.
338  float imageMax = -std::numeric_limits<float>::max();
339  float imageMin = std::numeric_limits<float>::max();
340  for (int k = 0; k < totalElements; k++) {
341  if (regionBuffer.at(k) > 0.0f) {
342  float const v = dataBuffer.at(k);
343  imageMax = v > imageMax ? v : imageMax;
344  imageMin = v < imageMin ? v : imageMin;
345  }
346  }
347  if (imageMax > imageMin) {
348  float imageStretch = 1.0f / (imageMax - imageMin);
349  for (int k = 0; k < totalElements; k++) {
350  if (regionBuffer.at(k) > 0.0f) {
351  float const v = (dataBuffer.at(k) - imageMin) * imageStretch;
352  dataBuffer.set(k, v);
353  }
354  }
355  }
356  else {
357  for (int k = 0; k < totalElements; k++) {
358  if (regionBuffer.at(k) > 0.0f) {
359  dataBuffer.set(k, 0.0f);
360  }
361  }
362  }
363  }
364  }
365  if (mInverseFlag) {
366  if (mNormalizeLuminanceFlag) {
367  for (int k = 0; k < totalElements; k++) {
368  if (regionBuffer.at(k) > 0.0f) {
369  float const v = -dataBuffer.at(k);
370  dataBuffer.set(k, v);
371  }
372  }
373  }
374  else {
375  float imageMax = -std::numeric_limits<float>::max();
376  float imageMin = std::numeric_limits<float>::max();
377  for (int k = 0; k < totalElements; k++) {
378  if (regionBuffer.at(k) > 0.0f) {
379  float const v = dataBuffer.at(k);
380  imageMax = v > imageMax ? v : imageMax;
381  imageMin = v < imageMin ? v : imageMin;
382  }
383  }
384  for (int k = 0; k < totalElements; k++) {
385  if (regionBuffer.at(k) > 0.0f) {
386  float const v = imageMax + imageMin - dataBuffer.at(k);
387  dataBuffer.set(k, v);
388  }
389  }
390  }
391  }
392 }
393 
394 void InputLayer::cropToMPIBlock(Buffer<float> &buffer) {
395  const PVLayerLoc *loc = getLayerLoc();
396  int const startX = getMPIBlock()->getStartColumn() * loc->nx;
397  int const startY = getMPIBlock()->getStartRow() * loc->ny;
398  buffer.translate(-startX, -startY);
399  int const xMargins = mUseInputBCflag ? loc->halo.lt + loc->halo.rt : 0;
400  int const yMargins = mUseInputBCflag ? loc->halo.dn + loc->halo.up : 0;
401  int const blockWidth = getMPIBlock()->getNumColumns() * loc->nx + xMargins;
402  int const blockHeight = getMPIBlock()->getNumRows() * loc->ny + yMargins;
403  buffer.crop(blockWidth, blockHeight, Buffer<float>::NORTHWEST);
404 }
405 
406 double InputLayer::getDeltaUpdateTime() { return mDisplayPeriod > 0 ? mDisplayPeriod : DBL_MAX; }
407 
408 int InputLayer::requireChannel(int channelNeeded, int *numChannelsResult) {
409  if (parent->getCommunicator()->commRank() == 0) {
410  ErrorLog().printf("%s cannot be a post-synaptic layer.\n", getDescription_c());
411  }
412  *numChannelsResult = 0;
413  return PV_FAILURE;
414 }
415 
416 void InputLayer::allocateV() { clayer->V = nullptr; }
417 
418 void InputLayer::initializeV() { pvAssert(getV() == nullptr); }
419 
420 void InputLayer::initializeActivity() {
421  retrieveInput(parent->simulationTime(), parent->getDeltaTime());
422 }
423 
424 int InputLayer::ioParamsFillGroup(enum ParamsIOFlag ioFlag) {
425  int status = HyPerLayer::ioParamsFillGroup(ioFlag);
426  ioParam_displayPeriod(ioFlag);
427  ioParam_inputPath(ioFlag);
428  ioParam_offsetAnchor(ioFlag);
429  ioParam_offsets(ioFlag);
430  ioParam_maxShifts(ioFlag);
431  ioParam_flipsEnabled(ioFlag);
432  ioParam_flipsToggle(ioFlag);
433  ioParam_jitterChangeInterval(ioFlag);
434  ioParam_autoResizeFlag(ioFlag);
435  ioParam_aspectRatioAdjustment(ioFlag);
436  ioParam_interpolationMethod(ioFlag);
437  ioParam_inverseFlag(ioFlag);
438  ioParam_normalizeLuminanceFlag(ioFlag);
439  ioParam_normalizeStdDev(ioFlag);
440  ioParam_useInputBCflag(ioFlag);
441  ioParam_padValue(ioFlag);
442  ioParam_batchMethod(ioFlag);
443  ioParam_randomSeed(ioFlag);
444  ioParam_start_frame_index(ioFlag);
445  ioParam_skip_frame_index(ioFlag);
446  ioParam_resetToStartOnLoop(ioFlag);
447  ioParam_writeFrameToTimestamp(ioFlag);
448  return status;
449 }
450 
451 Response::Status InputLayer::registerData(Checkpointer *checkpointer) {
452  auto status = HyPerLayer::registerData(checkpointer);
453  if (!Response::completed(status)) {
454  return status;
455  }
456  if (checkpointer->getMPIBlock()->getRank() == 0) {
457  mRNG.seed(mRandomSeed);
458  int numBatch = getLayerLoc()->nbatch;
459  int nBatch = getMPIBlock()->getBatchDimension() * numBatch;
460  mRandomShiftX.resize(nBatch);
461  mRandomShiftY.resize(nBatch);
462  mMirrorFlipX.resize(nBatch);
463  mMirrorFlipY.resize(nBatch);
464  mInputData.resize(numBatch);
465  mInputRegion.resize(numBatch);
466  initializeBatchIndexer();
467  mBatchIndexer->setWrapToStartIndex(mResetToStartOnLoop);
468  mBatchIndexer->registerData(checkpointer);
469 
470  if (mWriteFrameToTimestamp) {
471  std::string timestampFilename = std::string("timestamps/");
472  timestampFilename += name + std::string(".txt");
473  std::string cpFileStreamLabel(getName());
474  cpFileStreamLabel.append("_TimestampState");
475  bool needToCreateFile = checkpointer->getCheckpointReadDirectory().empty();
476  mTimestampStream = new CheckpointableFileStream(
477  timestampFilename, needToCreateFile, checkpointer, cpFileStreamLabel);
478  }
479  }
480  return Response::SUCCESS;
481 }
482 
483 Response::Status InputLayer::readStateFromCheckpoint(Checkpointer *checkpointer) {
484  auto status = Response::NO_ACTION;
485  if (initializeFromCheckpointFlag) {
486  status = HyPerLayer::readStateFromCheckpoint(checkpointer);
487  if (!Response::completed(status)) {
488  return status;
489  }
490  if (mBatchIndexer) {
491  pvAssert(getMPIBlock()->getRank() == 0);
492  }
493  }
494  return status;
495 }
496 
497 int InputLayer::checkValidAnchorString(const char *offsetAnchor) {
498  int status = PV_SUCCESS;
499  if (offsetAnchor == NULL || strlen(offsetAnchor) != (size_t)2) {
500  status = PV_FAILURE;
501  }
502  else {
503  char xOffsetAnchor = offsetAnchor[1];
504  if (xOffsetAnchor != 'l' && xOffsetAnchor != 'c' && xOffsetAnchor != 'r') {
505  status = PV_FAILURE;
506  }
507  char yOffsetAnchor = offsetAnchor[0];
508  if (yOffsetAnchor != 't' && yOffsetAnchor != 'c' && yOffsetAnchor != 'b') {
509  status = PV_FAILURE;
510  }
511  }
512  return status;
513 }
514 
515 void InputLayer::ioParam_inputPath(enum ParamsIOFlag ioFlag) {
516  char *tempString = nullptr;
517  if (ioFlag == PARAMS_IO_WRITE) {
518  tempString = strdup(mInputPath.c_str());
519  }
520  parent->parameters()->ioParamStringRequired(ioFlag, name, "inputPath", &tempString);
521  if (ioFlag == PARAMS_IO_READ) {
522  mInputPath = std::string(tempString);
523  }
524  free(tempString);
525 }
526 
527 void InputLayer::ioParam_useInputBCflag(enum ParamsIOFlag ioFlag) {
528  parent->parameters()->ioParamValue(
529  ioFlag, name, "useInputBCflag", &mUseInputBCflag, mUseInputBCflag);
530 }
531 
532 int InputLayer::ioParam_offsets(enum ParamsIOFlag ioFlag) {
533  parent->parameters()->ioParamValue(ioFlag, name, "offsetX", &mOffsetX, mOffsetX);
534  parent->parameters()->ioParamValue(ioFlag, name, "offsetY", &mOffsetY, mOffsetY);
535  return PV_SUCCESS;
536 }
537 
538 int InputLayer::ioParam_maxShifts(enum ParamsIOFlag ioFlag) {
539  parent->parameters()->ioParamValue(ioFlag, name, "maxShiftX", &mMaxShiftX, mMaxShiftX);
540  parent->parameters()->ioParamValue(ioFlag, name, "maxShiftY", &mMaxShiftY, mMaxShiftY);
541  return PV_SUCCESS;
542 }
543 
544 int InputLayer::ioParam_flipsEnabled(enum ParamsIOFlag ioFlag) {
545  parent->parameters()->ioParamValue(ioFlag, name, "xFlipEnabled", &mXFlipEnabled, mXFlipEnabled);
546  parent->parameters()->ioParamValue(ioFlag, name, "yFlipEnabled", &mYFlipEnabled, mYFlipEnabled);
547  return PV_SUCCESS;
548 }
549 
550 int InputLayer::ioParam_flipsToggle(enum ParamsIOFlag ioFlag) {
551  parent->parameters()->ioParamValue(ioFlag, name, "xFlipToggle", &mXFlipToggle, mXFlipToggle);
552  parent->parameters()->ioParamValue(ioFlag, name, "yFlipToggle", &mYFlipToggle, mYFlipToggle);
553  return PV_SUCCESS;
554 }
555 
556 int InputLayer::ioParam_jitterChangeInterval(enum ParamsIOFlag ioFlag) {
557  parent->parameters()->ioParamValue(
558  ioFlag, name, "jitterChangeInterval", &mJitterChangeInterval, mJitterChangeInterval);
559  return PV_SUCCESS;
560 }
561 
562 void InputLayer::ioParam_offsetAnchor(enum ParamsIOFlag ioFlag) {
563  if (ioFlag == PARAMS_IO_READ) {
564  char *offsetAnchor = nullptr;
565  parent->parameters()->ioParamString(ioFlag, name, "offsetAnchor", &offsetAnchor, "tl");
566  if (checkValidAnchorString(offsetAnchor) == PV_FAILURE) {
567  Fatal() << "Invalid value for offsetAnchor\n";
568  }
569  if (strcmp(offsetAnchor, "tl") == 0) {
570  mAnchor = Buffer<float>::NORTHWEST;
571  }
572  else if (strcmp(offsetAnchor, "tc") == 0) {
573  mAnchor = Buffer<float>::NORTH;
574  }
575  else if (strcmp(offsetAnchor, "tr") == 0) {
576  mAnchor = Buffer<float>::NORTHEAST;
577  }
578  else if (strcmp(offsetAnchor, "cl") == 0) {
579  mAnchor = Buffer<float>::WEST;
580  }
581  else if (strcmp(offsetAnchor, "cc") == 0) {
582  mAnchor = Buffer<float>::CENTER;
583  }
584  else if (strcmp(offsetAnchor, "cr") == 0) {
585  mAnchor = Buffer<float>::EAST;
586  }
587  else if (strcmp(offsetAnchor, "bl") == 0) {
588  mAnchor = Buffer<float>::SOUTHWEST;
589  }
590  else if (strcmp(offsetAnchor, "bc") == 0) {
591  mAnchor = Buffer<float>::SOUTH;
592  }
593  else if (strcmp(offsetAnchor, "br") == 0) {
594  mAnchor = Buffer<float>::SOUTHEAST;
595  }
596  else {
597  if (parent->getCommunicator()->commRank() == 0) {
598  ErrorLog().printf(
599  "%s: offsetAnchor must be a two-letter string. The first character must be "
600  "\"t\", \"c\", or \"b\" (for top, center or bottom); and the second character "
601  "must be \"l\", \"c\", or \"r\" (for left, center or right).\n",
602  getDescription_c());
603  }
604  MPI_Barrier(parent->getCommunicator()->communicator());
605  exit(EXIT_FAILURE);
606  }
607  free(offsetAnchor);
608  }
609  else { // Writing
610  // The opposite of above. Find a better way to do this that isn't so gross
611  char *offsetAnchor = (char *)calloc(3, sizeof(char));
612  offsetAnchor[2] = '\0';
613  switch (mAnchor) {
616  case Buffer<float>::NORTHEAST: offsetAnchor[0] = 't'; break;
617  case Buffer<float>::WEST:
619  case Buffer<float>::EAST: offsetAnchor[0] = 'c'; break;
622  case Buffer<float>::SOUTHEAST: offsetAnchor[0] = 'b'; break;
623  }
624  switch (mAnchor) {
627  case Buffer<float>::SOUTH: offsetAnchor[1] = 'c'; break;
628  case Buffer<float>::EAST:
630  case Buffer<float>::SOUTHEAST: offsetAnchor[1] = 'r'; break;
631  case Buffer<float>::WEST:
633  case Buffer<float>::SOUTHWEST: offsetAnchor[1] = 'l'; break;
634  }
635  parent->parameters()->ioParamString(ioFlag, name, "offsetAnchor", &offsetAnchor, "tl");
636  free(offsetAnchor);
637  }
638 }
639 
640 void InputLayer::ioParam_autoResizeFlag(enum ParamsIOFlag ioFlag) {
641  parent->parameters()->ioParamValue(
642  ioFlag, name, "autoResizeFlag", &mAutoResizeFlag, mAutoResizeFlag);
643 }
644 
645 void InputLayer::ioParam_aspectRatioAdjustment(enum ParamsIOFlag ioFlag) {
646  assert(!parent->parameters()->presentAndNotBeenRead(name, "autoResizeFlag"));
647  if (mAutoResizeFlag) {
648  char *aspectRatioAdjustment = nullptr;
649  if (ioFlag == PARAMS_IO_WRITE) {
650  switch (mRescaleMethod) {
651  case BufferUtils::CROP: aspectRatioAdjustment = strdup("crop"); break;
652  case BufferUtils::PAD: aspectRatioAdjustment = strdup("pad"); break;
653  }
654  }
655  parent->parameters()->ioParamString(
656  ioFlag, name, "aspectRatioAdjustment", &aspectRatioAdjustment, "crop");
657  if (ioFlag == PARAMS_IO_READ) {
658  assert(aspectRatioAdjustment);
659  for (char *c = aspectRatioAdjustment; *c; c++) {
660  *c = tolower(*c);
661  }
662  }
663  if (strcmp(aspectRatioAdjustment, "crop") == 0) {
664  mRescaleMethod = BufferUtils::CROP;
665  }
666  else if (strcmp(aspectRatioAdjustment, "pad") == 0) {
667  mRescaleMethod = BufferUtils::PAD;
668  }
669  else {
670  if (parent->getCommunicator()->commRank() == 0) {
671  ErrorLog().printf(
672  "%s: aspectRatioAdjustment must be either \"crop\" or \"pad\".\n",
673  getDescription_c());
674  }
675  MPI_Barrier(parent->getCommunicator()->communicator());
676  exit(EXIT_FAILURE);
677  }
678  free(aspectRatioAdjustment);
679  }
680 }
681 
682 void InputLayer::ioParam_interpolationMethod(enum ParamsIOFlag ioFlag) {
683  pvAssert(!parent->parameters()->presentAndNotBeenRead(name, "autoResizeFlag"));
684  if (mAutoResizeFlag) {
685  char *interpolationMethodString = nullptr;
686  if (ioFlag == PARAMS_IO_READ) {
687  parent->parameters()->ioParamString(
688  ioFlag,
689  name,
690  "interpolationMethod",
691  &interpolationMethodString,
692  "bicubic",
693  true /*warn if absent*/);
694  assert(interpolationMethodString);
695  for (char *c = interpolationMethodString; *c; c++) {
696  *c = tolower(*c);
697  }
698  if (!strncmp(interpolationMethodString, "bicubic", strlen("bicubic"))) {
699  mInterpolationMethod = BufferUtils::BICUBIC;
700  }
701  else if (
702  !strncmp(interpolationMethodString, "nearestneighbor", strlen("nearestneighbor"))) {
703  mInterpolationMethod = BufferUtils::NEAREST;
704  }
705  else {
706  if (parent->getCommunicator()->commRank() == 0) {
707  ErrorLog().printf(
708  "%s: interpolationMethod must be either \"bicubic\" or \"nearestNeighbor\".\n",
709  getDescription_c());
710  }
711  MPI_Barrier(parent->getCommunicator()->communicator());
712  exit(EXIT_FAILURE);
713  }
714  }
715  else {
716  assert(ioFlag == PARAMS_IO_WRITE);
717  switch (mInterpolationMethod) {
718  case BufferUtils::BICUBIC: interpolationMethodString = strdup("bicubic"); break;
719  case BufferUtils::NEAREST: interpolationMethodString = strdup("nearestNeighbor"); break;
720  }
721  parent->parameters()->ioParamString(
722  ioFlag,
723  name,
724  "interpolationMethod",
725  &interpolationMethodString,
726  "bicubic",
727  true /*warn if absent*/);
728  }
729  free(interpolationMethodString);
730  }
731 }
732 
733 void InputLayer::ioParam_inverseFlag(enum ParamsIOFlag ioFlag) {
734  parent->parameters()->ioParamValue(ioFlag, name, "inverseFlag", &mInverseFlag, mInverseFlag);
735 }
736 
737 void InputLayer::ioParam_normalizeLuminanceFlag(enum ParamsIOFlag ioFlag) {
738  parent->parameters()->ioParamValue(
739  ioFlag, name, "normalizeLuminanceFlag", &mNormalizeLuminanceFlag, mNormalizeLuminanceFlag);
740 }
741 
742 void InputLayer::ioParam_normalizeStdDev(enum ParamsIOFlag ioFlag) {
743  assert(!parent->parameters()->presentAndNotBeenRead(name, "normalizeLuminanceFlag"));
744  if (mNormalizeLuminanceFlag) {
745  parent->parameters()->ioParamValue(
746  ioFlag, name, "normalizeStdDev", &mNormalizeStdDev, mNormalizeStdDev);
747  }
748 }
749 void InputLayer::ioParam_padValue(enum ParamsIOFlag ioFlag) {
750  parent->parameters()->ioParamValue(ioFlag, name, "padValue", &mPadValue, mPadValue);
751 }
752 
753 void InputLayer::ioParam_InitVType(enum ParamsIOFlag ioFlag) {
754  assert(mInitVObject == NULL);
755  return;
756 }
757 
758 void InputLayer::ioParam_triggerLayerName(enum ParamsIOFlag ioFlag) {
759  if (ioFlag == PARAMS_IO_READ) {
760  triggerLayerName = NULL;
761  triggerFlag = false;
762  parent->parameters()->handleUnnecessaryStringParameter(
763  name, "triggerLayerName", NULL /*correct value*/);
764  }
765 }
766 
767 void InputLayer::ioParam_displayPeriod(enum ParamsIOFlag ioFlag) {
768  parent->parameters()->ioParamValue(
769  ioFlag, name, "displayPeriod", &mDisplayPeriod, mDisplayPeriod);
770 }
771 
772 void InputLayer::ioParam_batchMethod(enum ParamsIOFlag ioFlag) {
773  char *batchMethod = nullptr;
774  if (ioFlag == PARAMS_IO_WRITE) {
775  switch (mBatchMethod) {
776  case BatchIndexer::BYFILE: batchMethod = strdup("byFile"); break;
777  case BatchIndexer::BYLIST: batchMethod = strdup("byList"); break;
778  case BatchIndexer::BYSPECIFIED: batchMethod = strdup("bySpecified"); break;
779  case BatchIndexer::RANDOM: batchMethod = strdup("random"); break;
780  }
781  }
782  parent->parameters()->ioParamString(ioFlag, name, "batchMethod", &batchMethod, "byFile");
783  if (strcmp(batchMethod, "byImage") == 0 || strcmp(batchMethod, "byFile") == 0) {
784  mBatchMethod = BatchIndexer::BYFILE;
785  }
786  else if (strcmp(batchMethod, "byMovie") == 0 || strcmp(batchMethod, "byList") == 0) {
787  mBatchMethod = BatchIndexer::BYLIST;
788  }
789  else if (strcmp(batchMethod, "bySpecified") == 0) {
790  mBatchMethod = BatchIndexer::BYSPECIFIED;
791  }
792  else if (strcmp(batchMethod, "random") == 0) {
793  mBatchMethod = BatchIndexer::RANDOM;
794  }
795  else {
796  Fatal() << getName() << ": Input layer " << name
797  << " batchMethod not recognized. Options "
798  "are \"byFile\", \"byList\", bySpecified, and random.\n";
799  }
800  free(batchMethod);
801 }
802 
803 void InputLayer::ioParam_randomSeed(enum ParamsIOFlag ioFlag) {
804  parent->parameters()->ioParamValue(ioFlag, name, "randomSeed", &mRandomSeed, mRandomSeed);
805 }
806 
807 void InputLayer::ioParam_start_frame_index(enum ParamsIOFlag ioFlag) {
808  int *paramsStartFrameIndex;
809  int length = 0;
810  if (ioFlag == PARAMS_IO_WRITE) {
811  length = mStartFrameIndex.size();
812  paramsStartFrameIndex = static_cast<int *>(calloc(length, sizeof(int)));
813  for (int i = 0; i < length; ++i) {
814  paramsStartFrameIndex[i] = mStartFrameIndex.at(i);
815  }
816  }
817  this->parent->parameters()->ioParamArray(
818  ioFlag, this->getName(), "start_frame_index", &paramsStartFrameIndex, &length);
819  FatalIf(
820  length != 0 && length != parent->getNBatchGlobal(),
821  "%s: start_frame_index requires either 0 or nbatch values.\n",
822  getName());
823  mStartFrameIndex.clear();
824  mStartFrameIndex.resize(parent->getNBatchGlobal());
825  if (length > 0) {
826  for (int i = 0; i < length; ++i) {
827  mStartFrameIndex.at(i) = paramsStartFrameIndex[i];
828  }
829  }
830  free(paramsStartFrameIndex);
831 }
832 
833 void InputLayer::ioParam_skip_frame_index(enum ParamsIOFlag ioFlag) {
834  pvAssert(!parent->parameters()->presentAndNotBeenRead(name, "batchMethod"));
835  if (mBatchMethod != BatchIndexer::BYSPECIFIED) {
836  mSkipFrameIndex.resize(parent->getNBatchGlobal(), 0);
837  // Earlier behavior made it a fatal error if skip_frame_index was used
838  // and batchMethod was not bySpecified. Now the parameter is skipped and
839  // a warning will be issued when params are scanned for unread values.
840  return;
841  }
842  int *paramsSkipFrameIndex = nullptr;
843  int length = 0;
844  if (ioFlag == PARAMS_IO_WRITE) {
845  length = mSkipFrameIndex.size();
846  paramsSkipFrameIndex = static_cast<int *>(calloc(length, sizeof(int)));
847  for (int i = 0; i < length; ++i) {
848  paramsSkipFrameIndex[i] = mSkipFrameIndex.at(i);
849  }
850  }
851  this->parent->parameters()->ioParamArray(
852  ioFlag, this->getName(), "skip_frame_index", &paramsSkipFrameIndex, &length);
853  FatalIf(
854  length != parent->getNBatchGlobal(),
855  "%s: skip_frame_index requires nbatch values.\n",
856  getName());
857  mSkipFrameIndex.clear();
858  mSkipFrameIndex.resize(length);
859  for (int i = 0; i < length; ++i) {
860  mSkipFrameIndex.at(i) = paramsSkipFrameIndex[i];
861  }
862  free(paramsSkipFrameIndex);
863 }
864 
865 void InputLayer::ioParam_resetToStartOnLoop(enum ParamsIOFlag ioFlag) {
866  assert(!parent->parameters()->presentAndNotBeenRead(name, "batchMethod"));
867  if (mBatchMethod == BatchIndexer::BYSPECIFIED) {
868  parent->parameters()->ioParamValue(
869  ioFlag, name, "resetToStartOnLoop", &mResetToStartOnLoop, mResetToStartOnLoop);
870  }
871  else {
872  mResetToStartOnLoop = false;
873  }
874 }
875 
876 void InputLayer::ioParam_writeFrameToTimestamp(enum ParamsIOFlag ioFlag) {
877  assert(!parent->parameters()->presentAndNotBeenRead(name, "displayPeriod"));
878  if (mDisplayPeriod > 0) {
879  parent->parameters()->ioParamValue(
880  ioFlag, name, "writeFrameToTimestamp", &mWriteFrameToTimestamp, mWriteFrameToTimestamp);
881  }
882  else {
883  mWriteFrameToTimestamp = false;
884  }
885 }
886 
887 } // end namespace PV
virtual int ioParamsFillGroup(enum ParamsIOFlag ioFlag) override
Definition: HyPerLayer.cpp:571
virtual Buffer< float > retrieveData(int inputIndex)=0
void fitBufferToGlobalLayer(Buffer< float > &buffer, int blockBatchElement)
Definition: InputLayer.cpp:244
int scatterInput(int localBatchIndex, int mpiBatchIndex)
Definition: InputLayer.cpp:159
int getNumColumns() const
Definition: MPIBlock.hpp:130
int getNumRows() const
Definition: MPIBlock.hpp:125
void retrieveInputAndAdvanceIndex(double timef, double dt)
Definition: InputLayer.cpp:149
virtual double getDeltaUpdateTime() override
Definition: InputLayer.cpp:406
static bool completed(Status &a)
Definition: Response.hpp:49
int getRank() const
Definition: MPIBlock.hpp:100
int getStartColumn() const
Definition: MPIBlock.hpp:151
int getGlobalBatchDimension() const
Definition: MPIBlock.hpp:120
int initialize(const char *name, HyPerCol *hc)
Definition: HyPerLayer.cpp:129
virtual void normalizePixels(int batchElement)
Definition: InputLayer.cpp:279
int getStartRow() const
Definition: MPIBlock.hpp:146
int getBatchDimension() const
Definition: MPIBlock.hpp:135
virtual int ioParamsFillGroup(enum ParamsIOFlag ioFlag) override
Definition: InputLayer.cpp:424
virtual void ioParam_triggerLayerName(enum ParamsIOFlag ioFlag) override
triggerLayerName: Specifies the name of the layer that this layer triggers off of. If set to NULL or the empty string, the layer does not trigger but updates its state on every timestep.
Definition: InputLayer.cpp:758
virtual std::string describeInput(int index)
Definition: InputLayer.hpp:201
int getBatchIndex() const
Definition: MPIBlock.hpp:171
int getStartBatch() const
Definition: MPIBlock.hpp:156
virtual void ioParam_InitVType(enum ParamsIOFlag ioFlag) override
initVType: Specifies how to initialize the V buffer.
Definition: InputLayer.cpp:753
virtual int countInputImages()=0
void retrieveInput(double timef, double dt)
Definition: InputLayer.cpp:99
void handleUnnecessaryStringParameter(const char *group_name, const char *param_name)
Definition: PVParams.cpp:1704