1
$\begingroup$

I wrote codes to record list of coordinates when mouse clicked. It is expected that the coordinates should be plotted dynamically. But actually nothing happened. Can anybody help me to find the reason?

i = ExampleData[{"TestImage", "Lena"}];
dgray = ImageData[i, Interleaving -> False][[2]];
iedge = EdgeDetect[i, 5];
igray = Image[dgray];
dmorc = MorphologicalComponents[iedge];
imorc = Image[dmorc];

DynamicModule[{ncurve = {}}, 
  EventHandler[
    ImageCompose[
      igray, 
      Graphics[ListLinePlot[ncurve, Axes -> False]]], 
    {"MouseClicked" :> 
       (ncurve = 
          PixelValuePositions[imorc, 
            PixelValue[imorc, MousePosition["Graphics"] /. None -> {0, 0}]])}]]
$\endgroup$
  • 2
    $\begingroup$ Can you explain the difference to your previous questions: mathematica.stackexchange.com/q/63319/131 and mathematica.stackexchange.com/q/60911/131? $\endgroup$ – Yves Klett Oct 20 '14 at 15:02
  • $\begingroup$ I solved the raised error issue, discard the trans[] function, and correct the point coordinates. Now the only issue is no response. $\endgroup$ – xibinke Oct 20 '14 at 15:19
  • $\begingroup$ It's totally different now as it is only a grammar issue, I think. Please let it post openly. I really need somebody's help. $\endgroup$ – xibinke Oct 20 '14 at 15:23
  • $\begingroup$ Thank you for the clarification. $\endgroup$ – Yves Klett Oct 20 '14 at 15:26
4
$\begingroup$

The first step in debugging is always to narrow down the problem. To do this, you can make ncurve global:

DynamicModule[{(* ncurve = {} *) }, 
 EventHandler[
  ...

Then, you can click on the image and evaluate ncurve in a separate cell:

Length[ncurve]

248652

(You can and should put ncurve in a DynamicModule once you're done, of course. But for debugging, it's common practice to make local variables global and play with them in another notebook cell.)

Now you a lot more than just "nothing seems to have happened": You know that

  1. ncurve was set, but the display wasn't updated. That's the easy part: You just have to wrap the ImageCompose[...] part with Dynamic[...] and it's updated dynamically. (DynamicModule localizes variables, like Module, it doesn't make the contained code block dynamic, like Dynamic.)
  2. those are way too many points. In fact, unless you (accidentally) manage to hit one of the few edge points in the image, ncurve will simply contain all the black points in the edge-image. (Which could easily hang or crash the Mathematica Frontend.)

That's because the imorc image is zero for all non-edge pixels. What you would need is an image that contains a unique index for each connected component, or the index of the closest connected component, if it's a background pixel. Here, WatershedComponents is very useful:

watersheds = 
  Image[WatershedComponents[ColorNegate[iedge], Method -> "Basins"]];
imorc = Image[ImageData[watersheds] * ImageData[iedge]];

Now, if you use PixelValue[watersheds, ..., you get a component index, for edge- and non-edge pixels, and you can use PixelValuePositions[imorc, ... to look up the corresponding edge pixels:

DynamicModule[{(*ncurve={}*)}, 
 EventHandler[
  Dynamic[ImageCompose[igray, 
    Graphics[
     ListLinePlot[ncurve, 
      Axes -> False]]]], {"MouseClicked" :> (ncurve = 
      PixelValuePositions[imorc, 
       PixelValue[watersheds, 
        MousePosition["Graphics"] /. None -> {0, 0}]])}]]

Now, if you click on the image, you see a superimposed edge line. But it's probably not what you expected:

enter image description here

If you don't understand what you're seeing, try evaluating ListLinePlot[ncurve] alone. ListLinePlot chooses a PlotRange that fits the list of points, and ImageCompose simply composes the two resulting images, ignoring the different coordinate systems. What you really want is Show:

DynamicModule[{(*ncurve={}*)}, 
 EventHandler[
  Dynamic@Show[igray, 
    ListLinePlot[ncurve]], {"MouseClicked" :> (ncurve = 
      PixelValuePositions[imorc, 
       PixelValue[watersheds, 
        MousePosition["Graphics"] /. None -> {0, 0}]])}]]

enter image description here

Also: EventHandler is a relatively low-level function. Mathematica has higher-level functions like LocatorPane that can make your life simpler:

DynamicModule[{mousePt = {1, 1}},
 LocatorPane[Dynamic[mousePt],
  Dynamic[
   Show[igray, 
    ListLinePlot[
     PixelValuePositions[imorc, PixelValue[watersheds, mousePt]]]]]]]

One last note: I've learned to avoid dynamic code during debugging as much as possible. I've lost too much work because the notebook interface hung or crashed evaluating a programming error (e.g. like displaying a list plot with 248652 points). Use a LocatorPane to choose a point, and a separate non-dynamic cell with Show, Graphics, ListPlot and friends until you're satisfied with the result. Then put them in a dynamic block. (Or actually, save your notebook, then put them in a dynamic block.)

$\endgroup$
  • $\begingroup$ Thank you,nikie. Your detailed answer really helps me a lot. $\endgroup$ – xibinke Oct 21 '14 at 15:17

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.