I'd like to learn how to code some very basic image editing in Visual Studio. I'm using the openFileDialog to load a picture into a pictureBox. I've found some loops on the internet that are designed to convert color on a pixel by pixel basis, but for various reasons (that differ with each code sample) none work. What is the correct way to add to (or subtract from), for example, the red value, to change the tint of an image in a pictureBox? I'm using C#.
我想学习如何在Visual Studio中编写一些非常基本的图像编辑。我正在使用openFileDialog将图片加载到pictureBox中。我在互联网上发现了一些设计用于逐个像素地转换颜色的循环,但由于各种原因(每个代码示例不同)都没有用。添加到(或减去)的正确方法是什么,例如红色值,以更改pictureBox中图像的色调?我正在使用C#。
Thank you
谢谢
EDIT: Here is an example of something that's at least a starting point:
编辑:这是一个至少是一个起点的例子:
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");
This allows me to get the image pixel by pixel, and change the color, in this case, to purple. Of course, that's not what I want to do. What I want to do, is get the original RGB value of each pixel, and increase or decrease the values. In other words, perform some very basic color correction.
这允许我逐个像素地获取图像,并且在这种情况下将颜色改变为紫色。当然,这不是我想要做的。我想要做的是获取每个像素的原始RGB值,并增加或减少值。换句话说,执行一些非常基本的颜色校正。
How do I get the current RGB of each pixel, and set the RGB of the new pixel?
如何获取每个像素的当前RGB,并设置新像素的RGB?
I have also seen this posted as an example. The problem is, I don't see how to use ModifyHue:
我也看到过这个例子。问题是,我没有看到如何使用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;
I realize I should have posted code samples the first time around. Thank you
我意识到我应该第一次发布代码示例。谢谢
1 个解决方案
#1
2
This is an example of using Getpixel
and SetPixel
.
这是使用Getpixel和SetPixel的示例。
For (much much) faster filter results look into Lockbits and also into using a ColorMatrix
对于(更多)更快的滤波器结果,可以查看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;
}
Update:
更新:
The code above is a very simple introduction. It is simple but also very slow and it is not very flexible.
上面的代码是一个非常简单的介绍。它很简单但也很慢,而且不是很灵活。
Here is a version that is both very fast and and much more flexible:
这是一个非常快速且更灵活的版本:
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;
}
The above code calls a delegate
:
上面的代码调用一个委托:
public delegate Color ModifyHue(Color c, int ch);
And the delegate
is set to call a simple filter function:
并且委托设置为调用简单的过滤器函数:
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;
}
And here is another one that changes a Color
to grey
这是另一个将颜色变为灰色的颜色
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);
}
Note that all methods we want to call via a delegate
need to have the same signature
. Therefore ToGreyscale
also takes an integer
as second parameter, even though it doesn't use it..
请注意,我们要通过委托调用的所有方法都需要具有相同的签名。因此ToGreyscale也会将整数作为第二个参数,即使它不使用它。
Also note that you can limit the LockBits
loop start and end values just like in the simple example before to get the second screenshot..
另请注意,您可以限制LockBits循环开始和结束值,就像之前的简单示例一样,以获取第二个屏幕截图。
#1
2
This is an example of using Getpixel
and SetPixel
.
这是使用Getpixel和SetPixel的示例。
For (much much) faster filter results look into Lockbits and also into using a ColorMatrix
对于(更多)更快的滤波器结果,可以查看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;
}
Update:
更新:
The code above is a very simple introduction. It is simple but also very slow and it is not very flexible.
上面的代码是一个非常简单的介绍。它很简单但也很慢,而且不是很灵活。
Here is a version that is both very fast and and much more flexible:
这是一个非常快速且更灵活的版本:
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;
}
The above code calls a delegate
:
上面的代码调用一个委托:
public delegate Color ModifyHue(Color c, int ch);
And the delegate
is set to call a simple filter function:
并且委托设置为调用简单的过滤器函数:
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;
}
And here is another one that changes a Color
to grey
这是另一个将颜色变为灰色的颜色
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);
}
Note that all methods we want to call via a delegate
need to have the same signature
. Therefore ToGreyscale
also takes an integer
as second parameter, even though it doesn't use it..
请注意,我们要通过委托调用的所有方法都需要具有相同的签名。因此ToGreyscale也会将整数作为第二个参数,即使它不使用它。
Also note that you can limit the LockBits
loop start and end values just like in the simple example before to get the second screenshot..
另请注意,您可以限制LockBits循环开始和结束值,就像之前的简单示例一样,以获取第二个屏幕截图。