Laden...

Bitmap.GetPixelFormatSize() liefert falsche Farbtiefe

Erstellt von toxic vor 9 Jahren Letzter Beitrag vor 9 Jahren 6.783 Views
T
toxic Themenstarter:in
64 Beiträge seit 2010
vor 9 Jahren
Bitmap.GetPixelFormatSize() liefert falsche Farbtiefe

Hi,

ich bin grad völligst am verzweifeln. Ich verwende von Codeproject (Fast Bitmaps) diese Klasse um ein Bild zu erstellen und bearbeiten.

Problem ist, das es mir das ganze Bild verzerrt. siehe Screenshot.
Ich hab jetzt rausbekommen das es wohl an der falsch gelesen Farbtiefe liegt.
Ich hab mal ein Testprogramm gemacht:


		public static void Main(string[] args)
		{
			Bitmap bmp = (Bitmap)Image.FromFile(@"C:\_trash\pdfdiff\t_24bit.bmp");
			
			Console.WriteLine(Bitmap.GetPixelFormatSize(bmp.PixelFormat));
			
			Console.ReadKey(true);
		}

Dieser gibt mir dann schon die falsche Farbtiefe aus. Im Windows Explorer Eigenschaften-Fenster steht die Richtige.
Anbei noch die Testbilder (in Photoshop CS2 gespeichert). Das absolut noch dubiosere ist, dass wenn ich ein PDF im Acrobat mit speichern unter "JPG" mache, das Tool ohne Probleme funktioniert. Aber alle anderen Bilder (getestet Ghostscript, Photoshop, Paint) Probleme machen.

Dateiformat ist auch egal (PNG, BMP, JPG).

Ich weiss leider echt nicht mehr weiter. Hat jemand schon mal ein ähnliches Problem gehabt?

Vielen Dank

T
toxic Themenstarter:in
64 Beiträge seit 2010
vor 9 Jahren
T
toxic Themenstarter:in
64 Beiträge seit 2010
vor 9 Jahren

Screenshot (Sorry, bekam eine Fehlermeldung beim Hochladen...)

5.658 Beiträge seit 2006
vor 9 Jahren

Hi toxic,

ein Bild zu erstellen und bearbeiten.

Dann solltest du mal erklären oder zeigen was du machst. Wenn du nur das Ergebnis postest, kann es doch keiner nachvollziehen!

Dieser gibt mir dann schon die falsche Farbtiefe aus. Im Windows Explorer Eigenschaften-Fenster steht die Richtige.

Was wird denn ausgegeben und was ist die von dir erwartete Ausgabe? Bitte beachte [Hinweis] Wie poste ich richtig?, Punkt 5!

Christian

Weeks of programming can save you hours of planning

T
toxic Themenstarter:in
64 Beiträge seit 2010
vor 9 Jahren

Hallo Christian,

ich habe geschrieben, dass ich bereits eine Vermutung für den Fehler hab. Deswegen hab ich einen Code eingestellt der die Farbtiefe aus Bilder ausließt und hab dazu Testbilder gepackt.

Die Dateinamen heißen so, mit welcher Farbtiefe das Bild gespeichert wurde. Wenn ich mir die Farbtiefe im Explorer anzeigen lassen passt diese. Wenn ich sie mir über .Net mit der obenen genannten Funktion ausgeben lassen, stimmt diese nicht überein. z.B. Bild hat 16 Bit Farbtiefe, Ausgabe 8 Bit

Sorry wenn das ganze unverständlich war...

T
toxic Themenstarter:in
64 Beiträge seit 2010
vor 9 Jahren

Hier nochmal ein Bild zur Verdäutlichung:
Ich hab das Bild mit 16 Bit (Wird auch im Explorer angezeigt) eingelesen. Im .Net bekomm ich aber die Anzeige 32 Bit?! Deswegen komm ich dann auch mit den LockBits-Funktion nicht richtig zu Rande.

Das Grundproblem ist, dass ich nicht die richtige Farbtiefe bekomme

16.835 Beiträge seit 2008
vor 9 Jahren

IIRC kann Image.FromFile() erst mal nur mit 32 Bit arbeiten. Beim Laden ist es 32 Bit und der Cast hilft Dir hier auch nicht.
Du musst ein neues Bitmap-Objekt erstellen mit dem gewünschten 16-Bit Parameter und dann das originale Image in das neue Objekt laden.

T
toxic Themenstarter:in
64 Beiträge seit 2010
vor 9 Jahren

Das tolle ist aber das:

Bild: Ausgabe:
16 --> 32
24 --> 24
32 --> 24

Das gibt doch überhaupt keinen Sinn!
*grml*

5.658 Beiträge seit 2006
vor 9 Jahren

Ist doch völlig egal, wie die Ausgabe von irgendwelchen Programmen aussehen, wenn du nicht zeigst, wie du das Bild erstellst. Da wird nämlich das eigentliche Problem liegen.

Christian

Weeks of programming can save you hours of planning

16.835 Beiträge seit 2008
vor 9 Jahren

Das gibt doch überhaupt keinen Sinn!
*grml*

Doch. Siehe auch Loading image from 32 bits bitmap file.
Bestätigt meine Aussage.

