-
Notifications
You must be signed in to change notification settings - Fork 270
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
Add get_pixel_id method #918
Conversation
Awesome. How does this behave for points outside the camera? |
ctapipe/instrument/camera.py
Outdated
pixel_centers = np.stack([self.pix_x.to_value(u.m),self.pix_y.to_value(u.m)]).T | ||
points_searched = np.dstack([x.to_value(u.m),y.to_value(u.m)]) | ||
|
||
kdtree = KDTree(pixel_centers) |
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.
I would suggest to build the kdtree in __init__
, as it is used by more than one method.
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.
agreed - right now we always make the assumption that you cannot modify the geometry (many functions cache a result). It could also go as a lazyproperty
like the neighbor matrix, since most of the time you don't need it.
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.
put it into lazyproperty in 7114978
ctapipe/instrument/camera.py
Outdated
# pixel. If not, pos lies outside the camera. This approach does not need to know the | ||
# particular pixel shape. | ||
|
||
borderpix_ids = np.where(self.get_border_pixel_mask())[0] |
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.
I think this can be vectorized by just using the mask
Returns -1 and prints a warning. |
At the moment, the Outside-of-camera-check is exact (for arbitrary pixel shapes), but presumes that camera_pixels[0] lies in the center of the camera (which may not be always the case). A much simpler, but only approximate alternative would be to check if the position distance to the nearest pixel center is smaller than than the radius of the pixels' circumcircle. |
Dropped assumption on central pixel in e95ff10, still being exact. However, the code might be much simpler and & readable if the inside-camera-check is only approximate. Or doing an exact check in a different, smarter way. |
Codecov Report
@@ Coverage Diff @@
## master #918 +/- ##
==========================================
- Coverage 82.31% 78.34% -3.98%
==========================================
Files 186 191 +5
Lines 10716 10778 +62
==========================================
- Hits 8821 8444 -377
- Misses 1895 2334 +439
Continue to review full report at Codecov.
|
ctapipe/instrument/camera.py
Outdated
|
||
""" | ||
|
||
pixel_centers = np.stack([self.pix_x.to_value(u.m), self.pix_y.to_value(u.m)]).T |
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.
np.column_stack
gives directly what you want here without having to transpose.
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.
done in e43e781
ctapipe/instrument/camera.py
Outdated
outside camera | ||
''' | ||
|
||
if not np.all(self.pix_area == self.pix_area[0], axis=0): |
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.
Use if np.any(self.pix_area != self.pix_area[0])
. all
always needs to test every value, any
exits at the first True
value it encounters.
Also this looks like it should be a @lazyproperty
, like all_pixel_areas_equal
or better name you can come up with.
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.
also might want to use a tolerance, in case of any rounding error in case the area of the pixel is calculated somewhere. (or np.isclose()
- is there an anyclose()
function?),
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.
np.any(~np.isclose(...
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.
ctapipe/instrument/camera.py
Outdated
for borderpix_id in borderpix_ids_in_list: | ||
index = np.where(pixel_ids == borderpix_id)[0][0] | ||
# compare with inside pixel: | ||
xprime = points_searched[0][index, 0] \ |
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.
for calculations, use implicit line continuations in parenthese instead of explicit line continuations, like this:
a = (
b
+ c
)
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.
Also, converting units is kind of expensive, so you should do self.pix_y[index].to_value(u.m)
instead of converting all values and then only using one.
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.
done in fd5d48f
ctapipe/instrument/camera.py
Outdated
points_searched = np.dstack([x.to_value(u.m), y.to_value(u.m)]) | ||
|
||
kdtree = self.all_pixels_kdtree | ||
dist, pixel_ids = kdtree.query(points_searched) |
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.
the kdtree..query has an option distance_upper_bound
for where you could use the outer circle diameter of a pixel. this would immediately return n_pixel
for points far away from any pixel.
At a certain size, it also might be faster to build a kdtree for the points that are looked for.
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.
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.
distance_upper_bound
implemented in a34e9bd. I left the precise check for now (only done for points between circumference and actual surface), but the corresponding code can simply be removed (is only called if points haven't been sorted out already by distance_upper_bound
). For many queried points, distance_upper_bound
seems a factor 3 faster than the current exact check.
At a certain size, it also might be faster to build a kdtree for the points that are looked for
Absolutely, but I fear this might turn into some over-engineering?
ctapipe/instrument/camera.py
Outdated
@@ -518,6 +533,69 @@ def get_shower_coordinates(self, x, y, psi): | |||
|
|||
return longi, trans | |||
|
|||
def get_pixel_id(self, x, y): |
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.
I'd suggest renaming this - it's not clear what the input is ("get" could mean just lookup the pixel_id from the pixel index). Maybe position_to_pixel_id(x,y)
?
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.
renamed method to position_to_pix_index() in 49e3572
ctapipe/instrument/camera.py
Outdated
|
||
Returns | ||
------- | ||
pixel_ids: Pixel number or array of pixel numbers. Returns -1 if position falls |
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.
Does this give the pixel ID (the name of the pixel), or the pixel index the index into the arrays in the CameraGeometry
(these are not the same thing)
given a pixel index i, the x-position of that pixel isgeom.pix_x[i]
, and the pixel_id of that pixel is geom.pix_id[i]
.
You should avoid the word "pixel number" and don't confuse the concepts.
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.
Looking at the code, it seem to compute the pixel_index
, not the pixel_id
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.
Indeed, it's the pixel index. Wasn't aware of the difference in ctapipe, but of course makes sense to differentiate, thanks for pointing this out. Renamed the method to position_to_pix_index(), because thought it's useful to let the user decide if s/he wants to obtain the id in a separate step. Also clarified the variable naming to avoid confusing expression 'number'.
ctapipe/instrument/camera.py
Outdated
outside camera | ||
''' | ||
|
||
if not np.all(self.pix_area == self.pix_area[0], axis=0): |
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.
also might want to use a tolerance, in case of any rounding error in case the area of the pixel is calculated somewhere. (or np.isclose()
- is there an anyclose()
function?),
ctapipe/instrument/camera.py
Outdated
@@ -203,6 +203,21 @@ def _calc_pixel_area(pix_x, pix_y, pix_type): | |||
|
|||
return np.ones(pix_x.shape) * area | |||
|
|||
@lazyproperty | |||
def all_pixels_kdtree(self): |
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.
is there a case where this is not for all pixels? Otherwise, perhaps just name this property kdtree
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.
Renamed it to simply kdtree in f1a7b92. If finally satisfied with this PR, maybe have a look at already existing _find_neighbor_pixels method() to work with the same kdtree (seems to also use all pixels, but slightly differently implemented)
Couldn't that just be a bounding box? E.g. |
I implemented @maxnoe's suggestion to do it approximately with the circumference on the individual pixel basis. As commented above, can easily simply remove the complicated chunk of code now. |
Added CameraGeometry.get_pixel_id() method to return the pixel number(s) corresponding to a (or an array of) x,y position(s), to be used like: