Laden...

Klärung der Begriffe "LockBit", "Marshal", "verwalteter Speicher" und "nicht verwalteter Speicher"

Erstellt von Gimmick vor 7 Jahren Letzter Beitrag vor 7 Jahren 3.287 Views
G
Gimmick Themenstarter:in
154 Beiträge seit 2015
vor 7 Jahren
Klärung der Begriffe "LockBit", "Marshal", "verwalteter Speicher" und "nicht verwalteter Speicher"

Hallo,

ich habe momentan viel mit Bildern und derren Bearbeitung/Auswertung zu tun und dabei stößt man man ja quasi zwangläufig auf die Begriffe LockBit, Marshal, verwalteter Speicher und nicht verwalteter Speicher.
Dabei habe ich ein Verständnisproblem.

Erstmal wie ich das verstanden habe:

Ein Bitmap kann im Speicher irgendwo liegen und wird durch die Speicherwaltung möglichst günstig verschoben. Möchte ich jetzt möglichst schnell alle Bytes auslesen wäre es geschickt möglichst selten nachgucken zu müssen wo die Informationen liegen.

Daher werden die Daten mit LockBits im Speicher an einer Position gehalten (der Bereich ist dann nicht mehr verwaltet) und über die Bitmap.Scan0 fragt man die Position (Zeiger) der ersten Bytes ab. Die anderen Bytes liegen dann entsprechend dahinter.
Über die Marshal.Copy(IntPtr,Byte[],Int32,Int32) Methode kopiert man dann die Bytes des nichtmehr verwalteten Speichers in ein verwaltetes Byte-Array.

In so einem Fall liegt das Bitmap ja schon im Speicher und ich muss ja nicht über Marshal.AllocHGlobal() Speicher reservieren, mein Array ist ja verwaltet und Änderungen der Pixel ändern ja nichts an der Größe, richtig?

Nur wenn ich IN einen eigenen, neuen, nicht verwalteten Speicher schreiben will muss ich für den vorher Platz schaffen, sonst kann ich ja nicht vorhersagen wohin ich konkret im Speicher schreiben muss oder überschreibe einfach andere Inhalte. Auch richtig? ^^

Mein Problem ist nämlich folgendes:
Ich kann hier zur Vereinfachung diverser Standard-Bildverarbietungsgeschichten wie Zoom, Level, umwandeln in Graustufen etc. eine Bibliothek verwenden, die die Bilder nicht als Bitmap verwaltet sondern ein eigenes Format hat.

Diese Objekte stellen keine LockBits Methode zur Verfügung, aber eine "GetScanLine(int Zeilen-Nummer)" Methode.

https://www.imageen.com/ievolutionhelp/html/310fec55-f5b8-93f0-28f1-f2a07c3e7265.htm

Mit dieser Methode durchlaufe ich die Zeilen und schreibe über Marshal.Copy immer Bildbreite*3 (RGB) Bytes in mein, vorher passend festgelegtes, Array. Der Inhalt ist dann auch korrekt.

Wenn die Aussagen von mir oben im Text stimmen müsste ja dann jede Zeile zeitweise nicht-verwaltet sein, aber schon im Speicher vorhanden.
Also müsste ich doch eigentlich keinen Speicher über Marshal.AllocHGlobal() freihalten müssen, oder?

Die Sache ist jetzt, dass mein Programm mittendrin oder beim Beenden abschmiert (vs32host.exe abgestürzt). Wenn ich Speicher in Größe des Arrays reserviere stürzt es seltener ab, mit der doppelten Größe (zum Testen 😄) ist es bisher nicht abgestürzt.

Aber eigentlich erzeuge ich doch garnichts in nicht-verwaltetem Speicher.
Sollte man grundsätzlich auch beim lesen aus nicht verwaltetem Speicher Platz reservieren?

D
985 Beiträge seit 2014
vor 7 Jahren

Du bist dir auch ganz sicher, dass du da immer ein 24Bit RGB Bitmap hast?

Was sagt denn die Eigenschaft IEImage.Format?

G
Gimmick Themenstarter:in
154 Beiträge seit 2015
vor 7 Jahren

Jap, sind ie24RGB Bilder.

2.298 Beiträge seit 2010
vor 7 Jahren

