Mittlerweile in Form einer DLL von janismac
Hallo alle,
Die Geschwindigkeit von GetPixel und SetPixel ging mir schon längst auf die Nerven.
Da ich jetzt beim Lernen etwas weiter bin, habe ich eine Klasse gemacht, die dies weit in den Schatten stellt. Fast 800 mal schneller.
Die Klasse ist noch nicht fertig, aber für meine Bedürfnisse reicht es. Sollte Interesse bestehen, mache ich natürlich weiter.
Da ich noch Frischling bin in CSharp bind ich für jede Kritik und Anregung sehr dankbar.
Zunächst: So habe ich die Geschwindigkeit getestet:
private void button1_Click(object sender, EventArgs e)
{
OpenFileDialog of = new OpenFileDialog();
of.Filter = "Bilder (*.BMP;*.JPG;*.GIF;*.PNG)|*.BMP;*.JPG;*.GIF;*.PNG|All files (*.*)|*.*";
if (of.ShowDialog() == DialogResult.OK)
{
// Vorbereitungen
Bitmap test = (Bitmap)Image.FromFile(of.FileName); // Eine Bitmap laden Size: (755 x 666)
RoBitmap rob = new RoBitmap(test); // RoBitmap davon erzeugen
Bitmap test2 = new Bitmap(test.Width, test.Height, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
// leere Bitmap gleicher Größe erstellen
RoBitmap rob2 = new RoBitmap(test2); // RoBitmap davon erzeugen
// Test 1 mit Orginal GetPixel/SetPixel Die Bitmap test wird Pixel für Pixel in test2 kopiert
DateTime now = DateTime.Now; // Zeit nehmen
for (int x = 0; x < test.Width; x++)
for (int y = 0; y < test.Height; y++)
test2.SetPixel(x, y, test.GetPixel(x, y));
TimeSpan usedTime = DateTime.Now - now;
MessageBox.Show(usedTime.ToString()); // 35,5 Sekunden
// Test 2 mit Orginal SetPixel und RoBitmap GetPixel
test2 = new Bitmap(test.Width, test.Height, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
now = DateTime.Now;
for (int x = 0; x < test.Width; x++)
for (int y = 0; y < test.Height; y++)
test2.SetPixel(x, y, rob.GetPixel(x, y));
usedTime = DateTime.Now - now;
MessageBox.Show(usedTime.ToString()); // 22,14 Sekunden
// Test 3 mit RoBitmap SetPixel / GetPixel
test2 = new Bitmap(test.Width, test.Height, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
now = DateTime.Now;
for (int x = 0; x < test.Width; x++)
for (int y = 0; y < test.Height; y++)
rob2.SetPixel(x, y, rob.GetPixel(x, y));
usedTime = DateTime.Now - now;
MessageBox.Show(usedTime.ToString()); // 4,45 Sekunden
// Test 4 mit RoBitmap SetPixel / GetPixel und Width/Height
test2 = new Bitmap(test.Width, test.Height, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
now = DateTime.Now;
for (int x = 0; x < rob.Width; x++)
for (int y = 0; y < rob.Height; y++)
rob2.SetPixel(x, y, rob.GetPixel(x, y));
usedTime = DateTime.Now - now;
MessageBox.Show(usedTime.ToString()); // 0,047 Sekunden
pictureBox1.Image = rob2.Image; // kommt das Richtige Bild heraus?
}
}
Und nun die noch nicht fertige Klasse:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.IO;
using System.Windows.Forms;
namespace BildMischer
{
class RoBitmap
{
private byte[] bildDaten;
private Color[,] color;
private int width;
private int height;
Bitmap Bild;
Rectangle rect;
bool modified;
int bytes;
int stride;
System.Drawing.Imaging.PixelFormat pixelFormat;
System.Drawing.Imaging.ColorPalette colorPalette;
public RoBitmap(Bitmap bld)
{
Bild = bld;
SetzeWerte();
}
void SetzeWerte()
{
colorPalette = Bild.Palette;
pixelFormat = Bild.PixelFormat;
width = Bild.Width;
height = Bild.Height;
rect = new Rectangle(0, 0, width, height);
System.Drawing.Imaging.BitmapData bmpData =
Bild.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadOnly,
Bild.PixelFormat);
IntPtr ptr = bmpData.Scan0;
stride = bmpData.Stride;
bytes = stride * height;
bildDaten = new byte[bytes];
System.Runtime.InteropServices.Marshal.Copy(ptr, bildDaten, 0, bytes);
Bild.UnlockBits(bmpData);
color = new Color[width, height];
switch (pixelFormat)
{
case System.Drawing.Imaging.PixelFormat.Format32bppArgb:
Format32BppArgb();
break;
case System.Drawing.Imaging.PixelFormat.Format24bppRgb:
Format24BppRgb();
break;
case System.Drawing.Imaging.PixelFormat.Format8bppIndexed:
Format8BppIndexed();
break;
case System.Drawing.Imaging.PixelFormat.Format4bppIndexed:
Format4BppIndexed();
break;
case System.Drawing.Imaging.PixelFormat.Format1bppIndexed:
Format1BppIndexed();
break;
}
modified = false;
}
void Format32BppArgb()
{
for (int y = 0; y < height; y++)
for (int x = 0; x < width; x++)
color[x, y] = Color.FromArgb(bildDaten[y * stride + x * 4 + 3], bildDaten[y * stride + x * 4 + 2], bildDaten[y * stride + x * 4 + 1], bildDaten[y * stride + x * 4]);
}
void Format24BppRgb()
{
for (int y = 0; y < height; y++)
for (int x = 0; x < width; x++)
color[x, y] = Color.FromArgb(bildDaten[y * stride + x * 3 + 2], bildDaten[y * stride + x * 3 + 1], bildDaten[y * stride + x * 3]);
}
void Format8BppIndexed()
{
for (int y = 0; y < height; y++)
for (int x = 0; x < width; x++)
color[x, y] = colorPalette.Entries[bildDaten[y * stride + x]];
}
void Format4BppIndexed()
{
for (int y = 0; y < height; y++)
for (int x = 0; x < width; x++)
if (x % 2 == 0)
color[x, y] = colorPalette.Entries[LowByte(bildDaten[y * stride + x / 2])];
else
color[x, y] = colorPalette.Entries[HighByte(bildDaten[y * stride + x / 2])];
}
void Format1BppIndexed()
{
int rest = width % 8;
byte bits;
int x, y;
for (y = 0; y < height; y++)
{
for (x = 0; x < width - 8; x += 8)
{
bits = bildDaten[y * stride + x / 8];
color[x, y] = colorPalette.Entries[(bits & 128) / 128];
color[x + 1, y] = colorPalette.Entries[(bits & 64) / 64];
color[x + 2, y] = colorPalette.Entries[(bits & 32) / 32];
color[x + 3, y] = colorPalette.Entries[(bits & 16) / 16];
color[x + 4, y] = colorPalette.Entries[(bits & 8) / 8];
color[x + 5, y] = colorPalette.Entries[(bits & 4) / 4];
color[x + 6, y] = colorPalette.Entries[(bits & 2) / 2];
color[x + 7, y] = colorPalette.Entries[bits & 1];
}
bits = bildDaten[y * stride + x / 8];
int teiler = 128;
for (int i = 0; i < rest; i++)
{
color[x + i, y] = colorPalette.Entries[(bits & teiler) / teiler];
teiler /= 2;
}
}
}
int HighByte(byte zahl)
{
return zahl >> 4;
}
int LowByte(byte zahl)
{
return zahl & 15;
}
public Color GetPixel(int x, int y)
{
return color[x, y];
}
public void SetPixel(int x, int y, Color col)
{
color[x, y] = col;
modified = true;
}
public int Width
{
get { return width; }
}
public int Height
{
get { return height; }
}
public Bitmap Image
{
set
{
Bild = value;
SetzeWerte();
}
get
{
if (!modified) return Bild;
switch (pixelFormat)
{
case System.Drawing.Imaging.PixelFormat.Format32bppArgb:
return ReturnFormat32BppArgb();
case System.Drawing.Imaging.PixelFormat.Format24bppRgb:
return ReturnFormat24BppRgb();
case System.Drawing.Imaging.PixelFormat.Format8bppIndexed:
//ReturnFormat8BppIndexed();
break;
case System.Drawing.Imaging.PixelFormat.Format4bppIndexed:
//ReturnFormat4BppIndexed();
break;
case System.Drawing.Imaging.PixelFormat.Format1bppIndexed:
//ReturnFormat1BppIndexed();
break;
}
return null;
}
}
Bitmap ReturnFormat24BppRgb()
{
for (int y = 0; y < height; y++)
for (int x = 0; x < width; x++)
{
bildDaten[y * stride + x * 3 + 2] = color[x, y].R;
bildDaten[y * stride + x * 3 + 1] = color[x, y].G;
bildDaten[y * stride + x * 3] = color[x, y].B;
}
System.Drawing.Imaging.BitmapData bmpData =
Bild.LockBits(rect, System.Drawing.Imaging.ImageLockMode.WriteOnly,
Bild.PixelFormat);
IntPtr ptr = bmpData.Scan0;
System.Runtime.InteropServices.Marshal.Copy(bildDaten, 0, ptr, bytes);
Bild.UnlockBits(bmpData);
modified = false;
return Bild;
}
Bitmap ReturnFormat32BppArgb()
{
for (int y = 0; y < height; y++)
for (int x = 0; x < width; x++)
{
bildDaten[y * stride + x * 4 + 3] = color[x, y].A;
bildDaten[y * stride + x * 4 + 2] = color[x, y].R;
bildDaten[y * stride + x * 4 + 1] = color[x, y].G;
bildDaten[y * stride + x * 4] = color[x, y].B;
}
System.Drawing.Imaging.BitmapData bmpData =
Bild.LockBits(rect, System.Drawing.Imaging.ImageLockMode.WriteOnly,
Bild.PixelFormat);
IntPtr ptr = bmpData.Scan0;
System.Runtime.InteropServices.Marshal.Copy(bildDaten, 0, ptr, bytes);
Bild.UnlockBits(bmpData);
modified = false;
return Bild;
}
}
}
Gruß Robert
Der Ansatz ist prinzipiell nicht schlecht.
Allerdings musst du beim jedem Lesen/Zuweisen einer Bitmap immer den Speicher kopieren. Zumindest das Kopieren beim Lesen ließe sich durch Benutzung eines Dirty-Flags (bzw. modified) umgehen.
Der nächste Evolutionsschritt wäre direkt in den Speicher der Bitmap zu schreiben.
EDIT: Und der übernächste ist, das ganze von Bitmap abzuleiten um diesen ersetzen zu können...
Hallo Borg,
Punkt 1 (gute Idee) habe ich umgesetzt. Mit den anderen werde ich mich befassen.
Gruß Robert
Hallo Borg,
meinst du jetzt das Marshal.Copy? Das ist doch sinnvoll, damit man nicht unsafe verwenden muss. Gerade für eine Bibliothek ist es aus meiner Sicht wünschenswert, unsafe zu vermeiden.
Und der Vorschlag, von Bitmap abzuleiten, scheitert daran, dass Bitmap sealed ist 🙂
herbivore
PS: Verschoben nach .NET-Komponentensammlung
Ist es doch sealed? Verdammt. Ich hatte gestern gehofft, dass es das nicht ist; war jedoch zu faul nachzuschauen, da ich wusste, dass mich schon jemand korrigieren würde, falls es nicht stimmt... 😉
Desweiteren ist mir der Bitmap-ctor public Bitmap ( int width, int height, int stride, PixelFormat format, IntPtr scan0 ) aufgefallen.
Zur Umsetzung von IntPtr in Byte[] bietet sich public static Object PtrToStructure ( IntPtr ptr, Type structureType ) an.
Für die Umwandlung des Byte[] in IntPtr bietet sich public static IntPtr UnsafeAddrOfPinnedArrayElement ( Array arr, int index ) an.
Ich habe das nicht ausprobiert, aber wenn es so funktioniert, wie ich mir das erhoffe und denke, kann man mit den Pointern arbeiten, ohne einen unsafe Kontext zu benutzen.
Eine zusätzliche Frage:
Mittels Reflection ist es doch möglich, protected und private Methoden aufzurufen. Kann man denn darüber nicht auch eine Ableitung einer sealed-Klasse erzeugen?
Habe noch etwas probiert und es klappte.
Da ich nicht weis, ob es sinnvoll ist zunächt mal hier:
public Bitmap GetTransparent(Color transparentColor)
{
return GetTransparent(transparentColor, 0);
}
public Bitmap GetTransparent(Color transparentColor, byte tranparenz)
{
// vorerst nur für zwei Formate
int newStride;
if (pixelFormat == System.Drawing.Imaging.PixelFormat.Format24bppRgb)
newStride = stride / 3 * 4;
else if (pixelFormat != System.Drawing.Imaging.PixelFormat.Format32bppArgb)
newStride = stride;
else
return Bild;
Bitmap F32 = new Bitmap(width, height, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
byte[] f32 = new byte[height * newStride];
for (int y = 0; y < height; y++)
for (int x = 0; x < width; x++)
{
if (color[x, y].ToArgb() != transparentColor.ToArgb())
f32[y * width * 4 + x * 4 + 3] = color[x, y].A;
else
f32[y * width * 4 + x * 4 + 3] = tranparenz;
f32[y * width * 4 + x * 4 + 2] = color[x, y].R;
f32[y * width * 4 + x * 4 + 1] = color[x, y].G;
f32[y * width * 4 + x * 4] = color[x, y].B;
}
System.Drawing.Imaging.BitmapData bmpData =
F32.LockBits(rect, System.Drawing.Imaging.ImageLockMode.WriteOnly,
F32.PixelFormat);
IntPtr ptr = bmpData.Scan0;
System.Runtime.InteropServices.Marshal.Copy(f32, 0, ptr, height * newStride);
F32.UnlockBits(bmpData);
return F32;
}
Habe noch etwas probiert und es klappte.
Was hast du probiert und was hat geklappt?
Dachte Bitmap GetTransparent(Color transparentColor) sagt alles aus.
Die Funktion gibt die Bitmap zurück in der die Farbe **transparentColor **transparent ist.
Was die Methode tut, habe ich gesehen.
Nur brachte mich der zweite Satz
Da ich nicht weis, ob es sinnvoll ist zunächt mal hier: auf die Idee, dass der erste Satz sich nicht auf diese Methode bezieht...
EDIT: spellcheck
Hallo Borg,
Mittels Reflection ist es doch möglich, protected und private Methoden aufzurufen. Kann man denn darüber nicht auch eine Ableitung einer sealed-Klasse erzeugen?
Reflection ist eine Laufzeitgeschichte. Die Definition einer Klasse ist Compilezeit. Natürlich könnte man zur Laufzeit Emit verwenden, aber das ist nichts anderes als ein Compileraufruf zur Laufzeit, d.h. man kann nur syntaktisch Korrektes emitieren. Also ich sehe keine Weg, sealed zu umgehen.
herbivore
Angesprochen auf die Möglichkeit dies einzubauen, habe ich es versucht.
Hier das Ergebnis:
//
#region 8bpp
Color ColorFuer8bpp(Color col)
{
int r = col.R - col.R % 51;
int g = col.G - col.G % 51;
int b = col.B - col.B % 51;
return Color.FromArgb(r, b, g);
}
static Color ColorFuer8bpp(Color col, int zusatz)
{
int r = (col.R + zusatz) - (col.R + zusatz) % 51;
int g = (col.G + zusatz) - (col.G + zusatz) % 51;
int b = (col.B + zusatz) - (col.B + zusatz) % 51;
return Color.FromArgb(r, b, g);
}
static ColorPalette AddSystemColor(ColorPalette palette)
{
int counter = 16;
palette.Entries[counter++] = SystemColors.ActiveBorder;
palette.Entries[counter++] = SystemColors.ActiveCaption;
palette.Entries[counter++] = SystemColors.ButtonFace;
palette.Entries[counter++] = SystemColors.ButtonShadow;
palette.Entries[counter++] = SystemColors.Control;
palette.Entries[counter++] = SystemColors.ControlDark;
palette.Entries[counter++] = SystemColors.ControlDarkDark;
palette.Entries[counter++] = SystemColors.ControlLight;
palette.Entries[counter++] = SystemColors.Desktop;
palette.Entries[counter++] = SystemColors.GradientActiveCaption;
palette.Entries[counter++] = SystemColors.GradientInactiveCaption;
palette.Entries[counter++] = SystemColors.GrayText;
palette.Entries[counter++] = SystemColors.HotTrack;
palette.Entries[counter++] = SystemColors.InactiveBorder;
palette.Entries[counter++] = SystemColors.InactiveCaption;
palette.Entries[counter++] = SystemColors.InactiveCaptionText;
palette.Entries[counter++] = SystemColors.MenuBar;
palette.Entries[counter++] = SystemColors.MenuHighlight;
palette.Entries[counter++] = SystemColors.ScrollBar;
//hier können noch 5 weitere Farben hinzu
return palette;
}
public static void InitializeColor()
{
ColorPalette tmp = Color8bppPalette;
Hashtable hash = PalettenFarben;
}
static ColorPalette color8bppPalette;
static ColorPalette Color8bppPalette
{
get
{
if (color8bppPalette == null)
{
Bitmap tmpImage = new Bitmap(1, 1, PixelFormat.Format8bppIndexed);
color8bppPalette = tmpImage.Palette;
color8bppPalette = AddSystemColor(color8bppPalette);
}
return color8bppPalette;
}
}
static Hashtable palettenFarben;
static Hashtable PalettenFarben
{
get
{
if (palettenFarben == null)
{
palettenFarben = new Hashtable();
int r1, g1, b1, r1min, g1min, b1min;
Color tmpColor;
ColorPalette tmpPal = Color8bppPalette;
for (int i = 0; i < tmpPal.Entries.Length; i++)
{
tmpColor = tmpPal.Entries[i];
r1min = Math.Max(tmpColor.R - 5, 0);
g1min = Math.Max(tmpColor.G - 5, 0);
b1min = Math.Max(tmpColor.B - 5, 0);
r1 = Math.Min(r1min + 10, 255);
g1 = Math.Min(g1min + 10, 255);
b1 = Math.Min(b1min + 10, 255);
for (int r = r1min; r < r1; r++)
for (int g = g1min; g < g1; g++)
for (int b = b1min; b < b1; b++)
{
if (!palettenFarben.ContainsKey(Color.FromArgb(r, g, b)))
{
palettenFarben[Color.FromArgb(r, g, b)] = (byte)i;
}
}
}
}
return palettenFarben;
}
}
public Bitmap ConvertTo8Bpp()
{
// mit dieser 15 habe ich das beste Ergebnis erzielt.
return ConvertTo8Bpp(15);
}
public Bitmap ConvertTo8Bpp(int zusatz)
{
DateTime now = DateTime.Now;
Bitmap destImage = new Bitmap( width, height, PixelFormat.Format8bppIndexed );
BitmapData bitmapData = destImage.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadWrite, PixelFormat.Format8bppIndexed);
int tmpStride = bitmapData.Stride;
destImage.Palette = Color8bppPalette;
Hashtable colNummer = new Hashtable();
Color tmpColor;
byte offset = 40;
for (int r = 0; r < 256; r += 51)
for (int g = 0; g < 256; g += 51)
for (int b = 0; b < 256; b += 51)
{
colNummer[Color.FromArgb(r, b, g)] = offset++;
}
int tmpBytes = height * tmpStride;
byte[] tmpBildDaten = new byte[tmpBytes];
for (int x = 0; x < width; x++)
{
for (int y = 0; y < height; y++)
{
tmpColor = color[x, y];
int offset2 = (y * tmpStride) + x;
if (PalettenFarben.ContainsKey(tmpColor))
tmpBildDaten[offset2] = (byte)PalettenFarben[tmpColor];
else
tmpBildDaten[offset2] = (byte)colNummer[ColorFuer8bpp(tmpColor, zusatz)];
}
}
IntPtr ptr = bitmapData.Scan0;
System.Runtime.InteropServices.Marshal.Copy(tmpBildDaten, 0, ptr, tmpBytes);
destImage.UnlockBits(bitmapData);
TimeSpan verbr = DateTime.Now - now;
MessageBox.Show(verbr.ToString());
return destImage;
}
#endregion
Damit das erste Bild nicht etwas länger braucht, sollte man :
RoBitmap.InitializeColor();
vor Gebrauch machen.
Bin zwar noch nicht ganz zufrieden, aber man kann damit schon leben.
Gruß Robert.
PS. Wie gesagt: Kritik gerne gesehen. 😉
Erweiterte Version:
Auf Anregung von nils in eine txt-Datei gepackt und angehängt.
Die Endung cs ist komischerweise in myCSharp.de nicht erlaubt 😁
Gruß Robert
PS: Als Admin ohne Probleme änderbar
public RoBitmap(Bitmap bld)
public RoBitmap(string fileName)
public Color[,] GetColorArray()
public Color GetPixel(int x, int y)
public void SetPixel(int x, int y, Color col)
public int Width
public int Height
public Bitmap Image
public Bitmap ConvertTo8Bpp()
public Bitmap ConvertTo8Bpp(int zusatz)
public Bitmap ConvertToGrayScale24bppBitmap()
public Bitmap ConvertToGrayScale8bppBitmap()
public Bitmap GetNegativ()
public Bitmap GetTransparent(Color transparentColor)
public Bitmap GetAllTransparent(byte tranparenz)
public Bitmap GetTransparent(Color transparentColor, byte tranparenz)
public Bitmap GetContrastedImage(float _contrastFactor)
public Bitmap GetFormat1BppIndexed()
public Bitmap GetFormat1BppIndexed(float brightness)
public Bitmap GetFormat1BppIndexed(float brightness, Color _color1, Color _color2)
public Hashtable ZaehleFarben()
public void Save(string fileName)
Hallo Robertico,
Die Endung cs ist komischerweise in myCSharp.de nicht erlaubt
weil es in der Regel sinnvoller und sicherer ist, Code in Zip-Dateien zu packen, aber ich habe cs mal als erlaubte Dateiendung hinzugefügt.
herbivore
Hallo herbivore,
Danke, ich denke, das ist für dieses Forum auch sinnvoll.
Nach meinem "kleinen" Veständnis ist ja nur eine Gefahr denkbar, wenn der Browser etwas anderes damit anstellen kann als herunter laden. (ausführen)
Denke nicht, dass ein Browser bei cs dies anbietet.
Gruß Robert
Hallo!
Habe die Klasse mittlerweile ebenfalls in Benutzung, und bin über die Zeitersparnis wirklich erstaunt.
Beim Durchlesen der Beiträge kam auch der Wunsch/Vorschlag, die Klasse von Bitmap abzuleiten, was aber nicht möglich ist.
Aber es sollte doch möglich sein, von Image abzuleiten, oder?
Nobody is perfect. I'm sad, i'm not nobody 🙁
Ja von Image ableiten ist ja möglich, nur dann hast du die Funktionen von Bitmap nicht
Es gibt 3 Arten von Menschen, die die bis 3 zählen können und die, die es nicht können...
Hallo!
Original von kleines_eichhoernchen
Ja von Image ableiten ist ja möglich, nur dann hast du die Funktionen von Bitmap nicht
Stimmt. Aber die Kompatibilität wäre zumindest ein wenig erhöht worden.
Wichtig wären natürlich GetPixel und SetPixel gewesen. Keine Ahnung, warum die in der Image-Klasse fehlen.
Genauso wenig kann ich nachvollziehen, warum Bitmap sealed ist...
Nobody is perfect. I'm sad, i'm not nobody 🙁
Hallo ihr beiden,
bitte das Thema nicht hier ausdiskutieren, weil wir in ".NET-Komponentensammlung" sind. Der Vorschlag ist da und Robertico kann ihn prüfen.
Nur der kurze Hinweis: In Image fehlt Get- und SetPixel, weil ein Image keine Pixelgrafik sein muss, sondern auch eine Vektorgrafik sein kann.
herbivore
Hallo!
An sich fand ich die Idee und die Umsetzung schon ganz interessant, allerdings hat mich die Verzögerung beim Erstellen der Klasse gestört (z.B. 300dpi A4-Seite).
Man kann das Ganze noch weiter beschleunigen, indem man bei GetPixel und SetPixel direkt auf die Bilddaten zugreift. Neben der Vermeidung der anfänglichen Verzögerung kann man zumindest GetPixel bei entsprechender Optimierung nochmal um einiges beschleunigen, bei SetPixel hab' ich's noch nicht getestet. Und ausserdem wird der Speicher radikal entlastet.
Nobody is perfect. I'm sad, i'm not nobody 🙁
Hallo tom-essen,
Weiss nicht genau was du meinst.
Das erste was ich tue ist, die Bilddaten aus dem Bild zu holen. Dann damit arbeiten und erst wieder bei Gebrauch in das Bild zu schreiben.
Daher ist das so schnell.
Eine Verzögerung ist nur beim ersten Gebrauch der Klasse da durch:
Damit das erste Bild nicht etwas länger braucht, sollte man :
C#-Code:
RoBitmap.InitializeColor();vor Gebrauch machen.
Sonst sollte es schnell gehen.
Gruß Robert
Hallo!
@Robertico:
Du holst die Daten aus dem Bitmap mittels Marshal.Copy in ein eigenes Byte-Array.
Wenn du nun anstatt einer neuen Matrix mit x*y Color-Einträgen die entsprechende Farbe immer erst bei Bedarf in GetPixel aus dem Byte-Array berechnest, sparst du dir die Matrix und kannst je nach Pixelformat noch ein wenig schneller werden. Besonders die Color-Klasse hat noch einige Überprüfungen, die man übergehen kann (z.B. kann aufgund des Byte-Arrays kein Wert über 255 sein).
Mit InitializeColor habe ich keine Geschwindigkeitssprünge bemerkt.
Wichtig war mir besonders die Vermeidung der anfänglichen Verzögerung, da ich mehrere Bilddateien der Reihe nach einlese, analysiere und dann die nächste nehme.
Die Barcode-Analyse z.B. benötigt nur ca. 1 Sekunde, aber das Einlesen mit RoBitmap dauert 2 Sekunden. Und das ist bei mehreren 100 Dateien (Rechnungszentrum) schon ein Unterschied.
Ich hoffe, ich konnte meine Gedanken diesmal verständlicher formulieren, evtl. krieg' ich auch den Code nochmal vernünftig zusammen.
Nobody is perfect. I'm sad, i'm not nobody 🙁
Hallo tom-essen,
ich bin bei weitem kein C# Profi.
alles was ich hier produziere ist auf das wenige Wissen der Sprache, dass ich mir bisher angeeignet habe, aufgebaut.
Ich kenne noch lange nicht alle Möglichkeiten. Das einzige was ich gut kann, ist kombinieren.
Dann fummle ich mir daraus etwas zurecht.
Mein Wissen wird zwar täglich größer, aber wie hier bin ich immer sehr dankbar wenn mir jemand hilft, der mehr Ahnung hat.
Baue sie einfach um und hänge sie an. Mit der Zeit bekommen wir dann vielleicht eine super Klasse.
Gruß Robert
Hallo Robertico
mit großem Interesse habe ich deinen Beitrag gelesen.
Ich denke, dass ich mit deinen Überlegungen zu der Klasse der Lösung meines Problems näher komme.
Was habe ich gemacht:
Ich habe mir eine Klasse zum Konvertieren von Bildformaten geschrieben, wobei ich die gegebenen Möglichkeiten der Framework und hier der Image-Klasse ausnutze. Laut "Petzold" sollte man immer die Image-Klasse nutzen, wenn man seine Aufgaben damit lösen kann.
Wo hab ich nun ein Problem? Wenn ich nach TIF oder GIF konvertieren lasse, dann wirft es mir immer eine Exception, wobei ich beim Debuggen festgestellt habe, dass das Pixelformat immer wieder auf PixelFormat.Format8bppIndexed zurückgesetzt wurde. Der resultierende Stream ist dann auch nur noch 2408 groß X( 🤔
Eine weitere Sache wäre noch die Speicherverwaltung? Da weiß ich auch noch nicht, ob das so alles optimal gelöst ist...
Vielleicht hat jemand noch eine Idee... ich komme hier jedenfalls nicht mehr weiter (nicht so richtig...).
Danke
Rene
Die vollständige Klasse ist im Anhang
private Image ConvertImage(Image sourceImage, ImageFormat targetImageFormat)
{
if (sourceImage.RawFormat.Equals(targetImageFormat))
{
return sourceImage;
}
PixelFormat _pixelFormat = PixelFormat.Format32bppArgb;
if (targetImageFormat.Equals(ImageFormat.Jpeg))
{
_pixelFormat = PixelFormat.Format24bppRgb;
}
else if (targetImageFormat.Equals(ImageFormat.Gif))
{
_pixelFormat = PixelFormat.Format32bppArgb;
}
else if (targetImageFormat.Equals(ImageFormat.Bmp))
{
_pixelFormat = PixelFormat.Format32bppArgb;
}
else if (targetImageFormat.Equals(ImageFormat.Png))
{
_pixelFormat = PixelFormat.Format32bppArgb;
}
else if (targetImageFormat.Equals(ImageFormat.Tiff))
{
_pixelFormat = PixelFormat.Format32bppArgb;
}
else if (targetImageFormat.Equals(ImageFormat.Wmf))
{
_pixelFormat = PixelFormat.Undefined;
}
else
{
_pixelFormat = PixelFormat.Format24bppRgb;
}
Bitmap bmp = new Bitmap(sourceImage.Size.Width,
sourceImage.Size.Height,
_pixelFormat);
Graphics g1 = Graphics.FromImage(bmp);
g1.DrawImage(bmp,
0,0,
bmp.Size.Width,
bmp.Size.Height);
g1.CompositingQuality = CompositingQuality.HighQuality;
g1.SmoothingMode = SmoothingMode.HighQuality;
g1.InterpolationMode = InterpolationMode.HighQualityBicubic;
g1.Dispose();
using (Stream imgStream = new MemoryStream())
{
bmp.Save(imgStream, targetImageFormat);
bmp.Dispose();
imgStream.Seek(0, SeekOrigin.Begin);
return GetImage(imgStream);
}
return null;
}
public Image GetImage(Stream imageSourceStream)
{
return Image.FromStream(imageSourceStream);
}
wäre http://www.fh-wedel.de/~si/seminare/ws06/Ausarbeitung/15.CSharp/c-sharp3.0.htm#extensions keine möglichleit um Bitmap zu erweitern 🙂
Wir Arbeiten eigendlich nicht wir nehmen nur das geld
Erfordert Marshal.Copy im Zusammenhang mit einer ASP.NET Anwendung eine spezielle Berechtigung in der Anwendung?
Danke
Rene
Hallo alle,
Habe Unterstützung bekommen.
**
janismac** hat sich bereit erklärt in der Klasse mal richtig Ordnung zu machen und Kommentare hinein zu schreiben.
Dann wird er es in eine DLL packen und hier zur Verfügung stellen. 😁
Bei der Gelegenheit möchte ich noch ein Schnipselchen vorstellen, dass ich letztens gebraucht habe. Es tauscht im Bild eine Farbe gegen eine andere. Und das in gewohnter Schnelle.
public void TauscheFarbe(Color f1, Color f2)
{
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
if (color[x, y] == f1)
{
color[x, y] = f2;
}
}
}
}
Gruß Robert
hallo
habe wie (von Robertico) angekündigt, das ganze einwenig überarbeitet und in eine DLL gepackt
es ist erstmal nur ne beta
es fehlen leider immernoch die funktionen zum umrechnen in Drawing.Bitmap aus 1bpp, 4bpp und 8bpp (bpp = Bits per Pixel)
wenn jemand weiß wie das geht (und zeit hat) wär es schön wenn das noch wergänzt würde
MfG janismac
Hallo,
kann man diese Klasse auch im Compact Framework nutzen? In der MSDN steht ja das der Namespace Drawing komplett vorhanden ist. Aber die ColorPalette scheint nicht vorhanden zu sein. PixelFormat macht keine Probleme.
Könnte man sich die ColorPalette dann irgendwie nachbauen? Oder mache ich irgendwas anders falsch?
Viele Grüße,
Meli
Hallo,
das ist nen ziemlich netter Code leider fehlte mir die rückwandlung in 8bpp. Deswegen hab ich sie mal nach implementiert und anscheinend funktioniert sie auch.
Bitmap ReturnFormat8BppIndexed()
{
for (int y = 0; y < height; y++)
for (int x = 0; x < width; x++)
bildDaten[y * stride + x] = color[x, y].R;
System.Drawing.Imaging.BitmapData bmpData =
Bild.LockBits(rect, System.Drawing.Imaging.ImageLockMode.WriteOnly,
Bild.PixelFormat);
IntPtr ptr = bmpData.Scan0;
System.Runtime.InteropServices.Marshal.Copy(bildDaten, 0, ptr, bytes);
Bild.UnlockBits(bmpData);
modified = false;
return Bild;
}
Gute Klasse! Funzt auch als Beta prima! =)
Gruß, Christian.
GDI32 GetPixel und SetPixel sind ebenfalls so schnell 😉
Dein Code ist aber super. Die ganzen Funktionen sind nicht neu und GDI32 hat sie schon alle intus, aber es mal vereinfachter für den faulen GDI+ programmierer zu machen find ich klasse 👍
Gruss Ari
Wer lesen kann ist klar im vorteil!
MSDN
Dein Feund in allen fragen
Super Sache, danke!
Ich bin auch eigentlich zufällig auf deine dll datei gestoßen. Funktioniert wirklich prima. Hab geanu so was für mein derzeitiges projekt gesucht, was für ein zufall! Falls deine Klasse schon verbessert worden ist, freu ich mich schon auf die neue dll 😁
lg
Daniel
Hallo kleine_eiche,
bin im Moment voll im Siedler 6 - Wahn. 😉
Danach gehts weiter.
Gruß Robert
Ich konnte den Code für mein Projekt auch benutzen. Danke.
Das ganze verwendete ich mit einem 1Bpp Bitmap.
Es funktionierte aber nicht als ich ein Bild verwendet habe, welches im Stride kein Padding hatte (Breite durch 8 teilbar).
Das Problem konnte ich lösen, indem ich in der Methode **Format1BppIndexed **unterhalb von
int rest = width % 8;
folgenden Code anhängte
if (rest == 0)
rest = 8;
Ich hab nicht die dll benutzt, sondern den Code für mein Projekt angepasst. Vielleicht habe ich dabei einen Fehler gemacht und die dll ist nicht betroffen.
Hallo miteinander,
auch wenn dieser Beitrag schon älter als zwei Jahre ist und schon ne Weile niemand mehr darauf geantwortet hat, ist er für mich aktueller denn je... 😃
Ich hab sehr große Performance-Probleme in einem zeitkritischen C#-Programm durch das langsame getpixel und setpixel, weshalb ich sehr froh war diese Klasse hier zu finden. 😃 Nur leider ist bei mir das Image der RoBitmap immer leer! Liegt das an dem Format8BppIndexed-Format meiner Bilder? Wenn ja, gibt es schon eine aktuellere Version um deren Berarbeitung zu ermöglichen?
Ich hab mir die Klasse schon angeschaut, fühl mich aber selber nicht fit genug um da was zu implementieren... 😃
Ich würde mich sehr über eine Antwort freuen!
Viele Grüße
Micha
Hallo mutzu2210,
in der Tat ist es schon lange her. Habe im Moment auch keine Zeit mich groß damit zu beschäftigen.
Was ich tun kann, ist dir meine aktuelle Version hochzuladen. An der habe ich immer wieder etwas gemacht.
Vielleicht hilfts. 😉
Gruß Robert
Hallo,
hab den Code kurz überflogen und festgestellt dass sich die Schleifen für die Matrixmanipulaiton sehr gut parallelisieren lassen. Das könnte die Ausführungsgeschwinidigkeit (bei einem Mehrkern-Rechner) noch steigern.
mfG Gü
Stellt fachliche Fragen bitte im Forum, damit von den Antworten alle profitieren. Daher beantworte ich solche Fragen nicht per PM.
"Alle sagten, das geht nicht! Dann kam einer, der wusste das nicht - und hat's gemacht!"
danke erstmal für den code!
ich habe allerdings ein problem: wenn ich setpixel für eine monochrome bitmap verwenden möchte, funktioniert das ganze nicht. weiss jemand woran das liegen könnte?
getpixel gibt ganz normale Argb werte für schwarz bzw weiss zurück, also habe ich diese palette auch bei setpixel benutzt. das bild bleibt aber unverändert