-
-
Notifications
You must be signed in to change notification settings - Fork 54
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
Account for finite pixel size in PointPixelRegion.contains #474
Comments
I'm not sure I agree - isn't I can certainly imagine situations where it would be useful to have a pixel-sized region, though. I think it would be best to have that be a |
Thanks @keflavich! I guess the main question is what should happen in the following cases: from regions import PointPixelRegion, PointSkyRegion
from astropy.coordinates import SkyCoord
from astropy.wcs import WCS
from astropy.io import fits
from astropy.utils.data import get_pkg_data_filename
center = PixCoord(0, 0)
point = PointPixelRegion(center=center)
print(point.contains(center))
fn = get_pkg_data_filename("galactic_center/gc_msx_e.fits", package='astropy.wcs.tests')
header = fits.getheader(fn)
wcs = WCS(header)
center = SkyCoord("0d", "0d", frame="galactic")
point = PointSkyRegion(center=center)
print(point.contains(center, wcs=wcs)) My expectation was, that both cases would return This also allows to actually implement the |
I agree with @keflavich here that a Point shouldn't contain anything. In your use case, if you want to check the center, you could use |
Thanks for the feedback @keflavich and @larrybradley, however I don't agree with you both here. Let me try to illustrate with a few use-cases, why I think the behavior should be the way I requested in my initial message. I think the main argument is consistent behavior with other regions and behavior in context of integration: Creating Masksfrom matplotlib import pyplot as plt
from regions import CirclePixelRegion, PointPixelRegion, PixCoord
circle = CirclePixelRegion(PixCoord(7.3, 6.8), radius=5)
mask = circle.to_mask(mode="exact").to_image((15, 15))
plt.imshow(mask); Now for the PointPixelRegion import numpy as np
from matplotlib import pyplot as plt
from regions import PointPixelRegion, PixCoord
point = PointPixelRegion(PixCoord(7.3, 6.8))
# This currently raises NotImplementedError
mask = circle.to_mask().to_image((15, 15))
# However it is actually well defined in pixel space
y, x = np.indices((15, 15))
dx = np.abs(x - point.center.x)
dx = np.where(dx < 1, 1 - dx, 0)
dy = np.abs(y - point.center.y)
dy = np.where(dy < 1, 1 - dy, 0)
mask = dx * dy
plt.imshow(mask) Should give: The computation of the mask is numerically very well defined. There is no reason to not implement it. Checking Overlap of Region with Mask Imagefrom matplotlib import pyplot as plt
from regions import CirclePixelRegion, PointPixelRegion, PixCoord
circle_mask = CirclePixelRegion(PixCoord(51, 51), radius=20)
mask = circle_mask.to_mask().to_image((101, 101)).astype(bool)
y, x = np.indices((101, 101))
coords_mask = PixCoord(x[mask], y[mask])
circle = CirclePixelRegion(PixCoord(37, 38), radius=10)
print(circle.contains(coords_mask).any())
point = PointPixelRegion(PixCoord(37, 38))
print(point.contains(coords_mask).any())
ax = plt.subplot()
ax.imshow(mask)
circle.plot(ax=ax, color="r")
point.plot(ax=ax, color="b") In this case the statement for the red circle clearly gives the right result, however the point region will always return False, even if it overlaps with the region defined by the mask image. This does not make sense to me. IntegrationOften regions are used to define integration boundaries. This is related to the mask behavior I mentioned above. See e.g.: from matplotlib import pyplot as plt
from regions import CirclePixelRegion, PointPixelRegion, PixCoord
data = Gaussian2DKernel(x_stddev=5).array
circle = CirclePixelRegion(PixCoord(20, 20), radius=5)
weights = circle.to_mask(mode='exact').to_image(data.shape)
print((data * weights).sum())
point = PointPixelRegion(PixCoord(20, 20))
# Fails, but should give the value at the center of the Gauss,
# thats how a delta function behave when integrating
weights = point.to_mask(mode='exact').to_image(data.shape)
print((data * weights).sum())
ax = plt.subplot()
ax.imshow(data)
circle.plot(ax=ax, color="r")
point.plot(ax=ax, color="b") And the illustration: These are all use-cases where the behavior for the
I think this is only half-true. To me the pixel coordinate system implies, that the data space is discretized / represented as a pixelized image. This is also the behavior currently implemented for other regions, especially with the |
I agree that you've shown cases where a region that contains a single pixel is useful. The question before was mostly one of terminology; in a mathematically purist view, a point can't contain anything. But, I think you're making a good case that a Is there any possible downside to treating I also see the use of defining a I'd be in favor of a PR that implements this functionality. Maybe we'd call this just a I suggest that this is a critical case to document very explicitly. For most of the regions in this package, a small difference in interpretation of what an edge is will not result in major differences in the outcome of a project. For these pixel regions, when we're potentially talking about a boolean difference between 'included' and 'not included', the consequences could be more pronounced. |
I guess the general issue is more conceptional:
Indeed
One more thing I found: I think it's non ideal to return a for region_pix in regions_pix:
if isinstance(region, PointSkyRegion):
artist = region_pix.as_artist(**kwargs_line2d)
else:
artist = region_pix.as_artist(**kwargs)
ax.add_artist(artist) Can this behavior be unified as well? Or is it intended? |
I agree, I like your proposed points. Regarding the inconsistency of the Line2D object: I agree, it would be better to have these be consistent. Is there a matplotlib object type that fits this? Maybe |
Currently the
PointPixelRegion.contains
method always returnsFalse
. However this is not correct for the cases of sampled image data, because the size of the pixel is finite, namely1 x 1 pix
. This mean the.contains()
method should return true if a position is contained in the finite support of the pixel.The text was updated successfully, but these errors were encountered: