I have an isosurface/mesh (generated via marching cubes) of a micro-CT scan. Originally the surface looks very noisy, which becomes prevalent when the mean curvature is visualized:

unedited surface

To reduce the noise I've applied Gaussian smoothing to the imagedata and regenerated the mesh. This time, artificial looking rings appear:

surface after smoothing

I would like to reduce these circles/rings/terracing artifacts to obtain a more smooth distribution of the curvature values.

I was wondering if anybody would know the name of this problem since I'm struggling to find anything similar online. All help is appreciated!

  • 2
    $\begingroup$ It's possible that it is something specific to marching cubes. Looks like aliasing or ringing. $\endgroup$
    – lightxbulb
    Commented Jun 30, 2020 at 12:55
  • $\begingroup$ I was thinking that too. So I ran Dual Marching Cubes and SurfaceNets on a voxelized sphere (also with Gaussian smoothing) but these rings kept appearing (although less apparent for SurfaceNets) $\endgroup$
    – Steven
    Commented Jun 30, 2020 at 14:01
  • 4
    $\begingroup$ Try ray marching this with a high resolution (zoom into the problematic region). If the rings disappear you know it's the discretization. If they do not disappear, then it's a feature if your surface. $\endgroup$
    – lightxbulb
    Commented Jun 30, 2020 at 14:39

1 Answer 1


This is just an educated guess, but the artifacts in your visualization of the curvature values reminded me of Moiré patterns. The images in the Wikipedia article don't show circle patterns like the ones you are observing, but I have seen similar patterns before when I tried to visualize highly oscillating functions. (Edit: The function you are visualizing, i.e. the curvature, is likely not oscillating quickly. However, the following example still illustrates that it may not only be the discretization or mesh generation algorithm that is problematic, but also the computation of the curvature.)

For example, if you try to visualize the function $f(x,y) = |\cos((x^2+y^2)^2)|$ for $x$ and $y$ ranging from $-3.3$ to $3.3$ on a grid of $256\times 256$ pixels, what you get is the following image:

Moiré pattern

The Moiré patterns appear in this image since the function $f$ is only evaluated at a single point (x,y) for every pixel, although each pixel covers not just a single point but an area. A better visualization of the function $f$ would involve integrating over the corresponding area for each pixel, which would result in a uniform gray color near the image boundaries reduces the Moiré patterns.

My guess is that there is something similar happening in your situation. As for solving your problem, this essentially means that the curvature is computed in an erroneous way if the discretization is too coarse. So you could either try to improve the algorithm that is used for the computation of the curvature or simply use a finer discretization. Unless, of course, if the rings really are a feature of your surface, in which case they will not disappear as lightxbulb pointed out.

In case you are interested, I have attached the C++ code that I used to generate the above image. Note that if you increase X and Y while keeping lX and lY constant, the discretization is refined and the Moiré patterns will disappear.

#include <fstream>
#include <vector>
#include <string>
#include <cmath>

using namespace std;

void WriteGrayscalePPM(const std::string& file, const std::vector<unsigned char>& values, const unsigned int X) {
  const unsigned int Y = values.size ( ) / X;
  std::ofstream image(file.c_str(), ios::out | ios::binary);
  image << "P6\n" << X << ' ' << Y << "\n255\n";
  for (unsigned int i=0; i<X*Y; i++) {
    unsigned char color[3] = {values[i], values[i], values[i]};
    image.write((const char*)color, 3*sizeof(char));

int main(int argc, char **argv) {
  const int X = 256;
  const int Y = 256;
  const double lX = 3.3;
  const double lY = 3.3;
  std::vector<unsigned char> values(X*Y, 0);
  for (int y=0; y<Y; y++) for (int x=0; x<X; x++) {
    double xd = lX / (X/2) * (x - X/2);
    double yd = lY / (Y/2) * (y - Y/2);
    double d = sqrt(xd*xd + yd*yd);
    values[x + y*X] = 255*abs(cos(d*d*d*d));
  WriteGrayscalePPM("moire.ppm", values, X);
  return 0;

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.