Laden...

GetPixel und SetPixel um Längen geschlagen. 800 mal schneller

Letzter Beitrag vor 11 Jahren 40 Posts 93.435 Views
GetPixel und SetPixel um Längen geschlagen. 800 mal schneller

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

Umwandlung in 8bpp (1280 x 998 < 1Sek.)

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,

was ein ellenlanger quellcode.... 8o
vielleicht doch das nächste mal einfach als dateianhang gemacht, das erspart einem das viele scrollen 🙂

danke!

nils

?( wer suchet, der findet auch! :]

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

RoBitmap noch schneller machen

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



Wir Arbeiten eigendlich nicht wir nehmen nur das geld

Could not load file or assembly 'IwagImages, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'

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

Cf

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.

`There are 10 types of people in the world: Those, who think they understand the binary system Those who don't even have heard about it And those who understand "Every base is base 10"`

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.

Aktueller denn je... :-)

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