objeto - C#- Alternativas más rápidas a SetPixel y GetPixel para mapas de bits para la aplicación Windows Forms
objeto imagen c# (4)
Estoy tratando de enseñarme C # y he escuchado de una variedad de fuentes que las funciones se ponen y setpixel puede ser terriblemente lento. ¿Cuáles son algunas de las alternativas y es la mejora del rendimiento realmente tan importante? ¡Gracias por adelantado!
Una parte de mi código de referencia:
public static Bitmap Paint(Bitmap _b, Color f)
{
Bitmap b = new Bitmap(_b);
for (int x = 0; x < b.Width; x++)
{
for (int y = 0; y < b.Height; y++)
{
Color c = b.GetPixel(x, y);
b.SetPixel(x, y, Color.FromArgb(c.A, f.R, f.G, f.B));
}
}
return b;
}
El código inmediatamente utilizable
public class DirectBitmap : IDisposable
{
public Bitmap Bitmap { get; private set; }
public Int32[] Bits { get; private set; }
public bool Disposed { get; private set; }
public int Height { get; private set; }
public int Width { get; private set; }
protected GCHandle BitsHandle { get; private set; }
public DirectBitmap(int width, int height)
{
Width = width;
Height = height;
Bits = new Int32[width * height];
BitsHandle = GCHandle.Alloc(Bits, GCHandleType.Pinned);
Bitmap = new Bitmap(width, height, width * 4, PixelFormat.Format32bppPArgb, BitsHandle.AddrOfPinnedObject());
}
public void SetPixel(int x, int y, Color colour)
{
int index = x + (y * Width);
int col = colour.ToArgb();
Bits[index] = col;
}
public Color GetPixel(int x, int y)
{
int index = x + (y * Width);
int col = Bits[index];
Color result = Color.FromArgb(col);
return result;
}
public void Dispose()
{
if (Disposed) return;
Disposed = true;
Bitmap.Dispose();
BitsHandle.Free();
}
}
No hay necesidad de LockBits
o SetPixel
. Utilice la clase anterior para acceder directamente a los datos de mapa de bits.
Con esta clase, es posible establecer datos de mapa de bits sin procesar como datos de 32 bits. Tenga en cuenta que es PARGB, que es alpha premultiplicado. Consulte Alpha Compositing en Wikipedia para obtener más información sobre cómo funciona esto y ejemplos en el artículo de MSDN sobre BLENDFUNCTION para averiguar cómo calcular correctamente el alfa.
Si la premultiplicación puede complicar PixelFormat.Format32bppArgb
cosas, use PixelFormat.Format32bppArgb
en PixelFormat.Format32bppArgb
lugar. Un golpe de rendimiento se produce cuando se dibuja, porque se convierte PixelFormat.Format32bppPArgb
a PixelFormat.Format32bppPArgb
. Si la imagen no tiene que cambiar antes de dibujarse, el trabajo se puede hacer antes de la premultiplicación, dibujar en un buffer PixelFormat.Format32bppArgb
, y seguir utilizándolo desde allí.
El acceso a los miembros de Bitmap
estándar se expone a través de la propiedad de Bitmap
. Se accede directamente a los datos de mapa de bits utilizando la propiedad Bits
.
Usar byte
lugar de int
para datos de píxeles sin procesar
Cambie ambas instancias de Int32
a byte
, y luego cambie esta línea:
Bits = new Int32[width * height];
A esto:
Bits = new byte[width * height * 4];
Cuando se usan bytes, el formato es Alfa / Rojo / Verde / Azul en ese orden. Cada píxel toma 4 bytes de datos, uno para cada canal. Las funciones GetPixel y SetPixel necesitarán ser reelaboradas en consecuencia o eliminadas.
Beneficios de usar la clase anterior
- La asignación de memoria para simplemente manipular los datos es innecesaria; los cambios realizados en los datos brutos se aplican inmediatamente al mapa de bits.
- No hay objetos adicionales para administrar. Esto implementa
IDisposable
comoBitmap
. - No requiere un bloque
unsafe
.
Consideraciones
- La memoria fija no se puede mover. Es un efecto secundario necesario para que este tipo de acceso a la memoria funcione. Esto reduce la eficacia del recolector de basura ( Artículo de MSDN ). Hazlo solo con bitmaps donde se requiera rendimiento, y asegúrate de
Dispose
cuando hayas terminado para que la memoria pueda ser desenganchada.
Acceso a través del objeto Graphics
Como la propiedad Bitmap
es en realidad un objeto .NET Bitmap
, es sencillo realizar operaciones utilizando la clase Graphics
.
var dbm = new DirectBitmap(200, 200);
using (var g = Graphics.FromImage(dbm.Bitmap))
{
g.DrawRectangle(Pens.Black, new Rectangle(50, 50, 100, 100));
}
El motivo por el cual las operaciones de mapa de bits son tan lentas en C # se debe al bloqueo y desbloqueo. Cada operación realizará un bloqueo en los bits requeridos, manipulará los bits y luego desbloqueará los bits.
Puede mejorar enormemente la velocidad manejando las operaciones usted mismo. Vea el siguiente ejemplo.
using (var tile = new Bitmap(tilePart.Width, tilePart.Height))
{
try
{
BitmapData srcData = sourceImage.LockBits(tilePart, ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
BitmapData dstData = tile.LockBits(new Rectangle(0, 0, tile.Width, tile.Height), ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
unsafe
{
byte* dstPointer = (byte*)dstData.Scan0;
byte* srcPointer = (byte*)srcData.Scan0;
for (int i = 0; i < tilePart.Height; i++)
{
for (int j = 0; j < tilePart.Width; j++)
{
dstPointer[0] = srcPointer[0]; // Blue
dstPointer[1] = srcPointer[1]; // Green
dstPointer[2] = srcPointer[2]; // Red
dstPointer[3] = srcPointer[3]; // Alpha
srcPointer += BytesPerPixel;
dstPointer += BytesPerPixel;
}
srcPointer += srcStrideOffset + srcTileOffset;
dstPointer += dstStrideOffset;
}
}
tile.UnlockBits(dstData);
aSourceImage.UnlockBits(srcData);
tile.Save(path);
}
catch (InvalidOperationException e)
{
}
}
Ha pasado un tiempo, pero encontré un ejemplo que podría ser útil.
BitmapData BtmpDt = a.LockBits(new Rectangle(0,0,btm.Width,btm.Height),ImageLockMode.ReadWrite,btm.PixelFormat);
IntPtr pointer = BtmDt.Scan0;
int size = Math.Abs(BtmDt.Stride)*btm.Height;
byte[] pixels = new byte[size];
Marshal.Copy(pointer,pixels,0, size);
for (int b = 0; b < pixels.Length; b++) {
pixels[b] = 255;// do something here
}
Marshal.Copy(pixels,0,pointer, size);
btm.UnlockBits(BtmDt);
Donde btm es una variable de mapa de bits.
Puede usar el método Bitmap.LockBits. Además, si desea utilizar la ejecución de tareas paralelas, puede usar la clase Parallel en el espacio de nombres System.Threading.Tasks. Los siguientes enlaces tienen algunas muestras y explicaciones.