Skip to content

Commit

Permalink
Merge pull request #345 from dirac-institute/prefactor
Browse files Browse the repository at this point in the history
Cleanup LayeredImage PSF usage
  • Loading branch information
jeremykubica authored Sep 24, 2023
2 parents 9e86cd6 + 1401864 commit 87fd249
Show file tree
Hide file tree
Showing 18 changed files with 163 additions and 81 deletions.
10 changes: 7 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,9 +111,13 @@ velocity = (2, 0)
# Inject object into images
for im in imgs:
dt = im.get_obstime() - t0
im.add_object(position[0] + dt * velocity[0],
position[1] + dt * velocity[1],
flux)
add_fake_object(
im,
position[0] + dt * velocity[0],
position[1] + dt * velocity[1],
flux,
psf,
)

# Create a new image stack with the inserted object.
stack = kb.image_stack(imgs)
Expand Down
44 changes: 41 additions & 3 deletions src/kbmod/fake_data_creator.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,44 @@
from kbmod.search import *


def add_fake_object(img, x, y, flux, psf=None):
"""Add a fake object to a LayeredImage or RawImage
Parameters
----------
img : RawImage or LayeredImage
The image to modify.
x : float
The x pixel location of the fake object.
y : float
The y pixel location of the fake object.
flux : float
The flux value.
psf : PointSpreadFunc
The PSF for the image.
"""
if type(img) is layered_image:
sci = img.get_science()
else:
sci = img

if psf is None:
sci.add_pixel_interp(x, y, flux)
else:
dim = psf.get_dim()
initial_x = x - psf.get_radius()
initial_y = y - psf.get_radius()

for i in range(dim):
for j in range(dim):
sci.add_pixel_interp(initial_x + i, initial_y + j, flux * psf.get_value(i, j))

# The python/C++ interface requires us to explicitly re-set the science
# image in a LayeredImage.
if sci is not img:
img.set_science(sci)


class FakeDataSet:
"""This class creates fake data sets for testing and demo notebooks."""

Expand Down Expand Up @@ -107,9 +145,9 @@ def insert_object(self, trj):
# Get the image for the timestep, add the object, and
# re-set the image. This last step needs to be done
# explicitly because of how pybind handles references.
current_layered_image = self.stack.get_single_image(i)
current_layered_image.add_object(px, py, trj.flux)
self.stack.set_single_image(i, current_layered_image)
current = self.stack.get_single_image(i)
add_fake_object(current, px, py, trj.flux, current.get_psf())
self.stack.set_single_image(i, current)

# Save the trajectory into the internal list.
self.trajectories.append(trj)
Expand Down
48 changes: 17 additions & 31 deletions src/kbmod/search/LayeredImage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,7 @@

