Commits

Ville Suoranta authored 6541319b7b5 Merge
Merge pull request #99 in CASA/casa from bugfix/CAS-10109 to master

* commit 'f6e3baa42a60c1fdf2ef973c6e61c872c6742f1a': added docs regarding masking of output image mask output pixels where interpolation was unsuccessful implement copying of input mask to output

code/imageanalysis/ImageAnalysis/StatImageCreator.cc

Modified
64 64
65 65 void StatImageCreator::setGridSpacing(uInt x, uInt y) {
66 66 _grid.first = x;
67 67 _grid.second = y;
68 68 }
69 69
70 70 SPIIF StatImageCreator::compute() {
71 71 static const AxesSpecifier dummyAxesSpec;
72 72 *_getLog() << LogOrigin(getClass(), __func__);
73 73 auto mylog = _getLog().get();
74 - auto subImage = SubImageFactory<Float>::createSubImageRO(
74 + auto subImageRO = SubImageFactory<Float>::createSubImageRO(
75 75 *_getImage(), *_getRegion(), _getMask(),
76 76 mylog, dummyAxesSpec, _getStretch()
77 77 );
78 - _doMask = ! ImageMask::isAllMaskTrue(*subImage);
79 - const auto imshape = subImage->shape();
78 + auto subImageRW = SubImageFactory<Float>::createImage(
79 + *_getImage(), "", *_getRegion(), _getMask(),
80 + dummyAxesSpec, False, False, _getStretch()
81 + );
82 + _doMask = ! ImageMask::isAllMaskTrue(*subImageRO);
83 + const auto imshape = subImageRO->shape();
80 84 const auto xshape = imshape[_dirAxes[0]];
81 85 const auto yshape = imshape[_dirAxes[1]];
82 - const auto& csys = subImage->coordinates();
86 + const auto& csys = subImageRO->coordinates();
83 87 auto anchorPixel = csys.toPixel(_anchor);
84 - TempImage<Float> output(imshape, csys);
85 - output.set(0);
86 - if (_doMask) {
87 - output.attachMask(ArrayLattice<Bool>(imshape));
88 - output.pixelMask().set(True);
89 - }
90 88 Int xanchor = rint(anchorPixel[_dirAxes[0]]);
91 89 Int yanchor = rint(anchorPixel[_dirAxes[1]]);
92 90 // ensure xanchor and yanchor are positive
93 91 if (xanchor < 0) {
94 92 // ugh, mod of a negative number in C++ doesn't do what I want it to
95 93 // integer division
96 94 xanchor += (abs(xanchor)/_grid.first + 1)*_grid.first;
97 95 }
98 96 if (yanchor < 0) {
99 97 yanchor += (abs(yanchor)/_grid.second + 1)*_grid.second;
117 115 ++nxpts;
118 116 }
119 117 auto nypts = (yshape - ystart)/ _grid.second;
120 118 if ((yshape - ystart) % _grid.second != 0) {
121 119 ++nypts;
122 120 }
123 121 IPosition storeShape = imshape;
124 122 storeShape[_dirAxes[0]] = nxpts;
125 123 storeShape[_dirAxes[1]] = nypts;
126 124 auto interpolate = _grid.first > 1 || _grid.second > 1;
127 - TempImage<Float>* writeTo = &output;
125 + TempImage<Float>* writeTo = dynamic_cast<TempImage<Float> *>(subImageRW.get());
128 126 std::unique_ptr<TempImage<Float>> store;
129 127 if (interpolate) {
130 128 store.reset(new TempImage<Float>(storeShape, csys));
131 129 store->set(0.0);
132 130 if (_doMask) {
133 131 store->attachMask(ArrayLattice<Bool>(storeShape));
134 132 store->pixelMask().set(True);
135 133 }
136 134 writeTo = store.get();
137 135 }
138 - _computeStat(*writeTo, subImage, nxpts, nypts, xstart, ystart);
136 + _computeStat(*writeTo, subImageRO, nxpts, nypts, xstart, ystart);
139 137 if (_doPhi) {
140 138 writeTo->copyData((LatticeExpr<Float>)(*writeTo * PHI));
141 139 }
142 -
143 140 if (interpolate) {
144 141 _doInterpolation(
145 - output, *store,
146 - subImage, nxpts, nypts,
147 - xstart, ystart
142 + subImageRW, *store, subImageRO,
143 + nxpts, nypts, xstart, ystart
148 144 );
149 145 }
150 - auto res = _prepareOutputImage(output);
146 + auto res = _prepareOutputImage(*subImageRW);
151 147 return res;
152 148 }
153 149
154 150 void StatImageCreator::_computeStat(
155 151 TempImage<Float>& writeTo, SPCIIF subImage,
156 152 uInt nxpts, uInt nypts, Int xstart, Int ystart
157 153 ) {
158 154 SHARED_PTR<Array<Bool>> regionMask;
159 155 uInt xBlcOff = 0;
160 156 uInt yBlcOff = 0;
179 175 chunkShape[_dirAxes[0]] = xChunkSize;
180 176 chunkShape[_dirAxes[1]] = yChunkSize;
181 177 auto xsize = imshape[_dirAxes[0]];
182 178 auto ysize = imshape[_dirAxes[1]];
183 179 IPosition planeShape(imshape.size(), 1);
184 180 planeShape[_dirAxes[0]] = xsize;
185 181 planeShape[_dirAxes[1]] = ysize;
186 182 RO_MaskedLatticeIterator<Float> lattIter (
187 183 *subImage, planeShape, True
188 184 );
189 - // Vector rather than IPosition because blc values can be negative
190 - Vector<Int> blc(ndim, 0);
191 - blc[_dirAxes[0]] = xstart - xBlcOff;
192 - blc[_dirAxes[1]] = ystart - yBlcOff;
193 185 String algName;
194 186 auto alg = _getStatsAlgorithm(algName);
195 187 std::set<StatisticsData::STATS> statToCompute;
196 188 statToCompute.insert(_statType);
197 189 alg->setStatsToCalculate(statToCompute);
198 190 auto ngrid = nxpts*nypts;
199 191 auto nPts = ngrid;
200 192 IPosition loopAxes;
201 193 for (uInt i=0; i<ndim; ++i) {
202 194 if (i != _dirAxes[0] && i != _dirAxes[1]) {
203 195 loopAxes.append(IPosition(1, i));
204 196 nPts *= imshape[i];
205 197 }
206 198 }
207 - auto hasLoopAxes = loopAxes.size() > 0;
208 - ProgressMeter pm(0, nPts, "Processing stats at grid points");
209 199 auto doCircle = _ylen.getValue() <= 0;
210 200 *_getLog() << LogOrigin(getClass(), __func__) << LogIO::NORMAL
211 201 << "Using ";
212 202 if (doCircle) {
213 203 *_getLog() << "circular region of radius " << _xlen;
214 204 }
215 205 else {
216 206 *_getLog() << "rectangular region of specified dimensions " << _xlen
217 207 << " x " << _ylen;
218 208 }
219 209 *_getLog() << " (because of centering and "
220 210 << "rounding to use whole pixels, actual dimensions of bounding box are "
221 211 << xChunkSize << " pix x " << yChunkSize << " pix";
222 212 if (doCircle) {
223 213 *_getLog() << " and there are " << ntrue(*regionMask)
224 214 << " good pixels in the circle that are being used";
225 215 }
226 216 *_getLog() << ") to choose pixels for computing " << _statName
227 217 << " using the " << algName << " algorithm around each of "
228 218 << ngrid << " grid points in " << (nPts/ngrid) << " planes." << LogIO::POST;
219 + auto imageChunkShape = chunkShape;
220 + _doStatsLoop(
221 + writeTo, lattIter, nxpts, nypts, xstart, ystart,
222 + xBlcOff, yBlcOff, xChunkSize, yChunkSize, imshape,
223 + chunkShape, regionMask, alg, regMaskCopy, loopAxes, nPts
224 + );
225 +}
226 +
227 +void StatImageCreator::_doStatsLoop(
228 + TempImage<Float>& writeTo, RO_MaskedLatticeIterator<Float>& lattIter,
229 + uInt nxpts, uInt nypts, Int xstart, Int ystart, uInt xBlcOff,
230 + uInt yBlcOff, uInt xChunkSize, uInt yChunkSize, const IPosition& imshape,
231 + const IPosition& chunkShape, SHARED_PTR<Array<Bool>> regionMask,
232 + SHARED_PTR<
233 + StatisticsAlgorithm<
234 + Double, Array<Float>::const_iterator, Array<Bool>::const_iterator,
235 + Array<Float>::const_iterator
236 + >
237 + >& alg, const Array<Bool>& regMaskCopy, const IPosition& loopAxes, uInt nPts
238 +) {
239 + auto ximshape = imshape[_dirAxes[0]];
240 + auto yimshape = imshape[_dirAxes[1]];
241 + auto ndim = imshape.size();
229 242 IPosition planeBlc(ndim, 0);
230 243 auto& xPlaneBlc = planeBlc[_dirAxes[0]];
231 244 auto& yPlaneBlc = planeBlc[_dirAxes[1]];
232 - auto imageChunkShape = chunkShape;
233 - // pixels at the TRC are included in the statistics
234 245 auto planeTrc = planeBlc + chunkShape - 1;
235 246 auto& xPlaneTrc = planeTrc[_dirAxes[0]];
236 247 auto& yPlaneTrc = planeTrc[_dirAxes[1]];
237 248 uInt nCount = 0;
238 - auto ximshape = imshape[_dirAxes[0]];
239 - auto yimshape = imshape[_dirAxes[1]];
249 + auto hasLoopAxes = ! loopAxes.empty();
250 + ProgressMeter pm(0, nPts, "Processing stats at grid points");
251 + // Vector rather than IPosition because blc values can be negative
252 + Vector<Int> blc(ndim, 0);
253 + blc[_dirAxes[0]] = xstart - xBlcOff;
254 + blc[_dirAxes[1]] = ystart - yBlcOff;
240 255 for (lattIter.atStart(); ! lattIter.atEnd(); ++lattIter) {
241 256 auto plane = lattIter.cursor();
242 257 auto lattMask = lattIter.getMask();
258 + auto checkGrid = ! allTrue(lattMask);
243 259 auto outPos = lattIter.position();
244 260 auto& xOutPos = outPos[_dirAxes[0]];
245 261 auto& yOutPos = outPos[_dirAxes[1]];
262 + // inPos is the position of the current grid point
263 + // This is the position in the current chunk, not the entire lattice
264 + auto inPos = plane.shape() - 1;
265 + inPos[_dirAxes[0]] = xstart;
266 + inPos[_dirAxes[1]] = ystart;
267 + auto& xInPos = inPos[_dirAxes[0]];
268 + auto& yInPos = inPos[_dirAxes[1]];
246 269 Int yblc = ystart - yBlcOff;
247 - for (uInt yCount=0; yCount<nypts; ++yCount, yblc+=_grid.second) {
270 + for (uInt yCount=0; yCount<nypts; ++yCount, yblc+=_grid.second, yInPos+=_grid.second) {
248 271 yOutPos = yCount;
249 272 yPlaneBlc = max(0, yblc);
250 273 yPlaneTrc = min(
251 274 yblc + (Int)yChunkSize - 1, (Int)yimshape - 1
252 275 );
253 276 IPosition regMaskStart(ndim, 0);
254 277 auto& xRegMaskStart = regMaskStart[_dirAxes[0]];
255 278 auto& yRegMaskStart = regMaskStart[_dirAxes[1]];
256 279 auto regMaskLength = regMaskStart;
257 280 auto& xRegMaskLength = regMaskLength[_dirAxes[0]];
262 285 if (yblc < 0) {
263 286 yRegMaskStart = -yblc;
264 287 yRegMaskLength += yblc;
265 288 yDoMaskSlice = True;
266 289 }
267 290 else if (yblc + yChunkSize > yimshape) {
268 291 yRegMaskLength = yimshape - yblc;
269 292 yDoMaskSlice = True;
270 293 }
271 294 }
295 + xInPos = xstart;
272 296 Int xblc = xstart - xBlcOff;
273 - for (uInt xCount=0; xCount<nxpts; ++xCount, xblc+=_grid.first) {
297 + for (uInt xCount=0; xCount<nxpts; ++xCount, xblc+=_grid.first, xInPos+=_grid.first) {
298 + Float res = 0;
274 299 xOutPos = xCount;
275 - xPlaneBlc = max(0, xblc);
276 - xPlaneTrc = min(
277 - xblc + (Int)xChunkSize - 1, (Int)ximshape - 1
278 - );
279 - SHARED_PTR<Array<Bool>> subRegionMask;
280 - if (regionMask) {
281 - auto doMaskSlice = yDoMaskSlice;
282 - xRegMaskStart = 0;
283 - if (xblc < 0) {
284 - xRegMaskStart = -xblc;
285 - xRegMaskLength = regionMask->shape()[_dirAxes[0]] + xblc;
286 - doMaskSlice = True;
287 - }
288 - else if (xblc + xChunkSize > ximshape) {
289 - regMaskLength[_dirAxes[0]] = ximshape - xblc;
290 - doMaskSlice = True;
291 - }
292 - else {
293 - xRegMaskLength = xChunkSize;
294 - }
295 - if (doMaskSlice) {
296 - Slicer sl(regMaskStart, regMaskLength);
297 - subRegionMask.reset(new Array<Bool>(regMaskCopy(sl)));
300 + if (checkGrid && ! lattMask(inPos)) {
301 + // grid point is masked, no computation should be done
302 + writeTo.putAt(res, outPos);
303 + writeTo.pixelMask().putAt(False, outPos);
304 + }
305 + else {
306 + xPlaneBlc = max(0, xblc);
307 + xPlaneTrc = min(
308 + xblc + (Int)xChunkSize - 1, (Int)ximshape - 1
309 + );
310 + SHARED_PTR<Array<Bool>> subRegionMask;
311 + if (regionMask) {
312 + auto doMaskSlice = yDoMaskSlice;
313 + xRegMaskStart = 0;
314 + if (xblc < 0) {
315 + xRegMaskStart = -xblc;
316 + xRegMaskLength = regionMask->shape()[_dirAxes[0]] + xblc;
317 + doMaskSlice = True;
318 + }
319 + else if (xblc + xChunkSize > ximshape) {
320 + regMaskLength[_dirAxes[0]] = ximshape - xblc;
321 + doMaskSlice = True;
322 + }
323 + else {
324 + xRegMaskLength = xChunkSize;
325 + }
326 + if (doMaskSlice) {
327 + Slicer sl(regMaskStart, regMaskLength);
328 + subRegionMask.reset(new Array<Bool>(regMaskCopy(sl)));
329 + }
330 + else {
331 + subRegionMask.reset(new Array<Bool>(regMaskCopy));
332 + }
298 333 }
299 - else {
300 - subRegionMask.reset(new Array<Bool>(regMaskCopy));
334 + auto maskChunk = lattMask(planeBlc, planeTrc).copy();
335 + if (subRegionMask) {
336 + maskChunk = maskChunk && *subRegionMask;
301 337 }
302 - }
303 - auto maskChunk = lattMask(planeBlc, planeTrc).copy();
304 - if (subRegionMask) {
305 - maskChunk = maskChunk && *subRegionMask;
306 - }
307 - Float res = 0;
308 - if (anyTrue(maskChunk)) {
309 - auto chunk = plane(planeBlc, planeTrc);
310 - if (allTrue(maskChunk)) {
311 - alg->setData(chunk.begin(), chunk.size());
338 + if (anyTrue(maskChunk)) {
339 + auto chunk = plane(planeBlc, planeTrc);
340 + if (allTrue(maskChunk)) {
341 + alg->setData(chunk.begin(), chunk.size());
342 + }
343 + else {
344 + alg->setData(chunk.begin(), maskChunk.begin(), chunk.size());
345 + }
346 + res = alg->getStatistic(_statType);
312 347 }
313 348 else {
314 - alg->setData(chunk.begin(), maskChunk.begin(), chunk.size());
349 + // no pixels in region on which to do stats, mask grid point
350 + writeTo.pixelMask().putAt(False, outPos);
315 351 }
316 - res = alg->getStatistic(_statType);
352 + writeTo.putAt(res, outPos);
317 353 }
318 - else {
319 - writeTo.pixelMask().putAt(False, outPos);
320 - }
321 - writeTo.putAt(res, outPos);
322 354 }
323 355 nCount += nxpts;
324 356 pm.update(nCount);
325 357 }
326 358 if (hasLoopAxes) {
327 359 Bool done = False;
328 360 uInt idx = 0;
329 361 while (! done) {
330 362 auto targetAxis = loopAxes[idx];
331 363 ++blc[targetAxis];
468 500 templateMask.reset();
469 501 if (chunkImage->isMasked()) {
470 502 auto mask = chunkImage->getMask();
471 503 if (! allTrue(mask)) {
472 504 templateMask.reset(new Array<Bool>(mask));
473 505 }
474 506 }
475 507 }
476 508
477 509 void StatImageCreator::_doInterpolation(
478 - TempImage<Float>& output, TempImage<Float>& store,
510 + SPIIF output, TempImage<Float>& store,
479 511 SPCIIF subImage, uInt nxpts, uInt nypts,
480 512 Int xstart, Int ystart
481 513 ) const {
482 514 const auto imshape = subImage->shape();
483 515 const auto xshape = imshape[_dirAxes[0]];
484 516 const auto yshape = imshape[_dirAxes[1]];
485 517 const auto ndim = subImage->ndim();
486 518 *_getLog() << LogOrigin(getClass(), __func__)
487 519 << LogIO::NORMAL << "Interpolate using "
488 520 << _interpName << " algorithm." << LogIO::POST;
494 526 }
495 527 Matrix<Float> storage(nxpts, nypts);
496 528 Matrix<Bool> storeMask(nxpts, nypts);
497 529 std::pair<uInt, uInt> start;
498 530 start.first = xstart;
499 531 start.second = ystart;
500 532 if (ndim == 2) {
501 533 store.get(storage);
502 534 store.getMask(storeMask);
503 535 _interpolate(result, resultMask, storage, storeMask, start);
504 - output.put(result);
536 + output->put(result);
505 537 if (_doMask) {
506 - output.pixelMask().put(resultMask);
538 + output->pixelMask().put(resultMask && subImage->pixelMask().get());
507 539 }
508 540 }
509 541 else {
510 542 // get each direction plane in the storage lattice at each chan/stokes
511 543 IPosition cursorShape(ndim, 1);
512 544 cursorShape[_dirAxes[0]] = nxpts;
513 545 cursorShape[_dirAxes[1]] = nypts;
514 546 auto axisPath = _dirAxes;
515 - axisPath.append((IPosition::otherAxes(ndim,_dirAxes)));
547 + axisPath.append((IPosition::otherAxes(ndim, _dirAxes)));
516 548 LatticeStepper stepper(store.shape(), cursorShape, axisPath);
517 549 Slicer slicer(stepper.position(), stepper.endPosition(), Slicer::endIsLast);
550 + IPosition myshape(ndim, 1);
551 + myshape[_dirAxes[0]] = xshape;
552 + myshape[_dirAxes[1]] = yshape;
518 553 for (stepper.reset(); ! stepper.atEnd(); stepper++) {
519 554 auto pos = stepper.position();
520 555 slicer.setStart(pos);
521 556 slicer.setEnd(stepper.endPosition());
522 557 storage = store.getSlice(slicer, True);
523 558 storeMask = store.getMaskSlice(slicer, True);
524 559 _interpolate(result, resultMask, storage, storeMask, start);
525 - output.putSlice(result, pos);
560 + output->putSlice(result, pos);
526 561 if (_doMask) {
527 - output.pixelMask().putSlice(resultMask, pos);
562 + output->pixelMask().putSlice(
563 + resultMask && subImage->pixelMask().getSlice(pos, myshape, True),
564 + pos
565 + );
528 566 }
529 567 }
530 568 }
531 569 }
532 570
533 571 void StatImageCreator::setInterpAlgorithm(Interpolate2D::Method alg) {
534 572 switch (alg) {
535 573 case Interpolate2D::CUBIC:
536 574 _interpName = "CUBIC";
537 575 return;
658 696 // exactly on a grid point, no interpolation needed
659 697 // just copy value directly from storage matrix
660 698 *iter = storage(xStoreInt, yStoreInt);
661 699 if (_doMask) {
662 700 *miter = storeMask(xStoreInt, yStoreInt);
663 701 }
664 702 }
665 703 else {
666 704 xStoreFrac = (Double)xCell/(Double)_grid.first;
667 705 storeLoc[0] = xStoreInt + xStoreFrac;
668 - _interpolater.interp(*iter, storeLoc, storage, storeMask);
706 + if (! _interpolater.interp(*iter, storeLoc, storage, storeMask)) {
707 + ThrowIf(
708 + ! _doMask,
709 + "Logic error: bad interpolation but there is no mask to set"
710 + );
711 + *miter = False;
712 + }
669 713 }
670 714 ++iter;
671 715 if (_doMask) {
672 716 ++miter;
673 717 }
674 718 }
675 719 }
676 720 }
677 721
678 722 }

Everything looks good. We'll let you know here if there's anything you should know about.

Add shortcut