5
$\begingroup$

This is some extremely simple code that shows an image and manipulates a circle (that will be used to select a region of the image to analyze). All those Dynamics eliminate some terrible lag, I understand the one for the circle, but why are the other necessary?

theImage = pickAnImage;  (*mine is 368 by 252*)
Manipulate[
 circularSelector = Graphics[{Yellow, Dynamic[Circle[center, radius]]}];
 xRange = Dynamic[{center[[1]] - radius, center[[1]] + radius}];
 yRange = Dynamic[{center[[2]] - radius, center[[2]] + radius}];

   Column[{Show[theImage, circularSelector],
   xRange,
   yRange}],

 {{center, {184, 126}}, {0, 0}, {368, 252}, 1, Locator, Appearance -> None},
 {{radius, 50}, 2, 100, 1, Appearance -> "Labeled"}]

Consider now adding one line of code to show the section of the image in the selection, something like

ImageTake[theImage,xRange,yRange],

Well, that doesn't work. It works if the Dynamics are removed from the definition of xRange and yRange, but then all the lag is back!:

theImage = pickAnImage;  (*mine is 368 by 252*)
Manipulate[
 circularSelector = Graphics[{Yellow, Dynamic[Circle[center, radius]]}];
 xRange = {center[[1]] - radius, center[[1]] + radius};
 yRange = {center[[2]] - radius, center[[2]] + radius};

 Column[{Show[theImage, circularSelector],
   xRange,
   yRange,
   ImageTake[theImage, yRange, xRange]}],

 {{center, {184, 126}}, {0, 0}, {368, 252}, 1, Locator, Appearance -> None},
 {{radius, 50}, 2, 100, 1, Appearance -> "Labeled"}] 

How do I fix this? More importantly, what's the reason behind it? When using Manipulate, I seem to run into lag and this Dynamic stuff seems to be behind it. I've checked the usual resources, but something as simple as this (A Manipulate with several images, or how to optimize code for snappiness) is rarely described.

EDIT: As requested by Nasser (thanks btw) I'm adding the code that doesn't work (selection is not shown):

theImage = pickAnImage;  (*mine is 368 by 252*)
Manipulate[
 circularSelector = 
  Graphics[{Yellow, Dynamic[Circle[center, radius]]}];
 xRange = Dynamic[{center[[1]] - radius, center[[1]] + radius}];
 yRange = Dynamic[{center[[2]] - radius, center[[2]] + radius}];

 Column[{Show[theImage, circularSelector],
   xRange,
   yRange,
   ImageTake[theImage, yRange, xRange]}], 

   {{center, {184, 126}}, {0, 0}, {368, 252}, 1, Locator, Appearance -> None}, 
   {{radius, 50}, 2, 100, 1, Appearance -> "Labeled"}]

As mentioned before, if the Dynamic are removed from xRange and yRange this works (second code above), but moving the circle is laggy compared to the first code (and it will get worst once more complex code is added).

$\endgroup$
2
  • $\begingroup$ Fixed the typo and added the code that doesn't work. $\endgroup$
    – respin4
    Commented Aug 2, 2013 at 3:12
  • $\begingroup$ related question $\endgroup$
    – cormullion
    Commented Aug 2, 2013 at 7:23

3 Answers 3

5
$\begingroup$

The code is bit complicated, at least the prospect of explaining exactly what's going on seems complicated.

Roughly some principles that can help someone understand what's going on.

  1. In Manipulate[body, etc], anytime a variable that body depends on changes, body will be executed. Nested Dynamic and Refresh within body will make things more complicated (Ref: [1] etc.) You set a lot of variables in the body of your Manipulate. They cause the body to be reevaluated.

  2. Use Dynamic to isolate parts of the viewable output that can be updated independently. Dynamic[x] is not the same as x, but it will display as the value of x if and when it is displayed by the Front End. Thus ImageTake[theImage, Dynamic[{row1, row2}],..] won't work because it happens in the Kernel; and Dynamic[{row1, row2}] is not the same as {row1, row2}.

  3. Display theImage can take an appreciable amount of time if the image is large, especially if done multiple times whenever the Locator is moved. (If the image is small, one might not notice the slow down.)

  4. Generally global variables in Manipulate are bugs waiting to happen. That's not a particular issue here from what I see, but I offer it as general advice.


The easiest way to fix 1 is TrackedSymbols :> {center, radius}. It improves the performance, but theImage will be re-displayed, which will still cause a lag. Below I used Dynamic in places to isolated dynamic segments of the output and With to replace your global variables. With is nice in that it inserts code without creating a variable that will be tracked. To isolate theImage, I had to make the Column structure more complicated. I also made the domain for the Locator depend on the radius, which you may or may not have wanted. ;)

Manipulate[
 With[{circularSelector = Graphics[{Yellow, Dynamic[Circle[center, radius]]}]},
  Column[{Show[theImage, circularSelector], 
    Dynamic @ Column @
      With[{
        xRange = {center[[1]] - radius, center[[1]] + radius},
        yRange = ImageDimensions[theImage][[2]] - center[[2]] + {-radius, +radius}},
         {xRange,
          yRange, 
          ImageTake[theImage, yRange, xRange]}]}]],
 {{center, {184, 126}}, {radius, radius}, ImageDimensions@theImage - radius, 1, 
  Locator, Appearance -> None},
 {{radius, 50}, 2, 100, 1, Appearance -> "Labeled"},
 {{theImage, ExampleData[{"TestImage", "Mandrill"}]}, None}]

