31 #include "boost/make_shared.hpp"
36 #include "Eigen/Eigenvalues"
41 namespace afwMath = lsst::afw::math;
48 template <
typename ImageT>
54 _constantWeight(constantWeight),
55 _eigenValues(std::vector<double>()),
64 template <
typename ImageT>
68 if (_imageList.empty()) {
69 _dimensions = img->getDimensions();
71 if (getDimensions() != img->getDimensions()) {
73 lsst::pex::exceptions::LengthError,
74 (
boost::format(
"Dimension mismatch: saw %dx%d; expected %dx%d") %
75 img->getWidth() % img->getHeight() %
76 _dimensions.getX() % _dimensions.getY()
83 throw LSST_EXCEPT(lsst::pex::exceptions::OutOfRangeError,
"Flux may not be zero");
86 _imageList.push_back(img);
87 _fluxList.push_back(flux);
91 template <
typename ImageT>
100 template <
typename ImageT>
102 if (_imageList.empty()) {
103 throw LSST_EXCEPT(lsst::pex::exceptions::LengthError,
"You haven't provided any images");
106 typename ImageT::Ptr mean(
new ImageT(getDimensions()));
107 *mean =
static_cast<typename ImageT::Pixel
>(0);
109 for (
typename ImageList::const_iterator ptr = _imageList.begin(), end = _imageList.end();
113 *mean /= _imageList.size();
126 struct SortEvalueDecreasing :
public std::binary_function<std::pair<T, int> const&,
127 std::pair<T, int> const&, bool> {
128 bool operator()(std::pair<T, int>
const& a, std::pair<T, int>
const&
b) {
129 return a.first > b.first;
134 template <
typename ImageT>
137 int const nImage = _imageList.size();
140 throw LSST_EXCEPT(lsst::pex::exceptions::LengthError,
"No images provided for PCA analysis");
146 _eigenImages.clear();
147 _eigenImages.push_back(
typename ImageT::Ptr(
new ImageT(*_imageList[0],
true)));
149 _eigenValues.clear();
150 _eigenValues.push_back(1.0);
157 Eigen::MatrixXd R(nImage, nImage);
160 for (
int i = 0; i != nImage; ++i) {
162 double const flux_i = getFlux(i);
165 for (
int j = i; j != nImage; ++j) {
167 double const flux_j = getFlux(j);
170 if (_constantWeight) {
171 dot /= flux_i*flux_j;
173 R(i, j) = R(j, i) = dot/nImage;
177 Eigen::SelfAdjointEigenSolver<Eigen::MatrixXd> eVecValues(R);
178 Eigen::MatrixXd
const& Q = eVecValues.eigenvectors();
179 Eigen::VectorXd
const& lambda = eVecValues.eigenvalues();
184 std::vector<std::pair<double, int> > lambdaAndIndex;
185 lambdaAndIndex.reserve(nImage);
187 for (
int i = 0; i != nImage; ++i) {
188 lambdaAndIndex.push_back(std::make_pair(lambda(i), i));
190 std::sort(lambdaAndIndex.begin(), lambdaAndIndex.end(), SortEvalueDecreasing<double>());
194 _eigenValues.clear();
195 _eigenValues.reserve(nImage);
196 for (
int i = 0; i != nImage; ++i) {
197 _eigenValues.push_back(lambdaAndIndex[i].first);
204 _eigenImages.clear();
205 _eigenImages.reserve(ncomp < nImage ? ncomp : nImage);
207 for(
int i = 0; i < ncomp; ++i) {
212 int const ii = lambdaAndIndex[i].second;
214 typename ImageT::Ptr eImage(
new ImageT(_dimensions));
215 *eImage =
static_cast<typename ImageT::Pixel
>(0);
217 for (
int j = 0; j != nImage; ++j) {
218 int const jj = lambdaAndIndex[j].second;
219 double const weight = Q(jj, ii)*(_constantWeight ? flux_bar/getFlux(jj) : 1);
220 eImage->scaledPlus(weight, *_imageList[jj]);
222 _eigenImages.push_back(eImage);
236 template<
typename MaskedImageT>
237 typename MaskedImageT::Image::Ptr fitEigenImagesToImage(
240 MaskedImageT
const&
image
243 typedef typename MaskedImageT::Image ImageT;
246 throw LSST_EXCEPT(lsst::pex::exceptions::LengthError,
247 "You must have at least one eigen image");
248 }
else if (nEigen > static_cast<int>(eigenImages.size())) {
249 throw LSST_EXCEPT(lsst::pex::exceptions::LengthError,
250 (
boost::format(
"You only have %d eigen images (you asked for %d)")
251 % eigenImages.size() % nEigen).str());
257 Eigen::MatrixXd A(nEigen, nEigen);
258 Eigen::VectorXd
b(nEigen);
260 for (
int i = 0; i != nEigen; ++i) {
261 b(i) =
innerProduct(*eigenImages[i]->getImage(), *image.getImage());
263 for (
int j = i; j != nEigen; ++j) {
264 A(i, j) = A(j, i) =
innerProduct(*eigenImages[i]->getImage(), *eigenImages[j]->getImage());
267 Eigen::VectorXd
x(nEigen);
272 x = A.jacobiSvd(Eigen::ComputeThinU | Eigen::ComputeThinV).solve(
b);
277 typename ImageT::Ptr bestFitImage = boost::make_shared<ImageT>(eigenImages[0]->
getDimensions());
279 for (
int i = 0; i != nEigen; ++i) {
280 bestFitImage->scaledPlus(
x[i], *eigenImages[i]->getImage());
288 template <
typename ImageT>
289 double do_updateBadPixels(detail::basic_tag
const&,
290 typename ImagePca<ImageT>::ImageList
const&,
291 std::vector<double>
const&,
292 typename ImagePca<ImageT>::ImageList
const&,
293 unsigned long,
int const)
298 template <
typename ImageT>
299 double do_updateBadPixels(
300 detail::MaskedImage_tag
const&,
301 typename ImagePca<ImageT>::ImageList
const& imageList,
302 std::vector<double>
const& fluxes,
303 typename ImagePca<ImageT>::ImageList
const& eigenImages,
308 int const nImage = imageList.size();
310 throw LSST_EXCEPT(lsst::pex::exceptions::LengthError,
311 "Please provide at least one Image for me to update");
314 int const height = dimensions.getY();
316 double maxChange = 0.0;
319 typename ImageT::Image mean(dimensions);
322 for (
int i = 0; i != nImage; ++i) {
323 double const flux_i = fluxes[i];
326 typename ImageT::const_x_iterator iptr = imageList[i]->row_begin(
y);
328 for (
typename ImageT::Image::x_iterator mptr = mean.row_begin(
y), end = mean.row_end(
y);
329 mptr != end; ++mptr, ++iptr, ++wptr) {
330 if (!(iptr.mask() & mask) && iptr.variance() > 0.0) {
331 typename ImageT::Image::Pixel value = iptr.image()/flux_i;
332 float const var = iptr.variance()/(flux_i*flux_i);
333 float const ivar = 1.0/var;
348 for (
typename ImageT::Image::x_iterator mptr = mean.row_begin(
y), end = mean.row_end(
y);
349 mptr != end; ++mptr, ++wptr) {
360 for (
int i = 0; i != nImage; ++i) {
361 double const flux_i = fluxes[i];
364 typename ImageT::x_iterator iptr = imageList[i]->row_begin(
y);
365 for (
typename ImageT::Image::x_iterator mptr = mean.row_begin(
y), end = mean.row_end(
y);
366 mptr != end; ++mptr, ++iptr) {
367 if ((iptr.mask() & mask)) {
368 double const delta = ::fabs(flux_i*(*mptr) - iptr.image());
369 if (delta > maxChange) {
372 iptr.image() = flux_i*(*mptr);
378 if (ncomp > static_cast<int>(eigenImages.size())) {
379 throw LSST_EXCEPT(lsst::pex::exceptions::LengthError,
380 (
boost::format(
"You only have %d eigen images (you asked for %d)")
381 % eigenImages.size() % ncomp).str());
384 for (
int i = 0; i != nImage; ++i) {
385 typename ImageT::Image::Ptr fitted = fitEigenImagesToImage(eigenImages, ncomp, *imageList[i]);
388 typename ImageT::x_iterator iptr = imageList[i]->row_begin(
y);
389 for (
typename ImageT::Image::const_x_iterator fptr = fitted->row_begin(
y),
390 end = fitted->row_end(
y); fptr != end; ++fptr, ++iptr) {
391 if (iptr.mask() & mask) {
392 double const delta = ::fabs(*fptr - iptr.image());
393 if (delta > maxChange) {
397 iptr.image() = *fptr;
416 template <
typename ImageT>
422 return do_updateBadPixels<ImageT>(
typename ImageT::image_category(),
423 _imageList, _fluxList, _eigenImages, mask, ncomp);
428 template<
typename T,
typename U>
430 IsSame(T
const&, U
const&) {}
431 bool operator()() {
return false; }
435 struct IsSame<T, T> {
436 IsSame(T
const& im1, T
const& im2) :
_same(im1.row_begin(0) == im2.row_begin(0)) {}
437 bool operator()() {
return _same; }
443 template<
typename Image1T,
typename Image2T>
444 bool imagesAreIdentical(Image1T
const& im1, Image2T
const& im2) {
445 return IsSame<Image1T, Image2T>(im1, im2)();
453 template <
typename Image1T,
typename Image2T>
458 if (lhs.getWidth() <= 2*border || lhs.getHeight() <= 2*border) {
459 throw LSST_EXCEPT(lsst::pex::exceptions::LengthError,
460 (
boost::format(
"All image pixels are in the border of width %d: %dx%d") %
461 border % lhs.getWidth() % lhs.getHeight()).str());
468 if (imagesAreIdentical(lhs, rhs)) {
469 for (
int y = border;
y != lhs.getHeight() - border; ++
y) {
470 for (
typename Image1T::const_x_iterator lptr = lhs.row_begin(
y) + border,
471 lend = lhs.row_end(
y) - border; lptr != lend; ++lptr) {
472 typename Image1T::Pixel
val = *lptr;
479 if (lhs.getDimensions() != rhs.getDimensions()) {
480 throw LSST_EXCEPT(lsst::pex::exceptions::LengthError,
482 lhs.getWidth() % lhs.getHeight() % rhs.getWidth() % rhs.getHeight()).str());
485 for (
int y = border;
y != lhs.getHeight() - border; ++
y) {
486 typename Image2T::const_x_iterator rptr = rhs.row_begin(
y) + border;
487 for (
typename Image1T::const_x_iterator lptr = lhs.row_begin(
y) + border,
488 lend = lhs.row_end(
y) - border; lptr != lend; ++lptr, ++rptr) {
489 double const tmp = (*lptr)*(*rptr);
505 #define INSTANTIATE(T) \
506 template class ImagePca<Image<T> >; \
507 template double innerProduct(Image<T> const&, Image<T> const&, int); \
508 template class ImagePca<MaskedImage<T> >;
510 #define INSTANTIATE2(T, U) \
511 template double innerProduct(Image<T> const&, Image<U> const&, int); \
512 template double innerProduct(Image<U> const&, Image<T> const&, int);
520 INSTANTIATE2(
float,
double)
tbl::Key< double > weight
Support for PCA analysis of 2-D images.
double innerProduct(Image1T const &lhs, Image2T const &rhs, int const border=0)
#define INSTANTIATE(MATCH)
ImageList getImageList() const
Return the list of images being analyzed.
void addImage(typename ImageT::Ptr img, double flux=0.0)
geom::Extent2I const getDimensions() const
Return the dimension of the images being analyzed.
boost::enable_if< typename ExpressionTraits< Scalar >::IsScalar, Scalar >::type sum(Scalar const &scalar)
table::Key< table::Array< Kernel::Pixel > > image
ImageT::Ptr getMean() const
afw::table::PointKey< int > dimensions
std::vector< typename ImageT::Ptr > ImageList
void ImageT ImageT int float saturatedPixelValue int const height
ImagePca(bool constantWeight=true)
ctor
#define LSST_EXCEPT(type,...)
Compute Image Statistics.
afw::table::Key< double > b
virtual double updateBadPixels(unsigned long mask, int const ncomp)