Introduction
This second part of the series deals with generative art based on images as input.
Background
The basis of the representational example in this tip is the following photo:
Alida Altemburg (model), Robert Anthony (photographer), Vanee Pham (makeup), Crown Casino Melbourne
Basic Approach
For producing an artwork based on an input image, you have to accomplish the following tasks:
- Read all pixels of the input image
- Create a canvas with the appropriate size
- Generate something influenced by the pixels of the input image
- Sign the result
- Save the artwork
var inputImage = new InputImage(currentDirectory + inputFile);
var canvas = new GenArtCanvas(inputImage.Width * zoom, inputImage.Height * zoom, backgroundColor);
canvas.Generate(zoom, inputImage, margin);
canvas.Sign(zoom, signature, margin);
canvas.Save(currentDirectory + title + format);
Read Input Image
Creating a new InputImage
reads all Pixels
of the image into an array of bytes.
public class InputImage
{
public byte[] Pixels;
public int Width, Height;
public InputImage(string InputFileName)
{
using (FileStream inStream =
new FileStream(InputFileName, FileMode.Open, FileAccess.Read, FileShare.Read))
{
BitmapSource Image = new JpegBitmapDecoder
(inStream, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.Default).Frames[0];
Width = Image.PixelWidth;
Height = Image.PixelHeight;
int stride = Width * ((Image.Format.BitsPerPixel + 7) / 8);
Pixels = new byte[Height * stride];
Image.CopyPixels(Pixels, stride, 0);
}
}
}
The InputImage
itself is not stored in memory. Only the Pixels
are kept for further processing and the Width
and Height
are taken as ratio for the creation of the empty canvas of the new generative artwork:
var canvas = new GenArtCanvas(inputImage.Width * zoom, inputImage.Height * zoom, backgroundColor);
Generate Artwork
This algorithm takes every single pixel of each 40th row and each 40th column of the InputImage
. Then it adds a shape (Ellipse
or Rectangle
) to the canvas filling the shape with the color of the specific pixel (PixelColor
). Each shape is set to its corresponding position from the InputImage
.
public void Generate(int Zoom, InputImage InputImage, double Margin)
{
Generator.ImageBaseShapes(this, InputImage, Zoom, Margin);
}
public static void ImageBaseShapes(Canvas Canvas, InputImage InputImage, int Zoom, double Margin)
{
for (var row = Margin; row <= InputImage.Height - Margin; row += 5)
for (var column = Margin; column <= InputImage.Width - Margin; column += 5)
{
var shape = new Ellipse() {
Width = Zoom * 3,
Height = Zoom * 3,
Fill = new SolidColorBrush(InputImage.PixelColor(new Point(column, row)))
Brushes.LightGray : Brushes.Black
};
Canvas.Children.Add(shape);
Canvas.SetLeft(shape, column * Zoom - shape.Width / 2);
Canvas.SetTop(shape, row * Zoom - shape.Height / 2);
}
}
The method PixelColor
analyses the red, green and blue portion (RGB) of a certain pixel:
public byte RedValue(Point Point)
{
return Pixels[PixelPos(Point, 2)];
}
public byte GreenValue(Point Point)
{
return Pixels[PixelPos(Point, 1)];
}
public byte BlueValue(Point Point)
{
return Pixels[PixelPos(Point)];
}
private int PixelPos(Point Point, int RgbDelta = 0)
{
return (int)(3 * ((int)Point.Y * Width + (int)Point.X) + RgbDelta);
}
public Color PixelColor(Point Point)
{
if (Point.X >= Width || Point.Y >= Height || Point.X < 0 || Point.Y < 0) return Colors.White;
return Color.FromRgb(RedValue(Point), GreenValue(Point), BlueValue(Point));
}
The generated result looks like this:
Limiting the color of the shapes to light gray and black produces a new result:
Adjustments of the step size and shape size lead to additional results:
Points of Interest
This approach was also used by human painters in the late 1880s, see "Pointillism".
History
- 1st January, 2015 - Published
- 11th January, 2015 - Links to other articles in the series added