LSSTApplications
10.0-2-g4f67435,11.0.rc2+1,11.0.rc2+12,11.0.rc2+3,11.0.rc2+4,11.0.rc2+5,11.0.rc2+6,11.0.rc2+7,11.0.rc2+8
LSSTDataManagementBasePackage
|
Demonstrate the use of SpatialCellSets; the code's in spatialCellExample.cc.
Start by including needed headers, and declaring namespace aliases and a routine readImage
We start by calling readImage
, and use boost::tie
to unpack the std::pair
. The tie
call does what you think, unpacking a pair
into a couple of variables (it works for boost::tuple
too, and is in TR1's <tuple>
header).
We want to learn something about the objects in this image, and would like to ensure that the ones we study are spread reasonably uniformly. We accordingly create a SpatialCellSet; a collection of SpatialCells, each of which will maintain its one list of candidate objects for study. For example, if we were estimating the PSF we'd want a set of isolated bright stars, but we wouldn't want them to all come from the top right corner of the image. A SpatialCellSet allows us to take the best n
candidates from each SpatialCell, ensuring a reasonable distribution across the image.
The constructor's first argument is the image's bounding box — it'd be nice to simply pass the image, wouldn't it, but that's not currently supported. The second and third arguments 260, 200
define the size (in pixels) of the SpatialCells.
If you run the python version of this example, spatialCellExample.py, with display = True
the 6 cells will be shown in green (why 6? Because the image is 512x512 and you can fit 260x200 into 512x512 6 times.)
Our SpatialCellSet is empty, so let's insert all the objects in the frame into it. We have a list of detections in the FootprintSet fs
, so this is easy. We package each object into an ExampleCandidate
, and insert it into the set. The SpatialCellSet is responsible for putting it into the correct cell, and SpatialCell for maintaining an order within each cell; this ordering is defined by a virtual function double ExampleCandidate::getCandidateRating() const
. The ExampleCandidate
class is implemented in testSpatialCell.h and testSpatialCell.cc
You can store anything you like in your candidate class, the only requirement is that it inherit from lsst::afw::math::SpatialCellCandidate or lsst::afw::math::SpatialCellImageCandidate (the latter adds some extra virtual methods). I chose to save a pointer to the parent image, and the object's bounding box.
It's possible to iterate over all the objects in a SpatialCellSet (we'll do so in a moment), but the simplest way to visit all cells is to pass in a visitor object. The ExampleCandidateVisitor
object (defined in testSpatialCell.h) counts the candidates and the number of pixels contained in their bounding boxes.
Now we'll visit each of our objects by explicit iteration. The iterator returns a base-class pointer so we need a dynamic_cast
(this cast is also available from python via a little swiggery). We decided that we don't like small objects, defined as those with less than 75 pixels in their bounding boxes, so we'll label them as BAD
.
What does BAD
mean (other options are UNKNOWN
and GOOD
)? Basically that that object is to be ignored. It no longer appears in the size of the SpatialCells, it is skipped by the iterators, and the visitors pass it by. You can turn this behaviour off with setIgnoreBad
.
Note that we pass the visitor before we decide to ignore BAD
so getN()
and getNPix()
return the number of good objects/pixels.
And count the good candidate again
Running the example should print
There are 22 candidates Cell 0x0 nobj=2 N_good=2 NPix_good=1858 Cell 1x0 nobj=2 N_good=1 NPix_good=210 Cell 0x1 nobj=4 N_good=4 NPix_good=1305 Cell 1x1 nobj=4 N_good=1 NPix_good=360 Cell 0x2 nobj=3 N_good=1 NPix_good=99 Cell 1x2 nobj=7 N_good=2 NPix_good=288 There are 11 good candidates
Here's the function that reads a FITS file and finds a set of object in it. It isn't really anything to do with SpatialCells, but for completeness...
First read a part of the FITS file. We use eups::productDir
to find the directory, and only read a part of the image (that's the BBox
). The use of a boost::shared_ptr<MaskedImage>
(written as MaskedImage::Ptr
) is because I want to call the actual constructor in the scope of the try block, but I want to use the image at function scope.
Subtract the background; the try
block is in case the image is too small for a spline fit.
Run an object detector
And return the desired data