c# - Interpolate picture to a specific color

I am looking to interpolate an image to a color so that the transition looks similar to this:

1 2 3 4 5 6

I thought this would be easy enough but it seems like it's a bit more difficult than I thought. Things I've tried:

  1. Smooth and linear interpolation between the starting pixel and a random color.
  2. Picking a random hue and saturation and interpolating to that color.

I'm not sure how this effect is being achieved. Here is how the pixels are changing color for 5 random non-black pixels:

╔══════════════════╦═════╦═════╦═════╦════════╦═══════╦═══════╦═════════╗
║ Sample.Iteration ║  R  ║  G  ║  B  ║   H    ║   S   ║   V   ║   X,Y   ║
╠══════════════════╬═════╬═════╬═════╬════════╬═══════╬═══════╬═════════╣
║ 1.1              ║ 104 ║ 168 ║ 144 ║ 157.50 ║ 38.10 ║ 65.88 ║ 121,81  ║
║ 1.2              ║  96 ║ 152 ║ 144 ║ 171.43 ║ 36.84 ║ 59.61 ║ 121,81  ║
║ 1.3              ║  80 ║ 120 ║ 152 ║ 206.67 ║ 47.37 ║ 59.61 ║ 121,81  ║
║ 1.4              ║  72 ║ 104 ║ 152 ║ 216.00 ║ 52.63 ║ 59.61 ║ 121,81  ║
║ 1.5              ║  56 ║  72 ║ 160 ║ 230.77 ║ 65.00 ║ 62.75 ║ 121,81  ║
║ 1.6              ║  40 ║  40 ║ 168 ║ 240.00 ║ 76.19 ║ 65.88 ║ 121,81  ║
║                  ║     ║     ║     ║        ║       ║       ║         ║
║ 2.1              ║  72 ║ 144 ║ 128 ║ 166.67 ║ 50.00 ║ 56.47 ║ 125,119 ║
║ 2.2              ║  64 ║ 128 ║ 128 ║ 180.00 ║ 50.00 ║ 50.20 ║ 125,119 ║
║ 2.3              ║  56 ║  96 ║ 128 ║ 206.67 ║ 56.25 ║ 50.20 ║ 125,119 ║
║ 2.4              ║  48 ║  88 ║ 128 ║ 210.00 ║ 62.50 ║ 50.20 ║ 125,119 ║
║ 2.5              ║  40 ║  56 ║ 136 ║ 230.00 ║ 70.59 ║ 53.33 ║ 125,119 ║
║ 2.6              ║  32 ║  32 ║ 136 ║ 240.00 ║ 76.47 ║ 53.33 ║ 125,119 ║
║                  ║     ║     ║     ║        ║       ║       ║         ║
║ 3.1              ║ 152 ║ 208 ║ 168 ║ 137.14 ║ 26.92 ║ 81.57 ║ 80,82   ║
║ 3.2              ║ 136 ║ 184 ║ 168 ║ 160.00 ║ 26.09 ║ 72.16 ║ 80,82   ║
║ 3.3              ║ 112 ║ 144 ║ 184 ║ 213.33 ║ 39.13 ║ 72.16 ║ 80,82   ║
║ 3.4              ║  96 ║ 128 ║ 192 ║ 220.00 ║ 50.00 ║ 75.29 ║ 80,82   ║
║ 3.5              ║  72 ║  88 ║ 200 ║ 232.50 ║ 64.00 ║ 78.43 ║ 80,82   ║
║ 3.6              ║  48 ║  48 ║ 216 ║ 240.00 ║ 77.78 ║ 84.71 ║ 80,82   ║
║                  ║     ║     ║     ║        ║       ║       ║         ║
║ 4.1              ║  40 ║  72 ║ 104 ║ 210.00 ║ 61.54 ║ 40.78 ║ 158,75  ║
║ 4.2              ║  32 ║  64 ║  96 ║ 210.00 ║ 66.67 ║ 37.65 ║ 158,75  ║
║ 4.3              ║  32 ║  48 ║  88 ║ 222.86 ║ 63.64 ║ 34.51 ║ 158,75  ║
║ 4.4              ║  24 ║  40 ║  88 ║ 225.00 ║ 72.73 ║ 34.51 ║ 158,75  ║
║ 4.5              ║  24 ║  32 ║  80 ║ 231.43 ║ 70.00 ║ 31.37 ║ 158,75  ║
║ 4.6              ║  16 ║  16 ║  72 ║ 240.00 ║ 77.78 ║ 28.24 ║ 158,75  ║
║                  ║     ║     ║     ║        ║       ║       ║         ║
║ 5.1              ║ 144 ║ 192 ║ 232 ║ 207.27 ║ 37.93 ║ 90.98 ║ 127,99  ║
║ 5.2              ║ 128 ║ 168 ║ 224 ║ 215.00 ║ 42.86 ║ 87.84 ║ 127,99  ║
║ 5.3              ║ 104 ║ 136 ║ 224 ║ 224.00 ║ 53.57 ║ 87.84 ║ 127,99  ║
║ 5.4              ║  96 ║ 120 ║ 216 ║ 228.00 ║ 55.56 ║ 84.71 ║ 127,99  ║
║ 5.5              ║  72 ║  88 ║ 216 ║ 233.33 ║ 66.67 ║ 84.71 ║ 127,99  ║
║ 5.6              ║  48 ║  48 ║ 208 ║ 240.00 ║ 76.92 ║ 81.57 ║ 127,99  ║
╚══════════════════╩═════╩═════╩═════╩════════╩═══════╩═══════╩═════════╝