Mathematica graphics

(For the None specificiation, see this question.)

$\endgroup$
2
  • $\begingroup$ Thanks! This one also works. Thanks for the explanation, it clarifies the issue a lot. Coding well for Manipulate clearly requires a good understanding of all these (somewhat obscure) details. Now, how robust is this method? To enhance the code I need to operate on the data from the selection before showing it and this will clearly involve introducing several new variables and actions on them. Would that break this "With" approach? For instance, if I was to simply define "selectedRegion = ImageTake[theImage, yRange, xRange]" and do a simple opertation on it, where would that be added? Thanks! $\endgroup$
    – respin4
    Commented Aug 2, 2013 at 15:39
  • $\begingroup$ @respin4 Sometimes With is inconvenient because of scoping issues. There is also the Manipulate option TrackedSymbols :> {...} (related questions). Put the variables you need to be tracked in there, and leave selectedRegion out. Then perhaps the program can be structured thus: Manipulate[Column[{Dynamic[<<selector code>>], Dynamic[selectedRegion = ImageTake[..]; <<processed image code>>]}], vars, opts]. $\endgroup$
    – Michael E2
    Commented Aug 2, 2013 at 17:08
3
$\begingroup$

I have to focus to answer why your way is slow but I just get up :)

Meanwhile I can offer a smooth solution:

image = ExampleData[{"TestImage", "Lena"}];
{w, h} = ImageDimensions[image];
{cx, cy} = {w, h}/2;


Manipulate[
     Column[{
             LocatorPane[Dynamic@center,
                         Show[image, circularSelector]
                        , {{0, 0}, {w, h}, {1, 1}}, Appearance -> None],
             Dynamic@Image[ImageTake[image, rows, cols], ImageSize -> 100] 
           }],
         {{radius, 50}, 2, 100, 1, Appearance -> "Labeled"},
         {selector, None},
         {cols, None},
         {rows, None},
         Initialization :> (
               cols := {center[[1]] - radius, center[[1]] + radius};
               rows := {h - center[[2]] - radius, h - center[[2]] + radius};
               circularSelector := Graphics[{Yellow, Dynamic[Circle[center, radius]]}];
                           )
         ]

enter image description here

$\endgroup$
2
  • $\begingroup$ That works! Thanks. I knew there was lag, and I knew it was an artifact of the code! Now... dare to try to explain why this one doesn't lag? I'm sure I (and MANY) will run into this subtleties often. You have the image and circle as a background in the Locator Pane but I don't think that's it (I believe I tried that). $\endgroup$
    – respin4
    Commented Aug 2, 2013 at 7:46
  • $\begingroup$ @respin4 I think LocatorPane insted of making locator via `Manipulate is the main differece which makes it faster, but still, I'm not so sure why. $\endgroup$
    – Kuba
    Commented Aug 2, 2013 at 9:00
1
$\begingroup$

I can find no slow response after rewriting your code as a simple Manipulate with no explicit calls to Dyanamic. I did find some bugs in your use of ImageTake, which I have fixed. Also, I have localized some of the variables (that's the purpose of the added specifications for selector, rows, and cols).

image.png

{w, h} = ImageDimensions[image];
{cx, cy} = {w, h}/2;

Manipulate[
  selector = Graphics[{Yellow, Thick, Circle[center, radius]}];
  Column[{Show[image, selector], box[center]}],
  {{center, {cx, cy}}, {0, 0}, {w, h}, 1, Locator, Appearance -> None},
  {{radius, 50}, 2, 100, 1, Appearance -> "Labeled"},
  {selector, None},
  TrackedSymbols -> {center, radius},
  Initialization :> (
    box[{cx_, cy_}] :=
      Module[{rows, cols},
        cols = {Max[0, cx - radius], Min[w, cx + radius]};
        rows = {Max[0, h - cy - radius], Min[h, h - cy + radius]};
        ImageTake[image, rows, cols]]
   )]

manipulate.png

Edit

I have edited my code to correct the behavior of TakeImage when a selection is made near the borders of the image. I have also included some of performance enhancements from other answers. With these changes, on my system, the selector circle will follow the mouse fairly smoothly even for an image of size 1200 x 768.

$\endgroup$
3
  • $\begingroup$ This is weird. I'm running your code and the circle clearly lags behind the mouse. I tested in two machines, different OSes. Removing your ImageTake line AND adding Dynamic to the definition of Circle, cols and rows is extremely snappy.... butn then... I can't show the selection. $\endgroup$
    – respin4
    Commented Aug 2, 2013 at 3:31
  • $\begingroup$ It is quite sensitive to image size and system performance. Are you aware that you don't have to drag the locator circle; you can just click on the point in the image where you want it to go. This will always be fast, because in that case Mathematica doesn't need to redraw the full image and the selected image repeatedly. $\endgroup$
    – m_goldberg
    Commented Aug 2, 2013 at 4:32
  • $\begingroup$ Yes. That's my plan B, to eliminate dragging completely. The code I'm asking help for is a simplified version of my real code, which actually does some analysis on the data the image is based from, and the lag there is extreme. This example kind of shows that the lag is more due to poor coding (those Dynamic DO make dragging the circle snappy). I was hoping that solving this simplified version would shed some light on how to properly implement the dragging. $\endgroup$
    – respin4
    Commented Aug 2, 2013 at 4:57

Your Answer

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

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