3
$\begingroup$

I have a long-ish code which stores the grey values of an image's pixels in a matrix called data and then does some numerical analysis using those values. At one point in the code, I need to ask the user to select a region of the image to serve as a reference for the analysis of the rest of the image. To do so, I've indexed the pixels (from 1 to rows x cols, which is the number of pixels in the image) and I'm using a dynamic module where the user can draw a polygon on the image and the coordinates of the pixels inside the polygon are stored in a vector; my code for this is a variation of Heike's code here. This is my code for the dynamic module:

points=ConstantArray[0,{rows*cols,2}];
For[j=1,j<=rows,j++,
 For[l=1,l<=cols,l++,
  points[[(j-1)cols+l,1]]=l;
  points[[(j-1)cols+l,2]]=j];];
selection[region_,pt_]:=Round[(
 Total@Mod[
  (#-RotateRight[#])&@(ArcTan@@(pt-#)&/@region),
  2Pi,
  -Pi
 ]/2/Pi
)];
DynamicModule[
 {plot, pos},
 plot=Legended[
  ListDensityPlot[
   data,
   PlotRange->Full,
   ColorFunction->"Rainbow",
   ImageSize->Large
  ],
  BarLegend[
   {"Rainbow",{Min[data],Max[data]}},
   LegendMarkerSize->575
  ]
 ];
 Manipulate[
  pos=Pick[Range[Length[points]],selection[region,#]&/@points,1];
  Show[
   plot,
   Epilog->{
    {Transparent,Point[points[[pos]]]},
    {Transparent,Point[Complement[points,points[[pos]]]]},
    {EdgeForm[Red],FaceForm[{Red,Opacity[0.35]}],Polygon[region]}
   }
  ],
  {{region,{}},Locator,LocatorAutoCreate->All},
  Row[{
   Button["confirm",MANY OPERATIONS GO HERE],
   Button["undo",region=Drop[region,-1]],
   Button["reset",region={};pos={}]
  }]
 ]
]

Now, pos (the vector where the selected pixels' indices are stored) does not work outside of the dynamic module; if I close the module and then ask Mathematica to tell me what's stored in pos, all I get is pos. Therefore, as you can see, all of the code for the analysis that takes place after the user draws the polygon is inside what the "confirm" button does.

The problem with this approach is that the code is really long and involves many operations and numerical solutions of equations and so on. This is a problem because Mathematica (10.4.1.0) doesn't seem able to handle so many operations inside a dynamic module; it stops without notice after a certain number of operations and throws an "internal error" message on the error-message window.

I've tested the code without the dynamic module (filling pos with an arbitrary amount of arbitrary integers beforehand), and the code works fine. The problem is clearly the dynamic module.

In order to avoid this, I'd like to output the result of the dynamic module (i.e. the numbers stored in pos) to the rest of the code. I thus have two questions:

  1. How do I do this?
  2. How can I a avoid forcing the user to run two separate pieces of code instead of just one? If I write ; after the dynamic module, the module doesn't appear at all; if I don't add it, I don't know how to add code after the dynamic module without starting a new input section.

Thanks a lot in advance.

$\endgroup$

1 Answer 1

2
$\begingroup$

DynamicModule automatically scopes its variables, so you'll want a top-level symbol that tracks the value. Try this:

DynamicModule[{plot, pos},
 plot =
  Legended[
   ListDensityPlot[data, PlotRange -> Full, 
    ColorFunction -> "Rainbow", ImageSize -> Large], 
   BarLegend[{"Rainbow", {Min[data], Max[data]}}, 
    LegendMarkerSize -> 575]];
 Manipulate[
  $externalPos = pos =
    Pick[
     Range[Length[points]],
     selection[region, #] & /@ points, 1
     ];
  Show[plot, 
   Epilog -> {{Transparent, Point[points[[pos]]]}, {Transparent, 
      Point[Complement[points, points[[pos]]]]}, {EdgeForm[Red], 
      FaceForm[{Red, Opacity[0.35]}], 
      Polygon[region]}}], {{region, {}}, Locator, 
   LocatorAutoCreate -> All}, 
  Row[{Button["confirm", MANY OPERATIONS GO HERE], 
    Button["undo", region = Drop[region, -1]], 
    Button["reset", region = {}; pos = {}]}]],
 Initialization :> (
   $externalPos = pos
   )]

Where $externalPos is your position list. Note that I have it in the Initialization getting assigned to pos instead of removing pos altogether. That's because DynamicModule will save the state, so we want to preserve that.

One other thing is that I see you're using a For loop. Generally we try to avoid this as For is clunkier than Do or Table. I think you can just replace that with:

points =
  Flatten[
   Table[
    {j, l},
    {l, cols},
    {j, rows}
    ],
   1
   ];
$\endgroup$
7
  • $\begingroup$ Thanks! This works perfectly. However, it only solves question 1 (which, admittedly, is the most important one). How can I then continue running code once the user presses "confirm"? $\endgroup$
    – Rain
    May 23, 2017 at 16:51
  • $\begingroup$ @Rain what exactly do you want to happen with the "continue running code"? Do you want to execute a specific command or simply replace the DynamicModule with the $externalPos? $\endgroup$
    – b3m2a1
    May 23, 2017 at 16:55
  • $\begingroup$ I want to calculate stuff using pos (or $externalpos). That vector contains the indices of certain pixels in the image which are to be the reference for a calculation; I want to calculate the average of the grey values of those pixels and then do something to the image which involves operating with that average. $\endgroup$
    – Rain
    May 23, 2017 at 17:22
  • $\begingroup$ Put the computation code in your confirm button and use NotebookWrite to overwrite the DynamicModuleBox. This will replace the dynamic module with the computed value. $\endgroup$
    – b3m2a1
    May 23, 2017 at 17:30
  • 1
    $\begingroup$ @Rain try using Button[label, code, Method->"Queued"]. If it takes more than 5 or so seconds Button will abort a computation by default. Using "Queued" takes it off the pre-emptive link and pushes it to the standard evaluator stack. NotebookWrite would just take the result of your computation and overwrite the Cell / DynamicModule. You can learn more about it in the docs. $\endgroup$
    – b3m2a1
    May 23, 2017 at 18:27

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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