PetaVision  Alpha
Segmentify.cpp
1 #include "Segmentify.hpp"
2 
3 namespace PV {
4 
5 Segmentify::Segmentify(const char *name, HyPerCol *hc) {
6  initialize_base();
7  initialize(name, hc);
8 }
9 
10 Segmentify::Segmentify() {
11  initialize_base();
12  // initialize() gets called by subclass's initialize method
13 }
14 
15 int Segmentify::initialize_base() {
16  numChannels = 0;
17  originalLayerName = NULL;
18  originalLayer = NULL;
19  segmentLayerName = NULL;
20  segmentLayer = NULL;
21  numLabelVals = 0;
22  labelIdxBuf = NULL;
23  labelVals = NULL;
24  labelCount = NULL;
25 
26  return PV_SUCCESS;
27 }
28 
29 int Segmentify::initialize(const char *name, HyPerCol *hc) {
30  int status = HyPerLayer::initialize(name, hc);
31  return status;
32 }
33 
34 int Segmentify::ioParamsFillGroup(enum ParamsIOFlag ioFlag) {
35  int status = HyPerLayer::ioParamsFillGroup(ioFlag);
36  ioParam_originalLayerName(ioFlag);
37  ioParam_segmentLayerName(ioFlag);
38  ioParam_inputMethod(ioFlag);
39  ioParam_outputMethod(ioFlag);
40  return status;
41 }
42 
43 void Segmentify::ioParam_inputMethod(enum ParamsIOFlag ioFlag) {
44  parent->parameters()->ioParamStringRequired(ioFlag, name, "inputMethod", &inputMethod);
45  if (strcmp(inputMethod, "average") == 0) {
46  }
47  else if (strcmp(inputMethod, "sum") == 0) {
48  }
49  else if (strcmp(inputMethod, "max") == 0) {
50  }
51  else {
52  if (parent->columnId() == 0) {
53  ErrorLog().printf(
54  "%s: inputMethod must be \"average\", \"sum\", or \"max\".\n", getDescription_c());
55  }
56  MPI_Barrier(parent->getCommunicator()->communicator());
57  exit(EXIT_FAILURE);
58  }
59 }
60 
61 void Segmentify::ioParam_outputMethod(enum ParamsIOFlag ioFlag) {
62  parent->parameters()->ioParamStringRequired(ioFlag, name, "outputMethod", &outputMethod);
63  if (strcmp(outputMethod, "centroid") == 0) {
64  }
65  else if (strcmp(outputMethod, "fill") == 0) {
66  }
67  else {
68  if (parent->columnId() == 0) {
69  ErrorLog().printf(
70  "%s: outputMethod must be \"centriod\" or \"fill\".\n", getDescription_c());
71  }
72  MPI_Barrier(parent->getCommunicator()->communicator());
73  exit(EXIT_FAILURE);
74  }
75 }
76 
77 void Segmentify::ioParam_originalLayerName(enum ParamsIOFlag ioFlag) {
78  parent->parameters()->ioParamStringRequired(
79  ioFlag, name, "originalLayerName", &originalLayerName);
80  assert(originalLayerName);
81  if (ioFlag == PARAMS_IO_READ && originalLayerName[0] == '\0') {
82  if (parent->columnId() == 0) {
83  ErrorLog().printf("%s: originalLayerName must be set.\n", getDescription_c());
84  }
85  MPI_Barrier(parent->getCommunicator()->communicator());
86  exit(EXIT_FAILURE);
87  }
88 }
89 
90 void Segmentify::ioParam_segmentLayerName(enum ParamsIOFlag ioFlag) {
91  parent->parameters()->ioParamStringRequired(ioFlag, name, "segmentLayerName", &segmentLayerName);
92  assert(segmentLayerName);
93  if (ioFlag == PARAMS_IO_READ && segmentLayerName[0] == '\0') {
94  if (parent->columnId() == 0) {
95  ErrorLog().printf("%s: segmentLayerName must be set.\n", getDescription_c());
96  }
97  MPI_Barrier(parent->getCommunicator()->communicator());
98  exit(EXIT_FAILURE);
99  }
100 }
101 
102 Response::Status
103 Segmentify::communicateInitInfo(std::shared_ptr<CommunicateInitInfoMessage const> message) {
104  auto status = HyPerLayer::communicateInitInfo(message);
105  if (!Response::completed(status)) {
106  return status;
107  }
108 
109  // Get original layer
110  originalLayer = message->lookup<HyPerLayer>(std::string(originalLayerName));
111  if (originalLayer == NULL) {
112  if (parent->columnId() == 0) {
113  ErrorLog().printf(
114  "%s: originalLayerName \"%s\" is not a layer in the HyPerCol.\n",
115  getDescription_c(),
116  originalLayerName);
117  }
118  MPI_Barrier(parent->getCommunicator()->communicator());
119  exit(EXIT_FAILURE);
120  }
121  if (originalLayer->getInitInfoCommunicatedFlag() == false) {
122  return Response::POSTPONE;
123  }
124 
125  // Get segment layer
126  segmentLayer = message->lookup<SegmentLayer>(std::string(segmentLayerName));
127  if (segmentLayer == NULL) {
128  if (parent->columnId() == 0) {
129  ErrorLog().printf(
130  "%s: segmentLayerName \"%s\" is not a SegmentLayer.\n",
131  getDescription_c(),
132  segmentLayerName);
133  }
134  MPI_Barrier(parent->getCommunicator()->communicator());
135  exit(EXIT_FAILURE);
136  }
137 
138  if (segmentLayer->getInitInfoCommunicatedFlag() == false) {
139  return Response::POSTPONE;
140  }
141 
142  // Sync with input layer
143  originalLayer->synchronizeMarginWidth(this);
144  this->synchronizeMarginWidth(originalLayer);
145 
146  // Check sizes
147  const PVLayerLoc *srcLoc = originalLayer->getLayerLoc();
148  const PVLayerLoc *segLoc = segmentLayer->getLayerLoc();
149  const PVLayerLoc *thisLoc = getLayerLoc();
150  assert(srcLoc != NULL && segLoc != NULL);
151 
152  // Src layer must have the same number of features as this layer
153  if (srcLoc->nf != thisLoc->nf) {
154  if (parent->columnId() == 0) {
155  ErrorLog(errorMessage);
156  errorMessage.printf(
157  "%s: originalLayer \"%s\" does not have the same feature dimension as this layer.\n",
158  getDescription_c(),
159  originalLayerName);
160  errorMessage.printf(" original (nf=%d) versus (nf=%d)\n", srcLoc->nf, thisLoc->nf);
161  }
162  MPI_Barrier(parent->getCommunicator()->communicator());
163  exit(EXIT_FAILURE);
164  }
165 
166  // Segment layer must have 1 feature
167  if (segLoc->nf != 1) {
168  if (parent->columnId() == 0) {
169  ErrorLog().printf(
170  "%s: segmentLayer \"%s\" can only have 1 feature.\n",
171  getDescription_c(),
172  segmentLayerName);
173  }
174  MPI_Barrier(parent->getCommunicator()->communicator());
175  exit(EXIT_FAILURE);
176  }
177 
178  return Response::SUCCESS;
179 }
180 
181 Response::Status Segmentify::allocateDataStructures() {
182  auto status = HyPerLayer::allocateDataStructures();
183  if (!Response::completed(status)) {
184  return status;
185  }
186 
187  labelToIdx.clear();
188  labelVals = (float **)calloc(getLayerLoc()->nf, sizeof(float *));
189  labelCount = (int **)calloc(getLayerLoc()->nf, sizeof(int *));
190  labelIdxBuf = NULL;
191  // Don't allocate inner buffers yet; this will get done based on how many labels are in the
192  // current image
193 
194  return Response::SUCCESS;
195 }
196 
197 int Segmentify::checkLabelValBuf(int newSize) {
198  if (newSize <= numLabelVals) {
199  return PV_SUCCESS;
200  }
201 
202  // Grow buffers
203  for (int i = 0; i < getLayerLoc()->nf; i++) {
204  labelVals[i] = (float *)realloc(labelVals[i], newSize * sizeof(float));
205  labelCount[i] = (int *)realloc(labelCount[i], newSize * sizeof(int));
206  }
207  labelIdxBuf = (int *)realloc(labelIdxBuf, newSize * sizeof(int));
208 
209  numLabelVals = newSize;
210 
211  return PV_SUCCESS;
212 }
213 
214 void Segmentify::allocateV() {
215  // Allocate V does nothing since binning does not need a V layer
216  clayer->V = NULL;
217 }
218 
219 void Segmentify::initializeV() { assert(getV() == NULL); }
220 
221 void Segmentify::initializeActivity() {}
222 
223 int Segmentify::buildLabelToIdx(int batchIdx) {
224  Communicator *icComm = parent->getCommunicator();
225  int numMpi = icComm->commSize();
226  int rank = icComm->commRank();
227 
228  labelToIdx.clear();
229  // First, we need a single scalar per feature per segment label
230  // We need to build a data structure that maps from labels to a vector index
231  int numLabels = 0;
232  if (rank == 0) {
233  std::map<int, int> segMap = segmentLayer->getCenterIdxBuf(batchIdx);
234  // From the map, we want to grab the set of keys and store it into an int array for
235  // broadcasting
236  numLabels = segMap.size();
237  // Adjust size of buffers
238  checkLabelValBuf(numLabels);
239  // Fill buffer
240  int l = 0;
241  for (auto &seg : segMap) {
242  labelIdxBuf[l] = seg.first;
243  l++;
244  }
245  }
246 
247  // Broadcast number and list of labels from the root process to rest
248  MPI_Bcast(&numLabels, 1, MPI_INT, 0, icComm->communicator());
249  checkLabelValBuf(numLabels);
250  MPI_Bcast(labelIdxBuf, numLabels, MPI_INT, 0, icComm->communicator());
251 
252  for (int l = 0; l < numLabels; l++) {
253  // Translate the label buffer into the labelToIdx buffer
254  labelToIdx[labelIdxBuf[l]] = l;
255  // Initialize labelVals based on value reduction type
256  // If max, initialize to -inf
257  for (int fi = 0; fi < getLayerLoc()->nf; fi++) {
258  // Set count to 0
259  labelCount[fi][l] = 0;
260  if (strcmp(inputMethod, "max") == 0) {
261  labelVals[fi][l] = -INFINITY;
262  }
263  // If average or sum, initialize to 0
264  else if (strcmp(inputMethod, "average") == 0 || strcmp(inputMethod, "sum") == 0) {
265  labelVals[fi][l] = 0;
266  }
267  else {
268  assert(0); // should never get here
269  }
270  }
271  }
272  return PV_SUCCESS;
273 }
274 
275 int Segmentify::calculateLabelVals(int batchIdx) {
276  Communicator *icComm = parent->getCommunicator();
277 
278  const PVLayerLoc *srcLoc = originalLayer->getLayerLoc();
279  const PVLayerLoc *segLoc = segmentLayer->getLayerLoc();
280 
281  assert(segLoc->nf == 1);
282 
283  float *srcA = originalLayer->getActivity();
284  float *segA = segmentLayer->getActivity();
285 
286  assert(srcA);
287  assert(segA);
288 
289  float *srcBatchA = srcA + batchIdx * originalLayer->getNumExtended();
290  float *segBatchA = segA + batchIdx * segmentLayer->getNumExtended();
291 
292  // Loop through source values
293  // As segments are restricted only, we loop through restricted activity
294  for (int yi = 0; yi < srcLoc->ny; yi++) {
295  // We caluclate the index into the segment buffer and this buffer based on the
296  // relative size differences between source and label buffers
297  float segToSrcScaleY = (float)segLoc->ny / (float)srcLoc->ny;
298  int segmentYi = round(yi * segToSrcScaleY);
299  for (int xi = 0; xi < srcLoc->nx; xi++) {
300  float segToSrcScaleX = (float)segLoc->nx / (float)srcLoc->nx;
301  int segmentXi = round(xi * segToSrcScaleX);
302  // Convert segment x and y index into extended linear index into the segment buffer
303  int extSegIdx =
304  (segmentYi + segLoc->halo.up) * (segLoc->nx + segLoc->halo.lt + segLoc->halo.rt)
305  + (segmentXi + segLoc->halo.lt);
306 
307  // Assuming segments are ints
308  int labelVal = round(segBatchA[extSegIdx]);
309 
310  // This label should always exist in the map
311  // labelIdx is the index into the vals buffer
312  int labelIdx = labelToIdx.at(labelVal);
313 
314  for (int fi = 0; fi < srcLoc->nf; fi++) {
315  // Convert restricted yi and xi to extended
316  // with resepct to the source
317  int extSrcIdx = (yi + srcLoc->halo.up)
318  * (srcLoc->nx + srcLoc->halo.lt + srcLoc->halo.rt) * srcLoc->nf
319  + (xi + srcLoc->halo.lt) * srcLoc->nf + fi;
320  float srcVal = srcBatchA[extSrcIdx];
321  labelCount[fi][labelIdx]++;
322  // Fill labelVals and labelCount
323  if (strcmp(inputMethod, "max") == 0) {
324  if (labelVals[fi][labelIdx] < srcVal) {
325  labelVals[fi][labelIdx] = srcVal;
326  }
327  }
328  else if (strcmp(inputMethod, "average") == 0 || strcmp(inputMethod, "sum") == 0) {
329  labelVals[fi][labelIdx] += srcVal;
330  }
331  } // End of fi loop
332  } // End of xi loop
333  } // End of yi loop
334 
335  int numLabels = labelToIdx.size();
336 
337  int rank = icComm->commRank();
338 
339  // We need to reduce our labelVec array
340  for (int fi = 0; fi < srcLoc->nf; fi++) {
341  MPI_Allreduce(
342  MPI_IN_PLACE, labelCount[fi], numLabels, MPI_INT, MPI_SUM, icComm->communicator());
343  if (strcmp(inputMethod, "max") == 0) {
344  MPI_Allreduce(
345  MPI_IN_PLACE, labelVals[fi], numLabels, MPI_FLOAT, MPI_MAX, icComm->communicator());
346  }
347  else if (strcmp(inputMethod, "sum") == 0 || strcmp(inputMethod, "average") == 0) {
348  MPI_Allreduce(
349  MPI_IN_PLACE, labelVals[fi], numLabels, MPI_FLOAT, MPI_SUM, icComm->communicator());
350  }
351  // If average, divide sum by count
352  if (strcmp(inputMethod, "average") == 0) {
353  for (int l = 0; l < numLabels; l++) {
354  labelVals[fi][l] = labelVals[fi][l] / labelCount[fi][l];
355  }
356  }
357  }
358 
359  return PV_SUCCESS;
360 }
361 
362 int Segmentify::setOutputVals(int batchIdx) {
363  // Given the labelVals, we want to fill the output A buffer with what each val should be
364  const PVLayerLoc *segLoc = segmentLayer->getLayerLoc();
365  const PVLayerLoc *thisLoc = getLayerLoc();
366 
367  assert(segLoc->nf == 1);
368 
369  float *segA = segmentLayer->getActivity();
370  float *thisA = getActivity();
371 
372  assert(thisA);
373  assert(segA);
374 
375  float *segBatchA = segA + batchIdx * segmentLayer->getNumExtended();
376  float *thisBatchA = thisA + batchIdx * getNumExtended();
377 
378  // Reset activity values
379  for (int ni = 0; ni < getNumExtended(); ni++) {
380  thisBatchA[ni] = 0;
381  }
382 
383  // Scale factors between this layer and segment layer
384  float thisToSegScaleX = (float)thisLoc->nx / (float)segLoc->nx;
385  float thisToSegScaleY = (float)thisLoc->ny / (float)segLoc->ny;
386 
387  // If by centroid, get centroid map from SegmentLayer and set each value
388  if (strcmp(outputMethod, "centroid") == 0) {
389  std::map<int, int> segMap = segmentLayer->getCenterIdxBuf(batchIdx);
390  // Centroids are stored in global restricted space, with respect to the segment layer
391  for (auto &seg : segMap) {
392  int label = seg.first;
393  int segGlobalResIdx = seg.second;
394  // Convert to restrictd x and y coords wrt segment layer
395  int segGlobalResX = segGlobalResIdx % (segLoc->nxGlobal);
396  int segGlobalResY = segGlobalResIdx / (segLoc->nyGlobal);
397  // Convert to x and y wrt this layer
398  int thisGlobalResX = round(segGlobalResX * thisToSegScaleX);
399  int thisGlobalResY = round(segGlobalResY * thisToSegScaleY);
400  // If we're within bounds in this process
401  if (thisGlobalResX >= thisLoc->kx0 && thisGlobalResX < thisLoc->kx0 + thisLoc->nx
402  && thisGlobalResY >= thisLoc->ky0
403  && thisGlobalResY < thisLoc->ky0 + thisLoc->ny) {
404  // Convert thisGlobalResX and Y to an extended local linear index
405  int thisLocalExtX = thisGlobalResX - thisLoc->kx0 + thisLoc->halo.lt;
406  int thisLocalExtY = thisGlobalResY - thisLoc->ky0 + thisLoc->halo.up;
407  for (int fi = 0; fi < thisLoc->nf; fi++) {
408  int thisLocalExtIdx = thisLocalExtY
409  * (thisLoc->nx + thisLoc->halo.lt + thisLoc->halo.rt)
410  * thisLoc->nf
411  + thisLocalExtX * thisLoc->nf + fi;
412  // Set value based on labelVals
413  thisBatchA[thisLocalExtIdx] = labelVals[fi][labelToIdx.at(label)];
414  }
415  }
416  }
417  }
418  else if (strcmp(outputMethod, "fill") == 0) {
419  // Loop through this layer's neurons
420  // Looping through restricted
421  for (int yi = 0; yi < thisLoc->ny; yi++) {
422  // Translate from this yi to segment's yi
423  int segResY = round((float)yi / (float)thisToSegScaleY);
424  for (int xi = 0; xi < thisLoc->nx; xi++) {
425  int segResX = round((float)xi / (float)thisToSegScaleX);
426  // Convert restricted segment index to extended
427  int segExtIdx =
428  (segResY + segLoc->halo.up) * (segLoc->nx + segLoc->halo.lt + segLoc->halo.rt)
429  + (segResX + segLoc->halo.lt);
430  // Get label based on segment layer
431  int label = round(segBatchA[segExtIdx]);
432  // Fill index with value from labelVals;
433  for (int fi = 0; fi < thisLoc->nf; fi++) {
434  // Calulate ext index
435  int thisExtIdx = (yi + thisLoc->halo.up)
436  * (thisLoc->nx + thisLoc->halo.lt + thisLoc->halo.rt)
437  * thisLoc->nf
438  + (xi + thisLoc->halo.lt) * thisLoc->nf + fi;
439  thisBatchA[thisExtIdx] = labelVals[fi][labelToIdx.at(label)];
440  }
441  }
442  }
443  }
444  return PV_SUCCESS;
445 }
446 
447 Response::Status Segmentify::updateState(double timef, double dt) {
448  // Using the segment activity, we want to compress all values within a segment to a single value
449  // (per feature)
450  for (int bi = 0; bi < getLayerLoc()->nbatch; bi++) {
451  buildLabelToIdx(bi);
452  calculateLabelVals(bi);
453  setOutputVals(bi);
454  }
455 
456  return Response::SUCCESS;
457 }
458 
459 Segmentify::~Segmentify() {
460  free(originalLayerName);
461  clayer->V = NULL;
462 }
463 
464 } /* namespace PV */
virtual int ioParamsFillGroup(enum ParamsIOFlag ioFlag) override
Definition: HyPerLayer.cpp:571
static bool completed(Status &a)
Definition: Response.hpp:49
int initialize(const char *name, HyPerCol *hc)
Definition: HyPerLayer.cpp:129
int ioParamsFillGroup(enum ParamsIOFlag ioFlag) override
Definition: Segmentify.cpp:34
bool getInitInfoCommunicatedFlag() const
Definition: BaseObject.hpp:95