Posts Tagged ‘visualization’

Port and Modification of Pascal Frey’s medit software for tet-mesh visualization on github

Wednesday, April 15th, 2015

I’m moving my patched and improved version of Pascal Frey’s medit software from libigl/external to its own github repository. The new version still depends on libigl and AntTweakbar. There’s a matlab wrapper for it in my gptoolbox.

This tool has been essential to my research on tetrahedral meshes. There are more advanced tet-mesh, 3d-slicing visualization tools around, but this one is straightforward and open-source. I’ve made some modifications to make it easier to visualization data and make more complicated slices (hot-dog view!). The input is the same .mesh tet mesh format used by TetGen.

medit screen capture

Slice through a tet-mesh in matlab

Monday, February 2nd, 2015

Matlab has only rudimentary support for visualizing data defined over a tetrahedral mesh. I use usually external calls to medit. Today I needed to implement slicing through a tet mesh for a different application and noticed that it’s also useful for visualization. I add slice_tets.m to gptoolbox. The slicing works in an obvious, yet vectorized way. The way a plane passes through a tet (if at all) can be described by the number of vertices on either side of the plane: 0-4, 1-3, 2-2, 3-1, 4-0. The 0-4 and 4-0 cases don’t add anything to the slice and are quickly ignored. The 1-3 and 3-1 cases add a single triangle to the slice and if we categorize the vertices with respect to their signed distance to the plane, then we only need to implement the 1-3 case and reverse the signs to handle the 3-1 case. Finally the 2-2 case adds a quad (i.e. two triangles) to the slice, and we should be careful to flip the orientation depending on which vertices end up on either side.

My vectorized matlab identifies the case each tet belongs to and computes triangles or split quads for all of that case in unison.

The slicing is actually pretty fast. For a 600,000 tetrahedra mesh, slicing through the middle takes ~0.11 secs (~9fps). With a little more time spent, I may also keep track of the linear interpolation coordinates so that I can visualize a function defined over the tet-mesh onto the slicing plane.

Here I’m showing a slice through a Poisson equation inside this reclining knight:

knight poisson equation slice

Burying the rainbow

Monday, October 13th, 2014

I come to bury Caesar, not to praise him.

MATLAB’s newest version has finally tossed the jet default colormap for parula.

matlab colormap jet parula

The visualization community has long been warning against the use of “rainbow” colormaps like jet. Today I looked around for some of the papers articulating why.

The well-cited, “Rainbow Color Map (Still) Considered Harmful” by David Borland and Russell M. Taylor II presents synthetic counterexamples, highlighting why the “Rainbow” colormap should be avoided.

The first counterexample cites the book Information Visualization: Perception for Design by C. Ware. From the text in [Borland and Taylor], it seems this book contains details of a perceptual study in which subjects correctly order grayscale paint chips consistently but often fail to order “rainbow” paint chips (presumably by hue).

colormap chips

Borland and Taylor go on to write,

Some even put them in alphabetical order. To put them in the so-called correct order, most people must remember Roy G. Biv (red, orange, yellow, green, blue, indigo, violet), or some other mnemonic representation of the order of colors in the rainbow.

I was a bit surprised. I’m quite accustomed to matlab’s jet and would have never chosen the order red, green, yellow, blue. I could admit that perhaps I’m perceptually faster at ordering grayscale colors that hue-based spectra. So I went digging in the Ware book to find the original study to find more details.

In Chapter 4, Application 3 “Application 3: Color Sequences for Data Maps” Ware cites an earlier paper of his called “Color Sequences for Univariate Maps: Theory, Experiments, and Principles”. This paper contains three small scale studies providing evidence against chromatic sequences: one quantitative study and two qualitative survey-style studies. The studies and results are aimed specifically at a pseudocoloring’s ability to represent a height field or surface.

The other cited study is “Face-based Luminance Matching for Perceptual Colormap Generation” by [Kindlmann et al.]. This paper seems to contain a user study regarding their proposed system for luminance calibration and doesn’t seem directly related to Ware’s claim that “Experimental studies have confirmed that grayscale maps are much better for form perception.”

Their more papers cited in “How NOT to Lie with Visualization” by [Rogowitz and Treinish] backing the finding that the “[rainbow hue colormap] produces several well-documented artifacts”. Some of these artifacts are convincing without a perceptual study: e.g. focus is drawn to bright, yellow regions of this MRI while subtleties are drown out in the large green span in the spectrum:

