Other Posts in Image Editing

  1. Perlin Noise
  2. Fault Formation
  3. Cellular Textures
  4. Resizing an Image in C#
  5. Box Blur and Gaussian Blur... Sort of...
  6. Thermal Erosion
  7. Using Mid Point Displacement to Create Cracks
  8. Fluvial Erosion
  9. Creating Marble Like Textures Procedurally
  10. Procedural Textures and Dilation
  11. Converting Image to Black and White in C#
  12. Getting an HTML Based Color Palette from an Image in C#
  13. Adding Noise/Jitter to an Image in C#
  14. Creating Pixelated Images in C#
  15. Edge detection in C#
  16. Using Sin to Get What You Want... In C#...
  17. Noise Reduction of an Image in C# using Median Filters
  18. Image Dilation in C#
  19. Sepia Tone in C#
  20. Kuwahara Filter in C#
  21. Matrix Convolution Filters in C#
  22. Symmetric Nearest Neighbor in C#
  23. Bump Map Creation Using C#
  24. Normal Map Creation Using C#
  25. Creating Negative Images using C#
  26. Red, Blue, and Green Filters in C#
  27. Converting an Image to ASCII Art in C#
  28. Adjusting Brightness of an Image in C#
  29. Adding Noise to an Image in C#
  30. Adjusting the Gamma of an Image Using C#
  31. Adjusting Contrast of an Image in C#
  32. Drawing a Box With Rounded Corners in C#
  33. Anding Two Images Together Using C#
  34. Motion Detection in C#
  35. Creating Thermometer Chart in C#
  36. Colorizing a Black and White Image in C#
  37. Extracting an Icon From a File
  38. Setting the Pixel Format and Image Format of an Image in .Net
  39. Using Unsafe Code for Faster Image Manipulation
  40. Sobel Edge Detection and Laplace Edge Detection in C#

Thermal Erosion

4/1/2008

To go along with the various procedural content algorithms, I figured I'd show how to do some erosion techniques. Specifically thermal erosion. Thermal erosion was proposed by Musgrave, et al and is a basic way to simulate erosion on a terrain.

Concept 

