Click here to Skip to main content
15,892,746 members
Articles / Desktop Programming / WPF
Tip/Trick

Generative Art V: Merge Pixels Like Warhol

Rate me:
Please Sign up or sign in to vote.
4.81/5 (9 votes)
18 Jan 2015CPOL2 min read 20.5K   233   9   5
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, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
schoder.uk
United Kingdom United Kingdom
generative artist: schoder.uk

Comments and Discussions

 
QuestionCool ! Pin
Cristiano1226-Jan-15 5:05
Cristiano1226-Jan-15 5:05 
AnswerRe: Cool ! Pin
dietmar schoder27-Jan-15 6:10
dietmar schoder27-Jan-15 6:10 
GeneralRe: Cool ! Pin
Cristiano1228-Jan-15 22:06
Cristiano1228-Jan-15 22:06 
GeneralRe: Cool ! Pin
dietmar schoder28-Jan-15 22:51
dietmar schoder28-Jan-15 22:51 
GeneralRe: Cool ! Pin
Cristiano1228-Jan-15 22:58
Cristiano1228-Jan-15 22:58 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.