ScanImage 2018 : Fix rolling frame average calculation

When setting the rolling average to a high value (e.g. 100 frames) the first frame is disproportionately weighted until near all the 100 frames are collected. Due to the inevitably higher noise in a single frame which will often contain very bright pixels, this noise persists and obscures the steadily improving average image for a longer period of time than desired. It seems that the buffer is pre-allocated by duplicating the first frame (in this case 100 times).

To allow the average image to steadily improve in quality, without being obscured by the high shot noise present in the first frame, would it be possible to pre-allocate the buffer with empty images (e.g. NaN) and use the MatLab nanmean() function to do the averaging?

Would it be possible for large displayRollingAverageFactor (say >=100) to run a median filter (e.g. a 3x3 filter) on the first frame before duplicating?

 

When setting the rolling average to a high value (e.g. 100 frames) the first frame is disproportionately weighted until near all the 100 frames are collected. Due to the inevitably higher noise in a single frame which will often contain very bright pixels, this noise persists and obscures the steadily improving average image for a longer period of time than desired. It seems that the buffer is pre-allocated by duplicating the first frame (in this case 100 times).

To allow the average image to steadily improve in quality, without being obscured by the high shot noise present in the first frame, would it be possible to pre-allocate the buffer with empty images (e.g. NaN) and use the MatLab nanmean() function to do the averaging?

Would it be possible for large displayRollingAverageFactor (say >=100) to run a median filter (e.g. a 3x3 filter) on the first frame before duplicating?

 

You are correct that we pre-allocate the frame buffer used for averaging by duplicating the first frame N times.
Unfortunately we can't use the proposed MatlLab nanmean() function. To increase performance we avoid floating point divisions altogether. Instead, we accumulate the frames into a int32 buffer, and multiply the figure CLim by N. This way the graphics hardware takes care of the averaging for us.

If you want to modify this behavior, you will find the code for pre-allocating the buffer in the file +scanimage+components\Display.m. The relevant code section is:

% no stripes in this frame
for i = 2:(obj.displayRollingAverageFactor+1)


obj.rollingStripeDataBuffer

{frameBatchIdx}{i} = newStripe;      % preallocate buffer with first frame


end

% buffer #1 is the averaged data. stripeData is a
% stripeData is a handle class! we need a fresh unique piece of memory for the averaged data to reside in
averagedStripe = copy(newStripe);
averagedStripe.multiplyRoiData(rollAveFactor);
obj.rollingStripeDataBuffer{frameBatchIdx} {1} = averagedStripe; % calculate averaged frame

 

This might reduce the effect of the shot noise, but 1) I'm not sure if it's possible, 2) I'm not certain it would actually help with the visual impression of the rolling average image. Any thoughts you have on feasibility, or likelihood of working would be appreciated.

 

I am not sure if running  a median filter (e.g. a 3x3 filter) on the first frame before duplicating for large displayRollingAverageFactor (say >=100) will improve the visualization quality, but it is certainly possible to implement.

The following code in '+scanimage+components\Display.m' is called when the rolling buffer is reset, so it won't slow down performance during an acquisition.

See the function 'averageStripe' and look for the comment:


%if any buffer element is empty it means buffers
%were just reset. fill all buffers with current
%image data to get the averaging started

Right below this comment, apply the filter to the data:


for chidx = 1:length(newStripe.roiData

{1}.channels)
newStripe.roiData{1}

.imageData

{chidx}{1} = myFilter(newStripe.roiData{1}.imageData{chidx} {1}

);

end