Prima di parlare di perfromance cerchiamo di controllare il tuo codice:
var originalColor = scrBitmap.GetPixel(i, j);
if (originalColor = Color.Black)
newBitmap.SetPixel(i, j, Color.Red);
Qui ci sono due errori:
- Lei non si confronta con
Color.Black
ma si assegnareColor.Black
-originalColor
.
- Non si gestisce la trasparenza.
Per controllare la trasparenza si dovrebbe confrontare non l'oggetto Color
ma la R, G, valori B, cambiamo per:
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));
Ora vedrai che funziona, ma ci vuole molto molto tempo per elaborare ogni immagine: GetPixel
e SetPixel
sono piuttosto lenti (primario perché controllano e calcolano tutto per ogni chiamata). È molto meglio gestire direttamente i dati bitmap. Se si conosce il formato di immagine in anticipo (ed è fisso per ogni immagine), allora si può fare molto molto più veloce con po 'più di codice:
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;
}
Naturalmente questo può essere further optimized e potrebbe essere necessario per gestire diversi formati (see this list of pixel formats e this article sul loro layout) ma considerano un punto di partenza per lavorare con le bitmap.
Per completezza si tratta di un colore equivalente senza accesso diretto ai dati bitmap. Si prega di notare che questo dovrebbe essere usato raramente perché è terribilmente 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;
}
Inoltre si ricorda che questa considerare canale alfa in confronto (verde così 50% trasparente, per esempio, non è stesso colore verde trasparente 30%). Per ignorare alpha si può usare qualcosa di simile:
if (color.R == toReplace.R && color.G == toReplace.G && color.B == toReplace.B)
Infine, se si sa che i pixel per sostituire sono poco è possibile creare una copia grezza di immagine originale (usando Graphics.FromImage
per creare un contesto e di trarre in esso source
bitmap), in tal modo chiamerai lo SetPixel()
solo quando c'è una sostituzione. IMO qualsiasi ottimizzazione qui è abbastanza inutile: se hai bisogno di prestazioni usa la prima soluzione ...
Questa è la risposta migliore e sono sicuro che il mio requisito è soddisfatto, grazie a DareDevil –
Beh, questo sostituirà ogni colore con quello nuovo (non solo quello selezionato) e il controllo su alfa produrrà risultati non ottimali se c'è un gradiente ma se soddisfa l'OP ... :) –
bene questo codice va bene per immagini di singolo colore. – DareDevil