web-dev-qa-db-fra.com

Comment changer la couleur des pixels d'une image en C # .NET

Je travaille avec des images en Java, j'ai conçu plus de 100+ images (.png) au format, elles étaient toutes transparentes et dessin en couleur noire.

Le problème est, Maintenant, on m'a demandé de changer la couleur du dessin (noir à).

J'ai recherché de nombreux codes coupés sur google, qui modifient le bitmap (pixels) de l'image, mais je ne devine pas ce que je dois faire pour correspondre au pixel exact et remplacer spécialement lorsque les images sont en mode transparent. Ci-dessous le code 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;

mais il ne correspondait pas du tout, je l'ai débogué, tout au long du fichier, il n'y avait pas de valeur de paramètres Rouge, Vert, Bleu de la variable Couleur (originalColor).

Quelqu'un peut-il aider?

17
Bibi Tahira

Voici la solution que j'ai faite avec les pixels.

Attacher le code source pour que l'on puisse essayer l'exact et obtenir le résultat.

J'ai des exemples d'images de 128x128 (largeur x hauteur).

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

// Ci-dessous l'image échantillon et différents résultats en appliquant des couleurs différentes enter image description here

Les modifications du code seront très appréciées.

28
DareDevil

Avant de parler de performance, vérifions votre code:

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

Ici, il y a deux erreurs:

  1. Vous ne vous comparez pas à Color.Black Mais vous affectez Color.Black À originalColor.
  2. Vous ne gérez pas la transparence.

Pour vérifier la transparence, vous devez comparer non pas l'objet Color mais les valeurs R, G, B, passons à:

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

Vous verrez maintenant que cela fonctionne, mais le traitement de chaque image prend beaucoup de temps: GetPixel et SetPixel sont assez lents (principaux car ils vérifient et calculent tout pour chaque appel). Il est préférable de gérer directement les données bitmap. Si vous connaissez le format d'image à l'avance (et qu'il est fixe pour chaque image), vous pouvez le faire beaucoup plus rapidement avec un peu plus de code:

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

Bien sûr, cela peut être optimisé et vous devrez peut-être gérer différents formats ( voir cette liste de formats de pixels et cet article à propos de leur disposition) mais considérez-le comme un point de départ pour travailler avec des bitmaps.

Pour être complet, il s'agit d'une couleur équivalente sans accès direct aux données bitmap. Veuillez noter que cela devrait être rarement utilisé car il est terriblement lent.

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

Veuillez également noter que cela considère le canal alpha en comparaison (donc 50% de vert transparent, par exemple, n'est pas de la même couleur que 30% de vert transparent). Pour ignorer alpha, vous pouvez utiliser quelque chose comme ceci:

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

Enfin, si vous savez que les pixels à remplacer sont peu nombreux, vous pouvez créer une copie brute de l'image originale (en utilisant Graphics.FromImage Pour créer un contexte et y dessiner source bitmap), de cette manière vous ' ll n'appelle SetPixel() qu'en cas de remplacement. IMO toute optimisation ici est assez inutile: si vous avez besoin de performances, utilisez la première solution ...

16
Adriano Repetti

Une façon de remplacer efficacement une couleur est d'utiliser une table de remappage. Dans l'exemple suivant, une image est dessinée à l'intérieur d'une zone d'image. Dans l'événement Paint, la couleur Color.Black est remplacée par 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);
        }
    }

Plus d'informations: http://msdn.Microsoft.com/en-us/library/4b4dc1kz%28v=vs.110%29.aspx

5
jcibar

Je vais vous donner une autre solution car cela ne calcule pas pour chaque pixel.

C'est court et simple. Le temps de conversion est 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;
        }
1
Nicholas