Hast du schon einmal versucht die Exception zu fangen?

Wissen ist nicht alles. Man muss es auch anwenden können.

PS Fritz!Box API - TR-064 Schnittstelle | PS EventLogManager |

G
Gimmick Themenstarter:in
154 Beiträge seit 2015
vor 7 Jahren

Hast du schon einmal versucht die Exception zu fangen?

Habs mal eingebaut, hoffe es passiert demnächst im Betrieb und nicht nur beim Beenden ^^.

/Edit: Da wird nichts gefangen. "vshost32.exe funktioniert nicht mehr.


Anwendungszeitstempel:	559b788a
  Fehlermodulname:	StackHash_2f1b
  Fehlermodulversion:	0.0.0.0
  Fehlermodulzeitstempel:	00000000
  Ausnahmecode:	c000041d
  Ausnahmeoffset:	PCH_03_FROM_iecore+0x000C4712
  Betriebsystemversion:	6.3.9600.2.0.0.256.48
  Gebietsschema-ID:	1031
  Zusatzinformation 1:	2f1b
  Zusatzinformation 2:	2f1bf9446d54e50ac9da9a509f8b6376
  Zusatzinformation 3:	404f
  Zusatzinformation 4:	404f05d1c5a5ec56a1252825f2a27b86

771 Beiträge seit 2009
vor 7 Jahren

Zeig doch mal deinen Code.

G
Gimmick Themenstarter:in
154 Beiträge seit 2015
vor 7 Jahren

Biddeschön 😃
Fehler passieren nur wenn ich diesen Teil auslöse. Der Rest vom Programm öffnet nur die Datei, konvertiert in Grauwerte etc. Das läuft alles problemlos.


