ejeschke / ginga

The Ginga astronomical FITS file viewer
BSD 3-Clause "New" or "Revised" License
121 stars 77 forks source link

Drawing plugin: Polygon crashing #89

Closed pllim closed 9 years ago

pllim commented 9 years ago

I am unable to find much documentation on how to properly draw a polygon in the Drawing.py plugin. I use right click and managed to draw one side of the intended polygon. I released the click so I can draw a different side of the same polygon, but I get the traceback below. Am I using the plugin wrongly, or is this a bug?

2015-03-11 09:58:37,784 | E | ImageView.py:642 (redraw_now) | Error redrawing image: cannot convert float NaN to integer
2015-03-11 09:58:37,785 | E | ImageView.py:647 (redraw_now) | Traceback:
  File ".../lib/python2.7/site-packages/ginga-2.2.20150303042350-py2.7.egg/ginga/ImageView.py", line 630, in redraw_now
    self.redraw_data(whence=whence)
  File ".../lib/python2.7/site-packages/ginga-2.2.20150303042350-py2.7.egg/ginga/qtw/ImageViewCanvasQt.py", line 52, in redraw_data
    self.draw()
  File ".../lib/python2.7/site-packages/ginga-2.2.20150303042350-py2.7.egg/ginga/canvas/DrawingMixin.py", line 83, in draw
    super(DrawingMixin, self).draw()
  File ".../lib/python2.7/site-packages/ginga-2.2.20150303042350-py2.7.egg/ginga/canvas/CompoundMixin.py", line 89, in draw
    obj.draw()
  File ".../lib/python2.7/site-packages/ginga-2.2.20150303042350-py2.7.egg/ginga/canvas/DrawingMixin.py", line 83, in draw
    super(DrawingMixin, self).draw()
  File ".../lib/python2.7/site-packages/ginga-2.2.20150303042350-py2.7.egg/ginga/canvas/CompoundMixin.py", line 89, in draw
    obj.draw()
  File ".../lib/python2.7/site-packages/ginga-2.2.20150303042350-py2.7.egg/ginga/qtw/ImageViewCanvasTypesQt.py", line 157, in draw
    self.draw_edit(cr)
  File ".../lib/python2.7/site-packages/ginga-2.2.20150303042350-py2.7.egg/ginga/qtw/ImageViewCanvasTypesQt.py", line 105, in draw_edit
    cpoints = self.get_cpoints(points=self.get_edit_points())
  File ".../lib/python2.7/site-packages/ginga-2.2.20150303042350-py2.7.egg/ginga/canvas/CanvasObject.py", line 388, in get_cpoints
    rpoints))
  File ".../lib/python2.7/site-packages/ginga-2.2.20150303042350-py2.7.egg/ginga/canvas/CanvasObject.py", line 387, in <lambda>
    cpoints = tuple(map(lambda p: self.crdmap.to_canvas(p[0], p[1]),
  File ".../lib/python2.7/site-packages/ginga-2.2.20150303042350-py2.7.egg/ginga/canvas/coordmap.py", line 41, in to_canvas
    return self.viewer.canvascoords(data_x, data_y)
  File ".../lib/python2.7/site-packages/ginga-2.2.20150303042350-py2.7.egg/ginga/qtw/ImageViewCanvasQt.py", line 44, in canvascoords
    x, y = self.get_canvas_xy(data_x, data_y, center=center)
  File ".../lib/python2.7/site-packages/ginga-2.2.20150303042350-py2.7.egg/ginga/ImageView.py", line 936, in get_canvas_xy
    win_x, win_y = self.offset2canvas(off_x, off_y)
  File ".../lib/python2.7/site-packages/ginga-2.2.20150303042350-py2.7.egg/ginga/ImageView.py", line 964, in offset2canvas
    win_x = int(round(win_x))
ejeschke commented 9 years ago

@pllim, the usual way is to press the right (or drawing) button down where you want the starting (first) point and drag. On the keyboard with the other hand, press 'v' whenever you want an intermediate point. For the final point before completing the polygon, simply release the drawing button.

While during the drawing, if you ever decide the previous point was incorrect, you can press 'x' 'z' to remove it.

I'm open to other, possibly additional, drawing techniques for polygons. One idea I had was to also allow left clicks while drawing to place the intermediate points.

Probably the error is due to the polygon being created with two few points. This should be corrected. I'll see if I can reproduce it.

ejeschke commented 9 years ago

Sorry, in that last comment I made a mistake. It looks like the default binding is 'z' to remove the last point, not 'x'.

pllim commented 9 years ago

Okay, thanks! Somewhat related... Is there an existing method to extract data from inside the polygon? There does not seem to be very much documentation on Drawing plugin.

ejeschke commented 9 years ago

@pllim, this would be a very welcome PR if you have the time to work on it. IMO, this is would be extremely useful. Ideally, we would have a method that we could pass a shape (any of the supported shapes), and it would return the data that exists within the shape. There is already a contains(x, y) method that could be used to help implement the method.

Seems like what you want is to find the smallest rectangle containing the shape, then extract that rectangle and use the contains method to set the non-interior pixels to masked out in a masked numpy array.

If ginga had something like this it would go a long way to becoming the basis of implementing ds9-like region analysis.

pllim commented 9 years ago

@ejeschke , do you think Ginga can benefit from https://github.com/spacetelescope/sphere in this matter?

ejeschke commented 9 years ago

It looks like there could be some useful synergy there. Maybe as a more accurate representation that has a mapping to 2D, either in WCS or data coordinates? I can well imagine that if you are combining or otherwise manipulating polygons that would be a desirable package to use.

Does it have a way to generate a bounding box from the sky polygon?

pllim commented 9 years ago

Regarding the bounding box, I think @bernie-simon or @mdboom might be able to answer.

mdboom commented 9 years ago

sphere doesn't have a way to generate a bounding box from a sky polygon, basically because it doesn't have any projections from 3D to 2D at all. But it would be simple enough to project the polygon to 2D using a given projection and then compute a bounding box from that.

bernie-simon commented 9 years ago

One "feature" or limitation of the sphere package is that the polygons it uses have sides that are constrained to be great circle arcs. I get the impression that that may not be the case for you

ejeschke commented 9 years ago

@bernie-simon, I think as long as there is a "reasonable mapping" from sphere polygons to 2D polygons in pixel (data) space it might be useful for the purpose @pllim is describing, which is to extract data regions inside the polygons. Would you agree?

bernie-simon commented 9 years ago

Sorry, I have not responded earlier because I've been on vacation.

A polygon is usually defined by its vertices, so the edge defined by the great circle arc may deviate from the polygon edge if they are large. As you say, the deviation may be made arbitrarily small by specifying more vertices and edges. It sounds like you want to define a mask using a polygon and mask all pixels outside it. Off the top of my head, the way to do this would be to get the line defining one row of pixels and compute where it intersects the polygon. If no intersection, you would test the end points to see if the line lies entirely outside or inside the polygon. The code to compute the intersection and do the endpoint test is in the sphere package, but there is no code yet to apply it to an image using its wcs or to create the mask.

ejeschke commented 9 years ago

@pllim, I should have replied on this earlier, but there is now support for cutting out arbitrary geometric shapes in ginga (basically any shape you can draw). Once you have a handle on the shape (e.g. from getting it via the draw-event callback) you can simply do:

arr_masked = image.cutout_shape(shape_obj)

You will get an array corresponding to the bounding box of the shape, with the non-containing pixels masked out. The containing test is done as a vector operation, which means it should be pretty fast.

This is fairly new and not well tested.

ejeschke commented 9 years ago

@pllim, BTW, is this still crashing for you when you draw a polygon "incorrectly"? I am unable to reproduce the crash even when I draw it (incorrectly) as you describe above. If it is not happening any more I'd like to close the issue.

BTW, I'm still interested in possibly incorporating the sphere package, but maybe this should go in as a separate issue, and maybe combined with collaborating/refactoring to use a new astropy "shapes" package.

pllim commented 9 years ago

@ejeschke , polygon does not crash anymore, and I see that you also updated the instructions on Drawing plugin. Thanks! I'll check out the cutout_shape() method, and open a separate issue regarding sphere.

bernie-simon commented 9 years ago

One of the constructors for the astropy sphere class takes a closed list of vertices. (Closed here means the last point on the list is identical to the first.) Since it sounds like Ginga is producing this list, there should be no problem passing it to sphere after transforming the pixel coordinates to world coordinates.

bernie-simon commented 9 years ago

"Does it have a way to generate a bounding box from the sky polygon?"

I'm not sure what you mean by a bounding box. Do you mean the smallest rectangle that contains the polygon? Or the min and max right ascensions and declinations, for searching some catalog?

The answer is no to both, but if there was a need, it could be added. The latter problem is very simple to implement a solution for. I don't know right now a solution to the first problem.

pllim commented 9 years ago

To me, bounding box should be the smallest rectangle that contains the polygon.

bernie-simon commented 9 years ago

What's the use case for this? Maybe this is a naive question, but it would be nice to know.

pllim commented 9 years ago

@bernie-simon , an example use case is 6 comments above yours, as mentioned by @ejeschke

bernie-simon commented 9 years ago

If I read that comment correctly, that bounding box is aligned with some image. That suggests to me that an input to the bounding box computation would be a rotation matrix, by default the identity matrix.