Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Generative Art V: Merge Pixels Like Warhol

0.00/5 (No votes)
18 Jan 2015 1  
Merge pixels of two images into one new pop artwork

Introduction

In this tip, we follow the footsteps of Andy Warhol and his screen printing technique. Finally, it was a combination of manual painting and generative art he used to produce the "third most influential work of modern art" (the guardian): Marilyn Diptych (1962), now part of the Tate collection.

Basic Approach

We start with a portrait like this (be it coloured or black and white):

Image 1

Now we paint on it - e.g. using Paint.NET, Adobe Photoshop or whatever:

Image 2

For the typical pop art effect we only work with a few colors per picture. Here you can see a comparison of colour scales between Warhol's Marilyn Monroe and da Vinci's Mona Lisa:

Image 3

Image 4

At the end, we get something like this. We save that manual painting as our first jpg:

Image 5

Now we derive a black and white pattern from the original photo (if it is not yet black and white) and we adapt the brightness and contrast of this pattern for our "silk screen". For that purpose, we use Paint.NET, Photoshop or whatever again, and then we save the result as our second jpg:

Image 6

That's it. Now the program presented here merges the two jpgs pixel by pixel to this:

Image 7

You can compare the result with the original here.

Algorithm in Detail

This is the code. We have to read pixels from images, we have to "merge" pixels, and we have to save an array of generated pixels to a new jpg file:

C#
public class InputImage
{
    public int Width, Height;
    public byte[] Pixels;
    private double dpiX, dpiY;
    private int strideFactor;
    private PixelFormat format;

    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;
            dpiX = image.DpiX;
            dpiY = image.DpiY;
            format = image.Format;
            strideFactor = (format.BitsPerPixel + 7) / 8;
            Pixels = new byte[Height * Width * strideFactor];
            image.CopyPixels(Pixels, Width * strideFactor, 0);
        }
    }
    public void MergeImage(InputImage SilkScreen)
    {
        for (var y = 0; y < Height; y++)
            for (var x = 0; x < Width; x++)
            {
                SetRed(x, y, Multiply(this.GetRed(x, y), SilkScreen.GetRed(x, y)));
                SetGreen(x, y, Multiply(this.GetGreen(x, y), SilkScreen.GetGreen(x, y)));
                SetBlue(x, y, Multiply(this.GetBlue(x, y), SilkScreen.GetBlue(x, y)));
            }
    }
    public byte GetRed(int x, int y)
    {
        return GetPixel(x, y, 2);
    }
    //...
    public void SetRed(int x, int y, byte RedValue)
    {
        SetPixel(RedValue, x, y, 2);
    }
    //...
    private byte GetPixel(int x, int y, int RgbDelta = 0)
    {
        return Pixels[PixelPos(x, y, RgbDelta)];
    }
    private void SetPixel(byte Value, int x, int y, int RgbDelta = 0)
    {
        Pixels[PixelPos(x, y, RgbDelta)] = Value;
    }
    private int PixelPos(int x, int y, int RgbDelta = 0)
    {
        return strideFactor * (y * Width + x) + RgbDelta;
    }
    private byte Multiply(byte A, byte B)
    {
        return (byte)(A * B / 255);
    }
    public void Save(string OutputFileName)
    {
        var encoder = new JpegBitmapEncoder();
        var wb = new WriteableBitmap(Width, Height, dpiX, dpiY, format, null);

        wb.WritePixels(new Int32Rect(0, 0, Width, Height), Pixels, Width * strideFactor, 0);

        using (FileStream outStream = new FileStream(OutputFileName, FileMode.Create))
        {
            encoder.QualityLevel = 100;
            encoder.Frames.Add(BitmapFrame.Create(wb));
            encoder.Save(outStream);
        }
    }
}

For the "merging of pixels" I decided to multiply the red value of one pixel with the red value of the other, and then divide the result by 255 (and repeat that for the green and for the blue value of each pixel). But I do not know an "official" name for that algorithm.

Points of Interest

I think, Andy Warhol would have liked this. In 1962, he presented his do-it-yourself series. So: do it yourself!

History

  • 18th January, 2015 - Published

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here