3 Stimmen

Erhalten der Farben von Pixeln eines monochromen Bildes mit C#

Wie im Betreff steht, habe ich ein .bmp-Bild und ich brauche einen Code, der in der Lage ist, die Farbe eines beliebigen Pixels des Bildes zu erhalten. Es handelt sich um ein 1bpp (indexiertes) Bild, daher wird die Farbe entweder schwarz oder weiß sein. Hier ist der Code, den ich derzeit habe:

    //Diese Methode sperrt die Bits einer Pixellinie
    private BitmapData LockLine(Bitmap bmp, int y)
    {
        Rectangle lineRect = new Rectangle(0, y, bmp.Width, 1);
        BitmapData line = bmp.LockBits(lineRect,
                                       ImageLockMode.ReadWrite,
                                       bmp.PixelFormat);
        return line;
    }
    //Diese Methode nimmt die BitmapData einer Pixellinie
    //und gibt die Farbe eines Pixels zurück, der die benötigte x-Koordinate hat
    private Color GetPixelColor(BitmapData data, int x)
    {
        //Ich bin mir nicht sicher, ob diese Zeile korrekt ist
        IntPtr pPixel = data.Scan0 + x; 
        //Der folgende Code funktioniert für ein 24bpp-Bild:
        byte[] rgbValues = new byte[3];
        System.Runtime.InteropServices.Marshal.Copy(pPixel, rgbValues, 0, 3);
        return Color.FromArgb(rgbValues[2], rgbValues[1], rgbValues[0]);
    }

Aber wie kann ich es für ein 1bpp-Bild zum Laufen bringen? Wenn ich nur ein Byte vom Zeiger lese, hat es immer den Wert 255, also gehe ich davon aus, dass ich etwas falsch mache.
Bitte schlagen Sie nicht vor, die Methode System.Drawing.Bitmap.GetPixel zu verwenden, da sie zu langsam ist und ich möchte, dass der Code so schnell wie möglich funktioniert. Vielen Dank im Voraus.

BEARBEITEN: Hier ist der Code, der funktioniert, falls jemand ihn benötigt:

    private Color GetPixelColor(BitmapData data, int x)
    {
        int byteIndex = x / 8;
        int bitIndex = x % 8;
        IntPtr pFirstPixel = data.Scan0+byteIndex;
        byte[] color = new byte[1];
        System.Runtime.InteropServices.Marshal.Copy(pFirstPixel, color, 0, 1);
        BitArray bits = new BitArray(color);
        return bits.Get(bitIndex) ? Color.Black : Color.White;
    }

2voto

Marcelo De Zen Punkte 9172

Ok, verstanden! Du musst die Bits aus dem BitmapData lesen und eine Maske auf das Bit anwenden, von dem du die Farbe extrahieren möchtest:

var bm = new Bitmap...

// alle Bildbits sperren
var bitmapData = bm.LockBits(new Rectangle(0, 0, bm.Width, bm.Height), ImageLockMode.ReadWrite, PixelFormat.Format1bppIndexed);

// gibt den Pixelindex im Farbpalette zurück
// da es sich um 1 Bit handelt, wird 0 oder 1 zurückgegeben
int pixelColorIndex = GetIndexedPixel(50, 30, bitmapData);

// Farbe aus der Farbpalette lesen
Color pixelColor = bm.Palette.Entries[pixelColorIndex];

Und hier ist die Methode:

// x, y relativ zum gesperrten Bereich
private int GetIndexedPixel(int x, int y, BitmapData bitmapData)
{
    var index = y * bitmapData.Stride + (x >> 3);
    var chunk = Marshal.ReadByte(bitmapData.Scan0, index);

    var mask = (byte)(0x80 >> (x & 0x7));
    return (chunk & mask) == mask ? 1 : 0;
}

Die Pixelposition wird in 2 Runden berechnet:

1) Finde das Byte, in dem der Pixel in 'x' liegt (x / 8): Jedes Byte enthält 8 Pixel. Um das Byte zu finden, teile x/8 ab und runde ab: 58 >> 3 = 7, der Pixel befindet sich im Byte 7 der aktuellen Zeile (Stride)

2) Finde das Bit im aktuellen Byte (x % 8): Führe x & 0x7 aus, um nur die 3 linken Bits zu erhalten (x % 8)

Beispiel:

x = 58 
// x / 8 - Der Pixel befindet sich im Byte 7
byte = 58 >> 3 = 58 / 8 = 7 

// x % 8 - Byte 7, Bit 2
bitPosition = 58 & 0x7 = 2 

// die Pixel werden von links nach rechts gelesen, daher beginnen wir mit 0x80 und verschieben dann nach rechts. 
maske = 0x80 >> bitPosition = 1000 0000b >> 2 =  0010 0000b

1voto

driis Punkte 156110

Zunächst einmal, wenn Sie eine einzelne Pixel in einem Schritt lesen müssen, wird GetPixel in der Leistung äquivalent sein. Die teure Operation besteht darin, die Bits zu sperren, d.h. Sie sollten den BitmapData für alle Lesevorgänge halten, die Sie benötigen, und ihn erst am Ende schließen - aber denken Sie daran, ihn zu schließen!

Es scheint einige Verwirrung über Ihr Pixelformat zu geben, aber lassen Sie uns davon ausgehen, dass es korrekt 1bpp ist. Dann wird jeder Pixel ein Bit belegen und es wird Daten für 8 Pixel in einem Byte geben. Daher ist Ihre Indexberechnung nicht korrekt. Die Position des Bytes würde in x/8 liegen, dann müssen Sie das Bit x%8 nehmen.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X