Как изменить цвет пикселя изображения в С#.NET
Я работаю с изображениями на Java, я разработал более 100 изображений (.png), все они были Trasparent и Black Color Drawing.
Проблема в том, что теперь меня попросили изменить цвет чертежа (черный -to).
Я искал много кода, отрезанных от google, который меняет растровое изображение (пиксели) изображения, но я не догадываюсь, что мне нужно сделать, чтобы соответствовать точному пикселю и заменять его особенно когда изображения в режиме прозрачного.
Ниже приведен код в .Net(С#)
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).
Кто-нибудь может помочь?
Ответы
Ответ 1
Вот решение, которое я сделал с пикселями.
Присоединение исходного кода, чтобы можно было точно и точнее получить результат.
У меня есть образцы изображений размером 128x128 (ширина 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;
}
}
}
//Ниже приведено изображение образца и разные результаты, применяя разные цвета
![enter image description here]()
Модификации кода будут высоко оценены.
Ответ 2
Прежде чем говорить о перфомансе, проверьте свой код:
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;
}
Также обратите внимание, что это рассмотрит альфа-канал в сравнении (например, 50% прозрачный зеленый цвет, например, не такой же, как 30% прозрачный зеленый). Чтобы игнорировать альфу, вы можете использовать что-то вроде этого:
if (color.R == toReplace.R && color.G == toReplace.G && color.B == toReplace.B)
Наконец, если вы знаете, что пиксели для замены немного, вы можете создать необработанную копию исходного изображения (используя Graphics.FromImage
для создания контекста и рисования в нем source
bitmap), таким образом вы вызовете SetPixel()
только при замене. IMO любая оптимизация здесь довольно бесполезна: если вам нужна производительность, используйте первое решение...
Ответ 3
Одним из способов эффективной замены цвета является использование таблицы переназначения. В следующем примере изображение рисуется внутри поля изображения. В событии 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 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
Ответ 4
Я дам вам другое решение, так как это не вычисляет для каждого пикселя.
Его короткий и простой. Время преобразования составляет 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;
}