Worth noting is that each RGB color seems to be a multiple of 8 and the decrement or increment amounts are a multiple of 8 as well. I just can't figure what kind of interpolation it's doing here.

One thing I need to be able to do is be able to pick a random color (say orange, pink, blue) and be able to achieve a similar interpolation to that random color.

1 Answer

  1. Alex- Reply

    2019-11-13

    Here is an approach that produces a rather simliar result. As I wrote in my comment maybe more sophisticated ways should be investigated, but for the example this seems to do it..

    I have added 6 PictureBoxes to mimic your screenshot. The first loads your first image.

    The last is assigned the result of a makeMonoChrome function, which uses a ColorMatrix; this is a variant to the usual grayscale code which multiplies a target color into the matrix. Here the important point are the magic numbers, which contain the weighted brightness of the 3 channels.

    You may want to play with the target color; I think (255, 64, 64, 255) looks even a little closer..

    The other images are created by straight interpolation between the first and the last image produced in the mix2Bitmaps method which takes two Bitmaps and the percentage the second should have in the result. (One could expand this method by checking for percent == 0 and percent == 100 respectively and returning a clone of one of the source Bitmaps..)

    Here is my result:

    enter image description here

    private void Form1_Load(object sender, EventArgs e)
    {
        Color targetColor = Color.FromArgb(255, 48, 48, 216);
        pictureBox6.Image = MakeMonoChrome ((Bitmap)pictureBox1.Image, targetColor);
    
        pictureBox2.Image = 
                    mix2Bitmaps((Bitmap)pictureBox1.Image, (Bitmap)pictureBox6.Image, 20);
        pictureBox3.Image = 
                    mix2Bitmaps((Bitmap)pictureBox1.Image, (Bitmap)pictureBox6.Image, 40);
        pictureBox4.Image = 
                    mix2Bitmaps((Bitmap)pictureBox1.Image, (Bitmap)pictureBox6.Image, 60);
        pictureBox5.Image = 
                    mix2Bitmaps((Bitmap)pictureBox1.Image, (Bitmap)pictureBox6.Image, 80);
    }
    
    
    Bitmap mix2Bitmaps(Bitmap bmp0, Bitmap bmp1, int percent)
    {
        Bitmap bmp2 = new Bitmap(bmp0.Width, bmp0.Height, PixelFormat.Format32bppArgb);
    
        int Bpp = 4;  // assuming an effective pixelformat of 32bpp
    
        var bmpData0 = bmp0.LockBits( new Rectangle(0, 0, bmp0.Width, bmp0.Height),
                                      ImageLockMode.ReadOnly, bmp0.PixelFormat);
        var bmpData1 = bmp1.LockBits( new Rectangle(0, 0, bmp1.Width, bmp1.Height),
                                      ImageLockMode.ReadOnly, bmp1.PixelFormat);
        var bmpData2 = bmp2.LockBits( new Rectangle(0, 0, bmp2.Width, bmp2.Height),
                                      ImageLockMode.ReadWrite, bmp2.PixelFormat);
    
    
        int len = bmpData0.Height * bmpData0.Stride;
        byte[] data0 = new byte[len];
        byte[] data1 = new byte[len];
        byte[] data2 = new byte[len];
        Marshal.Copy(bmpData0.Scan0, data0, 0, len);
        Marshal.Copy(bmpData1.Scan0, data1, 0, len);
        Marshal.Copy(bmpData2.Scan0, data2, 0, len);
    
        float pctD = (100f - percent) / 100f;
        float pct  =  percent / 100f;
    
        for (int i = 0; i < len; i += Bpp)
        {
            data2[i + 0] = (byte)(data0[i + 0] * pctD + data1[i + 0] * pct);
            data2[i + 1] = (byte)(data0[i + 1] * pctD + data1[i + 1] * pct);
            data2[i + 2] = (byte)(data0[i + 2] * pctD + data1[i + 2] * pct);
            if (Bpp == 4) data2[i + 3] = 255;   
        }
    
        Marshal.Copy(data2, 0, bmpData2.Scan0, len);
        bmp0.UnlockBits(bmpData0);
        bmp1.UnlockBits(bmpData1);
        bmp2.UnlockBits(bmpData2);
        return bmp2;
    }
    
    public static Bitmap MakeMonoChrome(Bitmap bmp0, Color tCol)
    {
        Bitmap bmp1 = new Bitmap(bmp0.Width, bmp0.Height);
        using (Graphics g = Graphics.FromImage(bmp1) )
        {
          float tr = tCol.R / 255f;
          float tg = tCol.G / 255f;
          float tb = tCol.B / 255f;
    
          ColorMatrix colorMatrix = new ColorMatrix(  new float[][] 
            {
                new float[] {.3f * tr, .3f * tg, .3f * tb, 0, 0},
                new float[] {.59f * tr, .59f * tg, .59f * tb, 0, 0},
                new float[] {.11f * tr, .11f * tg, .11f * tb, 0, 0},
                new float[] {0, 0, 0, 1, 0},
                new float[] {0, 0, 0, 0, 1}
            });
    
          ImageAttributes attributes = new ImageAttributes();
          attributes.SetColorMatrix(colorMatrix);
    
          g.DrawImage(bmp0, new Rectangle(0, 0, bmp0.Width, bmp0.Height),
              0, 0, bmp0.Width, bmp0.Height, GraphicsUnit.Pixel, attributes);
    
        }
        return bmp1;
    }
    

Leave a Reply

Your email address will not be published. Required fields are marked *

You can use these HTML tags and attributes <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>