GDI+ Bitmap Vergrößern -> Bitmap wird abgeschnitten

  • VB.NET

Es gibt 16 Antworten in diesem Thema. Der letzte Beitrag () ist von FreakJNS.

    GDI+ Bitmap Vergrößern -> Bitmap wird abgeschnitten

    NAbend.

    Ich stehe vor einem Problem, das wahrscheinlich ein Bug im Framework ist.
    Siehe folgender Code:

    VB.NET-Quellcode

    1. Dim Bipm As Bitmap = DirectCast(Bitmap.FromFile("C:\Users\Administrator\Desktop\Test.png"), Bitmap) 'Warum gibt Bitmap.FromFile() ein Image zurück?
    2. Dim PosX As Integer
    3. Dim PosY As Integer
    4. Private Sub MeMouseMove(ByVal sender As Object, ByVal e As MouseEventArgs) Handles Me.MouseMove
    5. PosX = e.X
    6. PosY = e.Y
    7. Me.Invalidate()
    8. End Sub
    9. Private Sub MePaint(ByVal sender As Object, ByVal e As PaintEventArgs) Handles Me.Paint
    10. 'So erkennt man es besser
    11. e.Graphics.InterpolationMode = Drawing2D.InterpolationMode.NearestNeighbor
    12. 'Vergrößerung kann hier verändert werden
    13. Dim BipmSizeFactor As Integer = 18
    14. 'Dieses Rechteck gibt den Bereich an, in dem die Bitmap gezeichnet wird.
    15. Dim BipmRectangle As New Rectangle(PosX - Bipm.Width * BipmSizeFactor \ 2, PosY - Bipm.Height * BipmSizeFactor \ 2, Bipm.Width * BipmSizeFactor, Bipm.Height * BipmSizeFactor)
    16. 'Einmal wird die Bitmap gezeichnet, einmal ein Rechteck außenherum. Beide Male der selbe Bereich!
    17. e.Graphics.DrawImage(Bipm, BipmRectangle)
    18. e.Graphics.DrawRectangle(Pens.Red, BipmRectangle)
    19. End Sub


    Hier das verwendete Bild:

    Wenn das Bild ursprünglich ziemlich klein war (in diesem Fall 7x7 Pixel), dann wird der Inhalt des Bildes nach links oben verschoben:


    Wenn man das Bild aber z.B. mit Paint anzeigt erkennt man, dass es schön zentriert ist. Ganz normal. So wie es sein sollte.
    Das scheint nur mit sehr kleinen Bildern zu passieren.

    Hat jemand Ideen warum das so sein könnte?

    Und ja: Es macht Sinn dieses kleine Bild so stark zu vergrößern.
    1. Speicherplatz sparen
    2. Ressourcen sparen
    3. Performance verbessern
    4. Einfacher zu zeichnen
    "Luckily luh... luckily it wasn't poi-"
    -- Brady in Wonderland, 23. Februar 2015, 1:56
    Desktop Pinner | ApplicationSettings | OnUtils

    VB.NET-Quellcode

    1. Dim Bipm As Bitmap = DirectCast(Bitmap.FromFile("C:\Users\Administrator\Desktop\Test.png"), Bitmap) 'Warum gibt Bitmap.FromFile() ein Image zurück?

    Lösung:

    VB.NET-Quellcode

    1. Dim Bimp As Bitmap = New Bitmap("C:\...")




    Das scheint aber wirklich ein Bug zu sein, die Grafik ist um einen halben Pixel (sieht man in der Vergrößerung) nach links/oben verschoben. Du kannst diese Verschiebung ausgleichen indem du den Bildbeginn ebenfalls einen halben Pixel nach links/oben verscheibst - das Gezeichnete rückt dann nämlich nach rechts/unten und alles ist wie es soll:

    e.Graphics.DrawImage(Bipm, BipmRectangle, -0.5F, -0.5F, Bipm.Width, Bipm.Height, GraphicsUnit.Pixel, New Imaging.ImageAttributes)



    EDIT: Mich hat es jetzt ja schon interessiert warum das so ist. Kurz gegoogelt: Die Lösung
    your problem is probably the nearest neighbor interpolation

    Ist ja schon irgendwie logisch xD Naja, wenn man schon nicht selbst drauf kommt kann man wenigstens die Auswirkung beseitigen:

    VB.NET-Quellcode

    1. e.Graphics.PixelOffsetMode = Drawing2D.PixelOffsetMode.Half

    Dieser Beitrag wurde bereits 4 mal editiert, zuletzt von „FreakJNS“ ()

    @Samus Aran: Bimap.FromStream gibt nebenbei erwähnt immer noch ein Image zurück. Da bleibe ich lieber bei Bitmap.FromFile oder besser gleich New Bitmap, denn die Geschwindigkeit ist hier wirklich nicht ausschlaggebend, und übersichtlicher ist es auch.

    @FreakJNS:

    Du kannst diese Verschiebung ausgleichen

    :D Hab ich auch schon versucht, aber dann fehlt oben und links ein Teil vom Bild.

    Mit PixelOffsetMode.Half funktioniert alles prima.

    Es gibt jetzt noch einen kleinen Bug beim Zeichnen des Rectangles. Das Pixel links oben wird nicht gezeichnet.


    Allerdings weiß ich nicht was das Verschieben mit nearest neighbour zu tun hat. Probier es selbst mal aus:

    Auch bei HighQualityBicubic erkennt man deutlich, dass es verschoben ist.

    Habe ich nicht verstanden wie NearestNeighbour funktioniert?
    "Luckily luh... luckily it wasn't poi-"
    -- Brady in Wonderland, 23. Februar 2015, 1:56
    Desktop Pinner | ApplicationSettings | OnUtils
    NearestNeighbour ist ja eine Interpolationsmethode, sprich das, was du später siehst ist nicht das was es mal war (Interpolieren bedeutet ja, dass Werte über die man garnicht verfügt aus der Umgebung berechnet werden - sie nähern sich nur der realität an) - siehst du ja bei higqualitybicubic extrem, da ist alles verschwommen. Der Interpolationsalgorithmus wirkt als ob er die Pixel 1:1 übernimmt und nur vergrößert darstellt - das tut er aber nicht. Wie genau de Algorithmus aussieht/funktioniert weiß ich nicht, das ist auf jeden fall seine Auswirkung. Ähnlich wird es sich bei den anderen Interpolations-Mehthoden verhalten...

    Darum PixelOffsetMode.Half benutzen und es gleicht sich aus - oder besteht das Problem bei dir weiterhin?
    Ja, aber vergleiche

    VB.NET-Quellcode

    1. DirectCast(Bitmap.FromFile("C:\Users\Administrator\Desktop\Test.png"), Bitmap)

    mit

    VB.NET-Quellcode

    1. DirectCast(Bitmap.FromStream(New IO.FileStream("C:\Users\Administrator\Desktop\Test.png", IO.FileMode.Open)), Bitmap)


    Höhere Verschachtelung bedeutet für mich weniger Übersichtlichkeit.
    "Luckily luh... luckily it wasn't poi-"
    -- Brady in Wonderland, 23. Februar 2015, 1:56
    Desktop Pinner | ApplicationSettings | OnUtils
    Ich habe das ganze mal getestet und bin zu folgendem Ergebnis gekommen:

    VB.NET-Quellcode

    1. Dim tmp as new bitmap("pfad")
    ist schneller, einfacher und besser als alle getesteten alternativen:

    Was mich verwundert hat: Die Lösung per Stream hat vollkommen versagt^^ Außerdem sieht man den enormen Geschwindigkeitsverlust wenn JPG-Komression im Spiel ist... Hier mal meine 6 Testbedingungen und deren Ergebnisse:

    Testbedingungen:
    'T1: *.bmp => 140MB
    'T2: *.jpg => 2,2MB
    'T3: *.jpg => 1MB (5Megapixel-Bild)
    'T4: *.bmp => 14,4MB (5Megapixel-Bild)
    'T5: *.bmp => 30KB 100x100 Pixel
    'T6: *.jpg => 2KB 100x100 Pixel

    Ergebnisse:
    --'T1----'T2----'T3---'T4---'T5--'T6
    a '100ms '414ms '83ms '16ms '2ms '2ms <- IMMER Testsieger!
    b '100ms '428ms '92ms '18ms '2ms '2ms
    c '191ms '439ms '87ms '61ms '3ms '3ms

    Die Funktionen:

    VB.NET-Quellcode

    1. Dim a As Bitmap = New Bitmap(Pfad)
    2. Dim b As Bitmap = DirectCast(Bitmap.FromFile(Pfad), Bitmap)
    3. Dim c As Bitmap = Nothing
    4. Using Str As New System.IO.FileStream(Pfad, IO.FileMode.Open)
    5. c = New Bitmap(Str)
    6. End Using


    Insofern werde ich es mir (wie schon immer) leicht machen und nur die überladung von "New Bitmap" verwenden. Wer einwende hat möge seine (per Stopwatch gemessenen!) Testergebnisse posten^^ lg


    Edit: FromStream ist übrigens auch nicht schneller als "new Bitmap(Pfad)" - ob es schneller ist als "new Bitmap(Stream)" weiß ich nicht

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „FreakJNS“ ()

    @ samus aran
    Siehe Test3: FromStream sollte (bei deinem Beispiel) schneller sein als FromFile - da hast du recht. "New Bitmap(Pfad)" dürfte aber trotzdem noch schneller sein. FromFile ist - finde ich - ja auch käse weil man dann casten muss... Bei FromStream hat man nur einen vorteil, wenn man die Datei nicht blockiert haben möchte, ansonsten noch mehr tipp-arbeit wegen stream closen - und das geht in einem einzeiler meines wissens nicht

    Ich habe dein Timer-Beispiel nicht getestet weil ich denke, dass es mich zum selben ergebnis bringt: FromStream (und FromFile) ist nicht schneller als die Überladung vom Sub New bei der man nur den Pfad angibt!

    Zumindest sagen das meine Testergebnisse^^
    @ samus aran

    so, hab das getestet:

    'T1: JPG 2592x1944Pixel, 970KB
    'T2: PNG 2592x1944Pixel, 10MB
    'T3: JPG 5184x3888Pixel, 1.42MB
    'T4: BMP 5184x3888Pixel, 57MB

    'NewBitmap(Pfad): T1=80ms T2=78ms T3=204ms T4=45ms
    'FromStream: T1=87ms T2=85ms T3=222ms T4=99ms


    Man sieht, dass FromStream bei Komprimierten Formaten das Kopf-An-Kopf-Rennen mit NewBitmap verliert. Bei *.bmp Dateien, die soweit ich weiß unkomprimiert sind ist die Niederlage sehr deutlich!

    Dim tmp as new Bitmap(Pfad) ist das schnellste!

    where is your god now?
    ja ist es. Ich habe 136 5-Megapixel JPG-Bilder eingelesen. (Der Vorherige Test hat ergeben, dass FromStream um wenige Millisekunden langsamer ist als die NewBitmap(Pfad)-Mehtode)

    Das Ergebnis des "neuen" Tests:

    .FromStream-Mehtode: 6200ms
    NewBitmap(Pfad)-Methode: 6100ms

    Bei Bitmaps vermute ich wird die newBitmap-Methode wieder um Längen besser sein.... Bei 5-Megapixel-JPGs ist es milisekundenfuchserei die sich nicht lohnt. FromStream IST aber etwas langsamer!