How can I make colormap operate faster

3 views (last 30 days)
I have a plot of lots of square polygons, about 28,000 oif them. They are coloured using about 500 colours. I index the colours using a colormap so that I can change the palette of colours without redrawing the whole thing but it still takes about 4 seconds to change the colormap. It seems like it should just be rewriting a table in the graphics card so I don't know why it takes to long. My hunch is that what takes the time is the drawnow command which I need to use in order to see the change.
Is there any way to speed it up?
Maybe bypass a lot of the checks that drawnow does before showing the recoloured plot?
  2 Comments
Matt J
Matt J on 21 Jan 2024
Wouldn't it make more sense to plot this as an image? Your squares must be a few pixels in size at most.
Michael
Michael on 22 Jan 2024
I might be able to get this to work but the squares are actually much higher resolution than the on screen image. See my answer to @Benjamin Kraus.

Sign in to comment.

Accepted Answer

Benjamin Kraus
Benjamin Kraus on 22 Jan 2024
Your guess about the behavior of the colormap command is incorrect. When you change the colormap, each individual rectangle is recalculated and resent to the graphics card.
I like @Matt J's suggestion to use an image instead, assuming that each square is fit together in a grid like pattern.
If the squares are not connected to one another (like an image) you are going to find substantially better performance if you use a single patch to draw all the rectangles.
For example:
% Coordinates for the lower-left of 1000 random squares.
n = 1000;
x = rand(1,n)*30;
y = rand(1,n)*30;
% Create the vertices for the four corners of 1000 1x1 squares.
xv = x + [0;0;1;1];
yv = y + [0;1;1;0];
v = [xv(:) yv(:)];
% Each "face" of the patch is a single square.
f = reshape(1:height(v),4,[])';
% Pick a random index for the color for each square.
c = randi(256,numel(x),1);
% Create a single patch to represent all the squares.
patch('Vertices',v,'Faces',f,'FaceVertexCData',c,'CDataMapping','direct','FaceColor','flat','EdgeColor','none');
  6 Comments
Michael
Michael on 22 Jan 2024
That is the plot. The rectangles are not filled and because they need to have a precise line thickness I actually was drawing the circumference lines as filled patches. At the same time as I draw them on screen I output the polygons to an SVG file which I then convert to a high resolution PDF or SVG for printing. This is just one part of a larger image. I am using this to test the palettes and I am drawing the rectangles bigger than they will actually be because it takes so long to draw if they are too small.
@Benjamin Kraus, I took your advice and instead of drawing each patch I now add it to a big patch. Now switching the palette takes only 0.17 seconds, so thanks! The downside is that it take 3 minutes to draw instead of 25 seconds. But it's a good tradeoff.
BTW I preallocate 200,000 faces and 600,000 vertices and make the faces all NANs initially and change them as I add the new patches. This could be slowing things up in the drawing.stage. Is there a faster way
function addpatch(verts,faces,colour)
persistent bigpatch pverts pfaces pcolours lastvert lastface
maxnFaces=200000;
if colour<0
% close the big patch cos we are finished
pverts=pverts(1:lastvert,:);
pfaces=pfaces(1:lastface,:);
pcolours=pcolours(1:lastvert,:);
fprintf('Closing. Big patch has %d vertices and %d faces\n',lastvert,lastface);
bigpatch.Faces=pfaces;
bigpatch.Vertices=pverts;
bigpatch.Vertices=pverts;
bigpatch.FaceVertexCData=pcolours;
pverts=[]; % free up some memory
pfaces=[];
pcolours=[];
return
end
if isempty(pverts) || ~isgraphics(bigpatch)
% initialise the big patch
pverts=zeros(maxnFaces*3,2);
pcolours=zeros(maxnFaces*3,1);
pfaces=nan(maxnFaces,4);
pcolours=ones(maxnFaces*3,1);
pfaces(1:size(faces,1),:)=faces;
pverts(1:size(verts,1),:)=verts;
pcolours(1:size(verts,1),:)=colour;
bigpatch=patch('Vertices',pverts,'Faces',pfaces,'FaceVertexCData',pcolours,'CDataMapping','direct','FaceColor','flat','EdgeColor','none');
lastface=size(faces,1);
lastvert=size(verts,1);
else
% add the new verts/faces to the big patch
pfaces(lastface+1:lastface+size(faces,1),:)=faces+lastvert;
pverts(lastvert+1:lastvert+size(verts,1),:)=verts;
pcolours(lastvert+1:lastvert+size(verts,1),:)=colour;
bigpatch.Faces=pfaces;
bigpatch.Vertices=pverts;
bigpatch.FaceVertexCData=pcolours;
lastface=lastface+size(faces,1);
lastvert=lastvert+size(verts,1);
end
if lastvert>3*maxnFaces
warning('Max number of vertices exceeded')
lastvert=1;
end
if lastface>maxnFaces
warning('Max number of faces exceeded')
lastface=1;
end
end
Michael
Benjamin Kraus
Benjamin Kraus on 22 Jan 2024
@Michael, you said "Now switching the palette takes only 0.17 seconds, so thanks! The downside is that it take 3 minutes to draw instead of 25 seconds."
Can you clarify what you mean by "3 minutes to draw"? If it only takes 0.17 seconds to switch the colormap, then my guess is the 3 minutes is being spent generating the data to populate the patch. This should show-up in the profiler.
I can't run your reproduction code without data, but if you ran the MATLAB Profiler on your code it may identify what is taking the most time, and give you some places to look to optimize.
However, one thing that jumps out at me is it looks like you are creating the patch early, and then updating the data on the patch in each iteration of a loop (I assume your addpatch function is being called in a loop). I would suggest not creating the actual graphics Patch object until you are done collecting all the faces and vertices (and colors). I think that would be a relatively small tweak in your script: Just have your function return pverts, pfaces, and pcolours and then call patch once when the script is done.
I'm guessing here, but I suspect what is happening is that whatever process is calling addpatch is (directly or indirectly) trigging a graphics update (for example, calling drawnow or pause). This will allow you to see the resulting figure getting progressively updated every time you add new data, but it means the patch needs to be redrawn every time you add new data.
  • How frequently is your addpatch script being called?
  • Are you calling drawnow (if so, try replacing it with drawnow limitrate)?

Sign in to comment.

More Answers (0)

Products


Release

R2023b

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!