ukoethe / vigra

a generic C++ library for image analysis
http://ukoethe.github.io/vigra/
Other
409 stars 191 forks source link

Add overlay and SVG capability to vigranumpy's ImageWindow #76

Open ukoethe opened 13 years ago

ukoethe commented 13 years ago

This is a patch that Rainer Herzog is currently implementing. A few remarks:

Once the ilastik image viewer is finished, this functionality should be carried over there.

hmeine commented 13 years ago

Maybe I don't know GitHub well enough, but I don't see the patch here?

Anyhow, the longer I think about this, and the more work goes into it, the more sure I am that we really want to use QGraphicsView…

BTW, Rainer, I have lost overview on all the forked image viewers / overlay implementations, but let me tell you again what code I have already written:

  1. The old, interactive2 code with several (fast and flexible) Qt3 overlays, including an overlay group. The repository has been converted from CVS to Mercurial.
  2. The C++ QImageViewer, which I released in VigraQt and ported it to Qt4. This is also rock-solid, fast, and comes with the OverlayImageViewer subclass. A PyQt example of an overlay is provided for testing, too. Compared with 1., it has a better API w.r.t. coordinate systems, which means less code in the overlays itself (in particular if you use Qt4's painter transformations).
  3. I also ported interactive2 to Qt4, and to the C++ imageviewer from VigraQt (i.e. combining 1+2). This is also in the hg repository; if you were not aware of it, please have a look there. I am not sure if all overlays were ported and tested, but I assume that I at least tried not to leave a mess of Qt3 code in there.
  4. Finally, I have also unpublished GeoMap egde&face overlays coded in C++ for the OverlayImageViewer. I guess you could be interested in these, too, so if I did not send them to you in the past, I could dig them up on request. (ATM, I don't see how to attach files here, is that not possible?)

But see above, I would really think that we can do even better with QGraphicsView. And starting with everything in Python, just using PyQt, NumPy, and qimage2ndarray should make it possible to implement a better viewer rather quickly, probably even without sacrificing any performance.

Oh, BTW, Rainer, concerning performance: A big penalty comes with antialiasing, or with arbitrary coordinate transformations done in Qt. I think if you have large data (many points / large polygons) it might still make sense to precompute zoomed coordinates for the display (and just leave the translation to Qt). Then, Qt4 should not be slower than Qt3 was.

ukoethe commented 13 years ago

Here is what I really want: a lightweight PyQt-based image viewer that can be shipped with VIGRA. It should be very easy to install, preferably without installing Qt (i.e. PyQt should suffice, but that's probably impossible, given the need for qimage2ndarray). Required features:

If I need a more powerful viewer, I'll very likely go with the one that is being developed in the ilastik project (and which already uses QGraphicsView). But for the simple viewer, I will accept any solution that's sufficiently clean -- please make a pull request if you have something (e.g. where exactly is the Qt4 port of interactive2, and can it be adapted quickly to vigranumpy?)

hzg1 commented 13 years ago

Hello Hans,

the patches are placed here: http://github.com/hzg1/vigra inside the overlay-viewer branch.

Concerning performance, you are right, disabling antialiasing helps a lot (at least on my current hardware).

ATM, I'm quite confident with the current implementation, however, I appreciate any improvements (namely in terms of QGraphicsView) without losing options available at this time.

ukoethe commented 13 years ago

I implemented the improved parent handling and the OverlayGroup class (see branch 'overlay-viewer' in this repo), and it works in principle. A problem I noticed are the size and position handling of TextOverlay (which now contains just a single text): When a TextOverlay is added on its own, the text size is independent of the viewer's zoom. If the TextOverlay is part of a OverlayGroup, the text size changes according to the zoom level (which I personnally like better).

I also tried the SVG writer that comes with PyQt, and it works nicely:

import PyQt4.QtSvg
g = PyQt4.QtSvg.QSvgGenerator()
g.setFileName('test.svg')
g.setSize(q.QSize(256,256))
g.setViewBox(q.QRect(0,0,256,256))
g.setTitle("SVG Generator Example Drawing")

p = QtGui.QPainter()
p.begin(g)
w.viewer().paintImage(p, q.QRect(0,0,256,256))
w.viewer().paintOverlays(p, q.QRect(0,0,256,256))
p.end()
ukoethe commented 13 years ago

Here's the code to display overlays in the new design:

w = i.show()
t1 = vigra.pyqt.overlays.TextOverlay('blah', [100,10])
t2 = vigra.pyqt.overlays.TextOverlay('foo', [100,100])
l = vigra.pyqt.overlays.OverlayGroup([t1, t2], parent=w.viewer())

t3 = vigra.pyqt.overlays.TextOverlay('hallo', [50,50])
w.viewer().addOverlay(t3)  # if parent was not given in the constructor
hzg1 commented 12 years ago

I played a bit around with the QSvgGenerator. When I use the values from the ImageViewer

from PyQt4 import QtSvg, QtCore, QtGui
g = QtSvg.QSvgGenerator()
g.setFileName('test.svg')
size = w.viewer().sizeHint()
viewRect = QtCore.QRect(w.viewer().upperLeft(), size)
g.setSize(size)
g.setViewBox(viewRect)
g.setTitle("SVG Generator Example Drawing")

p = QtGui.QPainter()
p.begin(g)
w.viewer().paintImage(p, viewRect)
w.viewer().paintOverlays(p, viewRect)
p.end()

I end up with an output not much different from my own generator, which is fine for me. Interestingly, the image is coded directly into the svg file. Still I like to have a simple option to get the original Image, not zoomed or normalized, into the svg file.

Concerning your changes for TextOverlay, I'm a bit worried about performance. Maybe this is not sensible. ATM, I use TextOverlay for displaying labels of regions, i.e. numbers of 1-4 digits. These are in one example more than 40, but I can easily imagine of hundreds of labels. That would make hundreds of TextOverlays, combined in one OverlayGroup. I'm not sure if it makes a big difference calling the draw method of hundreds of overlays which is quite simple now, or if it's better to have a single draw method, looping over all entrys in the list, which is the previous solution. Compared to the Point- and EdgeOverlay, also those expect some kind of a list of elements, though the matter of positioning is simpler there, as the position is implicit.

ukoethe commented 12 years ago

Concerning your changes for TextOverlay, I'm a bit worried about performance. Maybe this is not sensible.

I dont think there is a much of a difference: The loop just moved from TestOverlay to OverlayGroup, so there is only one additional function call. Of course, one could refactor the code a little to ensure that common setup calls are executed only once (before the loop).

BTW, when the painter is configured in a draw() function, the original settings are not restored at the end of draw(). I think this should be changed.

hzg1 commented 12 years ago

Meanwhile I merged all your changes and can't notice a big difference in performance regarding the method of text handling.

I found a possibility for choosing whether the text should be zoomed with the image or not. Personally, I prefer the second case. I use text for information purposes only, not as objects of the image itself. So the text should have a reasonably readable size, no matter how the image is zoomed. Maybe two or more text overlays are overlapping itselves at 1:1 zoom. Only by using a fixed size, those labels will get readable when zooming in.

Though I never used 'Grid' as coordinateSystem, I had to translate objects by (0.5, 0.5) on two occasions. I didn't understand why, but now in all my test cases, everything is positioned where it should.

ukoethe commented 12 years ago

I fixed a small bug and uploaded the change to my repo (branch overlay-viewer). Can you please provide a little demo programm that reads an image (e.g. vigra/src/examples/lenna_color.gif) and adds some representative overlays?