colormap chips

OK. But I’m still left wondering about this paint chip study.

Question: Is there no large scale study quantitatively confirming the perceptual limitations of the rainbow colormap?

Maybe now that Matlab’s changed its default everybody (including probably me) will just stop using it out of laziness and we won’t need to worry about verifying these claims empirically.

I continued through “Rainbow Color Map (Still) Considered Harmful” by David Borland and Russell M. Taylor II to the second counterexample. Here an scalar field varying frequency is mapped to a grayscale colormap and a rainbow” one:

frequency and color maps

This example immediately puzzled me. Why is so much lost in the green? I would not expect to see this in matlab using either its jet or hsv rainbow colormaps.

Indeed, this counterexample seems particular the choice of “rainbow” colormap.

frequency colormap visualization comparison

As a testament to the simplicity of the grayscale colormap, we can directly recover the underlying scalar field by taking the intensity value of the grayscale image in Fig 2 left. Using this, we can recover the colormap used for the “rainbow” visualization from their Fig 2 right. Then, to verify I reproduce the input in matlab above.

Now, I can easily try other colormaps like jet and hsv:

frequency colormap visualization comparison

Neither suffer from the washout of the original “rainbow” visualization. Though there’s plenty of room to argue about whether too much attention is drawn from the vertical stripes to the extrema values on the bottom.

Looking at just this scalar field makes it difficult to judge the colormaps being used. Here I’ve taken the domain of scalar values (roughly 0 to 250) linearly and applied colormap:

frequency colormap comparison

The top right shows the recovered “rainbow” colormap. Notice the wide range of green values and lack of purple values.

Figure 3 in the same paper shows a “rainbow” colormap with a wider gamut (left):

frequency colormap comparison extras

The recovered colormap seems is well approximated by a truncated versioned of this wider colormap (right).

If we apply the full “rainbow” from Fig 3 to the original data we get a bit more reasonable visualization (left):

frequency colormap comparison extras

whereas the truncated colormap produces a washed out visualization (right) similar to the original one in Figure 2.

It seems rather obvious that using a colormap with such a wide span covered by essentially the same green will be troublesome. Here’re the spans in the recovered colormap, the full “rainbow”, and the truncated rainbow which are within 30.72 RGB8 units from the central “green” value:

frequency green span

The jet and hsv colormaps don’t contain this same green. hsv seems to suffer from a similar problem, but jet to a much lesser degree (more cyan-ish colors). This finding seems to agree with the [Kindlmann et al.] idea that there are “good” rainbows and “bad” rainbows. Though it doesn’t quite count as evidence that all rainbows are bad, (even if we all know that they must be).

To come full circle, here’s that dataset visualized with MATLAB’s new parula colormap:

frequency parula

I’m sold on it. I agree with Robert Kosara’s explanation why rainbows are popular:

Given the issues, why are the rainbow colormap and its variants so popular? I think the answer is quite simple: it’s attractive. Using a single hue to show the data would be reasonably effective, but much less interesting to look at.

parula seems to offer a decent trade-off. I don’t think I’ll mind staring it from now on. Perhaps, I’ll still silently long to see those other hues of the rainbow.

MATLAB2014b features anti-aliasing

Monday, October 13th, 2014

Finally. I’m pretty happy about the results:

cheburashka with matlab anti-aliasing

[V,F] = load_mesh('/usr/local/igl/libigl/examples/shared/cheburashka.off');
AO = ambient_occlusion(V,F,V,per_vertex_normals(V,F),1000);
t = tsurf(F,V,fphong,'EdgeColor','none');
C = squeeze(ind2rgb(floor(matrixnormalize(t.FaceVertexCData)*size(colormap,1))+1,colormap));
t.FaceVertexCData = bsxfun(@times,C,1-AO);
t.SpecularStrength = 0.1;
t.DiffuseStrength = 0.1;
t.AmbientStrength = 0.8;
l = light('Position',[1 1 100],'Style','infinite');
l2 = light('Position',[1 -100 1],'Style','infinite');
set(gca,'XTickLabel',[],'YTickLabel',[],'ZTickLabel',[],'Color',[0.94 0.94 0.94]);
set(gcf,'Color','w');

And to spin the camera around:

