Fit a PSF + smooth background model (linear) to a small region around the peak.
584 Fit a PSF + smooth background model (linear) to a small region around the peak.
586 @param[in] fp Footprint
587 @param[in] fmask the Mask plane for pixels in the Footprint
588 @param[in] pk the Peak within the Footprint that we are going to fit a PSF model for
589 @param[in] pkF the floating-point coordinates of this peak
590 @param[in,out] pkres a PerPeak object that will hold the results for this peak
591 @param[in] fbb the bounding-box of this Footprint (a Box2I)
592 @param[in] peaks a list of all the Peak objects within this Footprint
593 @param[in] peaksF a python list of the floating-point (x,y) peak locations
594 @param[in,out] log pex Log object
595 @param[in] psf afw PSF object
596 @param[in] psffwhm FWHM of the PSF, in pixels
597 @param[in] img the Image in which this Footprint finds itself
598 @param[in] varimg Variance plane
599 @param[in] psfChisqCut* floats: cuts in chisq-per-pixel at which to accept the PSF model
608 R0 = int(math.ceil(psffwhm*1.))
610 R1 = int(math.ceil(psffwhm*1.5))
611 cx, cy = pkF.getX(), pkF.getY()
612 psfimg = psf.computeImage(cx, cy)
614 R2 = R1 + min(psfimg.getWidth(), psfimg.getHeight())/2.
616 pbb = psfimg.getBBox()
618 px0, py0 = psfimg.getX0(), psfimg.getY0()
623 pkres.setOutOfBounds()
627 xlo = int(math.floor(cx - R1))
628 ylo = int(math.floor(cy - R1))
629 xhi = int(math.ceil (cx + R1))
630 yhi = int(math.ceil (cy + R1))
633 xlo, xhi = stampbb.getMinX(), stampbb.getMaxX()
634 ylo, yhi = stampbb.getMinY(), stampbb.getMaxY()
635 if xlo > xhi
or ylo > yhi:
636 log.logdebug(
'Skipping this peak: out of bounds')
637 pkres.setOutOfBounds()
641 if tinyFootprintSize>0
and (((xhi - xlo) < tinyFootprintSize)
or
642 ((yhi - ylo) < tinyFootprintSize)):
643 log.logdebug(
'Skipping this peak: tiny footprint / close to edge')
644 pkres.setTinyFootprint()
649 for pk2, pkF2
in zip(peaks, peaksF):
652 if pkF.distanceSquared(pkF2) > R2**2:
654 opsfimg = psf.computeImage(pkF2.getX(), pkF2.getY())
655 if not opsfimg.getBBox(afwImage.LOCAL).overlaps(stampbb):
657 otherpeaks.append(opsfimg)
658 log.logdebug(
'%i other peaks within range' % len(otherpeaks))
664 NT1 = 4 + len(otherpeaks)
668 NP = (1 + yhi - ylo)*(1 + xhi - xlo)
680 ix0, iy0 = img.getX0(), img.getY0()
681 fx0, fy0 = fbb.getMinX(), fbb.getMinY()
682 fslice = (slice(ylo-fy0, yhi-fy0+1), slice(xlo-fx0, xhi-fx0+1))
683 islice = (slice(ylo-iy0, yhi-iy0+1), slice(xlo-ix0, xhi-ix0+1))
684 fmask_sub = fmask .getArray()[fslice]
685 var_sub = varimg.getArray()[islice]
686 img_sub = img .getArray()[islice]
689 psfarr = psfimg.getArray()[pbb.getMinY()-py0: 1+pbb.getMaxY()-py0,
690 pbb.getMinX()-px0: 1+pbb.getMaxX()-px0]
691 px0, px1 = pbb.getMinX(), pbb.getMaxX()
692 py0, py1 = pbb.getMinY(), pbb.getMaxY()
695 valid = (fmask_sub > 0)
696 xx, yy = np.arange(xlo, xhi+1), np.arange(ylo, yhi+1)
697 RR = ((xx - cx)**2)[np.newaxis, :] + ((yy - cy)**2)[:, np.newaxis]
698 valid *= (RR <= R1**2)
699 valid *= (var_sub > 0)
703 log.warn(
'Skipping peak at (%.1f, %.1f): no unmasked pixels nearby' % (cx, cy))
704 pkres.setNoValidPixels()
708 XX, YY = np.meshgrid(xx, yy)
709 ipixes = np.vstack((XX[valid] - xlo, YY[valid] - ylo)).T
711 inpsfx = (xx >= px0)*(xx <= px1)
712 inpsfy = (yy >= py0)*(yy <= py1)
713 inpsf = np.outer(inpsfy, inpsfx)
714 indx = np.outer(inpsfy, (xx > px0)*(xx < px1))
715 indy = np.outer((yy > py0)*(yy < py1), inpsfx)
720 def _overlap(xlo, xhi, xmin, xmax):
721 assert((xlo <= xmax)
and (xhi >= xmin)
and
722 (xlo <= xhi)
and (xmin <= xmax))
723 xloclamp = max(xlo, xmin)
725 xhiclamp = min(xhi, xmax)
726 Xhi = Xlo + (xhiclamp - xloclamp)
727 assert(xloclamp >= 0)
729 return (xloclamp, xhiclamp+1, Xlo, Xhi+1)
731 A = np.zeros((NP, NT2))
735 A[:, I_sky_ramp_x] = ipixes[:, 0] + (xlo-cx)
736 A[:, I_sky_ramp_y] = ipixes[:, 1] + (ylo-cy)
739 px0, px1 = pbb.getMinX(), pbb.getMaxX()
740 py0, py1 = pbb.getMinY(), pbb.getMaxY()
741 sx1, sx2, sx3, sx4 = _overlap(xlo, xhi, px0, px1)
742 sy1, sy2, sy3, sy4 = _overlap(ylo, yhi, py0, py1)
743 dpx0, dpy0 = px0 - xlo, py0 - ylo
744 psf_y_slice = slice(sy3 - dpy0, sy4 - dpy0)
745 psf_x_slice = slice(sx3 - dpx0, sx4 - dpx0)
746 psfsub = psfarr[psf_y_slice, psf_x_slice]
747 vsub = valid[sy1-ylo: sy2-ylo, sx1-xlo: sx2-xlo]
748 A[inpsf[valid], I_psf] = psfsub[vsub]
752 oldsx = (sx1, sx2, sx3, sx4)
753 sx1, sx2, sx3, sx4 = _overlap(xlo, xhi, px0+1, px1-1)
754 psfsub = (psfarr[psf_y_slice, sx3 - dpx0 + 1: sx4 - dpx0 + 1] -
755 psfarr[psf_y_slice, sx3 - dpx0 - 1: sx4 - dpx0 - 1])/2.
756 vsub = valid[sy1-ylo: sy2-ylo, sx1-xlo: sx2-xlo]
757 A[indx[valid], I_dx] = psfsub[vsub]
759 (sx1, sx2, sx3, sx4) = oldsx
762 sy1, sy2, sy3, sy4 = _overlap(ylo, yhi, py0+1, py1-1)
763 psfsub = (psfarr[sy3 - dpy0 + 1: sy4 - dpy0 + 1, psf_x_slice] -
764 psfarr[sy3 - dpy0 - 1: sy4 - dpy0 - 1, psf_x_slice])/2.
765 vsub = valid[sy1-ylo: sy2-ylo, sx1-xlo: sx2-xlo]
766 A[indy[valid], I_dy] = psfsub[vsub]
769 for j, opsf
in enumerate(otherpeaks):
771 ino = np.outer((yy >= obb.getMinY())*(yy <= obb.getMaxY()),
772 (xx >= obb.getMinX())*(xx <= obb.getMaxX()))
773 dpx0, dpy0 = obb.getMinX() - xlo, obb.getMinY() - ylo
774 sx1, sx2, sx3, sx4 = _overlap(xlo, xhi, obb.getMinX(), obb.getMaxX())
775 sy1, sy2, sy3, sy4 = _overlap(ylo, yhi, obb.getMinY(), obb.getMaxY())
776 opsfarr = opsf.getArray()
777 psfsub = opsfarr[sy3 - dpy0 : sy4 - dpy0, sx3 - dpx0: sx4 - dpx0]
778 vsub = valid[sy1-ylo: sy2-ylo, sx1-xlo: sx2-xlo]
779 A[ino[valid], I_opsf + j] = psfsub[vsub]
785 rw = np.ones_like(RR)
788 rw[ii] = np.maximum(0, 1. - ((rr - R0)/(R1 - R0)))
789 w = np.sqrt(rw[valid]/var_sub[valid])
791 sumr = np.sum(rw[valid])
792 log.log(-9,
'sumr = %g' % sumr)
796 Aw = A*w[:, np.newaxis]
805 im1 = np.zeros((1+yhi-ylo, 1+xhi-xlo))
806 im1[ipixes[:, 1], ipixes[:, 0]] = A[:, i]
807 plt.subplot(R, C, i+1)
808 plt.imshow(im1, interpolation=
'nearest', origin=
'lower')
809 plt.subplot(R, C, NT2+1)
810 im1 = np.zeros((1+yhi-ylo, 1+xhi-xlo))
811 im1[ipixes[:, 1], ipixes[:, 0]] = b
812 plt.imshow(im1, interpolation=
'nearest', origin=
'lower')
813 plt.subplot(R, C, NT2+2)
814 im1 = np.zeros((1+yhi-ylo, 1+xhi-xlo))
815 im1[ipixes[:, 1], ipixes[:, 0]] = w
816 plt.imshow(im1, interpolation=
'nearest', origin=
'lower')
828 X1, r1, rank1, s1 = np.linalg.lstsq(Aw[:, :NT1], bw)
830 X2, r2, rank2, s2 = np.linalg.lstsq(Aw, bw)
831 except np.linalg.LinAlgError, e:
832 log.log(log.WARN,
"Failed to fit PSF to child: %s" % e)
833 pkres.setPsfFitFailed()
836 log.log(-9,
'r1 r2 %s %s' % (r1, r2))
848 dof1 = sumr - len(X1)
849 dof2 = sumr - len(X2)
850 log.log(-9,
'dof1, dof2 %g %g' % (dof1, dof2))
853 if dof1 <= 0
or dof2 <= 0:
854 log.logdebug(
'Skipping this peak: bad DOF %g, %g' % (dof1, dof2))
860 log.logdebug(
'PSF fits: chisq/dof = %g, %g' % (q1, q2))
861 ispsf1 = (q1 < psfChisqCut1)
862 ispsf2 = (q2 < psfChisqCut2)
864 pkres.psfFit1 = (chisq1, dof1)
865 pkres.psfFit2 = (chisq2, dof2)
869 fdx, fdy = X2[I_dx], X2[I_dy]
874 ispsf2 = ispsf2
and (abs(dx) < 1.
and abs(dy) < 1.)
875 log.logdebug(
'isPSF2 -- checking derivatives: dx,dy = %g, %g -> %s' %
876 (dx, dy, str(ispsf2)))
878 pkres.psfFitBigDecenter =
True
883 psfimg2 = psf.computeImage(cx + dx, cy + dy)
885 pbb2 = psfimg2.getBBox()
894 px0, py0 = psfimg2.getX0(), psfimg2.getY0()
895 psfarr = psfimg2.getArray()[pbb2.getMinY()-py0:1+pbb2.getMaxY()-py0,
896 pbb2.getMinX()-px0:1+pbb2.getMaxX()-px0]
897 px0, py0 = pbb2.getMinX(), pbb2.getMinY()
898 px1, py1 = pbb2.getMaxX(), pbb2.getMaxY()
903 sx1, sx2, sx3, sx4 = _overlap(xlo, xhi, px0, px1)
904 sy1, sy2, sy3, sy4 = _overlap(ylo, yhi, py0, py1)
905 dpx0, dpy0 = px0 - xlo, py0 - ylo
906 psfsub = psfarr[sy3-dpy0:sy4-dpy0, sx3-dpx0:sx4-dpx0]
907 vsub = valid[sy1-ylo:sy2-ylo, sx1-xlo:sx2-xlo]
908 xx, yy = np.arange(xlo, xhi+1), np.arange(ylo, yhi+1)
909 inpsf = np.outer((yy >= py0)*(yy <= py1), (xx >= px0)*(xx <= px1))
910 Ab[inpsf[valid], I_psf] = psfsub[vsub]
912 Aw = Ab*w[:, np.newaxis]
914 Xb, rb, rankb, sb = np.linalg.lstsq(Aw, bw)
919 dofb = sumr - len(Xb)
921 ispsf2 = (qb < psfChisqCut2b)
924 log.logdebug(
'shifted PSF: new chisq/dof = %g; good? %s' %
926 pkres.psfFit3 = (chisqb, dofb)
929 if (((ispsf1
and ispsf2)
and (q2 < q1))
or
930 (ispsf2
and not ispsf1)):
934 log.log(-9,
'dof %g' % dof)
935 log.logdebug(
'Keeping shifted-PSF model')
938 pkres.psfFitWithDecenter =
True
944 log.log(-9,
'dof %g' % dof)
945 log.logdebug(
'Keeping unshifted PSF model')
947 ispsf = (ispsf1
or ispsf2)
951 SW, SH = 1+xhi-xlo, 1+yhi-ylo
952 psfmod = afwImage.ImageF(SW, SH)
953 psfmod.setXY0(xlo, ylo)
954 psfderivmodm = afwImage.MaskedImageF(SW, SH)
955 psfderivmod = psfderivmodm.getImage()
956 psfderivmod.setXY0(xlo, ylo)
957 model = afwImage.ImageF(SW, SH)
958 model.setXY0(xlo, ylo)
959 for i
in range(len(Xpsf)):
960 for (x, y),v
in zip(ipixes, A[:, i]*Xpsf[i]):
961 ix, iy = int(x), int(y)
962 model.set(ix, iy, model.get(ix, iy) + float(v))
963 if i
in [I_psf, I_dx, I_dy]:
964 psfderivmod.set(ix, iy, psfderivmod.get(ix, iy) + float(v))
967 psfmod.set(int(x), int(y), float(A[ii, I_psf]*Xpsf[I_psf]))
969 for (x, y)
in ipixes:
970 modelfp.addSpan(int(y+ylo), int(x+xlo), int(x+xlo))
973 pkres.psfFitDebugPsf0Img = psfimg
974 pkres.psfFitDebugPsfImg = psfmod
975 pkres.psfFitDebugPsfDerivImg = psfderivmod
976 pkres.psfFitDebugPsfModel = model
977 pkres.psfFitDebugStamp = img.Factory(img, stampbb,
True)
978 pkres.psfFitDebugValidPix = valid
979 pkres.psfFitDebugVar = varimg.Factory(varimg, stampbb,
True)
980 ww = np.zeros(valid.shape, np.float)
982 pkres.psfFitDebugWeight = ww
983 pkres.psfFitDebugRampWeight = rw
989 pkres.psfFitStampExtent = (xlo, xhi, ylo, yhi)
990 pkres.psfFitCenter = (cx, cy)
991 log.log(-9,
'saving chisq,dof %g %g' % (chisq, dof))
992 pkres.psfFitBest = (chisq, dof)
993 pkres.psfFitParams = Xpsf
994 pkres.psfFitFlux = Xpsf[I_psf]
995 pkres.psfFitNOthers = len(otherpeaks)
998 pkres.setDeblendedAsPsf()
1002 log.logdebug(
'Deblending as PSF; setting template to PSF model')
1005 psfimg = psf.computeImage(cx, cy)
1007 psfimg *= Xpsf[I_psf]
1008 psfimg = psfimg.convertF()
1012 psfbb = psfimg.getBBox()
1013 fpcopy.clipTo(psfbb)
1014 bb = fpcopy.getBBox()
1017 psfmod = afwImage.ImageF(bb)
1018 afwDet.copyWithinFootprintImage(fpcopy, psfimg, psfmod)
1020 fpcopy.clipToNonzero(psfmod)
1022 pkres.setTemplate(psfmod, fpcopy)
1025 pkres.setPsfTemplate(psfmod, fpcopy)