imshow with a slider object is blocking my Button Down function
36 ビュー (過去 30 日間)
古いコメントを表示
I have developed an app that reads all the frames of the video and allows the user to scroll through all the frames of a video with a line that I want to place at the centre of the frame during startup. Subsequently, it will become a user movable line. I initially created a ButtonDown callback on the Axes but it only works on the first image before I use the slider. After that, it doesn't respond to any clicks.
I then tried a ButtonDown callback on the figure and it doesn't work on the first image. It only works after I use the slider object and scroll through the frames. Can anyone figure out what the slider is doing to make my callbacks not work throughout the user interaction?
Also, I can't seem to get rid of the first line that is created by the app during start up. Both callbacks will create a second line when a click is detected but subsequently that second line will be movable while the first line just stays in place.
The last issue I have is that my app takes ages to load when I click the 'play' button. At least 30 seconds-1 minute to show the first frame and all the other UI objects in the function. Is there any way I can make my app more efficient?
Here are my codes:
P.S. I use either the UIFigure ButtonDown call back or the UIAxes ButtonDown callback when I run my app and not at the same time.
function play(app, event)
videoReader = VideoReader('C:/Capstone/Data/Patient/Left Diaphragm Expiration.mp4');
app.AllFrames=zeros(app.h,app.w,app.numFrames);
app.h=videoReader.height;
app.w=videoReader.width;
app.numFrames=videoReader.NumFrames;
app.framerate= videoReader.FrameRate;
app.Slider.Limits=[1,app.numFrames];
app.Slider.MinorTicks=[];
firstframe=read(videoReader,1);
for a=1:app.numFrames
frame=read(videoReader,a);
frame=im2gray(frame);
app.AllFrames(:,:,a)=frame;
end
app.framehandle=imshow(firstframe,'Parent',app.UIAxes);
set(app.framehandle,'HitTest','off');
set(app.framehandle, 'PickableParts','all');
hold (app.UIAxes,"on");
app.Mline= xline(app.UIAxes,(app.w/2),'Color','red');
app.Mline.LineWidth=2;
function changingValue(app, event)
value = floor(app.Slider.Value);
app.framehandle=imshow(app.AllFrames(:,:,value),[0,255],'Parent',app.UIAxes)
set(app.framehandle,'HitTest','off');
set(app.framehandle, 'PickableParts','all');
hold (app.UIAxes,"on");
% Value changed function: Slider
function valueChanged(app, event)
value = floor(app.Slider.Value);
app.framehandle=imshow(app.AllFrames(:,:,value),[0,255],'Parent',app.UIAxes)
set(app.framehandle,'HitTest','off');
set(app.framehandle, 'PickableParts','all');
hold (app.UIAxes,"on");
% Button down function: UIFigure
function UIFigureButtonDown(app, event)
delete(app.Mline)
app.Mline=xline(app.UIAxes,app.UIAxes.CurrentPoint(1),'Color','red');
app.Mline.LineWidth=2;
% Button down function: UIAxes
function UIAxesButtonDown(app, event)
app.Mline=xline(app.UIAxes,app.UIAxes.CurrentPoint(1),'Color','red');
app.Mline.LineWidth=2;
0 件のコメント
採用された回答
Voss
2025 年 1 月 15 日 19:14
編集済み: Voss
2025 年 1 月 15 日 19:17
(It's difficult to reproduce the behavior you describe without having the .mlapp file and necessary data files.)
Most likely, part of the problem with the interactivity is that imshow() makes the axes invisible, so if the axes has its PickableParts property set to 'visible' (the default), then mouse clicks on the axes will not execute the axes ButtonDownFcn.
To avoid that problem, I recommend not using imshow, certainly not in the slider callbacks. Create the image object (app.framehandle) once during initialization using the image() function, and update its CData as necessary in the slider callbacks or wherever else.
(Similarly, avoid calling xline() in the button down function(s); you can create the ConstantLine object once during initialization and update its properties as/when necessary.)
Alternatively, you can keep using imshow(), but set app.UIAxes.PickableParts='all'; once during initialization.
As for the question about why the app takes so long when the play button is clicked, I'd guess that reading all the frames of the video is what takes the most time. Instead, you can read the current frame only. Do this by adding a new app property that is index of the current frame, initialize that property to 1, update its value in the slider callbacks (and wherever else is necessary), and use that property to update the image object's CData whenever necessary. This approach would take less time in the play function but more time in the slider callbacks vs the current approach, but maybe the extra time to read one frame in the callbacks is not too bad, I don't know - try it and see.
6 件のコメント
Voss
2025 年 1 月 23 日 18:23
Thank you for the app code and video file.
The reason that the axes doesn't capture mouse clicks after the slider is interacted with is that the slider callback (either one - they are identical) creates a new image object (via imshow) in the axes, and that image object captures mouse clicks before the axes has a chance to.
The easiest way to fix that is to set the image's 'HitTest' property to 'off' so that the image doesn't capture clicks and they pass down through to the axes. But also, the axes 'PickableParts' property needs to be 'all' instead of 'visible' since imshow makes the axes invisible (or you could set the axes visible again but I assume that's not what's wanted since imshow is used).
I'm attaching a modiied version of the app with those changes. This version reads only the first 10 frames of the video, to save time. Comment out line 35 to revert back to reading all frames.
その他の回答 (0 件)
参考
カテゴリ
Help Center および File Exchange で Develop uifigure-Based Apps についてさらに検索
Community Treasure Hunt
Find the treasures in MATLAB Central and discover how the community can help you!
Start Hunting!