Using Cellular Maps in C# to Create Interesting Artwork

Before I show the code, I just want you to know that this is not efficient at all. That being said, this is really just to show a concept. Anyway, I've shown a couple of times how procedural image creation can be used for some interesting effects but I've never shown what can be done with an existing image. In this instance I'm going to use a cellular map on a pre existing image. So lets look at the code for the cellular map first:

    public class CellularMap
    {
        public CellularMap(int Seed, int Width, int Height,int NumberOfPoints)
        {
            _Width = Width;
            _Height = Height;
            Random Rand = new Random(Seed);
            Distances = new float[Width, Height];
            ClosestPoint = new int[Width, Height];
            for (int x = 0; x < NumberOfPoints; ++x)
            {
                Point TempPoint = new Point();
                TempPoint.X = Rand.Next(0, Width);
                TempPoint.Y = Rand.Next(0, Height);
                Points.Add(TempPoint);
            }
            CalculateDistances();
        }

        private void CalculateDistances()
        {
            for (int x = 0; x < _Width; ++x)
            {
                for (int y = 0; y < _Height; ++y)
                {
                    FindClosestPoint(x, y);
                }
            }
        }

        private void FindClosestPoint(int x, int y)
        {
            float MaxDistance=float.MaxValue;
            int Index = -1;
            for (int z = 0; z < Points.Count; ++z)
            {
                float Distance = (float)System.Math.Sqrt(((Points[z].X - x) * (Points[z].X - x)) + ((Points[z].Y - y) * (Points[z].Y - y)));
                if (Distance < MaxDistance)
                {
                    MaxDistance = Distance;
                    Index = z;
                }
            }
            ClosestPoint[x, y] = Index;
            Distances[x, y] = MaxDistance;
        }

        public int[,] ClosestPoint { get; set; }
        public float[,] Distances { get; set; }
        private List<Point> Points = new List<Point>();
        private int _Width = 0;
        private int _Height = 0;
    }

    internal class Point
    {
        public Point() { }
        public int X { get; set; }
        public int Y { get; set; }
    }

The cellular map above is really just a set of points and a list of distances. It doesn't do much really but what we've end up doing with this code is grouping sets of pixels together around a point. And what you'll notice, if you have a large number of them, is that it ends up creating some fairly interesting looking geometry. So, just for fun, I took a pre existing image and laid the cellular map over it. Then I took the groups of pixels around each point in the cellular map and averaged them together to get an "average" pixel and put the result into a new image (as you can see below):

    public class OilPainting:IDisposable
    {
        public OilPainting(Bitmap Image,int Seed,int NumberOfPoints)
        {
            _Image = new Bitmap(Image);
            _NumberOfPoints = NumberOfPoints;
            Map = new CellularMap(Seed, Image.Width, Image.Height, NumberOfPoints);
            SetupImage();
        }

        private void SetupImage()
        {
            for (int i = 0; i < _NumberOfPoints; ++i)
            {
                int Red=0;
                int Green=0;
                int Blue=0;
                int Counter=0;
                for (int x = 0; x < _Image.Width; ++x)
                {
                    for (int y = 0; y < _Image.Height; ++y)
                    {
                        if (Map.ClosestPoint[x, y] == i)
                        {
                            Color Pixel = _Image.GetPixel(x, y);
                            Red += Pixel.R;
                            Green += Pixel.G;
                            Blue += Pixel.B;
                            ++Counter;
                        }
                    }
                }
                int Counter2 = 0;
                for (int x = 0; x < _Image.Width; ++x)
                {
                    for (int y = 0; y < _Image.Height; ++y)
                    {
                        if (Map.ClosestPoint[x, y] == i)
                        {
                            _Image.SetPixel(x, y, Color.FromArgb(Red / Counter, Green / Counter, Blue / Counter));
                            ++Counter2;
                            if (Counter2 == Counter)
                                break;
                        }
                    }
                    if (Counter2 == Counter)
                        break;
                }
            }
        }

        private CellularMap Map = null;
        public Bitmap _Image { get; set; }
        private int _NumberOfPoints = 0;

        #region IDisposable Members

        public void Dispose()
        {
            if (_Image != null)
            {
                _Image.Dispose();
                _Image = null;
            }
        }

        #endregion
    }

The results end up looking like this:

 

And if you can't tell, that's the Mona Lisa. Like I said, it isn't a practical bit of code but it does result in interesting images. And if you want it to look even more like an oil painting, use the median instead of the average. So take a look, leave feedback, and happy coding.

Edit: This is just something that has bugging the hell out of me and completely unrelated to the post above but I don't want to give it a post of its own... Why does it seem that everyone is in love with the recent WWDC announcements and the new iPhone? Am I the only one that sees that as a pretty pathetic update (especially state side considering AT&T)? Meh. Palm Pre looks cool though...
kick it on DotNetKicks.com   Shout it
Digg It!DZone It!StumbleUponTechnoratiRedditDel.icio.usNewsVineFurlBlinkListEmail

Posted by: James Craig
Posted on: 6/19/2009 at 10:12 AM
Tags: , , ,
Categories: C#
Post Information: Permalink | Comments (0) | Post RSSRSS comment feed

Add comment




  Country flag

biuquote
  • Comment
  • Preview
Loading