2013-06-20 27 views
13

Pracuję z obrazami w języku Java, zaprojektowałem więcej niż ponad 100 obrazów (.png), wszystkie były rysunkami Trasparent i Black Color.Jak zmienić kolor piksela obrazu w języku C# .NET

Problem polega na tym, że teraz zostałem poproszony o zmianę koloru rysunku (czarny-do).

Mam przeszukane wiele kodu snipped w google, który zmienia bitmapę (piksele) obrazu, ale nie zgaduję, co muszę zrobić, aby dopasować dokładny piksel i zastąpić specjalnie, gdy obrazy są w trybie przezroczystym. Poniżej znajduje się kod w .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; 

ale nie było dopasowane w ogóle, ja debugowany go w całym pliku, nie było warto czerwony, zielony, niebieski parametrów kolorów (originalColor) zmienna.

Ktoś może pomóc?

Odpowiedz

24

Oto rozwiązanie, które zrobiłem z pikselami.

Dołączanie kodu źródłowego, aby można było wypróbować dokładnie i uzyskać wynik.

Mam przykładowe obrazy o wymiarach 128x128 (szerokość x wysokość).

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

// Poniżej obraz próbki i różne wyniki, stosując inny kolor enter image description here

modyfikacje kodu będzie wysoko cenione.

+1

To jest najlepsza odpowiedź i jestem pewien, że moje wymaganie jest spełnione, dzięki DareDevil –

+3

To zamieni każdy kolor na nowy (nie tylko na wybrany) i sprawdzi, czy alfa wygeneruje nieoptymalne wyniki, jeśli jest gradient, ale jeśli spełnia OP ... :) –

+1

również ten kod jest w porządku dla obrazy jednego koloru. – DareDevil

15

Przed mówimy o perfromance niech sprawdzić kod:

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

Tutaj są dwa błędy:

  1. Nie porównać do Color.Black ale przypisaćColor.Black do originalColor.
  2. Nie obsługuje przezroczystości.

Aby sprawdzić przejrzystości należy porównać nie przedmiot Color ale R, G, wartości B, zmieńmy do:

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

Teraz zobaczysz, że to działa, ale to trwa bardzo długi czas przetwarzania każdego obrazu: GetPixel i SetPixel są dość powolne (podstawowe, ponieważ sprawdzają i obliczają wszystko dla każdego połączenia). O wiele lepiej jest bezpośrednio obsługiwać dane bitmapowe. Jeśli znasz format obrazu z góry (i to ustalone dla każdego obrazu), to można to zrobić o wiele szybciej z trochę więcej kodu:

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

Oczywiście może to być further optimized i może trzeba obsługiwać różne formaty (see this list of pixel formats i this article dotyczące ich układu), ale uważają go za punkt wyjścia do pracy z bitmapami.

Dla kompletności jest to odpowiednik koloru bez bezpośredniego dostępu do danych bitmapowych. Pamiętaj, że powinno to być rzadko używane, ponieważ jest bardzo powolne.

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

Również należy pamiętać, że to rozważyć kanał alfa w porównaniu (tak 50% przezroczysty zielony, na przykład, nie jest taki sam kolor jak 30% przezroczysty zielony). Zignorować alfa można użyć mniej więcej tak:

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

Wreszcie, jeśli wiesz, że pikseli do zastąpienia są mało można stworzyć surową kopię oryginalnego obrazu (przy użyciu Graphics.FromImage stworzyć kontekst i wyciągnąć do niego source bitmapę), w ten sposób zadzwonisz pod numer SetPixel() tylko wtedy, gdy nastąpi wymiana. IMO optymalizacja tutaj jest całkiem bezużyteczna: jeśli potrzebujesz wydajności, skorzystaj z pierwszego rozwiązania ...

+0

W pośpiechu, zrobiłem wszystko i zrobiło usunięta == –

+1

@BibiTahira co został usunięty? –

+0

kiedy kopiowałem kolor piksela z kolorem systemu –

4

Jednym ze sposobów skutecznej zamiany koloru jest użycie tabeli mapowania. W poniższym przykładzie obraz jest rysowany wewnątrz ramki graficznej. W przypadku malowania The Color.Black kolor zmienia się na 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); 
     } 
    } 

Więcej informacji: http://msdn.microsoft.com/en-us/library/4b4dc1kz%28v=vs.110%29.aspx

1

dam Ci inne rozwiązanie ponieważ nie oblicza dla każdego piksela.

Jest krótki i prosty. Konwersja czas wynosi 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; 
     } 
+2

Jak utworzyć matrycę kolorów dla określonego koloru? Takich jak Green, Blue itp? –