Skip to content
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-12447: Remove TransformMap.transform, Camera.transform and Detector.transform #321

Merged
merged 4 commits into from Feb 27, 2018

Conversation

r-owen
Copy link
Contributor

@r-owen r-owen commented Feb 26, 2018

and CameraPoint

@PaulPrice
Copy link
Contributor

Can you state the reason for the removal in the commit log?

Copy link
Contributor

@PaulPrice PaulPrice left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we absolutely need a method that will do getTransform followed by applyForward.

detPixelsSys = det.makeCameraSys(cameraGeom.PIXELS)
detPointOnSpecifiedDetector = camera.transform(fieldPoint, detPixelsSys)
detPointOnFoundDetector = camera.transform(fieldPoint, cameraGeom.PIXELS)
assert detPointOnFoundDetector.getCameraSys() == detPixelsSys # same detector
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this gone? Does this represent loss of functionality?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Having Camera.transform search for a detector is gone. I don't think it's practical to implement it with CameraPoint gone. In my opinion it was never a good idea, as it could be quite slow, which could surprise users. I would much rather have users explicitly search for a detector if necessary.

Note that I found no instances where this feature was used, other than this example.

/**
* A Point2D with associated camera coordinate system
*/
class CameraPoint {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This used to be viewed as an important element of the design that made sure the user knew which coordinate system the points are in. Please explain in the commit log message why it is now being jettisoned.

"""!Transform a CameraPoint with a CameraSys to another CameraSys.
return det.getTransformMap().getTransform(fromSys, toSys)
else:
return self.getTransformMap().getTransform(fromSys, toSys)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the logic above should be refactored to eliminate duplicated code:

det = self
if fromSys.hasDetectorName():
    det = self[fromSys.getDetectorName()]
elif toSys.hasDetectorName():
    det = self[toSys.getDetectorName()]
return det.getTransformMap().getTransform(fromSys, toSys)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your suggested new code does not do the same thing because the else clause handles the camera's own transform map, not a detector's transform map.

I suppose we could retrieve the transform map. It will be a bit awkward since we then have to assume the code will never leave the transform map equal to None (which will be true initially, but perhaps not if the code evolves) or check for None even though it should be impossible. That is why I chose to use the current technique.

cameraPoint, self._nativeCameraSys)
# convert `point` to the native coordinate system
transform = self._getTransformFromOneTransformMap(cameraSys, self._nativeCameraSys)
nativePoint = transform.applyForward(point)

detectorList = []
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is there a list of detectors, as if a point could be on multiple detectors?

Copy link
Contributor Author

@r-owen r-owen Feb 27, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because a point can be on multiple detectors. I don't believe any instruments presently use that capability, but an imaging instrument with a beamsplitter certainly would.

Note that this is a pre-existing capability that has been retained.

@@ -196,31 +156,19 @@ def getTransformMap(self):
"""
return self._transformMap

def transform(self, cameraPoint, toSys):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't want a camera.transform(point, fromSys, toSys)?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added that in a subsequent commit.

@RobertLuptonTheGood
Copy link
Member

RobertLuptonTheGood commented Feb 27, 2018 via email

@r-owen
Copy link
Contributor Author

r-owen commented Feb 27, 2018

@RobertLuptonTheGood Having Camera.transform search for a detector (if a camera system prefix was specified). It is a very expensive operation and it is doubtful that anyone reading the code would realize that.

*
* @tparam FromSysT Class of fromSys: one of CameraSys or CameraSysPrefix
* @tparam ToSysT Class of toSys: one of CameraSys or CameraSysPrefix
* @param[in] point Camera point to transform
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

points, plural.

* @tparam FromSysT Class of fromSys: one of CameraSys or CameraSysPrefix
* @tparam ToSysT Class of toSys: one of CameraSys or CameraSysPrefix
* @param[in] point Camera point to transform
* @param[in] fromSys Camera coordinate system of `point`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ditto.

@@ -173,3 +165,25 @@ def getTransform(self, fromSys, toSys):
fromSysToNative = self._getTransformFromOneTransformMap(fromSys, self._nativeCameraSys)
nativeToToSys = self._getTransformFromOneTransformMap(self._nativeCameraSys, toSys)
return fromSysToNative.then(nativeToToSys)

def getTransformMap(self):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why a getter instead of the more pythonic property?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is existing functionality (I think github is a bit confused about what changed).

It's a good idea, but I'd rather that be a separate RFC and ticket.

@@ -70,7 +102,7 @@ PYBIND11_PLUGIN(_detector) {
table::AmpInfoCatalog const &, Orientation const &, geom::Extent2D const &,
TransformMap::Transforms const &, Detector::CrosstalkMatrix const &>(),
"name"_a, "id"_a, "type"_a, "serial"_a, "bbox"_a, "ampInfoCatalog"_a, "orientation"_a,
"pixelSize"_a, "transforms"_a, "crosstalk"_a=Detector::CrosstalkMatrix());
"pixelSize"_a, "transforms"_a, "crosstalk"_a = Detector::CrosstalkMatrix());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Formatting change shouldn't be included.