axis equal
axis vis3d;
camproj('persp');
for f = 1:numel(T)
  t = T(f);
  view(-cos(t*2*pi)*45,sin(t*2*pi)*45+45);
  drawnow;
  frame = getframe(gcf);
  [SIf,cm] = rgb2ind(frame.cdata,256);
  if f == 1
    imwrite(SIf,cm,filename,'Loop',Inf,'Delay',0);
  else
    imwrite(SIf,cm, filename,'WriteMode','append','Delay',0);
  end
end

With the awesome but now obsolete myaa.m hacked anti-aliasing, creating this gif would have taken many minutes. This runs in real time.

Append a progress bar to a video or image sequence and show frame reordering

Thursday, December 19th, 2013

For a project we’re reordering the frames of a video. Here’s how to append a progress bar that visualizes where the frames are coming from in the original video.

Load in a video, here I use a video object:

video = VideoReader('rhinos.avi');

Let’s define a vector of indices I to be a remapping of the video frames so that frame f of the new video will be frame I(f) of the original video. We’ll use the original ordering

I = 1:video.NumberOfFrames;

or a random ordering:

I = randperm(video.NumberOfFrames);

Then append a progress bar and store the composite as an Height-by-Width-by-3-by-NumberOfFrames image sequence matrix S:

bar_w = video.Width;
bar_h = 20;
S = zeros(video.Height+bar_h,video.Width,3,video.NumberOfFrames);
for f = 1:video.NumberOfFrames
  S(1:video.Height,:,:,f) = read(video,f);
  col_If = round((I(f)-1)/(video.NumberOfFrames-1) * (bar_w-1)) + 1;
  col_f = round((f-1)/(video.NumberOfFrames-1) * (bar_w-1)) + 1;
  S(video.Height+(1:bar_h),1:col_f,:,f) = 0.2;
  S(video.Height+(1:bar_h),col_If,1,f) = 1;
end

Here’s what the result looks like for the original ordering:

original rhino ordering with progress bar

And with the random ordering:

remapped rhino ordering with progress bar

Note: This assumes that the remapped video and the original video are the same length.

Plot piecewise constant function over 2D triangle mesh as height field

Tuesday, October 8th, 2013

I typically deal with piecewise linear functions defined over a triangle mesh (values defined per vertex). I can plot this over a 2d triangle mesh domain using the trisurf function:

trisurf(F,V(:,1),V(:,2),S);

I wrap this into my own function tsurf to make it easier to call since I call them so often:

tsurf(F,[V S]);

piecewise linear height-field over triangle mesh

Sometimes I deal with piecewise constant functions (values defined per triangle). To plot this I use:

