C# 将图片插值为特定颜色

C# 将图片插值为特定颜色,c#,colors,interpolation,C#,Colors,Interpolation,我希望将图像插值为颜色,以使过渡看起来类似于以下内容: 我原以为这很容易,但似乎比我想象的要难一点。我尝试过的事情: 开始像素和随机颜色之间的平滑线性插值 选择一个随机色调和饱和度,然后插值到该颜色 我不确定这种效果是如何实现的。以下是5个随机非黑色像素的像素如何改变颜色: ╔══════════════════╦═════╦═════╦═════╦════════╦═══════╦═══════╦═════════╗ ║ Sample.Iteration ║ R ║ G ║ B

我希望将图像插值为颜色,以使过渡看起来类似于以下内容:

我原以为这很容易,但似乎比我想象的要难一点。我尝试过的事情:

  • 开始像素和随机颜色之间的平滑线性插值
  • 选择一个随机色调和饱和度,然后插值到该颜色
  • 我不确定这种效果是如何实现的。以下是5个随机非黑色像素的像素如何改变颜色:

    ╔══════════════════╦═════╦═════╦═════╦════════╦═══════╦═══════╦═════════╗
    ║ 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  ║
    ╚══════════════════╩═════╩═════╩═════╩════════╩═══════╩═══════╩═════════╝
    
    值得注意的是,每个RGB颜色似乎是8的倍数,减量或增量也是8的倍数。我只是不知道它在这里做什么样的插值


    我需要能够做的一件事是能够选择一种随机颜色(比如橙色、粉色、蓝色),并能够实现与该随机颜色类似的插值。

    这里有一种方法可以产生一个非常相似的结果。正如我在评论中所写,也许应该研究更复杂的方法,但对于这个例子来说,这似乎可以做到

    我添加了6个图片框来模拟你的截图。第一个加载您的第一个图像

    最后一个被分配一个makemonology函数的结果,该函数使用一个ColorMatrix;这是将目标颜色乘以矩阵的常用灰度代码的变体。这里重要的一点是幻数,它包含3个通道的加权亮度

    你可能想玩目标颜色;我认为(255,64,64255)看起来更接近

    其他图像是通过在
    mix2Bitmaps
    方法中生成的第一个图像和最后一个图像之间直接插值创建的,该方法采用两个位图以及第二个位图在结果中所占的百分比。(可以通过分别检查
    percent==0
    percent==100
    并返回一个源位图的克隆来扩展此方法。)

    以下是我的结果:

    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;
    }
    

    private void Form1\u加载(对象发送方,事件参数e)
    {
    Color targetColor=Color.FromArgb(255,48,48,216);
    pictureBox6.Image=makemonology((位图)pictureBox1.Image,targetColor);
    pictureBox2.图像=
    mix2Bitmaps((位图)pictureBox1.Image,(位图)pictureBox6.Image,20);
    pictureBox3.图像=
    mix2Bitmaps((位图)pictureBox1.Image,(位图)pictureBox6.Image,40);
    pictureBox4.图像=
    mix2Bitmaps((位图)pictureBox1.Image,(位图)pictureBox6.Image,60);
    pictureBox5.图像=
    mix2Bitmaps((位图)pictureBox1.Image,(位图)pictureBox6.Image,80);
    }
    位图mix2Bitmaps(位图bmp0、位图bmp1、整数百分比)
    {
    位图bmp2=新位图(bmp0.Width、bmp0.Height、PixelFormat.Format32bppArgb);
    int Bpp=4;//假设有效的像素格式为32bpp
    var bmpData0=bmp0.LockBits(新矩形(0,0,bmp0.Width,bmp0.Height),
    ImageLockMode.ReadOnly,bmp0.PixelFormat);
    var bmpData1=bmp1.LockBits(新矩形(0,0,bmp1.Width,bmp1.Height),
    ImageLockMode.ReadOnly,bmp1.PixelFormat);
    var bmpData2=bmp2.LockBits(新矩形(0,0,bmp2.Width,bmp2.Height),
    ImageLockMode.ReadWrite,bmp2.PixelFormat);
    int len=bmpData0.身高*bmpData0.步幅;
    字节[]数据0=新字节[len];
    字节[]数据1=新字节[len];
    字节[]数据2=新字节[len];
    封送处理副本(bmpData0.Scan0,data0,0,len);
    封送处理副本(bmpData1.Scan0,data1,0,len);
    封送处理副本(bmpData2.Scan0,数据2,0,len);
    浮动pctD=(百分之一百华氏度)/百分之一百华氏度;
    浮动pct=百分比/100f;
    对于(int i=0;i
    颜色在3d空间中,如果颜色相距较远,则直接插值将穿过内部部分,即将穿过枯燥和模糊的灰色。如果您需要避免这种情况,您可能必须找到一个或两个中间的“停止”颜色。使用这种渐变的一种简单方法是LinearGradientBrush,它可以在两种基本端点颜色之间轻松使用StopColor。。为了找到最好的方法,我需要了解更多关于源图像、目标颜色和步骤数的信息。这实际上非常聪明。我只是想弄清楚ColorMatrix的实际功能,因为我需要将这个概念抽象到一个不同的非GDI+应用程序中。这是另一个有趣的讨论。也来自。编辑:事实上我用过酷。(哈-我从来没有听说过……)