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

Colorizing a Black and White Image in C#

I've shown in the past how easy it is to convert an image to black and white. That's a fairly useful technique, but what if we wanted to take a black and white image and convert it back to color? Well to be honest, converting an image to color from black and white is not a perfect one for one matching. For instance, if we use the reverse of the .3, .59, .11 conversion we would end up with an extremely ugly image. So our best bet is to use the black and white colors as a palette with colors that we define ourselves. So lets take a look at what we need to do:

        public static Bitmap Colorize(Bitmap Image, Color[] Colors)
        {
            if (Colors.Length < 256)
                return null;
            Bitmap TempBitmap = new Bitmap(Image.Width, Image.Height);
            for (int x = 0; x < Image.Width; ++x)
            {
                for (int y = 0; y < Image.Height; ++y)
                {
                    int ColorUsing = Image.GetPixel(x, y).R;
                    TempBitmap.SetPixel(x, y, Colors[ColorUsing]);
                }
            }
            return TempBitmap;
        }

The function takes in an array of colors (256) and uses that to determine the color for each pixel. That's it really. All we have to do is determine what colors we want to use for that array. So try it out, leave feedback, and happy coding.

kick it on DotNetKicks.com   Shout it
Digg It!DZone It!StumbleUponTechnoratiRedditDel.icio.usNewsVineFurlBlinkListEmail

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

Creating Thermometer Chart in C#

Have you ever had someone calling you up and saying to you that they needed a chart that looked like a thermometer today and they didn't want it to be simply an image that they update? No? Well I did the other day. So I quickly slapped together a handler to create the chart for me. Note that this isn't a nicely created piece of code but hey, I thought someone might get something out of it:

    public class Thermometer : IHttpHandler
    {
        #region IHttpHandler Members

        public bool IsReusable
        {
            get { return false; }
        }

        public void ProcessRequest(HttpContext context)
        {
            if (context.Request.QueryString["Percent"] != null)
            {
                int Percent = int.Parse(context.Request.QueryString["Percent"]);
                int Max = int.Parse(context.Request.QueryString["Max"]);
                bool Dollar = int.Parse(context.Request.QueryString["Dollars"]) == 1;
                float Pixels=(float)Percent*2.15f;
                Bitmap TempImage = new Bitmap(100, 270);
                Graphics TempGraphics = Graphics.FromImage(TempImage);
                TempGraphics.FillRectangle(Brushes.White, new Rectangle(0, 0, 100, 270));
                Pen TempPen = new Pen(Color.Black);
                Pen RedPen=new Pen(Color.Red);
                TempGraphics.DrawArc(TempPen, 45, 230, 30, 30, 320, 260);
                TempGraphics.DrawLine(TempPen, 49, 235, 49, 20);
                TempGraphics.DrawLine(TempPen, 71, 235, 71, 20);
                TempGraphics.DrawArc(TempPen, 49, 15, 22, 11, 180, 180);
                GreyFloodFill(TempImage);
                FloodFill(TempImage, Percent);
                bool DrawText = true;
                float Amount = Max;
                for (float y = 20.0f; y < 235.0f; y += 10.75f)
                {
                    TempGraphics.DrawLine(TempPen, 49, y, 55, y);
                    if (DrawText)
                    {
                        Font TempFont = new Font("Arial", 8.0f, FontStyle.Bold);
                        if (Dollar)
                        {
                            TempGraphics.DrawString("$" + Amount.ToString(), TempFont, Brushes.Black, 5, y);
                        }
                        else
                        {
                            TempGraphics.DrawString(Amount.ToString(), TempFont, Brushes.Black, 5, y);
                        }
                        DrawText = false;
                    }
                    else DrawText = true;
                    Amount -= ((Max / 100) * 5.0f);
                }

                string etag = "\"" + Percent.GetHashCode() + "\"";
                string incomingEtag = context.Request.Headers["If-None-Match"];

                context.Response.Cache.SetExpires(DateTime.Now.ToUniversalTime().AddDays(1));
                context.Response.Cache.SetCacheability(HttpCacheability.Public);
                context.Response.Cache.SetMaxAge(new TimeSpan(7, 0, 0, 0));
                context.Response.Cache.SetRevalidation(HttpCacheRevalidation.AllCaches);
                context.Response.Cache.SetETag(etag);

                if (String.Compare(incomingEtag, etag) == 0)
                {
                    context.Response.StatusCode = (int)HttpStatusCode.NotModified;
                    context.Response.End();
                }
                else
                {
                    context.Response.ContentType = "image/Gif";
                    TempImage.Save(context.Response.OutputStream, ImageFormat.Gif);
                }
            }
        }

        private void GreyFloodFill(Bitmap TempImage)
        {
            int RedColor = 255;
            for (int y = 20; y <= 235; ++y)
            {
                for (int x = 50; x <= 70; ++x)
                {
                    float Distance = Math.Abs(60 - x);
                    RedColor = (int)(255.0f * (1.0f - (Distance / 30.0f)));
                    TempImage.SetPixel(x, y, Color.FromArgb(RedColor, RedColor, RedColor));
                }
            }
        }

        private void FloodFill(Bitmap TempImage,int Percent)
        {
            if (Percent == 100)
            {
                FillCircle(TempImage, 60, 245, 15);
                FillCircle(TempImage, 60, 20, 10);
                FillRectangle(TempImage, 60, 20, 235);
            }
            else
            {
                FillCircle(TempImage, 60, 245, 15);
                FillRectangle(TempImage, 60, (int)(235.0f - ((float)Percent * 2.15f)), 235);
            }
        }

        private void FillRectangle(Bitmap TempImage, int x, int y1,int y2)
        {
            int MaxDistance=10;
            for (int i = x - MaxDistance; i < x + MaxDistance; ++i)
            {
                for (int j = y1; j < y2; ++j)
                {
                    float Distance = (float)Math.Abs((i - x));
                    int RedColor = (int)(255.0f * (1.0f - (Distance / (2.0f * MaxDistance))));
                    TempImage.SetPixel(i, j, Color.FromArgb(RedColor, 0, 0));
                }
            }
        }

        private void FillCircle(Bitmap TempImage, int x, int y, int MaxDistance)
        {
            for (int i = x - MaxDistance; i < x + MaxDistance; ++i)
            {
                for (int j = y - MaxDistance; j < y + MaxDistance; ++j)
                {
                    float Distance = (float)Math.Sqrt(((j - y) * (j - y)) + ((i - x) * (i - x)));
                    if (Distance < MaxDistance)
                    {
                        int RedColor = (int)(255.0f * (1.0f - (Distance / (2.0f * MaxDistance))));
                        TempImage.SetPixel(i, j, Color.FromArgb(RedColor, 0, 0));
                    }
                }
            }
        }

        #endregion
    }

The code above is a handler that takes in a Percent (percent of the graph to show as red), Max (maximum value), and Dollars value (Whether this is a number or dollar amount). But in return it creates a decent looking graph, as you can see below.

I mean it isn't perfect, but it works... Anyway, try it out, leave feedback, and happy coding.

kick it on DotNetKicks.com   Shout it
Digg It!DZone It!StumbleUponTechnoratiRedditDel.icio.usNewsVineFurlBlinkListEmail

Posted by: James Craig
Posted on: 6/11/2009 at 11:48 AM
Tags: , , ,
Categories: ASP.Net | C#
Post Information: Permalink | Comments (2) | Post RSSRSS comment feed