tsurf(bsxfun(@plus,size(F,1)*(0:size(F,2)-1),(1:size(F,1))'),[V(F(:),:) repmat(S,size(F,2),1)]);

Or broken down:

% positions of all "Corners"
C = V(F(:),:);
% Scalar field defined at corners
SC = repmat(S,size(F,2),1);
% new disjoint triangle indices into C for each original triangle
FC = bsxfun(@plus,size(F,1)*(0:size(F,2)-1),(1:size(F,1))');
% plot each triangle as piecewise linear function (each of which happens to be constant)
tsurf(FC,[C SC]);

piecewise constant height-field over triangle mesh

Vector field with actual arrows

Sunday, August 11th, 2013

I recently discovered MATLAB’s quiver function which produces visualizations of vector fields. This function creates “arrows” using three line segments. This is fine for prototyping, but not great for making nice images.

Here’s an example using quiver:


[X,Y] = meshgrid(linspace(0,1,40),linspace(0,1,40));
U = sin(X*2*pi).*sin(Y*2*pi)+X;
V = sin(X*2*pi).*cos(Y*2*pi);
quiver(X,Y,U,V);
set(gca,'Visible','off')
set(gcf,'color','w')

which produces:
quiver plot of trig function

Here’s my updated pipeline for making nice images of vector field using MATLAB and Adobe Illustrator.

In Matlab:


% scale appropriately
s = 1e-1;
% plot just lines
plot([X(:) X(:)+s*U(:)]',[Y(:) Y(:)+s*V(:)]','-b');
set(gca,'Visible','off')
set(gcf,'color','w')

which produces:
plot of trig function as lines

Save this as an .eps file and open it with illustrator.

Inevitably, matlab will have saved much more than just the line segments. Clean it up a bit. Select one of the “dot” markers (especially visible if any of your lines were very small). Then choose Select > Same > Stroke Weight. This will select all dots. Then hit Delete.

Now select one of the line segments and choose Select > Same > Stroke Weight. This will select all line segments. Now choose an appropriate arrowhead. This will produce something like:
arrows for vector field in illustrator

Interactive bilinear interpolation visualization with derivatives in matlab

Friday, July 19th, 2013

Interactive bilinear interpolation visualization with derivatives

Here’s a little matlab gui to play with a bilinar function in a square, visualizing its value and partial derivatives with respect to x and y. Save it into a file bilinear_interactive.m.


function bilinear_interactive()
  % BILINEAR_INTERACTIVE Create a little GUI to play with a bilinar function in
  % a square, visualizing its value and partial derivatives with respect to x
  % and y.
  % 
  % bilinear_interactive()
  %

  clf;
  [V,F] = create_regular_grid(50,50,0,0);
  corner = [1 1 2 0];
  bi = @(x,y,a,b,c,d) y.*(a+x.*(b-a)) + (1-y).*(c+x.*(d-c));
  grad_bi_x = @(x,y,a,b,c,d) y.*((b-a)-(d-c))+(d-c);
  grad_bi_y = @(x,y,a,b,c,d) x.*((b-a)-(d-c))+(a-c);

  subplot(1,3,1);
  tsh = trisurf(F,V(:,1),V(:,2), ...
    bi(V(:,1),V(:,2),corner(1),corner(2),corner(3),corner(4)), ...
    'EdgeColor','none','FaceColor','inter','FaceLighting','phong');
  caxis([0 2]);
  title('Bilinear function f','FontSize',20);
  colormap(jet(15));
  axis equal;
  view(2);

  subplot(1,3,2);
  xsh = trisurf(F,V(:,1),V(:,2), ...
    grad_bi_x(V(:,1),V(:,2),corner(1),corner(2),corner(3),corner(4)), ...
    'EdgeColor','none','FaceColor','inter','FaceLighting','phong');
  axis equal;
  caxis([-2 2]);
  title('df/dx','FontSize',20);
  view(2);

  subplot(1,3,3);
  ysh = trisurf(F,V(:,1),V(:,2), ...
    grad_bi_y(V(:,1),V(:,2),corner(1),corner(2),corner(3),corner(4)), ...
    'EdgeColor','none','FaceColor','inter','FaceLighting','phong');
  axis equal;
  title('df/dy','FontSize',20);
  caxis([-2 2]);
  view(2);

  set(gcf,'Position',[1 1 1440 830]);
  pos = [ ...
    [150-40 110+500 120 20];
    [450-20 110+500 120 20];
    [150-40 40+200  120 20];
    [450-20 40+200  120 20];];
  label = zeros(1,4);
  for ind = 1:4
    label(ind) = uicontrol('Style', 'text',...
      'Position',pos(ind,:), ...
      'FontSize',20, ...
      'String',num2str(corner(ind)));
    uicontrol('Style', 'slider',...
      'Min',0,'Max',2, ...
      'Value',corner(ind), ...
      'Position',get(label(ind),'Position')-[0 20 0 0], ...
      'Callback', @(src,evt) update(get(src,'Value'),ind));
  end

  function update(val,ind)
    corner(ind) = val;
    Z = bi(V(:,1),V(:,2),corner(1),corner(2),corner(3),corner(4));
    set(label(ind),'String',num2str(val));
    set(tsh,'Vertices',[V Z],'CData',Z);
    Z = grad_bi_x(V(:,1),V(:,2),corner(1),corner(2),corner(3),corner(4));
    set(xsh,'Vertices',[V Z],'CData',Z);
    Z = grad_bi_y(V(:,1),V(:,2),corner(1),corner(2),corner(3),corner(4));
    set(ysh,'Vertices',[V Z],'CData',Z);
    drawnow;
  end
end

Three body chaos visualization applet revived

Monday, October 12th, 2009

Googlepages has officially migrated to Googlesites. For me this means that my old website on Googlepages has been “migrated”. Migration apparently included only html files and perhaps PDFs. So I’ve been trying to revive my old java applets. Here’s the first to be brought back from the dead.


three body chaos

In a gravitational system with three or more bodies tracking position over time becomes chaotic. Slight perturbations (i.e. miscalculations) in the start position can greatly affect the calculated positions later as time goes by. This java applet that shows the 10 possible paths of a satellite travelling between two suns. All 10 paths start at the same position (with slight perturbations).

I’ve only changed a few minor things from the original 2006 train-wreck java source, so have a look only if you dare.