34#include "Eigen/Eigenvalues"
45template <
typename ImageT>
50 _constantWeight(constantWeight),
51 _eigenValues(
std::vector<double>()),
54template <
typename ImageT>
56template <
typename ImageT>
58template <
typename ImageT>
60template <
typename ImageT>
63template <
typename ImageT>
65 if (_imageList.empty()) {
66 _dimensions = img->getDimensions();
68 if (getDimensions() != img->getDimensions()) {
70 (boost::format(
"Dimension mismatch: saw %dx%d; expected %dx%d") %
71 img->getWidth() % img->getHeight() % _dimensions.getX() % _dimensions.getY())
80 _imageList.push_back(img);
81 _fluxList.push_back(flux);
84template <
typename ImageT>
89template <
typename ImageT>
91 if (_imageList.empty()) {
96 *mean =
static_cast<typename ImageT::Pixel
>(0);
98 for (
typename ImageList::const_iterator ptr = _imageList.begin(),
end = _imageList.end(); ptr !=
end;
102 *mean /= _imageList.size();
114struct SortEvalueDecreasing {
116 return a.first >
b.first;
121template <
typename ImageT>
123 int const nImage = _imageList.size();
132 _eigenImages.clear();
135 _eigenValues.clear();
136 _eigenValues.push_back(1.0);
143 Eigen::MatrixXd R(nImage, nImage);
146 for (
int i = 0; i != nImage; ++i) {
148 double const flux_i = getFlux(i);
151 for (
int j = i; j != nImage; ++j) {
153 double const flux_j = getFlux(j);
156 if (_constantWeight) {
157 dot /= flux_i * flux_j;
159 R(i, j) = R(j, i) = dot / nImage;
163 Eigen::SelfAdjointEigenSolver<Eigen::MatrixXd> eVecValues(R);
164 Eigen::MatrixXd
const& Q = eVecValues.eigenvectors();
165 Eigen::VectorXd
const& lambda = eVecValues.eigenvalues();
171 lambdaAndIndex.
reserve(nImage);
173 for (
int i = 0; i != nImage; ++i) {
176 std::sort(lambdaAndIndex.
begin(), lambdaAndIndex.
end(), SortEvalueDecreasing<double>());
180 _eigenValues.clear();
181 _eigenValues.reserve(nImage);
182 for (
int i = 0; i != nImage; ++i) {
183 _eigenValues.push_back(lambdaAndIndex[i].first);
190 _eigenImages.clear();
191 _eigenImages.reserve(ncomp < nImage ? ncomp : nImage);
193 for (
int i = 0; i < ncomp; ++i) {
198 int const ii = lambdaAndIndex[i].second;
201 *eImage =
static_cast<typename ImageT::Pixel
>(0);
203 for (
int j = 0; j != nImage; ++j) {
204 int const jj = lambdaAndIndex[j].second;
205 double const weight = Q(jj, ii) * (_constantWeight ? flux_bar / getFlux(jj) : 1);
206 eImage->scaledPlus(weight, *_imageList[jj]);
208 _eigenImages.push_back(eImage);
218template <
typename MaskedImageT>
222 MaskedImageT
const&
image
224 using ImageT =
typename MaskedImageT::Image;
228 }
else if (nEigen >
static_cast<int>(eigenImages.
size())) {
230 (boost::format(
"You only have %d eigen images (you asked for %d)") %
231 eigenImages.
size() % nEigen)
238 Eigen::MatrixXd A(nEigen, nEigen);
239 Eigen::VectorXd
b(nEigen);
241 for (
int i = 0; i != nEigen; ++i) {
244 for (
int j = i; j != nEigen; ++j) {
245 A(i, j) = A(j, i) =
innerProduct(*eigenImages[i]->getImage(), *eigenImages[j]->getImage());
248 Eigen::VectorXd
x(nEigen);
251 x(0) =
b(0) / A(0, 0);
253 x = A.jacobiSvd(Eigen::ComputeThinU | Eigen::ComputeThinV).solve(
b);
260 for (
int i = 0; i != nEigen; ++i) {
261 bestFitImage->scaledPlus(
x[i], *eigenImages[i]->getImage());
267template <
typename ImageT>
270 unsigned long,
int const) {
274template <
typename ImageT>
275double do_updateBadPixels(detail::MaskedImage_tag
const&,
282 int const nImage = imageList.size();
285 "Please provide at least one Image for me to update");
290 double maxChange = 0.0;
296 for (
int i = 0; i != nImage; ++i) {
297 double const flux_i = fluxes[i];
299 for (
int y = 0;
y != height; ++
y) {
300 typename ImageT::const_x_iterator iptr = imageList[i]->row_begin(
y);
302 for (
typename ImageT::Image::x_iterator mptr = mean.row_begin(
y),
end = mean.row_end(
y);
303 mptr !=
end; ++mptr, ++iptr, ++wptr) {
304 if (!(iptr.mask() &
mask) && iptr.variance() > 0.0) {
305 typename ImageT::Image::Pixel value = iptr.image() / flux_i;
306 float const var = iptr.variance() / (flux_i * flux_i);
307 float const ivar = 1.0 / var;
310 *mptr += value * ivar;
320 for (
int y = 0;
y != height; ++
y) {
322 for (
typename ImageT::Image::x_iterator mptr = mean.row_begin(
y),
end = mean.row_end(
y);
323 mptr !=
end; ++mptr, ++wptr) {
334 for (
int i = 0; i != nImage; ++i) {
335 double const flux_i = fluxes[i];
337 for (
int y = 0;
y != height; ++
y) {
338 typename ImageT::x_iterator iptr = imageList[i]->row_begin(
y);
339 for (
typename ImageT::Image::x_iterator mptr = mean.row_begin(
y),
end = mean.row_end(
y);
340 mptr !=
end; ++mptr, ++iptr) {
341 if ((iptr.mask() &
mask)) {
342 double const delta = ::fabs(flux_i * (*mptr) - iptr.image());
343 if (delta > maxChange) {
346 iptr.image() = flux_i * (*mptr);
352 if (ncomp >
static_cast<int>(eigenImages.size())) {
354 (boost::format(
"You only have %d eigen images (you asked for %d)") %
355 eigenImages.size() % ncomp)
359 for (
int i = 0; i != nImage; ++i) {
361 fitEigenImagesToImage(eigenImages, ncomp, *imageList[i]);
363 for (
int y = 0;
y != height; ++
y) {
364 typename ImageT::x_iterator iptr = imageList[i]->row_begin(
y);
365 for (
typename ImageT::Image::const_x_iterator fptr = fitted->row_begin(
y),
366 end = fitted->row_end(
y);
367 fptr !=
end; ++fptr, ++iptr) {
368 if (iptr.mask() &
mask) {
369 double const delta =
fabs(
static_cast<double>(*fptr) - iptr.image());
370 if (delta > maxChange) {
374 iptr.image() = *fptr;
384template <
typename ImageT>
386 return do_updateBadPixels<ImageT>(
typename ImageT::image_category(), _imageList, _fluxList, _eigenImages,
391template <
typename T,
typename U>
393 IsSame(T
const&, U
const&) {}
394 bool operator()() {
return false; }
399 IsSame(T
const& im1, T
const& im2) : _same(im1.row_begin(0) == im2.row_begin(0)) {}
400 bool operator()() {
return _same; }
407template <
typename Image1T,
typename Image2T>
408bool imagesAreIdentical(Image1T
const& im1, Image2T
const& im2) {
409 return IsSame<Image1T, Image2T>(im1, im2)();
412template <
typename Image1T,
typename Image2T>
413double innerProduct(Image1T
const& lhs, Image2T
const& rhs,
int border) {
414 if (lhs.getWidth() <= 2 * border || lhs.getHeight() <= 2 * border) {
416 (boost::format(
"All image pixels are in the border of width %d: %dx%d") % border %
417 lhs.getWidth() % lhs.getHeight())
425 if (imagesAreIdentical(lhs, rhs)) {
426 for (
int y = border;
y != lhs.getHeight() - border; ++
y) {
427 for (
typename Image1T::const_x_iterator lptr = lhs.row_begin(
y) + border,
428 lend = lhs.row_end(
y) - border;
429 lptr != lend; ++lptr) {
430 typename Image1T::Pixel val = *lptr;
437 if (lhs.getDimensions() != rhs.getDimensions()) {
439 (boost::format(
"Dimension mismatch: %dx%d v. %dx%d") % lhs.getWidth() %
440 lhs.getHeight() % rhs.getWidth() % rhs.getHeight())
444 for (
int y = border;
y != lhs.getHeight() - border; ++
y) {
445 typename Image2T::const_x_iterator rptr = rhs.row_begin(
y) + border;
446 for (
typename Image1T::const_x_iterator lptr = lhs.row_begin(
y) + border,
447 lend = lhs.row_end(
y) - border;
448 lptr != lend; ++lptr, ++rptr) {
449 double const tmp = (*lptr) * (*rptr);
464#define INSTANTIATE(T) \
465 template class ImagePca<Image<T> >; \
466 template double innerProduct(Image<T> const&, Image<T> const&, int); \
467 template class ImagePca<MaskedImage<T> >;
469#define INSTANTIATE2(T, U) \
470 template double innerProduct(Image<T> const&, Image<U> const&, int); \
471 template double innerProduct(Image<U> const&, Image<T> const&, int);
#define INSTANTIATE(FROMSYS, TOSYS)
#define LSST_EXCEPT(type,...)
afw::table::PointKey< int > dimensions
#define INSTANTIATE2(ImagePixelT1, ImagePixelT2)
x_iterator row_begin(int y) const
Return an x_iterator to the start of the y'th row.
A class to represent a 2-dimensional array of pixels.
virtual double updateBadPixels(unsigned long mask, int const ncomp)
Update the bad pixels (i.e.
void addImage(std::shared_ptr< ImageT > img, double flux=0.0)
Add an image to the set to be analyzed.
ImageList getImageList() const
Return the list of images being analyzed.
ImagePca(bool constantWeight=true)
ctor
std::shared_ptr< ImageT > getMean() const
Return the mean of the images in ImagePca's list.
std::vector< std::shared_ptr< ImageT > > ImageList
ImagePca & operator=(ImagePca const &)
T emplace_back(T... args)
double innerProduct(Image1T const &lhs, Image2T const &rhs, int const border=0)
Calculate the inner product of two images.