T
toxic Themenstarter:in
64 Beiträge seit 2010
vor 9 Jahren

Hi,

nochmal ich hab das Bitmap mit verschiedenen Programmen erstellt. in dem Fall wie oben geschrieben mit Adobe Photoshop CS 2. Datei Speichern unter BMP und die verschiedenen Farbtiefen ausgewählt...ich versuch sie doch nur zu laden...

16.835 Beiträge seit 2008
vor 9 Jahren

Ignorierst meine Beiträge mit Absicht?

T
toxic Themenstarter:in
64 Beiträge seit 2010
vor 9 Jahren

Nein Abt, sorry. Schau ich mir gerade an das Thema. Ich wollte nur Christian drauf hinweisen, das ich bereits erklärt habe wo die Bilder herkommen die ich laden will.

Ich will keinen Verärgern. Vielen Dank für die Hilfe! Ich werd mich mal einlesen.

49.485 Beiträge seit 2005
vor 9 Jahren

Hallo toxic,

im Explorer bekomme ich für die Dateien aus der Zip-Datei 16, 24, 24 Bit angezeigt. Kein Wunder, die Dateien t_24bit.bmp und t_32bit.bmp byteweise identisch.

Irfanview zeigt für alle Dateien 24 Bit an.

Ein anderes Programm zeigt mir bei der Datei t_16bit.bmp sogar:> Fehlermeldung:

can't read file header: bit count not 1, 4, 8 or 24

Die Ausgangssituation der Dateien in der Zip-Datei ist also nicht so, wie du sie vermutet/beschrieben hast.

herbivore

T
toxic Themenstarter:in
64 Beiträge seit 2010
vor 9 Jahren

Hallo herbivore,

Danke das du dir es auch nochmal anschaust. Ich habe jetzt mal Vergleichsbilder gemacht. Die Ursprungsbilder wurden von einem PDF mit Ghostscript als jpg umgewandelt.

(D:\pdf\gs\gs9.09\bin>gswin32c.exe -sDEVICE=jpeg -r50 -dBATCH -dNOPAUSE -sOutputFile=D:\pdf\pdf1.jpg D:\pdf\pdf1.pdf)

Wenn ich das Bild über die Funktionen Bitmap.GetPixel() und Bitmap.SetPixel() bearbeite und auslese ist alles so wie gewollt. Wenn ich aber über LockBits gehe, verschiebt er mir alles.
Ich pack mal ein Beispielprojekt mit rein. Die LockBitmap-Klasse ist die, die ich oben schon verlinkt habe.

Ich komm irgendwie nicht weiter. Hab versucht wie Abt das geschrieben hat, das Bitmap zu laden. Leider ohne erfolg

Erklärung der Bilder:
pdf1.jpg -->Ausgangsbild Source
pdf2.jpg -->Ausgangsbild Compare
compare_bitmap.jpg --> So sollte es aussehen (mit GetPixel und SetPixel)
compare_lockbits.jpg --> So sieht es mit LockBits aus...

LockBits brauch ich wegen der Performance.

Vielen Dank an alle!

T
toxic Themenstarter:in
64 Beiträge seit 2010
vor 9 Jahren

Kann man hier irgendwie mehr Bilder auf einmal hochladen?

T
toxic Themenstarter:in
64 Beiträge seit 2010
vor 9 Jahren
T
toxic Themenstarter:in
64 Beiträge seit 2010
vor 9 Jahren
T
toxic Themenstarter:in
64 Beiträge seit 2010
vor 9 Jahren
4.939 Beiträge seit 2008
vor 9 Jahren

Hallo toxic,

das verzerrte Bild kommt (üblicherweise) durch die Nichtbeachtung des "Stride" zustande (wenn die Breite des Bildes "ungerade" ist). Hier eine Erklärung dafür: Using LockBits in GDI+.

Unter [RESOLVED] C# lockbits. gibt es korrekten Source-Code dafür (im unsafe-Modus).

Zeig mal deinen Code für LockBits. Benutzt du denn den Stride-Parameter?

T
toxic Themenstarter:in
64 Beiträge seit 2010
vor 9 Jahren

Hi,
Vielen Dank für die Antwort. Nein von den Strides hab ich noc nichts gehört.
anbei der Code für die LockBits:


public class LockBitmap
{
    Bitmap source = null;
    IntPtr Iptr = IntPtr.Zero;
    BitmapData bitmapData = null;

    public byte[] Pixels { get; set; }
    public int Depth { get; private set; }
    public int Width { get; private set; }
    public int Height { get; private set; }
    public Bitmap Image { get { return source; } }

    public LockBitmap(Bitmap source)
    {
        this.source = source;
    }

