Zapit-Optostim / zapit

General purpose optostimulation system
GNU Lesser General Public License v2.1
6 stars 1 forks source link

Camera frame update can hog MATLAB and massively slow down some stuff #126

Open raacampbell opened 1 year ago

raacampbell commented 1 year ago

A lot of operations, like opening folders, fitting models, etc, run orders of magnitude slower because somehow they are being bogged down by the camera updating. Only certain things seem badly affected. It shouldn't really be like this but at present a bunch of Zapit operations that seem affected will shut down the camera temporarily whilst they complete before firing it back up again. We should work out why this happens and fix it. I'm sure we can do better than this. Maybe there should be a timer rather than a listener on the new frame property.

For sure something wrong is happening because if you ctrl-c at the CLI you abort the image acquisition and the following error pops up:

Operation terminated by user during imaqdevice/flushdata

In zapit.hardware.camera/flushdata (line 149)
                flushdata(obj.vid)

In zapit.pointer/storeLastFrame (line 26)
    obj.cam.flushdata

In zapit.pointer>@(varargin)obj.storeLastFrame(varargin{:}) (line 126)
                obj.cam.vid.FramesAcquiredFcn = @obj.storeLastFrame;

Warning: The FramesAcquiredFcn callback is being disabled.
To enable the callback, set the FramesAcquiredFcn property. 

You might also get:

Operation terminated by user during imaqdevice/peekdata

In zapit.hardware.camera/getLastFrame (line 156)
                lastFrame=squeeze(peekdata(obj.vid,1));

In zapit.pointer/storeLastFrame (line 16)
    tmp = obj.cam.getLastFrame;

In zapit.pointer>@(varargin)obj.storeLastFrame(varargin{:}) (line 126)
                obj.cam.vid.FramesAcquiredFcn = @obj.storeLastFrame;

Warning: The FramesAcquiredFcn callback is being disabled.
To enable the callback, set the FramesAcquiredFcn property. 
Warning: An error occurred while drawing the scene: Error in json_scenetree: Could not find node in replaceChild 

Exactly what you see must depend on what it was doing when ctrl-c is hit.

Other times it will start filling the screen with:

Warning: An error occurred while drawing the scene: Error in json_scenetree: Could not find node in replaceChild 

And you have to restart it.

raacampbell commented 2 months ago

To test this I start Zapit and look at the resource meter on Windows. It's 80% CPU use. Drops to 15% on hZP.cam.stopVideo. Let's look at the profiler. Seems like the line that is taking up a lot of CPU usage in my code is lastFrame=squeeze(peekdata(obj.vid,1)); in the method getLastFrame of the camera class. This is called by the callback pointer.storeLastFrame, which runs whenever a frame is available.

Is there a more efficient way? There must be. e.g. imaqtool runs preview and this does not hose the CPU. The idea might be something like this:

% Create a GUI figure window
fig = figure('Name', 'Camera Feed GUI');

% Create axes for displaying camera feed
ax = axes('Parent', fig);

% Create video input object
vid = videoinput('winvideo', 1, 'MJPG_1280x720'); % Adjust parameters as needed

% Configure video input object
vid.FramesPerTrigger = 1; % Grab one frame per trigger
vid.TriggerRepeat = inf; % Continue acquiring frames until stopped
% Adjust other properties such as resolution, frame rate, etc.

% Start preview
preview(vid, ax);
raacampbell commented 2 months ago

It is described here: https://uk.mathworks.com/help/imaq/preview-live-data-from-image-acquisition-device.html#f11-76067

raacampbell commented 2 months ago

I test and find that with the original frame update system involving the listener, it is laggy confirming the sample calib. With the preview approach the lag has gone. It still uses a lot of CPU but maybe a fifth less than the current approach. The initial issue, however, is that when I implement it this way the axes are wiped and for some reason I can not start and stop the feed. So have to figure out those things.

Edit it's just:

>> stoppreview(hZP.cam.vid)

>> preview(hZP.cam.vid)

To reverse axes:

OUT.hAx.XDir='reverse'
raacampbell commented 2 months ago

The dev_newpreview branch attempted to do the above, but it became very complicated because sample calibration was not honouring the image scale and caused chaos. So instead I switched to restricting the camera to 20 FPS (from 100) and the CPU is about 20 to 30% less after implementing this. 3d9bdaaedcf109d346f48b74e36787118fbe5e50 If we continue to have issues with responsiveness, we can try doing the update in a separate thread or adding a button to disable the camera.