Commits
Ville Suoranta authored 6541319b7b5 Merge
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 | } |