    /// <summary>
    /// Lock bitmap data
    /// </summary>
    public void LockBits()
    {
        try
        {
            // Get width and height of bitmap
            Width = source.Width;
            Height = source.Height;

            // get total locked pixels count
            int PixelCount = Width * Height;

            // Create rectangle to lock
            Rectangle rect = new Rectangle(0, 0, Width, Height);

            // get source bitmap pixel format size
            Depth = System.Drawing.Bitmap.GetPixelFormatSize(source.PixelFormat);

            // Check if bpp (Bits Per Pixel) is 8, 24, or 32
            if (Depth != 8 && Depth != 24 && Depth != 32)
            {
                throw new ArgumentException("Only 8, 24 and 32 bpp images are supported.");
            }

            // Lock bitmap and return bitmap data
            bitmapData = source.LockBits(rect, ImageLockMode.ReadWrite,
                                         source.PixelFormat);

            // create byte array to copy pixel values
            int step = Depth / 8;
            Pixels = new byte[PixelCount * step];
            Iptr = bitmapData.Scan0;

            // Copy data from pointer to array
            Marshal.Copy(Iptr, Pixels, 0, Pixels.Length);
        }
        catch (Exception ex)
        {
            throw ex;
        }
    }

    /// <summary>
    /// Unlock bitmap data
    /// </summary>
    public void UnlockBits()
    {
        try
        {
            // Copy data from byte array to pointer
            Marshal.Copy(Pixels, 0, Iptr, Pixels.Length);

            // Unlock bitmap data
            source.UnlockBits(bitmapData);
        }
        catch (Exception ex)
        {
            throw ex;
        }
    }

    /// <summary>
    /// Get the color of the specified pixel
    /// </summary>
    /// <param name="x"></param>
    /// <param name="y"></param>
    /// <returns></returns>
    public Color GetPixel(int x, int y)
    {
        Color clr = Color.Empty;

        // Get color components count
        int cCount = Depth / 8;

        // Get start index of the specified pixel
        int i = ((y * Width) + x) * cCount;

        if (i > Pixels.Length - cCount)
            throw new IndexOutOfRangeException();

        if (Depth == 32) // For 32 bpp get Red, Green, Blue and Alpha
        {
            byte b = Pixels[i];
            byte g = Pixels[i + 1];
            byte r = Pixels[i + 2];
            byte a = Pixels[i + 3]; // a
            clr = Color.FromArgb(a, r, g, b);
        }
        if (Depth == 24) // For 24 bpp get Red, Green and Blue
        {
            byte b = Pixels[i];
            byte g = Pixels[i + 1];
            byte r = Pixels[i + 2];
            clr = Color.FromArgb(r, g, b);
        }
        if (Depth == 8)
        // For 8 bpp get color value (Red, Green and Blue values are the same)
        {
            byte c = Pixels[i];
            clr = Color.FromArgb(c, c, c);
        }
        return clr;
    }

    /// <summary>
    /// Set the color of the specified pixel
    /// </summary>
    /// <param name="x"></param>
    /// <param name="y"></param>
    /// <param name="color"></param>
    public void SetPixel(int x, int y, Color color)
    {
        // Get color components count
        int cCount = Depth / 8;

        // Get start index of the specified pixel
        int i = ((y * Width) + x) * cCount;

        if (Depth == 32) // For 32 bpp set Red, Green, Blue and Alpha
        {
            Pixels[i] = color.B;
            Pixels[i + 1] = color.G;
            Pixels[i + 2] = color.R;
            Pixels[i + 3] = color.A;
        }
        if (Depth == 24) // For 24 bpp set Red, Green and Blue
        {
            Pixels[i] = color.B;
            Pixels[i + 1] = color.G;
            Pixels[i + 2] = color.R;
        }
        if (Depth == 8)
        // For 8 bpp set color value (Red, Green and Blue values are the same)
        {
            Pixels[i] = color.B;
        }
    }

    public Color GetBlackWhitePixel(int x, int y)
    {
        Color value = GetPixel(x, y);
        int grey = (value.R + value.G + value.B) / 3;
        if (grey > 127) return Color.White;
        else return Color.Black;
    }
}

4.939 Beiträge seit 2008
vor 9 Jahren

Wenn du den Code so unverändert von deinem CodeProject-Link verwendest hast, dann mußt du noch den Stride-Parameter da einbauen.
Hier noch ein anderer Link, der das (für C# und VB.NET) erklärt: Using the LockBits method to access image data

PS. Es hätte gereicht, wenn du den relevanten Code gepostet hättest. Trotzdem geht aus deinen Code noch nicht heraus, wie du jetzt genau dein Bild erzeugst (also welche Methoden du konkret aufrufst).

PPS. Du kannst dir auch mal die folgenden beiden Links anschauen:
GetPixel und SetPixel um Längen geschlagen. 800 mal schneller
Bitmap-Manipulation (MemBitmap)
(diese verwenden beide korrekterweise den Stride-Parameter - anstatt den Width-Parameter)

T
toxic Themenstarter:in
64 Beiträge seit 2010
vor 9 Jahren

Hi Th69,

du bist mein persönlicher Held des Tages! die beiden Links haben mir echt weiter geholfen. Das mit dem Stride habe ich nicht gewusst! Danach ist alles etwas klarer!

Vielen Dank nochmal und schönes WE! Auch an die anderen Vielen Dank für die Geduld!

PPS. Du kannst dir auch mal die folgenden beiden Links anschauen:

>


>

(diese verwenden beide korrekterweise den Stride-Parameter - anstatt den Width-Parameter)