如何在C#.NET中更改图像的像素颜色
我正在使用Java中的Images,我设计了超过100种图像(.png)格式,它们都是Trasparent和Black Color Drawing。
问题是,现在我被要求改变绘图的颜色(黑色 – )。
我搜索了许多在谷歌剪断的代码,它改变了图像的位图(像素),但我不是猜测我必须做什么来匹配精确的像素,并在图像处于透明模式时特别替换。 下面是.Net(C#)中的代码
Bitmap newBitmap = new Bitmap(scrBitmap.Width, scrBitmap.Height); for (int i = 0; i < scrBitmap.Width; i++) { for (int j = 0; j < scrBitmap.Height; j++) { originalColor = scrBitmap.GetPixel(i, j); if(originalColor = Color.Black) newBitmap.SetPixel(i, j, Color.Red); } } return newBitmap;
但它根本没有匹配,我调试了它,在整个文件中,没有Color(originalColor)变量的Red,Green,Blue参数的值。
有人可以帮忙吗?
这是我用Pixels做的解决方案。
附加源代码,以便可以尝试精确并获得结果。
我有128×128(宽x高)的样本图像。
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Drawing; using System.IO; //using System.Globalization; namespace colorchange { class Program { static void Main(string[] args) { try { Bitmap bmp = null; //The Source Directory in debug\bin\Big\ string[] files = Directory.GetFiles("Big\\"); foreach (string filename in files) { bmp = (Bitmap)Image.FromFile(filename); bmp = ChangeColor(bmp); string[] spliter = filename.Split('\\'); //Destination Directory debug\bin\BigGreen\ bmp.Save("BigGreen\\" + spliter[1]); } } catch (System.Exception ex) { Console.WriteLine(ex.ToString()); } } public static Bitmap ChangeColor(Bitmap scrBitmap) { //You can change your new color here. Red,Green,LawnGreen any.. Color newColor = Color.Red; Color actualColor; //make an empty bitmap the same size as scrBitmap Bitmap newBitmap = new Bitmap(scrBitmap.Width, scrBitmap.Height); for (int i = 0; i < scrBitmap.Width; i++) { for (int j = 0; j < scrBitmap.Height; j++) { //get the pixel from the scrBitmap image actualColor = scrBitmap.GetPixel(i, j); // > 150 because.. Images edges can be of low pixel colr. if we set all pixel color to new then there will be no smoothness left. if (actualColor.A > 150) newBitmap.SetPixel(i, j, newColor); else newBitmap.SetPixel(i, j, actualColor); } } return newBitmap; } } }
//下面是示例图像,并通过应用不同的颜色得到不同的结果
代码修改将受到高度赞赏。
在我们谈论性能之前,让我们检查你的代码:
var originalColor = scrBitmap.GetPixel(i, j); if (originalColor = Color.Black) newBitmap.SetPixel(i, j, Color.Red);
这里有两个错误:
- 您不能与
Color.Black
进行比较,但将Color.Black
指定给originalColor
。 - 你不处理透明度。
要检查透明度,您应该比较Color
对象而不是R,G,B值,让我们改为:
var originalColor = scrBitmap.GetPixel(i, j); if (originalColor.R == 0 && originalColor.G == 0 && originalColor.B == 0) newBitmap.SetPixel(i, j, Color.FromArgb(originalColor.A, Color.Red));
现在您将看到它可以工作,但处理每个图像需要很长时间: GetPixel
和SetPixel
非常慢(主要因为它们检查并计算每个调用的所有内容)。 直接处理位图数据要好得多。 如果您事先知道图像格式(并且每个图像都是固定的),那么您可以使用更多的代码更快地完成它:
static unsafe Bitmap ReplaceColor(Bitmap source, Color toReplace, Color replacement) { const int pixelSize = 4; // 32 bits per pixel Bitmap target = new Bitmap( source.Width, source.Height, PixelFormat.Format32bppArgb); BitmapData sourceData = null, targetData = null; try { sourceData = source.LockBits( new Rectangle(0, 0, source.Width, source.Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb); targetData = target.LockBits( new Rectangle(0, 0, target.Width, target.Height), ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb); for (int y = 0; y < source.Height; ++y) { byte* sourceRow = (byte*)sourceData.Scan0 + (y * sourceData.Stride); byte* targetRow = (byte*)targetData.Scan0 + (y * targetData.Stride); for (int x = 0; x < source.Width; ++x) { byte b = sourceRow[x * pixelSize + 0]; byte g = sourceRow[x * pixelSize + 1]; byte r = sourceRow[x * pixelSize + 2]; byte a = sourceRow[x * pixelSize + 3]; if (toReplace.R == r && toReplace.G == g && toReplace.B == b) { r = replacement.R; g = replacement.G; b = replacement.B; } targetRow[x * pixelSize + 0] = b; targetRow[x * pixelSize + 1] = g; targetRow[x * pixelSize + 2] = r; targetRow[x * pixelSize + 3] = a; } } } finally { if (sourceData != null) source.UnlockBits(sourceData); if (targetData != null) target.UnlockBits(targetData); } return target; }
当然,这可以进一步优化 ,您可能需要处理不同的格式( 请参阅此像素格式列表和本文关于其布局的文章 ),但将其视为使用位图的起点。
为了完整性,这是等效的颜色,不能直接访问位图数据。 请注意,这应该很少使用,因为它非常慢。
static Bitmap ReplaceColor(Bitmap source, Color toReplace, Color replacement) { var target = new Bitmap(source.Width, source.Height); for (int x = 0; x < source.Width; ++x) { for (int y = 0; y < source.Height; ++y) { var color = source.GetPixel(x, y); target.SetPixel(x, y, color == toReplace ? replacement : color); } } return target; }
另请注意,这比较考虑alpha通道(例如,50%透明绿色,与30%透明绿色不同)。 要忽略alpha,您可以使用以下内容:
if (color.R == toReplace.R && color.G == toReplace.G && color.B == toReplace.B)
最后,如果您知道要替换的像素很少,您可以创建原始图像的原始副本(使用Graphics.FromImage
创建上下文并绘制到source
位图),这样您只能在那里调用SetPixel()
是一个替代品。 IMO在这里的任何优化都是无用的:如果你需要性能使用第一个解决方案......
有效替换颜色的一种方法是使用重映射表。 在以下示例中,将在图片框内绘制图像。 在Paint事件中,Color.Black的颜色更改为Color.Blue:
private void pictureBox_Paint(object sender, PaintEventArgs e) { Graphics g = e.Graphics; using (Bitmap bmp = new Bitmap("myImage.png")) { // Set the image attribute's color mappings ColorMap[] colorMap = new ColorMap[1]; colorMap[0] = new ColorMap(); colorMap[0].OldColor = Color.Black; colorMap[0].NewColor = Color.Blue; ImageAttributes attr = new ImageAttributes(); attr.SetRemapTable(colorMap); // Draw using the color map Rectangle rect = new Rectangle(0, 0, bmp.Width, bmp.Height); g.DrawImage(bmp, rect, 0, 0, rect.Width, rect.Height, GraphicsUnit.Pixel, attr); } }
更多信息: http : //msdn.microsoft.com/en-us/library/4b4dc1kz%28v=vs.110%29.aspx
我会给你另一个解决方案,因为这不计算每个像素。
它简短而简单。 转换时间为62毫秒:
public Bitmap Color(Bitmap original) { //create a blank bitmap the same size as original Bitmap newBitmap = new Bitmap(original.Width, original.Height); //get a graphics object from the new Image Graphics g = Graphics.FromImage(newBitmap); //create the color you want ColorMatrix //now is set to red, but with different values //you can get anything you want. ColorMatrix colorMatrix = new ColorMatrix( new float[][] { new float[] {1f, .0f, .0f, 0, 0}, new float[] {1f, .0f, .0f, 0, 0}, new float[] {1f, .0f, .0f, 0, 0}, new float[] {0, 0, 0, 1, 0}, new float[] {0, 0, 0, 0, 1} }); //create some image attributes ImageAttributes attributes = new ImageAttributes(); //set the color matrix attribute attributes.SetColorMatrix(colorMatrix); //draw original image on the new image using the color matrix g.DrawImage(original, new Rectangle(0, 0, original.Width, original.Height), 0, 0, original.Width, original.Height, GraphicsUnit.Pixel, attributes); //release sources used g.Dispose(); return newBitmap; }