3
votes

I'm running a simulation in MATLAB in which I have a large 3D dataset that changes each time step. I'm trying to visualize the data using a 3D scatter plot with points that take on different locations, sizes, colors, and transparency levels as the simulation proceeds. The size and color information are redundant.

Rendering and rotating the figure in MATLAB is slow and choppy. My computer has a 4 GHz i7-4790 CPU and a NVIDIA GeForce GTX 750 Ti graphics card. I am using Matlab R2016a on Windows 7. I checked my MATLAB OpenGL settings and the hardware support level is full. (Hardware OpenGL is necessary for transparency.) Moreover, I'm monitoring my GPU usage using GPU-Z, and during plotting and rotation, the GPU load peaks at only 25-30%.

Here is my code sample:

load sample_data2
channels_matrix = cat(1, channels{:});
num_channels = length(channels);
channel_lengths = cellfun(@(x) size(x, 1), channels);

figure(1);
for i = 1:num_channels
    g = plot3(channels{i}(:, 1), channels{i}(:, 2), channels{i}(:, 3), 'k');
    set(g, 'LineWidth', 1.5)
    hold on;
    text(channels{i}(1, 1), channels{i}(1, 2), channels{i}(1, 3), num2str(i))
end
caxis([0 1])
colorbar
drawnow

numDivisions = 8;
ptsPerDivision = numel(grid_x)/numDivisions;
T = 1000;
numplotpts = 2E4;
for t = 1:T
    plot_signal = nan(size(grid_x));
    plot_signal(sort(randsample(numel(grid_x), numplotpts))) =...
        sort(rand(numplotpts, 1));
    tic
    for i = 1:numDivisions
        temp = plot_signal(dists_idx((i-1)*ptsPerDivision+1:i*ptsPerDivision));
        yplot = grid_y(dists_idx((i-1)*ptsPerDivision+1:i*ptsPerDivision));
        xplot = grid_x(dists_idx((i-1)*ptsPerDivision+1:i*ptsPerDivision));
        zplot = grid_z(dists_idx((i-1)*ptsPerDivision+1:i*ptsPerDivision));
        if t == 1
            h(i) = scatter3(yplot(~isnan(temp)), xplot(~isnan(temp)),...
                zplot(~isnan(temp)), 50*temp(~isnan(temp)), temp(~isnan(temp)), ...
                'filled', 'MarkerFaceAlpha', exp(-i)^0.25);
        else
            h(i).XData = yplot(~isnan(temp));
            h(i).YData = xplot(~isnan(temp));
            h(i).ZData = zplot(~isnan(temp));
            h(i).SizeData = 50*temp(~isnan(temp));
            h(i).CData = temp(~isnan(temp));
        end
    end
    drawnow
    toc
end

and here is a link to the data. Is there any way to speed up the rendering and make rotation smoother? I noticed that fixing the size of all data points to a single scalar greatly speeds up rendering and rotation. Is it possible to keep the size as it is in the code and still have the figure render and rotate quickly?

Edit: A related question I posted.

2
Does it need to be in real time or can you render it as a movie (.avi) for smoother playback later? - informaton
Real time is not a requirement, but it would be preferred. - Vivek Subramanian
I'm getting about 0.5 second delays between calls in your code (e.g. tic/toc). What are you at right now? - informaton
When I run the code linked above, the toc command outputs an elapsed time of about 9.5 seconds each iteration. If I fix the size data to some scalar, it drops to about 0.7 seconds per iteration. Are further speedups possible? - Vivek Subramanian
I can make the elapsed time drop to 5.5 seconds, but only if I change your code and put the drawnow inside the for loop (which is probably where you want it to see the changes). - informaton

2 Answers

1
votes

It sounds like the timer function is a good place to try next in order to get a sense of your simulation's progression and then making an AVI once you are satisfied with how things look.

MATLAB's got some great documentation for it with a variety of options for consecutive calls are made and the spacing between them. Check out the ExecutionMode and Period properties.

0
votes

I'm not sure this will solve all the problem, but as a first step I would suggest taking all calculations out of the loop that is used for drawing. Here is a suggestion how to do it:

load sample_data2
clf
channels_matrix = cat(1, channels{:});
num_channels = length(channels);
channel_lengths = cellfun(@(x) size(x, 1), channels);

figure(1);
for k = 1:num_channels
    g = plot3(channels{k}(:, 1), channels{k}(:, 2), channels{k}(:, 3), 'k');
    set(g, 'LineWidth', 1.5)
    hold on;
    text(channels{k}(1, 1), channels{k}(1, 2), channels{k}(1, 3), num2str(k))
end
caxis([0 1])
colorbar
drawnow

numDivisions = 8;
ptsPerDivision = numel(grid_x)/numDivisions;
T = 1000;
numplotpts = 2E4;

% -> chnages starts here:

% first loop for creating random indices
plot_signal = nan(size(grid_x));
rand_numplotpts =sort(rand(numplotpts,T),1);
rand_inds = zeros(numplotpts,T);
for t = 1:T % one loop for creating random indices
    rand_inds(:,t) = sort(randperm(numel(grid_x),numplotpts));
end
plot_signal(rand_inds(:,t)) = rand_numplotpts(:,t);

% second loop for drawing the first instance:
for k = 1:numDivisions
    temp = plot_signal(dists_idx((k-1)*ptsPerDivision+1:k*ptsPerDivision));
    yplot = grid_y(dists_idx((k-1)*ptsPerDivision+1:k*ptsPerDivision));
    xplot = grid_x(dists_idx((k-1)*ptsPerDivision+1:k*ptsPerDivision));
    zplot = grid_z(dists_idx((k-1)*ptsPerDivision+1:k*ptsPerDivision));
    h(k) = scatter3(yplot(~isnan(temp)), xplot(~isnan(temp)),...
        zplot(~isnan(temp)), 50*temp(~isnan(temp)), temp(~isnan(temp)), ...
        'filled', 'MarkerFaceAlpha', exp(-k)^0.25);
end

% third loop to calculate all timesteps:
[X,Y,Z,S,C] = deal(nan(size(temp,1),numDivisions,T));
for t = 2:T 
    plot_signal(rand_inds(:,t)) = rand_numplotpts(:,t);
    for k = 1:numDivisions
        temp = plot_signal(dists_idx((k-1)*ptsPerDivision+1:k*ptsPerDivision));
        yplot = grid_y(dists_idx((k-1)*ptsPerDivision+1:k*ptsPerDivision));
        xplot = grid_x(dists_idx((k-1)*ptsPerDivision+1:k*ptsPerDivision));
        zplot = grid_z(dists_idx((k-1)*ptsPerDivision+1:k*ptsPerDivision));
        non_nan_inds = ~isnan(temp);
        inds = 1:sum(non_nan_inds);
        X(inds,k,t) = yplot(non_nan_inds);
        Y(inds,k,t) = xplot(non_nan_inds);
        Z(inds,k,t) = zplot(non_nan_inds);
        S(inds,k,t) = 50*temp(non_nan_inds);
        C(inds,k,t) = temp(non_nan_inds);
    end
end

% forth loop to draw all data:
for t = 2:T
    for k = 1:numDivisions
        h(k).XData = Y(:,k,t);
        h(k).YData = X(:,k,t);
        h(k).ZData = Z(:,k,t);
        h(k).SizeData = S(:,k,t);
        h(k).CData = C(:,k,t);
        drawnow
    end
end