【问题标题】:Adding or subtracting color from an image in a pictureBox using C#使用C#从pictureBox中的图像中添加或减去颜色
【发布时间】:2015-05-01 19:08:15
【问题描述】:

我想学习如何在 Visual Studio 中编写一些非常基本的图像编辑。我正在使用 openFileDialog 将图片加载到图片框中。我在互联网上发现了一些循环,这些循环旨在逐个像素地转换颜色,但由于各种原因(每个代码示例不同)都不起作用。添加(或减去)红色值的正确方法是什么,例如更改图片框中图像的色调?我正在使用 C#。

谢谢

编辑:这是一个至少是一个起点的例子:

        Bitmap bmp = (Bitmap)Bitmap.FromFile(pictureBox1.ImageLocation);

        for (int x = 0; x < bmp.Width; x++)
        {
            for (int y = 0; y < bmp.Height; y++)
            {
                bmp.GetPixel(x, y);
                bmp.SetPixel(x, y, Color.FromArgb(128, 0, 128));
            }
        }

        pictureBox1.Image = bmp;
        MessageBox.Show("Done"); 

这允许我逐个像素地获取图像,并将颜色更改为紫色,在本例中为紫色。当然,这不是我想做的。我想要做的是获取每个像素的原始RGB值,并增加或减少这些值。换句话说,执行一些非常基本的颜色校正。

如何获取每个像素的当前 RGB,并设置新像素的 RGB?

我也看到此贴作为示例。问题是,我看不到如何使用 ModifyHue:

        var bmp = new Bitmap(pictureBox1.ImageLocation);
        for (int x = 0; x < bmp.Width; x++)
        {
            for (int y = 0; y < bmp.Height; y++)
            {
                Color oldColor = bmp.GetPixel(x, y);
                Color newColor = ModifyHue(oldColor);
                bmp.SetPixel(x, y, newColor);
            }
        }  

        pictureBox1.Image = bmp;

我意识到我应该第一次发布代码示例。谢谢

【问题讨论】:

  • 您应该尝试发布您的尝试之一,以便我们可以更好地帮助您。为了速度,您最终应该使用LockBits,但现在要了解基础知识,使用SetPixelBitmap 中的像素进行常规循环就可以了。之后,您需要将更改后的位图重新分配PictureBox.Image
  • 一个简单的问题:如何重新分配更改的位图?不这样做可能是我没有看到任何结果的原因。我正在刷新图片框,但这并没有完成。谢谢
  • 完成对Bitmap bmp 的更改后,您只需编写picturebox1.Image = bmp;
  • PS:我正在尝试 pictureBox1.Image = null;图片框1.Image = bmp;图片框1.Refresh();但没有看到结果。删除 null 也不起作用。谢谢
  • 现在得到一些结果,我需要稍后微调此代码,然后将其发布,以便每个人都可以按照您的建议提供更多帮助。谢谢你

标签: c# image-processing picturebox openfiledialog


【解决方案1】:

这是使用GetpixelSetPixel 的示例。

对于(很多)更快的过滤结果,请查看Lockbits 以及使用ColorMatrix

private void button2_Click(object sender, EventArgs e)
{
    // we pull the bitmap from the image
    Bitmap bmp = (Bitmap) pictureBox1.Image;

    // we change some picels
    for (int y = 100; y < bmp.Height; y++) 
    for (int x = 100; x < bmp.Width; x++)
    {
        Color c = bmp.GetPixel(x, y);
        bmp.SetPixel(x, y, Color.FromArgb(255, 255, c.G, c.B));
    }
    // we need to re-assign the changed bitmap
    pictureBox1.Image = (Bitmap) bmp;
}

更新:

上面的代码是一个很简单的介绍。它很简单,但也很慢,而且不是很灵活。

这是一个非常快速且更加灵活的版本:

private void button3_Click(object sender, EventArgs e)
{
    // pick one of our filter methods
    ModifyHue hueChanger = new ModifyHue(MaxChannel);

    // we pull the bitmap from the image
    Bitmap bmp = (Bitmap)pictureBox1.Image;
    Size s = bmp.Size;
    PixelFormat fmt = bmp.PixelFormat;
    // we need the bit depth and we assume either 32bppArgb or 24bppRgb !
    byte bpp = (byte)(fmt == PixelFormat.Format32bppArgb ? 4 : 3);
    // lock the bits and prepare the loop
    Rectangle rect = new Rectangle(Point.Empty, s);
    BitmapData bmpData = bmp.LockBits(rect, ImageLockMode.ReadOnly, fmt);
    int size1 = bmpData.Stride * bmpData.Height;
    byte[] data = new byte[size1];
    System.Runtime.InteropServices.Marshal.Copy(bmpData.Scan0, data, 0, size1);
    // loops
    for (int y = 0; y < s.Height; y++)
    {
        for (int x = 0; x < s.Width; x++)
        {
            // calculate the index
            int index = y * bmpData.Stride + x * bpp;
            // get the color
            Color c = Color.FromArgb( bpp == 4 ?data[index + 3]: 255 , 
                                      data[index + 2], data[index + 1], data[index]);
            // process it
            c = hueChanger(c, 2); 
            // set the channels from the new color
            data[index + 0] = c.B;
            data[index + 1] = c.G;
            data[index + 2] = c.R;
            if (bpp == 4) data[index + 3] = c.A;
        }
    }

    System.Runtime.InteropServices.Marshal.Copy(data, 0, bmpData.Scan0, data.Length);
    bmp.UnlockBits(bmpData);

   // we need to re-assign the changed bitmap
    pictureBox1.Image = (Bitmap)bmp;
}

以上代码调用了delegate

public delegate Color ModifyHue(Color c, int ch);

并且delegate被设置为调用一个简单的过滤函数:

public Color MaxChannel(Color c, int channel)
{
    if (channel == 1) return Color.FromArgb(255, 255, c.G, c.B);
    if (channel == 2) return Color.FromArgb(255, c.R, 255, c.B);
    if (channel == 3) return Color.FromArgb(255, c.R, c.G, 255);
    else return c;
}

这是另一个将Color 更改为灰色的方法

public Color ToGreyscale(Color c, int dummy)
{
    byte val = (byte) ( (c.R * 0.299f + c.G * 0.587f + c.B *0.114f)  ) ;
    return Color.FromArgb(255, val, val,val);
}

请注意,我们要通过delegate 调用的所有方法都需要具有相同的signature。因此ToGreyscale 也将integer 作为第二个参数,即使它不使用它..

还请注意,您可以像之前的简单示例一样限制 LockBits 循环开始和结束值以获得第二个屏幕截图..

【讨论】:

  • 这绝对让我朝着正确的方向前进,欢迎提出其他想法。谢谢!
  • 我已经扩展了答案以包含更复杂的解决方案。它非常快速且相当灵活。那里有很多东西要学习,但别担心,你不需要了解它的每一点来使用和修改它..
猜你喜欢
  • 2020-04-11
  • 2010-12-01
  • 2021-07-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-01-28
  • 2020-09-09
  • 2012-02-18
相关资源
最近更新 更多