posFocalPlane = fieldAngleToFocalPlane.applyForward(posFieldAngle)

# compute focal plane positions corresponding to field angles field_gridx, field_gridy
posFieldAngleRadiansList = [afwGeom.Point2D(x.asRadians(), y.asRadians())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you're turning Hungarian... Having such long variable names isn't good.

Copy link
Contributor Author

@r-owen r-owen Feb 27, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am Hungarian. But I'll get rid of "Radians"

# compute focal plane positions corresponding to field angles field_gridx, field_gridy
posFieldAngleRadiansList = [afwGeom.Point2D(x.asRadians(), y.asRadians())
for x, y in zip(field_gridx, field_gridy)]
posFocalPlaneList = camera.transform(posFieldAngleRadiansList, FIELD_ANGLE, FOCAL_PLANE)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why isn't std::pair<Angle, Angle> not a supported endpoint type?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that adds too much complexity, especially because it also requires adding support for std::vector<std::pair<Angle, Angle>> and adding a new Endpoint type. In any case it is clearly out of scope for this ticket.

std::vector<geom::Point2D> Detector::transform(std::vector<geom::Point2D> const &points,
FromSysT const &fromSys, ToSysT const &toSys) const {
return _transformMap.transform(points, makeCameraSys(fromSys), makeCameraSys(toSys));
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm surprised these aren't defined in the header, since they're so simple.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I prefer to define functions in the .cc file unless there's a good reason to do otherwse (e.g. inlining a method. It reduces clutter in the header saves on compilation time if something changes.

template std::shared_ptr<geom::TransformPoint2ToPoint2> Detector::getTransform(FROMSYS const &, \
TOSYS const &) const; \
template geom::Point2D Detector::transform(geom::Point2D const &, FROMSYS const &, TOSYS const &) const; \
template std::vector<geom::Point2D> Detector::transform(std::vector<geom::Point2D> const &, \
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the same reason we don't line up equal signs in a list of equations, I suggest not spacing out the backslashes.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

clang-format

pixPoint = dw.detector.transform(fpPoint, cameraGeom.FOCAL_PLANE, cameraGeom.PIXELS)
for i in range(2):
self.assertAlmostEqual(
fpPoint[i]/dw.pixelSize[i] + pixOffset[i], pixPoint[i])
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of iteration, can't you do this with Point and Extent arithmetic?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interesting idea. I tried fpPoint/dw.pixelSize + pixOffset but it was rejected with TypeError: unsupported operand type(s) for /: 'lsst.afw.geom.coordinates.coordinates.Point2D' and 'lsst.afw.geom.coordinates.coordinates.Extent2D'

I'll stick to this for now -- it is existing code and known to work.

Remove `CameraPoint`
Add support for transforming lists of points.
Remove support for `Camera.transform` searching for a suitable
detector. It was very inefficient and it would be difficult to
tell that code was using it. Fortunately it was not used anywhere.
Also fix broken links.
@r-owen
Copy link
Contributor Author

r-owen commented Feb 27, 2018

I rebased my changes

@r-owen r-owen merged commit 607a0d2 into master Feb 27, 2018
@ktlim ktlim deleted the tickets/DM-12447 branch August 25, 2018 06:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants