Line data Source code
1 : //# ImageFit1D.cc: Class to fit Spectral components to vectors in an image
2 : //# Copyright (C) 2004
3 : //# Associated Universities, Inc. Washington DC, USA.
4 : //#
5 : //# This library is free software; you can redistribute it and/or modify it
6 : //# under the terms of the GNU Library General Public License as published by
7 : //# the Free Software Foundation; either version 2 of the License, or (at your
8 : //# option) any later version.
9 : //#
10 : //# This library is distributed in the hope that it will be useful, but WITHOUT
11 : //# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 : //# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
13 : //# License for more details.
14 : //#
15 : //# You should have received a copy of the GNU Library General Public License
16 : //# along with this library; if not, write to the Free Software Foundation,
17 : //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA.
18 : //#
19 : //# Correspondence concerning AIPS++ should be addressed as follows:
20 : //# Internet email: casa-feedback@nrao.edu.
21 : //# Postal address: AIPS++ Project Office
22 : //# National Radio Astronomy Observatory
23 : //# 520 Edgemont Road
24 : //# Charlottesville, VA 22903-2475 USA
25 : //#
26 : //# $Id: ImageFit1D.tcc 20652 2009-07-06 05:04:32Z Malte.Marquarding $
27 :
28 : #include <imageanalysis/ImageAnalysis/ImageFit1D.h>
29 :
30 : #include <casacore/casa/Arrays/ArrayMath.h>
31 : #include <casacore/casa/Exceptions/Error.h>
32 : #include <casacore/coordinates/Coordinates/CoordinateUtil.h>
33 : #include <casacore/coordinates/Coordinates/CoordinateSystem.h>
34 : #include <casacore/coordinates/Coordinates/SpectralCoordinate.h>
35 : #include <casacore/images/Images/ImageInterface.h>
36 : #include <casacore/images/Images/SubImage.h>
37 : #include <casacore/images/Regions/ImageRegion.h>
38 : #include <casacore/lattices/Lattices/LatticeUtilities.h>
39 : #include <casacore/lattices/LatticeMath/LatticeMathUtil.h>
40 : #include <components/SpectralComponents/SpectralEstimate.h>
41 : #include <components/SpectralComponents/SpectralElement.h>
42 : #include <casacore/casa/Utilities/Assert.h>
43 :
44 : #include <memory>
45 :
46 : namespace casa {
47 :
48 : /*
49 : template <class T>
50 : ImageFit1D<T>::ImageFit1D()
51 : : _image(0),
52 : _weights(0),
53 : _axis(0),
54 : _converged(false), _success(false), _isValid(true), _x(0)
55 : {
56 : checkType();
57 : }
58 : */
59 :
60 : template <class T>
61 0 : ImageFit1D<T>::ImageFit1D(
62 : std::shared_ptr<const casacore::ImageInterface<T> > image, casacore::uInt pixelAxis
63 0 : ) : _image(image), _weights(), _axis(pixelAxis),
64 0 : _converged(false), _success(false), _isValid(true)
65 : {
66 : //checkType();
67 : //setImage(image, pixelAxis);
68 0 : _construct();
69 0 : }
70 :
71 : template <class T>
72 0 : ImageFit1D<T>::ImageFit1D(
73 : std::shared_ptr<const casacore::ImageInterface<T> > image,
74 : std::shared_ptr<const casacore::ImageInterface<T> > weights,
75 : casacore::uInt pixelAxis
76 0 : ) : _image(image), _weights(weights),
77 0 : _axis(pixelAxis), _converged(false), _success(false), _isValid(true)
78 : {
79 : //checkType();
80 : //setImage(image, pixelAxis);
81 : // setWeightsImage (weights);
82 0 : _construct();
83 0 : }
84 :
85 :
86 : template <class T>
87 : ImageFit1D<T>::ImageFit1D(const ImageFit1D<T>& other)
88 : : _image(other._image), _weights(other._weights),
89 : _axis(other._axis), _converged(false), _success(false), _isValid(true)
90 : {
91 : //checkType();
92 : copy(other);
93 : }
94 :
95 : template <class T>
96 : ImageFit1D<T>& ImageFit1D<T>::operator=(const ImageFit1D<T>& other)
97 : {
98 : if (this != &other) {
99 : copy(other);
100 : }
101 : return *this;
102 : }
103 :
104 0 : template <class T> ImageFit1D<T>::~ImageFit1D() {}
105 :
106 : template <class T>
107 0 : void ImageFit1D<T>::_construct() {
108 0 : checkType();
109 0 : _resetFitter();
110 0 : AlwaysAssert(_axis < _image->ndim(), casacore::AipsError);
111 :
112 0 : if (_weights) {
113 0 : AlwaysAssert (_image->shape().isEqual(_weights->shape()), casacore::AipsError);
114 : }
115 : else {
116 0 : _unityWeights.resize(_image->shape()[_axis], false);
117 0 : _unityWeights = 1.0;
118 : }
119 0 : _weightSlice.resize(_image->shape()[_axis], false);
120 0 : _sliceShape = casacore::IPosition(_image->ndim(), 1);
121 0 : _sliceShape[_axis] = _image->shape()[_axis];
122 0 : }
123 :
124 : /*
125 : template <class T>
126 : void ImageFit1D<T>::setImage (const casacore::ImageInterface<T>& image,
127 : const casacore::ImageInterface<T>& weights,
128 : casacore::uInt pixelAxis)
129 : {
130 : _resetFitter();
131 : setImage(image, pixelAxis);
132 : setWeightsImage(weights);
133 : }
134 : */
135 :
136 0 : template <class T> void ImageFit1D<T>::setData (
137 : const casacore::IPosition& pos,
138 : /*const ImageFit1D<T>::AbcissaType abcissaType,
139 : const casacore::Bool doAbs, const casacore::Double* const &abscissaDivisor,
140 : casacore::Array<casacore::Double> (*xfunc)(const casacore::Array<casacore::Double>&), */
141 : casacore::Array<FitterType> (*yfunc)(const casacore::Array<FitterType>&)
142 : ) {
143 0 : _resetFitter();
144 : /*
145 : const casacore::uInt nDim = _image->ndim();
146 : casacore::IPosition start(nDim);
147 : start(_axis) = 0;
148 : for (casacore::uInt i=0; i<nDim; i++) {
149 : if (i!=_axis) {
150 : start(i) = pos(i);
151 : }
152 : }
153 : */
154 0 : casacore::IPosition start = pos;
155 0 : start[_axis] = 0;
156 : // Get ordinate data
157 :
158 0 : casacore::Vector<T> y;
159 0 : y = _image->getSlice(start, _sliceShape, true);
160 :
161 : // Mask
162 :
163 0 : casacore::Vector<casacore::Bool> mask;
164 0 : mask = _image->getMaskSlice(start, _sliceShape, true);
165 :
166 : // Weights
167 :
168 0 : if (_weights.get()) {
169 0 : convertArray(_weightSlice, _weights->getSlice(start, _sliceShape, true));
170 : }
171 : else {
172 0 : _weightSlice = _unityWeights;
173 : }
174 : // Set data in fitter; we need to use a casacore::Double fitter at present
175 0 : casacore::Vector<FitterType> y2(y.shape());
176 0 : convertArray(y2, y);
177 0 : if (yfunc) {
178 0 : y2 = (*yfunc)(y2);
179 : // in some cases, the supplied function will return NAN values, eg
180 : // log(y) will return NAN for nonpositive y values. Just mask those.
181 0 : mask = mask && ! isNaN(y2);
182 : }
183 0 : ThrowIf(
184 : !_fitter.setData (_x, y2, mask, _weightSlice),
185 : _fitter.errorMessage()
186 : );
187 0 : }
188 :
189 0 : template <class T> void ImageFit1D<T>::setData (
190 : const casacore::IPosition& pos,
191 : const ImageFit1D<T>::AbcissaType abcissaType,
192 : const casacore::Bool doAbs, const casacore::Double* const &abscissaDivisor,
193 : casacore::Array<casacore::Double> (*xfunc)(const casacore::Array<casacore::Double>&),
194 : casacore::Array<FitterType> (*yfunc)(const casacore::Array<FitterType>&)
195 : ) {
196 0 : _resetFitter();
197 : /*
198 : const casacore::uInt nDim = _image->ndim();
199 : casacore::IPosition start(nDim);
200 : start(_axis) = 0;
201 : for (casacore::uInt i=0; i<nDim; i++) {
202 : if (i!=_axis) {
203 : start(i) = pos(i);
204 : }
205 : }
206 : */
207 :
208 0 : casacore::IPosition start = pos;
209 0 : start[_axis] = 0;
210 :
211 : // Get ordinate data
212 :
213 0 : casacore::Vector<T> y;
214 0 : y = _image->getSlice(start, _sliceShape, true);
215 :
216 : // Mask
217 :
218 0 : casacore::Vector<casacore::Bool> mask;
219 0 : mask = _image->getMaskSlice(start, _sliceShape, true);
220 :
221 : // Weights
222 :
223 0 : if (_weights.get()) {
224 0 : convertArray(_weightSlice, _weights->getSlice(start, _sliceShape, true));
225 : }
226 : else {
227 0 : _weightSlice = _unityWeights;
228 : }
229 :
230 : // Generate Abscissa
231 :
232 0 : casacore::Vector<casacore::Double> x = _x.copy();
233 0 : if (x.size() == 0) {
234 0 : x = makeAbscissa(abcissaType, doAbs, abscissaDivisor);
235 0 : if (xfunc) {
236 0 : x = (*xfunc)(x);
237 : }
238 : }
239 : // Set data in fitter; we need to use a casacore::Double fitter at present
240 :
241 0 : casacore::Vector<FitterType> y2(y.shape());
242 0 : convertArray(y2, y);
243 0 : if (yfunc) {
244 0 : y2 = (*yfunc)(y2);
245 : // in some cases, the supplied function will return NAN values, eg
246 : // log(y) will return NAN for nonpositive y values. Just mask those.
247 0 : mask = mask && ! isNaN(y2);
248 : }
249 0 : ThrowIf(
250 : !_fitter.setData (_x, y2, mask, _weightSlice),
251 : _fitter.errorMessage()
252 : );
253 0 : }
254 :
255 : /*
256 : template <class T> casacore::Bool ImageFit1D<T>::setData (
257 : const casacore::ImageRegion& region,
258 : const ImageFit1D<T>::AbcissaType abcissaType,
259 : const casacore::Bool doAbs)
260 : {
261 : _resetFitter();
262 : // Make SubImage
263 :
264 : const casacore::SubImage<T> subImage(*_image, region, false);
265 :
266 : // Average over non-profile axes
267 :
268 : const casacore::uInt nDim = subImage.ndim();
269 : casacore::IPosition axes = casacore::IPosition::otherAxes(nDim, casacore::IPosition(1,_axis));
270 : casacore::Bool dropDeg = true;
271 : casacore::Vector<T> y;
272 : casacore::Vector<casacore::Bool> mask;
273 : casacore::LatticeMathUtil::collapse (y, mask, axes, subImage, dropDeg);
274 :
275 : // Weights
276 :
277 : casacore::Vector<T> weights(y.nelements());
278 : weights = 1.0;
279 : if (_weights.get()) {
280 : casacore::LatticeMathUtil::collapse (weights, axes, *_weights, dropDeg);
281 : }
282 :
283 : // Generate Abcissa
284 :
285 : casacore::Vector<casacore::Double> x = _x;
286 : if (x.size() == 0) {
287 : x = makeAbscissa(abcissaType, doAbs, 0);
288 : }
289 :
290 : // Set data in fitter; we need to use a casacore::Double fitter at present
291 :
292 : casacore::Vector<FitterType> y2(y.shape());
293 : convertArray(y2, y);
294 : casacore::Vector<casacore::Double> w2(weights.shape());
295 : convertArray(w2, weights);
296 : if (!_fitter.setData (x, y2, mask, w2)) {
297 : _error = _fitter.errorMessage();
298 : return false;
299 : }
300 : return true;
301 : }
302 : */
303 :
304 : template <class T>
305 0 : void ImageFit1D<T>::setGaussianElements (casacore::uInt nGauss) {
306 0 : if (nGauss > 0) {
307 0 : check();
308 0 : ThrowIf(
309 : !_fitter.setGaussianElements (nGauss),
310 : _fitter.errorMessage()
311 : );
312 : }
313 0 : }
314 :
315 : template <class T>
316 0 : bool ImageFit1D<T>::fit () {
317 : // check();
318 0 : _converged = _fitter.fit();
319 0 : _success = true;
320 0 : return _converged;
321 : }
322 :
323 : template <class T>
324 0 : bool ImageFit1D<T>::succeeded() const {
325 0 : return _success;
326 : }
327 :
328 : template <class T>
329 0 : bool ImageFit1D<T>::converged() const {
330 0 : return _converged;
331 : }
332 :
333 : template <class T>
334 0 : bool ImageFit1D<T>::setAbcissaState (
335 : casacore::String& errMsg, ImageFit1D<T>::AbcissaType& type,
336 : casacore::CoordinateSystem& cSys, const casacore::String& xUnit,
337 : const casacore::String& doppler, casacore::uInt pixelAxis
338 : ) {
339 0 : if (xUnit == "native") {
340 0 : type = ImageFit1D<T>::IM_NATIVE;
341 0 : return true;
342 : }
343 0 : if (xUnit.contains(casacore::String("pix"))) {
344 0 : type = ImageFit1D<T>::PIXEL;
345 0 : return true;
346 : }
347 0 : casacore::Unit unitKMS(casacore::String("km/s"));
348 :
349 0 : auto isSpectral = cSys.spectralAxisNumber(false) == (casacore::Int)pixelAxis;
350 :
351 : // Defer unit making until now as 'pix' not a valid unit
352 :
353 0 : casacore::Bool ok(false);
354 0 : casacore::Unit unit(xUnit);
355 0 : if (unit==unitKMS && isSpectral) {
356 0 : ok = casacore::CoordinateUtil::setSpectralState (errMsg, cSys, xUnit, doppler);
357 0 : type = ImageFit1D<T>::VELOCITY;
358 : } else {
359 0 : casacore::Vector<casacore::String> units = cSys.worldAxisUnits().copy();
360 0 : units(pixelAxis) = xUnit;
361 0 : ok = cSys.setWorldAxisUnits(units);
362 0 : if (!ok) errMsg = cSys.errorMessage();
363 0 : type = ImageFit1D<T>::IM_NATIVE;
364 0 : }
365 : //
366 0 : return ok;
367 0 : }
368 :
369 : template <class T>
370 0 : casacore::Vector<casacore::Double> ImageFit1D<T>::makeAbscissa (
371 : ImageFit1D<T>::AbcissaType type,
372 : casacore::Bool doAbs, const casacore::Double* const &abscissaDivisor
373 : ) {
374 0 : const casacore::uInt n = _image->shape()(_axis);
375 0 : casacore::Vector<casacore::Double> x(n);
376 :
377 0 : const casacore::CoordinateSystem& csys = _image->coordinates();
378 0 : casacore::Double refPix = csys.referencePixel()(_axis);
379 0 : if (type==PIXEL) {
380 0 : indgen(x);
381 0 : if (!doAbs) {
382 0 : x -= refPix;
383 : }
384 0 : return x;
385 : }
386 :
387 : // Find the pixel axis
388 :
389 : casacore::Int coord, axisInCoord;
390 0 : csys.findPixelAxis (coord, axisInCoord, _axis);
391 0 : if (type==VELOCITY) {
392 0 : AlwaysAssert(csys.type(coord)==casacore::Coordinate::SPECTRAL, casacore::AipsError);
393 0 : const casacore::SpectralCoordinate& sCoord = csys.spectralCoordinate(coord);
394 : casacore::Double world;
395 0 : for (casacore::uInt i=0; i<n; i++) {
396 0 : if (!sCoord.pixelToVelocity (world, casacore::Double(i))) {
397 0 : throw casacore::AipsError(sCoord.errorMessage());
398 : } else {
399 0 : if (doAbs) {
400 0 : x[i] = world;
401 : } else {
402 : casacore::Double worldRefVal;
403 0 : sCoord.pixelToVelocity (worldRefVal, refPix);
404 0 : x -= worldRefVal;
405 : }
406 : }
407 : }
408 : }
409 0 : else if (type==IM_NATIVE) {
410 0 : const casacore::Coordinate& gCoord = csys.coordinate(coord);
411 0 : casacore::Vector<casacore::Double> pixel(gCoord.referencePixel().copy());
412 0 : casacore::Vector<casacore::Double> world;
413 :
414 0 : for (casacore::uInt i=0; i<n; i++) {
415 0 : pixel(axisInCoord) = i;
416 0 : if (!gCoord.toWorld(world, pixel)) {
417 0 : throw casacore::AipsError(gCoord.errorMessage());
418 : }
419 : //
420 0 : if (!doAbs) {
421 0 : gCoord.makeWorldRelative(world);
422 : }
423 0 : x[i] = world(axisInCoord);
424 : }
425 0 : if (abscissaDivisor) {
426 0 : x /= *abscissaDivisor;
427 : }
428 0 : } else {
429 0 : throw casacore::AipsError("Unrecognized abscissa type");
430 : }
431 0 : return x;
432 :
433 0 : }
434 :
435 :
436 : template <class T>
437 0 : void ImageFit1D<T>::check() const
438 : {
439 0 : if (!_image.get()) {
440 0 : throw(casacore::AipsError("Image has not been set"));
441 : }
442 0 : }
443 :
444 : /*
445 : template <class T>
446 : void ImageFit1D<T>::setWeightsImage (const casacore::ImageInterface<T>& image)
447 : {
448 : AlwaysAssert (_image->shape().isEqual(image.shape()), casacore::AipsError);
449 : _weights.reset(image.cloneII());
450 : }
451 : */
452 :
453 : template <class T>
454 : void ImageFit1D<T>::copy(const ImageFit1D<T>& other)
455 : {
456 : _image.reset(
457 : other._image.get()
458 : ? other._image->cloneII()
459 : : 0
460 : );
461 : _weights.reset(
462 : other._weights.get()
463 : ? other._weights->cloneII()
464 : : 0
465 : );
466 :
467 : // These things are copies
468 :
469 : _axis = other._axis;
470 : //
471 : _fitter = other._fitter;
472 :
473 : _converged = other._converged;
474 : _success = other._success;
475 : _isValid = other._isValid;
476 : _sliceShape = other._sliceShape;
477 : _unityWeights = other._unityWeights.copy();
478 : }
479 :
480 : template <class T>
481 0 : void ImageFit1D<T>::checkType() const
482 : //
483 : // At this point, ProfileFitter and SpectralFitter
484 : // take the *Same* template type for X and Y
485 : // To avoid precision problems we do it all in Double
486 : // at the moment. Later X<T> and Y<T> can be separated
487 : //
488 : {
489 0 : casacore::DataType tp = casacore::whatType<FitterType>();
490 0 : AlwaysAssert(tp==casacore::TpDouble, casacore::AipsError);
491 0 : }
492 :
493 :
494 : template <class T>
495 : casacore::Vector<T> ImageFit1D<T>::getEstimate (casacore::Int which) const
496 : {
497 : casacore::Vector<FitterType> e = _fitter.getEstimate(which);
498 : casacore::Vector<T> t(e.shape());
499 : convertArray (t, e);
500 : return t;
501 : }
502 :
503 :
504 : template <class T>
505 0 : casacore::Vector<T> ImageFit1D<T>::getFit (casacore::Int which) const
506 : {
507 0 : casacore::Vector<FitterType> f = _fitter.getFit(which);
508 0 : casacore::Vector<T> t(f.shape());
509 0 : convertArray (t, f);
510 0 : return t;
511 0 : }
512 :
513 : template <class T>
514 0 : casacore::Vector<T> ImageFit1D<T>::getResidual(casacore::Int which, casacore::Bool fit) const
515 : {
516 0 : casacore::Vector<FitterType> r = _fitter.getResidual(which, fit);
517 0 : casacore::Vector<T> t(r.shape());
518 0 : convertArray (t, r);
519 0 : return t;
520 0 : }
521 :
522 0 : template <class T> void ImageFit1D<T>::invalidate() {
523 0 : _isValid = false;
524 0 : }
525 :
526 : template <class T>
527 0 : bool ImageFit1D<T>::isValid() const {
528 0 : return _isValid;
529 : }
530 :
531 : template <class T>
532 0 : void ImageFit1D<T>::_resetFitter() {
533 0 : _fitter = ProfileFit1D<FitterType>();
534 0 : _fitter.setElements(_fitter.getList(false));
535 0 : _isValid = true;
536 0 : _converged = false;
537 0 : _success = false;
538 0 : }
539 :
540 :
541 : } //#End casa namespace
|