2
votes

I am working on a code to get positions of multiple draggable rectangles inputted by the user. The code is working completely fine. However after pressing on the pushbutton, the code doesn't move forward until you draw one more rectangle. The problem I believe is pos = uint32(wait(h)); statement waits indefinitely for another position and pushbutton handle is taken into consideration only after drawing another rectangle. I do need the wait (h) statement as getposition function doesn't work well for me. I really appreciate any help that you can provide, following is the part of my code.

Code:

im_des=rgb2gray('image_name.jpg');
ButtonHandle = uicontrol('style','push','String', 'STOP',...
'callback','set(gcbo,''userdata'',1,''string'',''DONE!'')', ...
'userdata',0) ;

while(1)
  if get(ButtonHandle,'userdata')
        break ;
  end

  h = imrect;
  pos = uint32(wait(h));

  if ~isempty(pos)    
      rectangle('Position', pos, 'LineWidth',1, 'EdgeColor','k');
      for i=1:4
      counter=counter+1;
      array (counter) = pos(i); 
      end
      delete(h);
   end
 end
1

1 Answers

1
votes

I've figured out the reason for needing to draw an extra rectangle after pushing the Stop button and I've found a couple of possible solutions to fix the problem.

In few words, the problem is that ... MatLab is faster than you.

In more words, this is what happen, step by step:

Let's suppose you want to draw just one rectangle, then push the "Stop" pushbutton to stop drawing.

The condition in the if statement at the beginning of the while loop is not verified (you have not pushed the button yet), so the the loop continues.

if get(ButtonHandle,'userdata')
   break ;
end

The imrect statement is then executed

 h = imrect;

it starts the rectangle drawing process: you can draw the rectangle, move it, resize it ans so on.

As soon as you start drawing the next statement is executed

pos = uint32(wait(h));

the wait function blocks the MatLab command line until you double-click on the rectangle to exit from the drawing mode

If you've drawn a valid rectangle, the next statement draws the actual rectangle, stores the rectangle's position in "array" and delete the handle to the imrect object

if ~isempty(pos)    
   rectangle('Position', pos, 'LineWidth',1, 'EdgeColor','k');
   for i=1:4
      counter=counter+1;
      array (counter) = pos(i); 
   end
   delete(h);
end

At this point you you decide to "Stop" adding rectangles and push the "Stop" pushbutton.

Here is the point at which MatLab is ... faster than you.

While you are moving the muose's cursor to reach the pushbutton, the executioin of your script continues and a new iteration of the while loop starts.

Again the first statement is executed

if get(ButtonHandle,'userdata')

if you was not so fast (and you can not be) to reach the "Stop" pushbutton and push it before the new iteration starts, the if condition is not verified, therefore the imrect sequence is executed

h = imrect;
pos = uint32(wait(h));

as your intent was to draw another rectangle.

This is the "additional" rectangle that "seeem" be required to actually stop the script after pushing the "Stop" pushbutton.

While the MatLab command line is blocked by the wait function, the callback listner is not, so you can push the pusshbutton and the value of userdata is set to 1 nevertheless, the drawing process has been activated.

Therefore you need to draw the "additional" rectangle, double-click to exit from wait so that anoter iteration of the `while loop can start.

At this point, eventually, the if condition is verified and the break statement stops the execution of the while loop.

This is what happens.

I've found two possible solutions: the first one allows keeping your script "almost" as it is, the second one implies to chenge the GUI concept.

The first solution allows you ... being faster then MatLab.

If you insert a pause statement after the call to wait (e. g. pause(3)) you will have 3 seconds to reach and push "Stop" before a new iteration starts.

This allows che callback setting userdata to be set to 1 and make the if condition at the beginning of the while loop being verified stopping the executin of the script without having to draw any additional rectangle.

The second solution consists in changing the behaviour of the GUI:

You can remove the while loop and change the pushbutton callback so that it takes care of calling imrect and plot the rectangle (in practical terms, you have to move the code inside the while loop in the pushbutton cllback.

This way the behaviour of the GUI will be: any time you want to add a rectangle, you have to push the pushbutton

Here after, you can find the script of both the solutions.

Notice, I've made few additional modification to your additional code, in order to make it running:

I've removed the uint32 cast: having not a picture in the axes, the default axes limits are [0 1], if you convert pos to unsigned 32-bit integer, pos will be [ 0 1 0 0]

Also I've removed the for loop to store pos in array: it is not actually needed.

Solution #1

% Commented since in the code excerpt is not used
% im_des=rgb2gray('pdb_img_1.jpg');
ButtonHandle = uicontrol('style','push','String', 'STOP',...
   'callback','set(gcbo,''userdata'',1,''string'',''DONE!'')', ...
   'userdata',0) ;

% Added initialization of "array" and array_32" arrays
%    array_32: stores the rectangle pos as uint32
%    array: stores the rectangle pos as double (added to make the script
%    running without an image on the axes)
array=[];
array_32=[];
%
while(1)
   if get(ButtonHandle,'userdata')
      break ;
   end

   % Commented type cast (default axix limit are [0 1] therefore
   %    pos=[ 0 1 0 0]
   h = imrect;
   %    pos = uint32(wait(h));
   pos = wait(h);

   % added message display and "pause" statement to allow pressing "Stop"
   % button before drawing next rectangle
   disp('Press STOP within 3 sec. to stop')
   pause(3)

   if ~isempty(pos)
      rectangle('Position', pos, 'LineWidth',1, 'EdgeColor','k')
      % if needed the edge color of each rectangle can be different
      %       rectangle('Position', pos, 'LineWidth',1,'EdgeColor',[rand(3,1)])
      %
      % Commented "for loop" to store rectangle pos since it is not needed
      %       for i=1:4
      %       counter=counter+1
      % "array" is built as (N x 4) matrix, each row contains the "pos" of
      % a rectangle (remove ";" to store it as a (1 x N) array
      array = [array ; pos]
      array_32 = [array_32 ; uint32(pos)]
      %       end

      delete(h)

   end

end

Solution #2 Main script

array=[];
axes
ButtonHandle = uicontrol('style','push','String', 'Add rect',...
   'callback','tmp=add_rect;array=[array;tmp]');

Pushbutton callback

function pos=add_rect()
h = imrect;
pos = wait(h);
rectangle('Position', pos, 'LineWidth',1, 'EdgeColor','k')
delete(h)

Hope this helps.