Cómo cambiar el color de píxel de una imagen en C # .NET

Estoy trabajando con imágenes en Java, he diseñado más de más de 100 imágenes en formato (.png), todas eran de color transparente y transparente.

El problema es que ahora me han pedido que cambie el color del Dibujo (Negro a).

He buscado muchos códigos recortados en Google, que cambian el bitmap (píxeles) de la imagen, pero no estoy adivinando qué debo hacer para que coincida con el píxel exacto y reemplazar especialmente cuando las imágenes están en modo transparente. A continuación se muestra el código en .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; 

pero no coincidía en absoluto, lo depuré, en todo el archivo, no había ningún valor de los parámetros Rojo, Verde, Azul de la variable Color (originalColor).

¿Alguien puede ayudar?

Aquí está la solución que he hecho con píxeles.

Adjuntando el código fuente para que uno pueda probar el exacto y obtener el resultado.

Tengo imágenes de muestra de 128×128 (Ancho x Alto).

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

// A continuación se muestra la imagen de muestra y los diferentes resultados aplicando diferentes colores. introduzca la descripción de la imagen aquí

Las modificaciones de código serán altamente apreciadas.

Antes de que hablemos del rendimiento, revisemos su código:

 var originalColor = scrBitmap.GetPixel(i, j); if (originalColor = Color.Black) newBitmap.SetPixel(i, j, Color.Red); 

Aquí hay dos errores:

  1. No se compara con Color.Black pero asigna Color.Black a Color.Black originalColor .
  2. Usted no maneja la transparencia.

Para verificar la transparencia, no debe comparar el objeto Color sino los valores R, G, B, cambiemos a:

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

Ahora verá que funciona, pero se tarda mucho tiempo en procesar cada imagen: GetPixel y SetPixel son bastante lentos (principalmente porque comprueban y calculan todo para cada llamada). Es mucho mejor manejar los datos de bitmap directamente. Si conoce el formato de la imagen de antemano (y es fijo para cada imagen), puede hacerlo mucho más rápido con un poco más de código:

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

Por supuesto, esto puede optimizarse aún más y es posible que deba manejar diferentes formatos ( consulte esta lista de formatos de píxeles y este artículo sobre su diseño), pero considere que es un punto de partida para trabajar con mapas de bits.

Para completar, este es un color equivalente sin acceso directo a los datos de bitmap. Tenga en cuenta que esto debe ser usado raramente porque es terriblemente lento.

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

También tenga en cuenta que esto considera el canal alfa en comparación (por lo tanto, el 50% de verde transparente, por ejemplo, no es del mismo color que el 30% de verde transparente). Para ignorar alfa puedes usar algo como esto:

 if (color.R == toReplace.R && color.G == toReplace.G && color.B == toReplace.B) 

Finalmente, si sabe que los píxeles a reemplazar son escasos, puede crear una copia en bruto de la imagen original (usando Graphics.FromImage para crear un contexto y dibujar en su bitmap de source ), de esa manera llamará SetPixel() solo cuando haya Es un reemplazo. En mi opinión, cualquier optimización aquí es bastante inútil: si necesita rendimiento, use la primera solución ...

Una forma de reemplazar un color de manera eficiente es usar una tabla de reasignación. En el siguiente ejemplo, una imagen se dibuja dentro de un cuadro de imagen. En el evento Paint, el color Color.Black se cambia a 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); } } 

Más información: http://msdn.microsoft.com/en-us/library/4b4dc1kz%28v=vs.110%29.aspx

Te daré otra solución ya que esto no se calcula para cada píxel.

Es corto y sencillo. El tiempo de conversión es de 62 ms:

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