Bitmap in Array umkopieren



  • Hallo,

    ich habe ein Bitmap, in dem nach bestimmten Mustern gesucht werden soll. Dazu benötige ich sehr viele Lesezugriffe auf die Pixel. Daher scheint es mir sinnvoll, das Bitmap in ein zweidimensionales Array umzukopieren, dann die Bildverarbeitung auf das Array anzuwenden, und dann das Array wieder zurück in das Bitmap zu kopieren. Ich brauche nur den Grün-Wert des Bildes, daher genügt ein Byte-Array. Es würde mich aber auch nicht stören, wenn ich 32 Bit pro Pixel in dem Array hätte. Das Hin- und Herkopieren funktioniert zwar, aber GetPixel und SetPixel sind viel zu langsam. Wie kann man das schneller machen?

    Gruß
    Michael

    Stopwatch w = new Stopwatch();
                int x, y;
                byte[,] myArray = new byte[myBitmap.Width, myBitmap.Height];
                Color c;
    
                w.Reset();
                w.Start();
    
                for (y = 0; y < myBitmap.Height; y++)     // Bitmap wird in Array umkopiert
                {
                    for (x = 0; x < myBitmap.Width; x++)
                    {
                        c = myBitmap.GetPixel(x,y);
                        myArray[x,y] = c.G;
                    }
                }
    
                w.Stop();
                textBox1.Text = w.ElapsedMilliseconds.ToString();
    
                //
                // hier wird Bildverarbeitung eingefügt, die sehr viele Lesezugriffe auf das Array benötigt
                //
    
                w.Reset();
                w.Start();
    
                for (y = 0; y < myBitmap.Height; y++)     // Array wird in Bitmap umkopiert
                {
                    for (x = 0; x < myBitmap.Width; x++)
                    {
                        c = Color.FromArgb(0, myArray[x, y], 0);
                        myBitmap.SetPixel(x, y, c);
                    }
                }
    
                w.Stop();
                textBox2.Text = w.ElapsedMilliseconds.ToString();
    


  • Du suchst die methode Bitmap.LockBits (http://msdn.microsoft.com/en-us/library/system.drawing.bitmap.lockbits.aspx).

    Wie es aussieht ist das Beispiel in C++. C# wäre so

    //
    // Image format: B G R A B G ...
    //               0 1 2 3 4 5 ...
    
    Rectangle rectangle = new Rectangle(0, 0, image.Width, image.Height);
    System.Drawing.Imaging.BitmapData imageData = image.LockBits(
        rectangle,
        System.Drawing.Imaging.ImageLockMode.ReadWrite,
        image.PixelFormat
    );
    
    IntPtr ptr = imageData.Scan0;
    
    int byteCount = Math.Abs(imageData.Stride) * image.Height;
    byte[] rgbValues = new byte[byteCount];
    
    System.Runtime.InteropServices.Marshal.Copy(ptr, rgbValues, 0, byteCount);
    
    for (int rgbIndex = 3; rgbIndex < rgbValues.Length; rgbIndex += 4)
    {
        // Do something with
        //rgbValues[rgbIndex - 3], // (Blue)
        //rgbValues[rgbIndex - 2], // (Green)
        //rgbValues[rgbIndex - 1], // (Red)
        //rgbValues[rgbIndex - 0], // (Alpha)
    }
    
    System.Runtime.InteropServices.Marshal.Copy(rgbValues, 0, ptr, byteCount);
    
    image.UnlockBits(imageData);
    


  • Hallo,

    System.Runtime.InteropServices.Marshal.Copy(ptr, rgbValues, 0, byteCount);
    

    In dieser Zeile kriege ich immer eine Fehlermeldung:

    An unhandled exception of type 'System.AccessViolationException' occurred in mscorlib.dll
    Additional information: Es wurde versucht, im geschützten Speicher zu lesen oder zu schreiben.

    Die Variable byteCount hat die richtige Grösse für mein Bitmap.

    Gruß
    Michael



  • Ich habe das Problem jetzt weiter eingegrenzt. Das Beispiel stammt von hier
    http://msdn.microsoft.com/de-de/library/5ey6h79d.aspx

    Wenn das Bitmap aus einer Datei geladen wird (siehe erste Zeile), dann funktioniert es. Wenn es aber aus dem Clipboard kommt (siehe zweite Zeile), dann kommt in der Zeile

    System.Runtime.InteropServices.Marshal.Copy(ptr, rgbValues, 0, bytes);
    

    die Fehlermeldung: "An unhandled exception of type 'System.AccessViolationException' occurred in mscorlib.dll
    Additional information: Es wurde versucht, im geschützten Speicher zu lesen oder zu schreiben."

    Ich verstehe nicht warum. Das Bild ist im Clipboard vorhanden, es wird ja 2 Sekunden lang angezeigt, und erst danach kommt die Fehlermeldung.

    //Bitmap bmp = new Bitmap("IMG_1806.jpg");      // das geht !
                Bitmap bmp = (Bitmap)(Clipboard.GetImage());    // das geht nicht !
    
                // Original-Bild 2 Sekunden lang anzeigen
                pictureBox1.SizeMode = PictureBoxSizeMode.Zoom;
                pictureBox1.Image = bmp;
                pictureBox1.Refresh();
                System.Threading.Thread.Sleep(2000);
    
                // Lock the bitmap's bits.  
                Rectangle rect = new Rectangle(0, 0, bmp.Width, bmp.Height);
                System.Drawing.Imaging.BitmapData bmpData =
                    bmp.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadWrite,
                    bmp.PixelFormat);
    
                // Get the address of the first line.
                IntPtr ptr = bmpData.Scan0;
    
                // Declare an array to hold the bytes of the bitmap.
                int bytes  = Math.Abs(bmpData.Stride) * bmp.Height;
                byte[] rgbValues = new byte[bytes];
    
                // Copy the RGB values into the array.
                 System.Runtime.InteropServices.Marshal.Copy(ptr, rgbValues, 0, bytes);
    
                for (int counter = 0; counter < rgbValues.Length; counter += 3)
                {
                    rgbValues[counter] = 0;  // Blauanteil löschen
                }
    
                // Copy the RGB values back to the bitmap
                System.Runtime.InteropServices.Marshal.Copy(rgbValues, 0, ptr, bytes);
    
                // Unlock the bits.
                bmp.UnlockBits(bmpData);
    
                // Draw the modified image.
                pictureBox1.SizeMode = PictureBoxSizeMode.Zoom;
                pictureBox1.Image = bmp;
    


  • Habe meinen Fehler gefunden, in der zweiten Zeile fehlte das "new", jetzt geht es.

    Gruß
    Michael



  • Also das Umkopieren in das Array funktioniert jetzt sehr gut. Aber bislang steht das ganze Bild in einem eindimensionalen Array. Für die Bildverarbeitung wäre es schöner wenn es ein zweidimensionales Array wäre. Und da fangen die Probleme wieder an...
    Marshal.Copy kann scheinbar nur in eindimensionale Arrays kopieren. Wie kriegt man die Bilddaten in ein zweidimensionales Array rein? Ich vermute, dass man jede Zeile in ein eigenes eindimensionales Array kopieren könnte, und dann müsste man ein Array von diesen Arrays anlegen. Aber ich weiss nicht wie das geht. Gibt's dazu ein Beispiel?

    Oder eine andere Idee... Kann man in C# eine Union mit einem eindimensionalen Array und einem zweidimensionalen Array anlegen, beide natürlich mit gleicher Gesamtgröße?

    Gruß
    Michael


  • Administrator

    😕
    Wozu willst du das? Nur weil es schön aussieht? Ein wenig unnötiger Aufwand.
    Wenn du per X und Y zugreifen möchtest, schreib dir kurz eine Wrapper Klasse. Über den Stride Wert kannst du dann jeweils ausrechnen, worauf du zugreifen musst.

    Grüssli



  • Dravere schrieb:

    😕
    Wozu willst du das? Nur weil es schön aussieht? Ein wenig unnötiger Aufwand.
    Wenn du per X und Y zugreifen möchtest, schreib dir kurz eine Wrapper Klasse. Über den Stride Wert kannst du dann jeweils ausrechnen, worauf du zugreifen musst.

    Grüssli

    Du hast Recht, das ist wirklich einfacher.

    Gruß
    Michael


Anmelden zum Antworten