The basic concept for thermal erosion is the temperature of the ground/material changes. This basic change in temperature causes the material to expand and contract, causing small bits to get loose and tumble to the bottom of whatever incline it's on. The algorithm to do this is really easy and as always I've provided some code:

   1: /// <summary>
   2: /// Does thermal erosion on the image
   3: /// </summary>
   4: /// <param name="OriginalImage">Image to manipulate</param>
   5: /// <param name="T">Threshold value</param>
   6: /// <param name="C">Change value</param>
   7: /// <param name="Iterations">Number of iterations</param>
   8: /// <returns>A bitmap image</returns>
   9: public static Bitmap ThermalErosion(Bitmap OriginalImage,float T,float C,int Iterations)
  10: {
  11:     Bitmap BlackAndWhiteImage = Image.ConvertBlackAndWhite(OriginalImage);
  12:     BitmapData BlackAndWhiteData = Image.LockImage(BlackAndWhiteImage);
  13:     int BlackAndWhitePixelSize = Image.GetPixelSize(BlackAndWhiteData);
  14:     float[] HeightDifferences = new float[8];
  15:     for (int i = 0; i < Iterations; ++i)
  16:     {
  17:         for (int x = 0; x < BlackAndWhiteImage.Width; ++x)
  18:         {
  19:             for (int y = 0; y < BlackAndWhiteImage.Height; ++y)
  20:             {
  21:                 int X1 = x - 1;
  22:                 int Y1 = y - 1;
  23:                 X1=Math.MathHelper.Clamp(X1, BlackAndWhiteImage.Width-1, 0);
  24:                 Y1=Math.MathHelper.Clamp(Y1, BlackAndWhiteImage.Height-1, 0);
  25:                 int X2 = x + 1;
  26:                 int Y2 = y + 1;
  27:                 X2=Math.MathHelper.Clamp(X2, BlackAndWhiteImage.Width-1, 0);
  28:                 Y2=Math.MathHelper.Clamp(Y2, BlackAndWhiteImage.Height-1, 0);
  29:                 HeightDifferences[0] = GetHeightDifferences(x, y, X1, Y1, BlackAndWhiteData, BlackAndWhitePixelSize);
  30:                 HeightDifferences[1] = GetHeightDifferences(x, y, x, Y1, BlackAndWhiteData, BlackAndWhitePixelSize);
  31:                 HeightDifferences[2] = GetHeightDifferences(x, y, X2, Y1, BlackAndWhiteData, BlackAndWhitePixelSize);
  32:                 HeightDifferences[3] = GetHeightDifferences(x, y, X1, y, BlackAndWhiteData, BlackAndWhitePixelSize);
  33:                 HeightDifferences[4] = GetHeightDifferences(x, y, X2, y, BlackAndWhiteData, BlackAndWhitePixelSize);
  34:                 HeightDifferences[5] = GetHeightDifferences(x, y, X1, Y2, BlackAndWhiteData, BlackAndWhitePixelSize);
  35:                 HeightDifferences[6] = GetHeightDifferences(x, y, x, Y2, BlackAndWhiteData, BlackAndWhitePixelSize);
  36:                 HeightDifferences[7] = GetHeightDifferences(x, y, X2, Y2, BlackAndWhiteData, BlackAndWhitePixelSize);
  37:                 float MaxDifference = 0.0f;
  38:                 float DifferenceTotal = 0.0f;
  39:                 float NumberOver = 0;
  40:                 for (int z = 0; z < 8; ++z)
  41:                 {
  42:                     if (HeightDifferences[z] > MaxDifference)
  43:                     {
  44:                         MaxDifference = HeightDifferences[z];
  45:                     }
  46:                     if (HeightDifferences[z] > T)
  47:                     {
  48:                         DifferenceTotal += HeightDifferences[z];
  49:                         ++NumberOver;
  50:                     }
  51:                 }
  52:                 float Height = GetHeight(x, y, BlackAndWhiteData, BlackAndWhitePixelSize);
  53:                 if (X1 != x && Y1 != y && HeightDifferences[0] != 0.0f && DifferenceTotal != 0.0f)
  54:                 {
  55:                     Height = GetHeight(X1, Y1, BlackAndWhiteData, BlackAndWhitePixelSize);
  56:                     Height = Height + C * (MaxDifference - T) * (HeightDifferences[0] / DifferenceTotal);
  57:                     Height = Math.MathHelper.Clamp(Height, 1.0f, -1.0f);
  58:                     SetHeight(X1, Y1, Height, BlackAndWhiteData, BlackAndWhitePixelSize);
  59:                 }
  60:                 if (Y1 != y && HeightDifferences[1] != 0.0f && DifferenceTotal != 0.0f)
  61:                 {
  62:                     Height = GetHeight(x, Y1, BlackAndWhiteData, BlackAndWhitePixelSize);
  63:                     Height = Height + C * (MaxDifference - T) * (HeightDifferences[1] / DifferenceTotal);
  64:                     Height = Math.MathHelper.Clamp(Height, 1.0f, -1.0f);
  65:                     SetHeight(x, Y1, Height, BlackAndWhiteData, BlackAndWhitePixelSize);
  66:                 }
  67:                 if (X2 != x && Y1 != y && HeightDifferences[2] != 0.0f && DifferenceTotal != 0.0f)
  68:                 {
  69:                     Height = GetHeight(X2, Y1, BlackAndWhiteData, BlackAndWhitePixelSize);
  70:                     Height = Height + C * (MaxDifference - T) * (HeightDifferences[2] / DifferenceTotal);
  71:                     Height = Math.MathHelper.Clamp(Height, 1.0f, -1.0f);
  72:                     SetHeight(X2, Y1, Height, BlackAndWhiteData, BlackAndWhitePixelSize);
  73:                 }
  74:                 if (X1 != x && HeightDifferences[3] != 0.0f && DifferenceTotal != 0.0f)
  75:                 {
  76:                     Height = GetHeight(X1, y, BlackAndWhiteData, BlackAndWhitePixelSize);
  77:                     Height = Height + C * (MaxDifference - T) * (HeightDifferences[3] / DifferenceTotal);
  78:                     Height = Math.MathHelper.Clamp(Height, 1.0f, -1.0f);
  79:                     SetHeight(X1, y, Height, BlackAndWhiteData, BlackAndWhitePixelSize);
  80:                 }
  81:                 if (X2 != x && HeightDifferences[4] != 0.0f && DifferenceTotal != 0.0f)
  82:                 {
  83:                     Height = GetHeight(X2, y, BlackAndWhiteData, BlackAndWhitePixelSize);
  84:                     Height = Height + C * (MaxDifference - T) * (HeightDifferences[4] / DifferenceTotal);
  85:                     Height = Math.MathHelper.Clamp(Height, 1.0f, -1.0f);
  86:                     SetHeight(X2, y, Height, BlackAndWhiteData, BlackAndWhitePixelSize);
  87:                 }
  88:                 if (X1 != x && Y2 != y && HeightDifferences[5] != 0.0f && DifferenceTotal != 0.0f)
  89:                 {
  90:                     Height = GetHeight(X1, Y2, BlackAndWhiteData, BlackAndWhitePixelSize);
  91:                     Height = Height + C * (MaxDifference - T) * (HeightDifferences[5] / DifferenceTotal);
  92:                     Height = Math.MathHelper.Clamp(Height, 1.0f, -1.0f);
  93:                     SetHeight(X1, Y2, Height, BlackAndWhiteData, BlackAndWhitePixelSize);
  94:                 }
  95:                 if (Y2 != y && HeightDifferences[6] != 0.0f && DifferenceTotal != 0.0f)
  96:                 {
  97:                     Height = GetHeight(x, Y2, BlackAndWhiteData, BlackAndWhitePixelSize);
  98:                     Height = Height + C * (MaxDifference - T) * (HeightDifferences[6] / DifferenceTotal);
  99:                     Height = Math.MathHelper.Clamp(Height, 1.0f, -1.0f);
 100:                     SetHeight(x, Y2, Height, BlackAndWhiteData, BlackAndWhitePixelSize);
 101:                 }
 102:                 if (X2 != x && Y2 != y && HeightDifferences[7] != 0.0f && DifferenceTotal != 0.0f)
 103:                 {
 104:                     Height = GetHeight(X2, Y2, BlackAndWhiteData, BlackAndWhitePixelSize);
 105:                     Height = Height + C * (MaxDifference - T) * (HeightDifferences[7] / DifferenceTotal);
 106:                     Height = Math.MathHelper.Clamp(Height, 1.0f, -1.0f);
 107:                     SetHeight(X2, Y2, Height, BlackAndWhiteData, BlackAndWhitePixelSize);
 108:                 }
 109:                 if (DifferenceTotal != 0.0f)
 110:                 {
 111:                     Height = GetHeight(x, y, BlackAndWhiteData, BlackAndWhitePixelSize);
 112:                     Height = Height + (MaxDifference - (NumberOver * MaxDifference * T / DifferenceTotal));
 113:                     Height = Math.MathHelper.Clamp(Height, 1.0f, -1.0f);
 114:                     SetHeight(x, y, Height, BlackAndWhiteData, BlackAndWhitePixelSize);
 115:                 }
 116:             }
 117:         }
 118:     }
 119:     Image.UnlockImage(BlackAndWhiteImage, BlackAndWhiteData);
 120:     return BlackAndWhiteImage;
 121: }
 122:  
 123: private static float GetHeightDifferences(int x, int y, int X1, int Y1, BitmapData BlackAndWhiteData, int BlackAndWhitePixelSize)
 124: {
 125:     Color TempColor = Image.GetPixel(BlackAndWhiteData, x, y, BlackAndWhitePixelSize);
 126:     float Height = GetHeight(TempColor);
 127:     TempColor = Image.GetPixel(BlackAndWhiteData, X1, Y1, BlackAndWhitePixelSize);
 128:     float Height2 = GetHeight(TempColor);
 129:     return Height - Height2;
 130: }
 131:  
 132: private static void SetHeight(int x, int y, float Value,BitmapData Data,int PixelSize)
 133: {
 134:     Value *= 0.5f;
 135:     Value += 0.5f;
 136:     int Value2 = (int)(Value * 255.0f);
 137:     Color TempColor = Color.FromArgb(Value2, Value2, Value2);
 138:     Image.SetPixel(Data, x, y, TempColor, PixelSize);
 139: }
 140:  
 141: private static float GetHeight(int x, int y, BitmapData BlackAndWhiteData, int BlackAndWhitePixelSize)
 142: {
 143:     Color TempColor = Image.GetPixel(BlackAndWhiteData, x, y, BlackAndWhitePixelSize);
 144:     return GetHeight(TempColor);
 145: }
 146:  
 147: private static float GetHeight(Color Color)
 148: {
 149:     return (float)Color.R / 255.0f;
 150: }

The algorithm above uses a couple of items from my utility library. Namely there is a clamp function used in there that simply makes sure that we don't go out of bounds. The other bit of code is from my unsafe, bitmap locking code. Those bits can be replaced with a simple GetPixel/SetPixel, but will run a lot slower. Anyway, it might take you some playing with the algorithm to get a noticeable difference in the output. In my experience, to get an even marginal difference, it took about 500 iterations. On top of that it's a slow algorithm but it's a decent way to smooth out terrains. And while the CPU version is slow, Davide Capolla (I have to admit I used info on his site to create my version) has a GPU version of the algorithm (and I'm sure you can find other implementations out there as well) that seems to run rather well. Anyway, download the code, leave feedback, and happy coding.



Comments