IEImage Verarbeitung = new IEImage(ieViewer2.Image.SelectedBoundingBox.Width, ieViewer2.Image.SelectedBoundingBox.Width, IEImage.PixelFormat.ie24RGB);
                    Verarbeitung.CastColor(0, 0, new IERGB(255, 255, 255), 0);

                    IEImage Komplett = new IEImage(ieViewer2.Image.Width, ieViewer2.Image.Width, IEImage.PixelFormat.ie24RGB);
                    
                    ieViewer2.Image.CopySelectedImageTo(Verarbeitung);
                    
                   
                    int size = Verarbeitung.Height * Verarbeitung.Width * 3;
                    //IntPtr ptr = Marshal.AllocHGlobal(Convert.ToInt32(Math.Floor(size*2)));
                    byte[] data = new byte[size];

                    for (int i = 0; i < Verarbeitung.Height; i++)
                    {

                        Marshal.Copy(Verarbeitung.GetScanline(i), data, i * (Verarbeitung.Width * 3), Verarbeitung.Width * 3);

                    }


                    for (int y = 0; y < Verarbeitung.Height; y++)
                    {
                        for (int x = 0; x < Verarbeitung.Width; x++)
                        {

                            arrayrunner = (Verarbeitung.Width * y + x) * 3;

                            byte R = data[arrayrunner + 2];
                            byte G = data[arrayrunner + 1];
                            byte B = data[arrayrunner];


                            if (R == 0 && G == 0 && B == 0)
                            {

                                IERGB Color2 = new IERGB(rot, grün, blau);

                                Verarbeitung.CastColor(x, y, new IERGB(Color2), 0);

                                for (int i = 0; i < Verarbeitung.Height; i++)
                                {

                                    Marshal.Copy(Verarbeitung.GetScanline(i), data, i * (Verarbeitung.Width * 3), Verarbeitung.Width * 3);

                                }

                                blau = Convert.ToByte(blau + blauschritt);
                                blaucount++;
                                if (blau >= 253)
                                {
                                    blau = 1;
                                    rot++;

                                }

                                else if (rot >= 253)
                                {
                                    blau = 1;
                                    rot = 1;
                                    grün++;

                                    if (grün == rot && grün == blau)
                                    {
                                        //Antigrau
                                        grün++;
                                    }
                                }

                            }

                        }

                    }


                    Verarbeitung.CopyToClipboard();
                    ieViewer2.Image.SelPasteFromClipboard();

                    ieViewer2.Image.CopyImageTo(Komplett);


                    int sizekomplett = Komplett.Height * Komplett.Width * 3;
                    //ptr = Marshal.AllocHGlobal(sizekomplett);
                    byte[] datakomplett = new byte[sizekomplett];

                    richTextBox1.AppendText(Environment.NewLine + "sizekomplett: " + sizekomplett + "bildmaße " + (Komplett.Height * Komplett.Width * 3));

                    for (int i = 0; i < Komplett.Height; i++)
                    {

                        Marshal.Copy(Komplett.GetScanline(i), datakomplett, i * (Komplett.Width * 3), Komplett.Width * 3);

                    }



                    //Abzählen
                    int[] Flächen = new int[blaucount];

                    rot = 0;
                    grün = 0;
                    blau = 1;


                    int Pixelcounter = 0;

                    for (int i = 1; i <= blaucount; i++)
                    {

                        IERGB Color2 = new IERGB(rot, grün, blau);
                        ieViewer2.Image.SelectColors(Color2, Color2, IEImage.SelOp.Replace);
                        Pixelcounter = 0;
                        for (int y = ieViewer2.Image.SelectedBoundingBox.Y; y <= ieViewer2.Image.SelectedBoundingBox.Y + ieViewer2.Image.SelectedBoundingBox.Height; y++)
                        {
                            for (int x = ieViewer2.Image.SelectedBoundingBox.X; x <= ieViewer2.Image.SelectedBoundingBox.X + ieViewer2.Image.SelectedBoundingBox.Width; x++)
                            {
                                
                                arrayrunner = (Komplett.Width * (y) + x) * 3;
                                int R = datakomplett[arrayrunner + 2];
                                int G = datakomplett[arrayrunner + 1];
                                int B = datakomplett[arrayrunner];

                                //richTextBox1.AppendText(Environment.NewLine + "B: " + B + " R: " + R + " G: " + G);


                                if (B == blau && R == rot && G == grün)
                                {
                                    //richTextBox1.AppendText(Environment.NewLine + "blau: " + blau + " rot: " + rot + " grün: " + grün);
                                    Pixelcounter++;
                                    richTextBox1.AppendText(Environment.NewLine + "Pixelcounter: " + Pixelcounter);

                                }
                                
                            }
                        }

                        Flächen[i-1] = Pixelcounter;


                         blau = Convert.ToByte(blau + blauschritt);

                        if (blau >= 253)
                        {
                            blau = 1;
                            rot++;

                        }

                        else if (rot >= 253)
                        {
                            blau = 1;
                            rot = 1;
                            grün++;

                            if (grün == rot && grün == blau)
                            {
                                //Antigrau
                                grün++;
                            }

                        }


                        if (Pixelcounter >= 3)
                        {
                       

                        IEObjectBox templatebox = new IEObjectBox(ieViewer2.Image.Annotations);
                        templatebox.Left = ieViewer2.Image.SelectedBoundingBox.X - 2;
                        templatebox.Top = ieViewer2.Image.SelectedBoundingBox.Y - 2;
                        templatebox.Height = ieViewer2.Image.SelectedBoundingBox.Height + 5;
                        templatebox.Width = ieViewer2.Image.SelectedBoundingBox.Width + 5;

                        templatebox.BrushColor = new IERGB(0, 0, 0);

                        templatebox.HighLight = false;

                        templatebox.BrushStyle = IEBrushStyles.Clear;
                        templatebox.PenColor = new IERGB(0, 200, 0);
                        templatebox.PenStyle = IEPenStyles.Solid;

                        richTextBox1.AppendText(Environment.NewLine + "Fläche: " + Flächen[i-1]);
                            

                        }

                        else
                        {
                            
                        }
                            
                    }



771 Beiträge seit 2009
vor 7 Jahren

Nur so auf die Schnelle:
Ist das doppelte "Width" in deinem IEImage-Konstruktor (Zeile 1 + 3) so gewollt?

PS: Du mixt UI und Logik in deinem Code -> Code ist viel zu groß und unübersichtlich!!!

G
Gimmick Themenstarter:in
154 Beiträge seit 2015
vor 7 Jahren

Nur so auf die Schnelle:
Ist das doppelte "Width" in deinem IEImage-Konstruktor (Zeile 1 + 3) so gewollt?

