-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
DM-17426 #1
base: master
Are you sure you want to change the base?
DM-17426 #1
Changes from 1 commit
7ce5656
b2ebc0a
832078c
773e3a2
a078e08
876d95d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -468,6 +468,9 @@ class FocalPlaneBackgroundConfig(Config): | |
"NONE": "No background estimation is to be attempted", | ||
}, | ||
) | ||
doSmooth = Field(dtype=bool, default=False, doc="Do smoothing?") | ||
smoothScale = Field(dtype=float, doc="Smoothing scale") | ||
smoothWindowSize = Field(dtype=int, default=15, doc="Window size for smoothing") | ||
binning = Field(dtype=int, default=64, doc="Binning to use for CCD background model (pixels)") | ||
|
||
|
||
|
@@ -721,6 +724,12 @@ def getStatsImage(self): | |
values /= self._numbers | ||
thresh = self.config.minFrac*self.config.xSize*self.config.ySize | ||
isBad = self._numbers.getArray() < thresh | ||
if self.config.doSmooth: | ||
array = values.getArray() | ||
array[isBad] = numpy.nan | ||
gridSize = min(self.config.xSize, self.config.ySize) | ||
array[:] = NanSafeSmoothing.gaussianSmoothing(array, self.config.smoothWindowSize, self.config.smoothScale / gridSize) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Line length. |
||
isBad = numpy.isnan(values.array) | ||
interpolateBadPixels(values.getArray(), isBad, self.config.interpolation) | ||
return values | ||
|
||
|
@@ -785,3 +794,52 @@ def run(self, exp): | |
detected = mask.array & mask.getPlaneBitMask(['DETECTED']) > 0 | ||
exp.maskedImage.image.array[detected] = smooth.getImage().getArray()[detected] | ||
|
||
|
||
class NanSafeSmoothing: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this section needs a different implementation. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
This is why I didn't use the existing convolution code. Replacing NaNs with 0 is not equivalent to my code. A direct convolution in Python is crazy slow, but the targets of this code are relatively small images such as FocalPlaneBackground._values whose dimensions are about 140x150. Convolutions on such images end in about 0.5 second on my mac.
I totally agree with you. I will put these functions flat in the module. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I figured out the difference between your convolution and the vanilla convolution. This will allow us to condense the code, and probably run it faster:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Excellent solution. |
||
''' | ||
Smooth image dealing with NaN pixels | ||
''' | ||
|
||
@classmethod | ||
def gaussianSmoothing(cls, array, windowSize, sigma): | ||
return cls._safeConvolve2d(array, cls._gaussianKernel(windowSize, sigma)) | ||
|
||
@classmethod | ||
def _gaussianKernel(cls, windowSize, sigma): | ||
''' Returns 2D gaussian kernel ''' | ||
s = sigma | ||
r = windowSize | ||
X, Y = numpy.meshgrid( | ||
numpy.linspace(-r, r, 2 * r + 1), | ||
numpy.linspace(-r, r, 2 * r + 1), | ||
) | ||
kernel = cls._normalDist(X, s) * cls._normalDist(Y, s) | ||
# cut off | ||
kernel[X**2 + Y**2 > (r + 0.5)**2] = 0. | ||
return kernel / kernel.sum() | ||
|
||
@staticmethod | ||
def _normalDist(x, s=1., m=0.): | ||
''' Normal Distribution ''' | ||
return 1. / (s * numpy.sqrt(2. * numpy.pi)) * numpy.exp(-(x-m)**2/(2*s**2)) / (s * numpy.sqrt(2*numpy.pi)) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why two normalisation factors of |
||
|
||
@staticmethod | ||
def _safeConvolve2d(image, kernel): | ||
''' Convolve 2D safely dealing with NaNs in `image` ''' | ||
assert numpy.ndim(image) == 2 | ||
assert numpy.ndim(kernel) == 2 | ||
assert kernel.shape[0] % 2 == 1 and kernel.shape[1] % 2 == 1 | ||
ks = kernel.shape | ||
kl = (ks[0] - 1) // 2, \ | ||
(ks[1] - 1) // 2 | ||
image2 = numpy.pad(image, ((kl[0], kl[0]), (kl[1], kl[1])), 'constant', constant_values=numpy.nan) | ||
convolved = numpy.empty_like(image) | ||
convolved.fill(numpy.nan) | ||
for yi in range(convolved.shape[0]): | ||
for xi in range(convolved.shape[1]): | ||
patch = image2[yi : yi + ks[0], xi : xi + ks[1]] | ||
c = patch * kernel | ||
ok = numpy.isfinite(c) | ||
if numpy.any(ok): | ||
convolved[yi, xi] = numpy.nansum(c) / kernel[ok].sum() | ||
return convolved |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No default?