namespace search {

LayeredImage::LayeredImage(std::string path, const PointSpreadFunc& psf) : psf(psf), psf_sq(psf) {
psf_sq.squarePSF();

LayeredImage::LayeredImage(std::string path, const PointSpreadFunc& psf) : psf(psf) {
int f_begin = path.find_last_of("/");
int f_end = path.find_last_of(".fits") - 4;
filename = path.substr(f_begin, f_end - f_begin);
Expand All @@ -35,7 +33,7 @@ LayeredImage::LayeredImage(std::string path, const PointSpreadFunc& psf) : psf(p

LayeredImage::LayeredImage(const RawImage& sci, const RawImage& var, const RawImage& msk,
const PointSpreadFunc& psf)
: psf(psf), psf_sq(psf) {
: psf(psf) {
// Get the dimensions of the science layer and check for consistency with
// the other two layers.
width = sci.getWidth();
Expand All @@ -45,9 +43,6 @@ LayeredImage::LayeredImage(const RawImage& sci, const RawImage& var, const RawIm
if (width != msk.getWidth() or height != msk.getHeight())
throw std::runtime_error("Science and Mask layers are not the same size.");

// Set the remaining variables.
psf_sq.squarePSF();

// Copy the image layers.
science = sci;
mask = msk;
Expand All @@ -60,11 +55,10 @@ LayeredImage::LayeredImage(std::string name, int w, int h, float noise_stdev, fl

LayeredImage::LayeredImage(std::string name, int w, int h, float noise_stdev, float pixel_variance, double time,
const PointSpreadFunc& psf, int seed)
: psf(psf), psf_sq(psf) {
: psf(psf) {
filename = name;
width = w;
height = h;
psf_sq.squarePSF();

std::vector<float> raw_sci(width * height);
std::random_device r;
Expand All @@ -84,34 +78,24 @@ LayeredImage::LayeredImage(std::string name, int w, int h, float noise_stdev, fl

void LayeredImage::setPSF(const PointSpreadFunc& new_psf) {
psf = new_psf;
psf_sq = new_psf;
psf_sq.squarePSF();
}

void LayeredImage::addObject(float x, float y, float flux) {
const std::vector<float>& k = psf.getKernel();
int dim = psf.getDim();
float initial_x = x - static_cast<float>(psf.getRadius());
float initial_y = y - static_cast<float>(psf.getRadius());

int count = 0;
for (int i = 0; i < dim; ++i) {
for (int j = 0; j < dim; ++j) {
science.addPixelInterp(initial_x + static_cast<float>(i), initial_y + static_cast<float>(j),
flux * k[count]);
count++;
}
}
}

void LayeredImage::growMask(int steps) {
science.growMask(steps);
variance.growMask(steps);
}

void LayeredImage::convolveGivenPSF(const PointSpreadFunc& given_psf) {
science.convolve(given_psf);

// Square the PSF use that on the variance image.
PointSpreadFunc psfsq = PointSpreadFunc(given_psf); // Copy
psfsq.squarePSF();
variance.convolve(psfsq);
}

void LayeredImage::convolvePSF() {
science.convolve(psf);
variance.convolve(psf_sq);
convolveGivenPSF(psf);
}

void LayeredImage::applyMaskFlags(int flags, const std::vector<int>& exceptions) {
Expand Down Expand Up @@ -217,7 +201,7 @@ RawImage LayeredImage::generatePsiImage() {
}

// Convolve with the PSF.
result.convolve(getPSF());
result.convolve(psf);

return result;
}
Expand All @@ -239,7 +223,9 @@ RawImage LayeredImage::generatePhiImage() {
}

// Convolve with the PSF squared.
result.convolve(getPSFSQ());
PointSpreadFunc psfsq = PointSpreadFunc(psf); // Copy
psfsq.squarePSF();
result.convolve(psfsq);

return result;
}
Expand Down
8 changes: 3 additions & 5 deletions src/kbmod/search/LayeredImage.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ class LayeredImage {
// Set an image specific point spread function.
void setPSF(const PointSpreadFunc& psf);
const PointSpreadFunc& getPSF() const { return psf; }
const PointSpreadFunc& getPSFSQ() const { return psf_sq; }

// Basic getter functions for image data.
std::string getName() const { return filename; }
Expand Down Expand Up @@ -65,9 +64,6 @@ class LayeredImage {
// Subtracts a template image from the science layer.
void subtractTemplate(const RawImage& sub_template);

// Adds an (artificial) object to the image (science) data.
void addObject(float x, float y, float flux);

// Saves the data in each later to a file.
void saveLayers(const std::string& path);

Expand All @@ -76,7 +72,10 @@ class LayeredImage {
void setMask(RawImage& im);
void setVariance(RawImage& im);

// Convolve with a given PSF or the default one.
void convolvePSF();
void convolveGivenPSF(const PointSpreadFunc& psf);

virtual ~LayeredImage(){};

// Generate psi and phi images from the science and variance layers.
Expand All @@ -91,7 +90,6 @@ class LayeredImage {
unsigned height;

PointSpreadFunc psf;
PointSpreadFunc psf_sq;
RawImage science;
RawImage mask;
RawImage variance;
Expand Down
7 changes: 7 additions & 0 deletions src/kbmod/search/PointSpreadFunc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,13 @@

namespace search {

PointSpreadFunc::PointSpreadFunc() : kernel(1, 1.0) {
dim = 1;
radius = 0;
width = 1e-12;
sum = 1.0;
}

PointSpreadFunc::PointSpreadFunc(float stdev) {
width = stdev;
float simple_gauss[MAX_KERNEL_RADIUS];
Expand Down
1 change: 1 addition & 0 deletions src/kbmod/search/PointSpreadFunc.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ namespace search {

class PointSpreadFunc {
public:
PointSpreadFunc(); // Create a no-op PSF.
PointSpreadFunc(float stdev);
PointSpreadFunc(const PointSpreadFunc& other); // Copy constructor
PointSpreadFunc(PointSpreadFunc&& other); // Move constructor
Expand Down
7 changes: 4 additions & 3 deletions src/kbmod/search/bindings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ PYBIND11_MODULE(search, m) {
2, {m.getDim(), m.getDim()},
{sizeof(float) * m.getDim(), sizeof(float)});
})
.def(py::init<>())
.def(py::init<float>())
.def(py::init<py::array_t<float>>())
.def(py::init<pf &>())
Expand Down Expand Up @@ -100,6 +101,7 @@ PYBIND11_MODULE(search, m) {
.def("create_stamp", &ri::createStamp)
.def("set_pixel", &ri::setPixel, "Set the value of a given pixel.")
.def("add_pixel", &ri::addToPixel, "Add to the value of a given pixel.")
.def("add_pixel_interp", &ri::addPixelInterp, "Add to the interpolated value of a given pixel.")
.def("apply_mask", &ri::applyMask)
.def("grow_mask", &ri::growMask)
.def("pixel_has_data", &ri::pixelHasData,
Expand Down Expand Up @@ -139,7 +141,6 @@ PYBIND11_MODULE(search, m) {
.def(py::init<std::string, int, int, double, float, float, pf &, int>())
.def("set_psf", &li::setPSF, "Sets the PSF object.")
.def("get_psf", &li::getPSF, "Returns the PSF object.")
.def("get_psfsq", &li::getPSFSQ)
.def("apply_mask_flags", &li::applyMaskFlags)
.def("apply_mask_threshold", &li::applyMaskThreshold)
.def("sub_template", &li::subtractTemplate)
Expand All @@ -150,8 +151,8 @@ PYBIND11_MODULE(search, m) {
.def("set_science", &li::setScience)
.def("set_mask", &li::setMask)
.def("set_variance", &li::setVariance)
.def("convolve_psf", &li::convolvePSF)
.def("add_object", &li::addObject)
.def("convolve_psf", &li::convolvePSF, "Convolve each layer with the object's PSF.")
.def("convolve_given_psf", &li::convolveGivenPSF, "Convolve each layer with a given PSF.")
.def("grow_mask", &li::growMask)
.def("get_name", &li::getName, "Returns the name of the layered image.")
.def("get_width", &li::getWidth, "Returns the image's width in pixels.")
Expand Down
3 changes: 2 additions & 1 deletion tests/regression_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import numpy as np
from astropy.io import fits

from kbmod.fake_data_creator import add_fake_object
from kbmod.file_utils import *
from kbmod.run_search import run_search
from kbmod.search import *
Expand Down Expand Up @@ -213,7 +214,7 @@ def make_fake_image_stack(times, trjs, psf_vals):
for trj in trjs:
px = trj.x + time * trj.x_v + 0.5
py = trj.y + time * trj.y_v + 0.5
img.add_object(px, py, trj.flux)
add_fake_object(img, px, py, trj.flux, p)

imlist.append(img)
stack = image_stack(imlist)
Expand Down
11 changes: 8 additions & 3 deletions tests/test_analysis_utils.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import unittest

from kbmod.analysis_utils import *
from kbmod.fake_data_creator import add_fake_object
from kbmod.result_list import *
from kbmod.search import *

Expand Down Expand Up @@ -179,10 +180,12 @@ def test_apply_stamp_filter(self):

for i in range(self.img_count):
time = i / self.img_count
self.imlist[i].add_object(
add_fake_object(
self.imlist[i],
self.start_x + time * self.x_vel + 0.5,
self.start_y + time * self.y_vel + 0.5,
self.object_flux,
self.p,
)

stack = image_stack(self.imlist)
Expand Down Expand Up @@ -228,10 +231,12 @@ def test_apply_stamp_filter_2(self):

for i in range(self.img_count):
time = i / self.img_count
self.imlist[i].add_object(
add_fake_object(
self.imlist[i],
self.start_x + time * self.x_vel,
self.start_y + time * self.y_vel,
self.object_flux,
self.p,
)

stack = image_stack(self.imlist)
Expand Down Expand Up @@ -346,7 +351,7 @@ def test_load_and_filter_results_lh(self):

# Add the objects.
for j, trj in enumerate(trjs):
im.add_object(trj.x, trj.y, fluxes[j])
add_fake_object(im, trj.x, trj.y, fluxes[j], self.p)

# Append the image.
imlist.append(im)
Expand Down
3 changes: 2 additions & 1 deletion tests/test_bilinear_interp.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import numpy

from kbmod.fake_data_creator import add_fake_object
import kbmod.search as kb


Expand All @@ -12,7 +13,7 @@ def setUp(self):
self.images = []
for c in range(self.im_count):
im = kb.layered_image(str(c), 10, 10, 0.0, 1.0, c, p)
im.add_object(2 + c * 0.5 + 0.5, 2 + c * 0.5 + 0.5, 1)
add_fake_object(im, 2 + c * 0.5 + 0.5, 2 + c * 0.5 + 0.5, 1, p)
self.images.append(im)

def test_pixels(self):
Expand Down
3 changes: 2 additions & 1 deletion tests/test_image_stack.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import tempfile
import unittest

from kbmod.fake_data_creator import add_fake_object
from kbmod.search import *


Expand Down Expand Up @@ -150,7 +151,7 @@ def test_different_psfs(self):
last_val = -100.0
for i in range(self.num_images):
img = self.im_stack.get_single_image(i)
img.add_object(10, 20, 500.0)
add_fake_object(img, 10, 20, 500.0, self.p[i])

sci = img.get_science()
pix_val = sci.get_pixel(10, 20)
Expand Down
Loading

0 comments on commit 87fd249

Please sign in to comment.