Wäre mir im Leben nicht mehr aufgefallen ^^.

PS: Du mixt UI und Logik in deinem Code -> Code ist viel zu groß und unübersichtlich!!!

Hmmm, mit UI meinst du die TemplateBoxes, BoundingBoxes und sowas?

5.658 Beiträge seit 2006
vor 7 Jahren

Hi Gimmick,

Wäre mir im Leben nicht mehr aufgefallen

Das bestätigt ja die Erkenntnis von Cat, daß dein Code viel zu unübersichtlich ist.

Wenn ich es richtig verstehe, ist deine Anforderung, ein Bild in Graustufen umzuwandeln? Warum sollte man dazu Zugriff auf unverwalteten Speicher oder die Zwischenablage benötigten?

Wenn dein Code nicht macht, was er soll, dann kannst du ihn debuggen ([Artikel] Debugger: Wie verwende ich den von Visual Studio?) und testen ([Artikel] Unit-Tests: Einführung in das Unit-Testing mit VisualStudio). Dann würde auch auffallen, wenn man die Bildgröße mit Breite x Breite berechnet anstatt mit Breite x Höhe.

UI bedeutet User Interface und beinhaltet alles, was in der Benutzeroberfläche angezeigt wird. Siehe dazu auch [Artikel] Drei-Schichten-Architektur.

Weeks of programming can save you hours of planning

G
Gimmick Themenstarter:in
154 Beiträge seit 2015
vor 7 Jahren

Hi Gimmick,

Wäre mir im Leben nicht mehr aufgefallen

Das bestätigt ja die Erkenntnis von Cat, daß dein Code viel zu unübersichtlich ist.

Ich werde aufräumen, versprochen :<.

Wenn ich es richtig verstehe, ist deine Anforderung, ein Bild in Graustufen umzuwandeln? Warum sollte man dazu Zugriff auf unverwalteten Speicher oder die Zwischenablage benötigten?

Ne, die Umwandlung in Graustufen, Binärbild oder sonst was ist nur ein vorhergehender Schritt.

Ein Knopfdruck zählt dann Flächen einer bestimmten Farbe und gibt derren Größe, Position und sowas aus.
Und für das Bestimmen der Farbwerte und Ändern der Werte zum Unterschieden der Flächen kopiere ich die Bytes des Bildes in ein Array.
Hatte ich vorher zum Testen des Prinzips mit GetPixel/SetPixel gemacht, aber das war unheimlich langsam.

Wenn dein Code nicht macht, was er soll, dann kannst du ihn debuggen (
>
) und testen (
>
). Dann würde auch auffallen, wenn man die Bildgröße mit Breite x Breite berechnet anstatt mit Breite x Höhe.

Ok, werde mir das über Haltepunkte mal Schritt für Schritt ansehen.
Im Gegensatz zu meinen bisherigen Progrämmchen ist es hier das erste mal so, dass das Ergebnis stimmt und während des Programmablaufs kein "üblicher" Fehler (zu weit gezählt, Array zu klein oder sowas) auftritt, der direkt ausgeworfen wird.

Habe das Programm mal 20 Minuten an einem sehr großen Bild arbeiten lassen, alles wunderbar, vom Start bis zum Schließen.

Dann starte ich es, lade ein anderes Bild und lasse es offen wenn es fertig ist, kommt irgendwann ein Absturz :<
Sowas ist neu für mich ^^.

Den Artiekl mit den Unit-Tests werde ich mir in Ruhe durchlesen, wusste gar nicht, dass es sowas wie einen Test-Explorer gibt, habe für einige Sachen immer neue Projekte erstellt 😄

UI bedeutet User Interface und beinhaltet alles, was in der Benutzeroberfläche angezeigt wird. Siehe dazu auch
>
.

Jo, ok. In dem Fall hier sind allerdings fast alle Ein- und Ausgaben zum Rumspielen und testen da ^^. Das ist aber auf jeden Fall ein Thema, das bisher zu 100% von mir ignoriert wurde. Bei bisherigem "Berechne XY, sortiere das Array und gib das Ergebnis in einer Konsole aus" gab es das Problem nicht 😄

D
985 Beiträge seit 2014
vor 7 Jahren

Den Teil mit dem Clipboard würde ich an deiner Stelle dringend mit ohne Clipboard umschreiben.

G
Gimmick Themenstarter:in
154 Beiträge seit 2015
vor 7 Jahren

Den Teil mit dem Clipboard würde ich an deiner Stelle dringend mit ohne Clipboard umschreiben.

Das ist die Methode, die ich im Forum des Bibliothekanbieters gefunden hatte um ein Bild in eine Auswahl in einem anderen Bild zu kopieren ^^.
Direkt reinkopieren gibt es wohl nicht.

Alternative wäre RGB-Werte wieder von Hand an die richtige Stelle zu schreiben.

G
Gimmick Themenstarter:in
154 Beiträge seit 2015
vor 7 Jahren

Den Teil mit dem Clipboard würde ich an deiner Stelle dringend mit ohne Clipboard umschreiben.

Ist geändert. 😃

Was ich jetzt gemacht habe:

Ein neues Projekt erstellt, mit einer Bildanzeige aus der Bibliothek, der Möglichkeit ein Bild zu laden, kleine Textausgabe und einem Kopf 😄

Alles in Allem enthält mein Code nur Folgendes:

Öffnen:


private void öffnenToolStripMenuItem_Click(object sender, EventArgs e)
        {
            //Öffnen der Datei
            openFileDialog1.Filter = "Bilder (*.jpg,*.jpeg,*.bmp)|*.jpg;*.jpeg;*.bmp|All files (*.*)|*.*";
            openFileDialog1.Title = "Open";
            openFileDialog1.InitialDirectory = @"C:\";
            openFileDialog1.RestoreDirectory = true;



            if (openFileDialog1.ShowDialog() == DialogResult.OK)
            {
                string file = openFileDialog1.FileName;
                
                ieViewer1.Image.LoadImage(file);

                
            }
        }

Kopieren:


private byte[] BildArray()
        {
            
            Bitmap bmp= ieViewer1.Image.CreateBitmap();
            Rectangle rect = new Rectangle(0, 0, bmp.Width, bmp.Height);
            BitmapData bmpData = bmp.LockBits(rect, ImageLockMode.ReadOnly,bmp.PixelFormat);

            IntPtr ptr = bmpData.Scan0;
            int size = Math.Abs(bmpData.Stride) * bmp.Height;
            byte[] Daten = new byte[size];

            Marshal.Copy(ptr, Daten, 0, size);

            bmp.UnlockBits(bmpData);
            
            return Daten;

        }

Ausgeben:


 private void button1_Click(object sender, EventArgs e)
        {
            for (int i = 0; i < BildArray().Length; i++)
            {
                richTextBox1.AppendText(Environment.NewLine + BildArray()[i]);
            }
        }

Ist das so korrekt? 😃

S
248 Beiträge seit 2008
vor 7 Jahren

Hallo Gimmick,

deine Methode BildArray() behandelt das mögliche Speicherlayout von Bildern nicht korrekt.
Weder achtest du auf Padding-Bytes, noch darauf, dass die Stride negativ sein kann (damit meine ich nicht, dass du diese mit Math.Abs positiv machst).
Wenn die Stride negativ ist, so liegt das Bild im Speicher Bottom-Up anstatt Top-Down, ergo kannst du nicht von Scan0 aus einfach das Bild kopieren.

Du musst entweder jede einzelne Zeile des Bildes kopieren und dabei auch die möglichen Padding-Bytes berücksichtigen. Oder du machst es dir etwas einfacher, und schreibst dir diese Methode so um, dass diese keine Konversion macht und byte[] zurückgibt. Dann musst du weder Stride noch Padding-Bytes berücksichtigen.

In der Methode button1_Click() solltest du den Rückgabewert zwischenspeichern und wiederverwenden, da du ansonsten bei jedem Durchlauf die Umwandlung zwei Mal durchführst.

Grüße
spooky

G
Gimmick Themenstarter:in
154 Beiträge seit 2015
vor 7 Jahren

Hallo,

ok, das das Speicherlayout nicht richtig war, dachte ich mir schon. Es war eher ein kurzer Test zum finden eines Absturzproblems und weil das Ausgliedern von Methoden neu für mich ist ^^.

Das mit dem Zwischenspeichern ist ein sehr guter Rat 😄