Bitmap zentriert drehen und Größe ermitteln

  • VB.NET
  • .NET (FX) 4.5–4.8

Es gibt 28 Antworten in diesem Thema. Der letzte Beitrag () ist von RodFromGermany.

    Bitmap zentriert drehen und Größe ermitteln

    Hallo,

    ich habe eine Bitmap und möchte dieses um einen beliebigen Winkel drehen. Danach möchte ich die neue Größe ermitteln und das neue Bitmap anzeigen lassen.

    Folgenden Code habe ich zusammengestellt:

    VB.NET-Quellcode

    1. Public Sub BildDrehen(Ursprungsbild As Bitmap, Winkel As Integer, Zielbild As Bitmap)
    2. Dim Grafikobjekt As Graphics = Graphics.FromImage(Zielbild)
    3. Dim Eckpunkte(3) As Point
    4. Dim Bildmatrix As New Drawing2D.Matrix
    5. Dim Breite As Integer = 1
    6. Dim Hoehe As Integer = 1
    7. Dim XMin As Single = Ursprungsbild.Width * Ursprungsbild.Height * 2
    8. Dim XMax As Single = -XMin
    9. Dim YMin As Single = XMin
    10. Dim YMax As Single = XMax
    11. Dim Zaehler As Integer
    12. Dim Mittelpunkt As Drawing.PointF
    13. Mittelpunkt.X = CSng(Ursprungsbild.Width / 2)
    14. Mittelpunkt.Y = CSng(Ursprungsbild.Height / 2)
    15. Eckpunkte(0).X = 0
    16. Eckpunkte(0).Y = 0
    17. Eckpunkte(1).X = Ursprungsbild.Width - 1
    18. Eckpunkte(1).Y = 0
    19. Eckpunkte(2).X = Ursprungsbild.Width - 1
    20. Eckpunkte(2).Y = Ursprungsbild.Height - 1
    21. Eckpunkte(3).X = 0
    22. Eckpunkte(3).Y = Ursprungsbild.Height - 1
    23. Bildmatrix.RotateAt(Winkel, Mittelpunkt)
    24. Bildmatrix.TransformPoints(Eckpunkte)
    25. For Zaehler = 0 To 3
    26. XMin = System.Math.Min(Eckpunkte(Zaehler).X, XMin)
    27. XMax = System.Math.Max(Eckpunkte(Zaehler).X, XMax)
    28. YMin = System.Math.Min(Eckpunkte(Zaehler).Y, YMin)
    29. YMax = System.Math.Max(Eckpunkte(Zaehler).Y, YMax)
    30. Next
    31. Breite = CInt(XMax - XMin)
    32. Hoehe = CInt(YMax - YMin)
    33. Zielbild = New Bitmap(Breite, Hoehe)
    34. Grafikobjekt = Graphics.FromImage(Zielbild)
    35. Grafikobjekt.TranslateTransform(CInt(Zielbild.Width / 2), CInt(Zielbild.Height / 2))
    36. Grafikobjekt.RotateTransform(Winkel)
    37. Grafikobjekt.TranslateTransform(-CInt(Zielbild.Width / 2), -CInt(Zielbild.Height / 2))
    38. Grafikobjekt.DrawImage(Ursprungsbild, CInt((Breite - Ursprungsbild.Width) / 2), CInt((Hoehe - Ursprungsbild.Height) / 2), Breite, Hoehe)
    39. Dim S As Graphics = Graphics.FromImage(SichtfensterPicture.Image)
    40. S.DrawImage(Zielbild, New Rectangle(0, 0, Breite, Hoehe), New Rectangle(0, 0, Breite, Hoehe), GraphicsUnit.Pixel)
    41. End Sub


    Das Drehen funktioniert. Allerdings ist noch ein Fehler darin, weil das neue Bild abgeschnittene Ecken hat.

    Hat jemand von euch eine Idee, was ich übersehen bzw. falsch gemacht habe?

    Vielen Dank schon mal im Voraus
    So was?
    Spoiler anzeigen

    VB.NET-Quellcode

    1. Imports System
    2. Imports System.Drawing
    3. Imports System.Drawing.Drawing2D
    4. Module Module1
    5. Function RotateImage(originalImage As Bitmap, angleDegrees As Single) As Bitmap
    6. ' Ursprüngliche Abmessungen des Bildes
    7. Dim originalWidth As Integer = originalImage.Width
    8. Dim originalHeight As Integer = originalImage.Height
    9. ' Winkel in Radiant umrechnen
    10. Dim angleRadians As Double = angleDegrees * Math.PI / 180.0
    11. ' Neue Abmessungen berechnen
    12. Dim cosTheta As Double = Math.Abs(Math.Cos(angleRadians))
    13. Dim sinTheta As Double = Math.Abs(Math.Sin(angleRadians))
    14. Dim newWidth As Integer = CInt(Math.Ceiling(originalWidth * cosTheta + originalHeight * sinTheta))
    15. Dim newHeight As Integer = CInt(Math.Ceiling(originalWidth * sinTheta + originalHeight * cosTheta))
    16. ' Neue Bitmap mit berechneten Abmessungen erstellen
    17. Dim rotatedBitmap As New Bitmap(newWidth, newHeight)
    18. rotatedBitmap.SetResolution(originalImage.HorizontalResolution, originalImage.VerticalResolution)
    19. ' Grafikobjekt erstellen und Transformation setzen
    20. Using g As Graphics = Graphics.FromImage(rotatedBitmap)
    21. g.SmoothingMode = SmoothingMode.AntiAlias
    22. g.InterpolationMode = InterpolationMode.HighQualityBicubic
    23. g.PixelOffsetMode = PixelOffsetMode.HighQuality
    24. ' Mittelpunkt der neuen Bitmap als Rotationszentrum setzen
    25. g.TranslateTransform(CSng(newWidth) / 2, CSng(newHeight) / 2)
    26. g.RotateTransform(angleDegrees)
    27. g.TranslateTransform(CSng(-originalWidth / 2), CSng(-originalHeight / 2))
    28. ' Originalbild in die neue Bitmap zeichnen
    29. g.DrawImage(originalImage, New Point(0, 0))
    30. End Using
    31. Return rotatedBitmap
    32. End Function
    33. Sub Main()
    34. Dim originalImage As New Bitmap("testbild.jpg") ' Pfad zu einem Bild
    35. Dim angle As Single = 45 ' Drehwinkel in Grad
    36. Dim rotatedImage As Bitmap = RotateImage(originalImage, angle)
    37. ' Gedrehtes Bild speichern
    38. rotatedImage.Save("gedrehtes_bild.jpg", Imaging.ImageFormat.Jpeg)
    39. ' Speicher freigeben
    40. originalImage.Dispose()
    41. rotatedImage.Dispose()
    42. Console.WriteLine("Bild wurde erfolgreich um {0}° gedreht und gespeichert!", angle)
    43. End Sub
    44. End Module


    EDIT Nachtrag:

    Definitionen:
    SmoothingMode
    SmoothingMode.None deaktiviert jegliche Glättung und sorgt für harte, scharfe Kanten zwischen Pixeln.
    SmoothingMode.HighQuality verstärkt die Kantenglättung noch weiter.
    SmoothingMode.AntiAlias sorgt für weiche Kanten und glättet Übergänge zwischen Pixeln.

    SmoothingMode.AntiAlias:
    Weichste Kanten und Übergänge

    InterpolationMode (Steuerung der Skalierungsqualität):
    NearestNeighbor - Sehr niedrige Qualität - Geschwindigkeit: Schnell, aber verpixelt (ideal für Pixel-Art)
    Bilinear - Mittlere Qualität - Gute Performance, aber etwas unscharf
    Bicubic - Hohe Qualität - Glatter, aber mehr Rechenaufwand
    HighQualityBilinear - Mittlere-Hohe Qualität - Mischung aus Qualität und Geschwindigkeit
    HighQualityBicubic - Sehr hohe Qualität - Beste Qualität, ideal für Rotation & Skalierung

    InterpolationMode.HighQualityBicubic:
    Höchste Bildqualität
    Erzeugt weiche Übergänge, ohne Treppeneffekte
    Ideal für Rotationen und Größenänderungen

    PixelOffsetMode (Steuerung der Pixelgenauigkeit):
    None - Standardpositionierung (kann Ungenauigkeiten erzeugen)
    HighSpeed - Schnell, aber weniger präzise
    Half - Pixel werden um 0.5 Pixel versetzt (für bessere Glättung)
    HighQuality - Maximale Präzision bei Bildmanipulationen

    PixelOffsetMode.HighQuality:
    Reduziert Treppeneffekte und Pixelverzerrungen
    Sorgt für präzisere Positionierung bei Transformationen
    Empfehlenswert für Rotationen & Skalierungen mit feinen Details

    So steht es in meinem Buch.
    Was für Deine Zwecke das Beste ist, kann ich leider nicht beantworten.
    Dazu müsste man wohl verschiedene "Lesbarkeitstests" auf einem Braille-Drucker oder einem 3D-Pin-Anzeigegerät fahren.

    Dieser Beitrag wurde bereits 9 mal editiert, zuletzt von „Dideldum“ ()

    Vielen Dank für die schnelle Antwort. Ich habe den Code an mein Programm angepaßt. Es funktioniert.

    Ich habe ein Programm, welches optische Grafiken umrechnet und eine Punktgrafik erzeugt, die dann für Blinde auf einem Brailledrucker oder einem Anzeigegerät ausgegeben werden kann. Bisher hatte ich im Hintergrund mit Tabellen, in deren Zellen eine 0 oder 100 steht. Vor Kurzem habe ich Funktionen hinzugefügt, die Grafiken um einen beliebigen Winkel drehen. Dabei mußte ich mit der Kreisfunktion jeden einzelnen Punkt neu berechnen und gegebenenfalls auf- bzw. abrunden.

    Mit der Neuen Funktion kann ich die Tabellen durch Bitmaps ersetzen und automatisch drehen lassen. Statt der einzelnen Zellen brauche ich nur die einzelnen Bits einer Bitmap zwarz bzw. weiß setzen.

    Eine Frage hätte ich zu deiner Funktion noch:

    Leider kann ich selbst das nicht prüfen, weil ich zu schlecht sehe. Gibt es beim Drehen unscharfe Bereiche? Beispielsweise, wenn ein Neuer Punkt rechnerisch gesehen zwischen zwei anderen Punkten liegt? Dies wäre für mein Vorhaben nicht gut, da ich immer klare Werte brauche - Schwarz oder Weiß - nicht Grau.

    Könntest du das bitte mal testen und mir das Ergebnis mitteilen?

    Vielen Dank nochmal für die Hilfe
    Hi,
    ich denke, das solltest Du in Zeile 26 mit:
    Leicht verwischt:

    VB.NET-Quellcode

    1. g.SmoothingMode = SmoothingMode.AntiAlias

    oder
    Scharfe Pixelabgrenzungen

    VB.NET-Quellcode

    1. g.SmoothingMode = SmoothingMode.none

    anpassen können ;)

    EDIT:
    Die erste Funktion ist zwar schneller, fügt technisch bedingt beim Drehen und zurückdrehen aber einen störenden Rahmen ein, welcher das Bild vergrössert.
    Hierbei kannst Du folgende Funktion verwenden, welche das Bild nach jedem Drehvorgang croppt - also unnötige Rahmen entfernen.
    Ist halt recht langsam, diese Option. Aber das Bild hat damit immer die geringstmögliche Abmessung.
    Spoiler anzeigen

    VB.NET-Quellcode

    1. Function RotateImage(originalImage As Bitmap, angleDegrees As Single) As Bitmap
    2. ' Ursprüngliche Abmessungen des Bildes
    3. Dim originalWidth As Single = originalImage.Width
    4. Dim originalHeight As Single = originalImage.Height
    5. ' Winkel in Radiant umrechnen
    6. Dim angleRadians As Double = angleDegrees * Math.PI / 180.0
    7. ' Neue Abmessungen berechnen (Bounding Box)
    8. Dim cosTheta As Double = Math.Abs(Math.Cos(angleRadians))
    9. Dim sinTheta As Double = Math.Abs(Math.Sin(angleRadians))
    10. Dim newWidth As Single = CSng(Math.Ceiling(originalWidth * cosTheta + originalHeight * sinTheta))
    11. Dim newHeight As Single = CSng(Math.Ceiling(originalWidth * sinTheta + originalHeight * cosTheta))
    12. ' Neue Bitmap mit berechneten Abmessungen erstellen
    13. Dim rotatedBitmap As New Bitmap(CInt(newWidth), CInt(newHeight))
    14. rotatedBitmap.SetResolution(originalImage.HorizontalResolution, originalImage.VerticalResolution)
    15. Using g As Graphics = Graphics.FromImage(rotatedBitmap)
    16. g.SmoothingMode = SmoothingMode.AntiAlias
    17. g.InterpolationMode = InterpolationMode.HighQualityBicubic
    18. g.PixelOffsetMode = PixelOffsetMode.HighQuality
    19. ' Rotationszentrum auf die Mitte setzen
    20. g.TranslateTransform(newWidth / 2, newHeight / 2)
    21. g.RotateTransform(angleDegrees)
    22. g.TranslateTransform(-originalWidth / 2, -originalHeight / 2)
    23. ' Originalbild in die neue Bitmap zeichnen
    24. g.DrawImage(originalImage, New Point(0, 0))
    25. End Using
    26. ' Überflüssigen Rand abschneiden
    27. Return AutoCrop(rotatedBitmap)
    28. End Function
    29. Function AutoCrop(image As Bitmap) As Bitmap
    30. Dim minX As Integer = image.Width
    31. Dim minY As Integer = image.Height
    32. Dim maxX As Integer = 0
    33. Dim maxY As Integer = 0
    34. ' Alle Pixel durchgehen, um sichtbare Pixel zu finden
    35. For x As Integer = 0 To image.Width - 1
    36. For y As Integer = 0 To image.Height - 1
    37. Dim pixel As Color = image.GetPixel(x, y)
    38. ' Falls der Pixel nicht transparent oder weiß ist, zur Begrenzung hinzufügen
    39. If pixel.A <> 0 AndAlso pixel.ToArgb <> Color.White.ToArgb Then
    40. If x < minX Then minX = x
    41. If y < minY Then minY = y
    42. If x > maxX Then maxX = x
    43. If y > maxY Then maxY = y
    44. End If
    45. Next
    46. Next
    47. ' Falls keine Änderung gefunden wurde, einfach zurückgeben
    48. If minX >= maxX OrElse minY >= maxY Then Return image
    49. ' Rechteck mit den gefundenen Begrenzungen erstellen
    50. Dim cropRect As New Rectangle(minX, minY, maxX - minX + 1, maxY - minY + 1)
    51. ' Neues Bild mit der minimalen Bounding Box erstellen
    52. Dim croppedBitmap As New Bitmap(cropRect.Width, cropRect.Height)
    53. Using g As Graphics = Graphics.FromImage(croppedBitmap)
    54. g.DrawImage(image, New Rectangle(0, 0, cropRect.Width, cropRect.Height), cropRect, GraphicsUnit.Pixel)
    55. End Using
    56. Return croppedBitmap
    57. End Function


    Wenn Dir jeweils 90°-Schritte reichen, kannst Du auch:

    VB.NET-Quellcode

    1. Bitmap.RotateFlip(RotateFlipType.Parameter)

    verwenden.
    Optionen für "Parameter" sind:
    RotateNoneFlipNone - Keine Änderung
    Rotate90FlipNone - Dreht um 90° im Uhrzeigersinn
    Rotate180FlipNone - Dreht um 180°
    Rotate270FlipNone - Dreht um 270° (90° gegen Uhrzeigersinn)
    RotateNoneFlipX - Spiegelt horizontal (um Y-Achse)
    RotateNoneFlipY - Spiegelt vertikal (um X-Achse)
    Rotate90FlipX - Dreht um 90° + horizontal spiegeln
    Rotate90FlipY - Dreht um 90° + vertikal spiegeln
    Rotate180FlipX - Dreht um 180° + horizontal spiegeln
    Rotate180FlipY - Dreht um 180° + vertikal spiegeln
    Rotate270FlipX - Dreht um 270° + horizontal spiegeln
    Rotate270FlipY - Dreht um 270° + vertikal spiegeln

    Dieser Beitrag wurde bereits 8 mal editiert, zuletzt von „Dideldum“ ()

    @tron25 Du musst Dir das drehen des Bildes folgendermaßen vorstellen. Nimm ein Blatt Papier (das Graphicsobjekt) und drehe dieses um den Mittelpunkt des Blattes um zb. 45°. Zeichne nun ein zb. Quadrat ((Dein Bild) senk- und wagerechte Linien) auf das Blatt Papier wobei der Mittelpunkt des Quadrates der Mittelpunkt des Blatt Papier ist. Drehe das Blatt Papier wieder in die Ausgangslage zurück. Nun ist das Quadrat um 45° gedreht. Bei dieser Art von Drehung werden keine Optimierungen wie SmoothingMode usw benötigt.
    Mfg -Franky-
    Hi @-Franky-
    Das ist auch für mich interessant, was Du schreibst.
    Mit welchen Quali-Einstellungen für SmoothingMode, InterpolationMode und PixelOffsetModedrehst Du i.d.R. Deine Bilder um freie Winkelgrade?
    Ich brauche die Funktion mit universal-Einstellungen in meinem Projekt , um Photos mit 16MBit bis runter zu B/W-Bildchen mit 32x32Px zu drehen.
    Bisher habe ich alle drei Settings auf höchster Quali-Stufe - aber das Drehen dauert damit endlos lange.
    EDIT:
    Und... Wie berechnest Du nach dem Drehen die entstehenden Rahmen weg?
    Meine Doppel-Schleife ist bei grossen Pixs saulangsam. ;(

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

    tron25 schrieb:

    wie man einem Pixel einen Wert zuweist und abfragt
    So was:

    VB.NET-Quellcode

    1. Dim col = bmp.SetPixel()
    2. bmp.SetPixel(x, y, col)
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    @Dideldum Wie geschrieben. Nimm ein Blatt Papier, drehe das um x Grad, zeichne was drauf und dreh das Blatt Papier zurück. Die Kanten des Bildes sind immer scharf. Entsprechend auch die Pixel des Bildes. Die Optimierungen die das Graphicsobjekt anbietet, sind eher was wenn Du ein Bild verkleinern oder vergrößern möchtest.

    Das einzige was man berechnen muss ist die Position der 4 Eckpunkte um einen Drehpunkt (Rotationsmatrix) und dem daraus resultierenden umgebenden Rahmen (Größe des neuen Bildes in dem das gedrehte Bild gezeichnet werden soll).
    Mfg -Franky-
    @tron25
    Gerne - ich habe in meiner ersten Antwort die zur Verfügung stehenden drei Optimierungsvariablen nachgetragen - da kannst damit testen.

    @-Franky-
    Danke Dir ^^
    Um mit dem Goethe zu sprechen: "Deine Worte höre ich wohl..."
    Ich verstehe auch, wie das funktioniert.
    Nur fehlt mir jede Idee, wie das umzusetzen ist.
    Bettle ja nicht gerne, aber hättest da vielleicht eine fertige Funktion für Tron25 und mich.
    Geometrie war nie meine Leidenschaft. ;(

    Dideldum schrieb:

    aber hättest da vielleicht eine fertige Funktion für Tron25 und mich

    Jupp, hab ich. Aber ich möchte es euch nicht ganz so einfach machen. Deshalb poste ich VB6 Code. :D Ist auch keine Raketenwissenschaft das nach .NET zu übersetzen.

    Spoiler anzeigen

    Visual Basic-Quellcode

    1. Option Explicit
    2. Private Type PointF
    3. X As Single
    4. Y As Single
    5. End Type
    6. Private Type RECTF
    7. Left As Single
    8. Top As Single
    9. Right As Single
    10. Bottom As Single
    11. End Type
    12. Private tPoint(3) As PointF
    13. Private tPointOut() As PointF
    14. Private tCenterPoint As PointF
    15. Private tBoundingRect As RECTF
    16. Private Sub Form_Load()
    17. Me.ScaleMode = vbPixels
    18. Me.BackColor = vbWhite
    19. tPoint(0).X = -150
    20. tPoint(0).Y = -100
    21. tPoint(1).X = 150
    22. tPoint(1).Y = -100
    23. tPoint(2).X = 150
    24. tPoint(2).Y = 100
    25. tPoint(3).X = -150
    26. tPoint(3).Y = 100
    27. tCenterPoint.X = 250
    28. tCenterPoint.Y = 200
    29. Timer1.Interval = 10
    30. Timer1.Enabled = True
    31. End Sub
    32. Private Function GetBoundingBox(ByRef xyPoint() As PointF) As RECTF
    33. Dim lngItem As Long
    34. Dim lngCount As Long
    35. Dim tRectF As RECTF
    36. lngCount = UBound(xyPoint)
    37. With tRectF
    38. .Left = xyPoint(0).X
    39. .Top = xyPoint(0).Y
    40. .Right = xyPoint(0).X
    41. .Bottom = xyPoint(0).Y
    42. End With
    43. For lngItem = 1 To lngCount
    44. If xyPoint(lngItem).X <= tRectF.Left Then tRectF.Left = xyPoint(lngItem).X
    45. If xyPoint(lngItem).X >= tRectF.Right Then tRectF.Right = xyPoint(lngItem).X
    46. If xyPoint(lngItem).Y <= tRectF.Top Then tRectF.Top = xyPoint(lngItem).Y
    47. If xyPoint(lngItem).Y >= tRectF.Bottom Then tRectF.Bottom = xyPoint(lngItem).Y
    48. Next
    49. GetBoundingBox = tRectF
    50. End Function
    51. Private Function Rotation(ByVal Degrees As Single, ByRef xyPoint() As PointF, ByRef xyCenter As PointF) As PointF()
    52. 'Operation eM11 eM12 eM21 eM22
    53. 'Rotation Cosine Sine Negative sine Cosine
    54. 'X = (x * Cos) + (y * Sin) + eDx,
    55. 'Y = (x * -Sin) + (y * Cos) + eDy,
    56. Dim Radians As Single
    57. Dim CosRadians As Single
    58. Dim SinRadians As Single
    59. Dim lngItem As Long
    60. Dim lngCount As Long
    61. Dim tPointF() As PointF
    62. lngCount = UBound(xyPoint)
    63. ReDim tPointF(lngCount)
    64. Radians = CSng(Degrees * (Atn(1) * 4) / 180)
    65. CosRadians = CSng(Cos(Radians))
    66. SinRadians = CSng(Sin(Radians))
    67. For lngItem = 0 To lngCount
    68. tPointF(lngItem).X = CSng((xyPoint(lngItem).X * CosRadians) + (xyPoint(lngItem).Y * SinRadians) + xyCenter.X)
    69. tPointF(lngItem).Y = CSng((xyPoint(lngItem).X * -SinRadians) + (xyPoint(lngItem).Y * CosRadians) + xyCenter.Y)
    70. Next
    71. Rotation = tPointF
    72. End Function
    73. Private Sub Timer1_Timer()
    74. Static grd As Single
    75. Me.Cls
    76. tPointOut = Rotation(grd, tPoint, tCenterPoint)
    77. Me.Line (tPointOut(0).X, tPointOut(0).Y)-(tPointOut(1).X, tPointOut(1).Y), vbBlack
    78. Me.Line (tPointOut(1).X, tPointOut(1).Y)-(tPointOut(2).X, tPointOut(2).Y), vbBlack
    79. Me.Line (tPointOut(2).X, tPointOut(2).Y)-(tPointOut(3).X, tPointOut(3).Y), vbBlack
    80. Me.Line (tPointOut(3).X, tPointOut(3).Y)-(tPointOut(0).X, tPointOut(0).Y), vbBlack
    81. tBoundingRect = GetBoundingBox(tPointOut)
    82. Me.Line (tBoundingRect.Left, tBoundingRect.Top)-(tBoundingRect.Right, tBoundingRect.Bottom), vbRed, B
    83. grd = grd + 1
    84. If grd >= 360 Then grd = 0
    85. End Sub


    Das schwarze Rechteck stellt sozusagen das Originalbild dar, das gedreht werden soll. Die Ausgangskoordinaten, also die 4 Eckpunkte, befinden sich im Array tPoint. Diese Punkte sollen um den tCenterPoint in einem Winkel (grd) gedreht werden. Das Ergebnis nach der Drehung der Eckpunkte steht in tPointOut. Aus diesen Punkten wird die BoundingBox berechnet (rotes Rechteck). Das rote Rechteck stellt sozusagen die neue Bitmap dar, von der ein Graphics erzeugt wird, Graphics entsprechend drehen, Originalbild reinzeichnen und die Graphics zurück drehen. Das Originalbild passt genau in die BoundingBox so das da nichts weggeschnitten wird oder nachträglich etwas weggeschnitten werden muss. Falls sich jemand wundert wo hier PI ist -> PI = Atn(1) * 4 <- hat man gern in VB6 so gemacht anstatt PI = 3,14..... irgendwo zu deklarieren.
    Bilder
    • RotateRect.png

      5,24 kB, 516×441, 8 mal angesehen
    Mfg -Franky-
    Ich habe jetzt folgenden Code:

    VB.NET-Quellcode

    1. Zielbild.SetPixel(0, 0, System.Drawing.Color.Red)
    2. Dim X As System.Drawing.Color = Zielbild.GetPixel(0, 0)

    Gibt es einen Weg, das ich in "X" so etwas wie "Red", "VbRed" oder "System.Drawing.Color.Red" herausbekomme? Wenn nicht, müßte ich dann jeweils die einzelnen Farbwerte prüfen.

    Oder ich arbeite nicht mit Farben, sondern mit dem Helligkeitswert, wie ich es schon an einer anderen Stelle des Programms mache:

    VB.NET-Quellcode

    1. X = CInt(PunktpositionX * Aufloesung)
    2. Y = CInt(PunktpositionY * Aufloesung)
    3. Helligkeit = 0
    4. Punktzahl = 0
    5. Do Until Y > (PunktpositionY * Aufloesung) + Aufloesung Or Y >= OriginalbildBitmap.Height
    6. Do Until X > (PunktpositionX * Aufloesung) + Aufloesung Or X >= OriginalbildBitmap.Width
    7. Punktzahl += 1
    8. 'Da die Rückgabe der Helligkeit zwischen 0 und 1 liegt,
    9. 'Wird das Ergebnis mit 100 multipliziert.
    10. Helligkeit += OriginalbildBitmap.GetPixel(X, Y).GetBrightness * 100
    11. X += Genauigkeit
    12. Loop
    13. X = CInt(PunktpositionX * Aufloesung)
    14. Y += Genauigkeit
    15. Loop


    Wundert euch nicht über die vielen Variablen. Bei dem Programm wird jeweils die Durchschnittshelligkeit einer Fläche geprüft, die so groß, wie ein Braillepunkt ist. Die Variable Genauigkeit hat nicht nur eine Auswirkung auf die tatsächliche Genauigkeit, sondern auch auf die Geschwindigkeit, die je nach Einstellung deutlich merkbar ist.
    Hi @-Franky-,
    merci sakrisch ^^ :thumbsup:
    Muss mir morgen den Code verinnerlichen.
    Heute bin ich zu groggy dafür.

    @tron25
    Meinst Du sowas:
    Spoiler anzeigen

    VB.NET-Quellcode

    1. Imports System.Drawing
    2. Imports System.Reflection
    3. Public Class Form1
    4. Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    5. ' Bild laden
    6. Dim bmp As New Bitmap("bild.bmp")
    7. ' Koordinaten des Pixels (z. B. 50,50)
    8. Dim x As Integer = 50
    9. Dim y As Integer = 50
    10. ' Farbe des Pixels auslesen
    11. Dim pixelColor As Color = bmp.GetPixel(x, y)
    12. ' Systemfarbe prüfen
    13. Dim systemColorName As String = GetSystemColorName(pixelColor)
    14. ' Ergebnis ausgeben
    15. If systemColorName <> "" Then
    16. MessageBox.Show("Der Pixel hat die Systemfarbe: " & systemColorName)
    17. Else
    18. MessageBox.Show("Die Pixel-Farbe entspricht keiner Systemfarbe.")
    19. End If
    20. End Sub
    21. ' Funktion zum Vergleichen der Farbe mit Systemfarben
    22. Private Function GetSystemColorName(pixelColor As Color) As String
    23. ' Durchläuft alle bekannten Systemfarben in SystemColors
    24. For Each prop As PropertyInfo In GetType(SystemColors).GetProperties()
    25. If prop.PropertyType = GetType(Color) Then
    26. Dim sysColor As Color = CType(prop.GetValue(Nothing), Color)
    27. If pixelColor.ToArgb() = sysColor.ToArgb() Then
    28. Return prop.Name ' Gibt den Namen der Systemfarbe zurück
    29. End If
    30. End If
    31. Next
    32. Return "" ' Keine Übereinstimmung gefunden
    33. End Function
    34. End Class


    P.S. ChatGPT ist mein Freund :D
    Für solche Code-Schnipsel (fast) das ideale Hilfsmittel.
    Windows-Anwendung

    Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „Dideldum“ ()

    @Dideldum Bedenke das die Formeln ein stark vereinfachtes Modell darstellen. Der CenterPoint kann irgendwo im Raum liegen. Die 4 Eckpunkte muss man berechnen. In meinem Bsp wäre das Originalbild 300x200 Pixel groß. Also Point(0) X=-300/2 und Y=-200/2. Die 4 Eckpunkte der berechneten BoundingBox können auch negative Werte enthalten. Ist aber nicht weiter tragisch. Bsp. für X(0) und X(1): -10 und 90 = 100 oder 36 und 136 = 100. Demnach wäre die Width für die neue Bitmap = 100.
    Mfg -Franky-

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

    @tron25 Kannst Du mal beschreiben, wofür Du diesen Aufwand treiben willst?
    Vielleicht findet sich noch eine ganz andere Lösung.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Hi @RodFromGermany,
    Wie ich @tron25 verstehe, will er Graphiken zu Ausgabe auf einem Blindendrucker bzw. einem Blinden-3D-Pin-Anzeige-Gerät umwandeln lassen.
    Aber warum die Bilder um freie Winkel gedreht können werden sollen, weiss ich auch nicht.
    @Dideldum So was hatte ich mir schon gedacht.
    @tron25 "Freie Winkel" auf einem Gerät mit relativ niedriger Pixelanzahl und nur 2 Farben (schwarz, weiß) bzw. { erhaben, nicht erhaben } sollten eigentlich Grund genug sein, zunächst die Aufgabenstellung von allen Seiten zu beleuchten, um dann die Programmierung zielgerichtet durchführen zu können.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    @-Franky-

    Habe eine superschnelle Lösung zur Bilddrehung und Rahmenbegrenzung gefunden (z.B. Bild 5000x6000 drehen in 4 Sec):

    Spoiler anzeigen

    VB.NET-Quellcode

    1. Imports System.Drawing
    2. Imports System.Drawing.Drawing2D
    3. Imports System.Drawing.Imaging
    4. Public Function RotateAndCropBitmap(ByVal bmp As Bitmap, ByVal angle As Single) As Bitmap
    5. Dim rect As RectangleF = GetRotatedBounds(bmp, angle)
    6. Dim newWidth As Integer = CInt(rect.Width)
    7. Dim newHeight As Integer = CInt(rect.Height)
    8. ' Neues größeres Bild erstellen
    9. Dim rotatedBmp As New Bitmap(newWidth, newHeight, PixelFormat.Format32bppArgb)
    10. rotatedBmp.SetResolution(bmp.HorizontalResolution, bmp.VerticalResolution)
    11. ' Grafikobjekt für die Transformation
    12. Using g As Graphics = Graphics.FromImage(rotatedBmp)
    13. g.InterpolationMode = InterpolationMode.HighQualityBicubic
    14. g.SmoothingMode = SmoothingMode.AntiAlias
    15. g.PixelOffsetMode = PixelOffsetMode.HighQuality
    16. ' Transformation für Drehung um Zentrum
    17. Dim matrix As New Matrix()
    18. matrix.RotateAt(angle, New PointF(CSng(bmp.Width / 2), CSng(bmp.Height / 2)))
    19. matrix.Translate((CSng(newWidth - bmp.Width) / 2), CSng((newHeight - bmp.Height) / 2), MatrixOrder.Append)
    20. g.Transform = matrix
    21. g.DrawImage(bmp, New Point(0, 0))
    22. End Using
    23. ' Bild auf sichtbaren Bereich beschneiden (schnell mit LockBits)
    24. Return CropToVisibleContent(rotatedBmp)
    25. End Function
    26. ' Berechnet die Begrenzung der rotierten Bitmap
    27. Private Function GetRotatedBounds(ByVal bmp As Bitmap, ByVal angle As Single) As RectangleF
    28. Dim sin As Double = Math.Abs(Math.Sin(angle * Math.PI / 180))
    29. Dim cos As Double = Math.Abs(Math.Cos(angle * Math.PI / 180))
    30. Dim newWidth As Double = bmp.Width * cos + bmp.Height * sin
    31. Dim newHeight As Double = bmp.Width * sin + bmp.Height * cos
    32. Return New RectangleF(0, 0, CSng(newWidth), CSng(newHeight))
    33. End Function
    34. ' Schneidet die Bitmap auf die sichtbare Fläche zu (schnell durch LockBits)
    35. Private Function CropToVisibleContent(ByVal bmp As Bitmap) As Bitmap
    36. Dim bounds As Rectangle = GetVisibleContentBounds(bmp)
    37. If bounds.Width <= 0 Or bounds.Height <= 0 Then Return bmp ' Falls nichts sichtbar ist
    38. Return bmp.Clone(bounds, bmp.PixelFormat)
    39. End Function
    40. ' **Optimierte Methode zur Begrenzungsberechnung (sehr schnell!)**
    41. Private Function GetVisibleContentBounds(ByVal bmp As Bitmap) As Rectangle
    42. Dim bmpData As BitmapData = bmp.LockBits(New Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb)
    43. Dim stride As Integer = bmpData.Stride
    44. Dim scan0 As IntPtr = bmpData.Scan0
    45. Dim pixels(bmp.Height * stride - 1) As Byte
    46. System.Runtime.InteropServices.Marshal.Copy(scan0, pixels, 0, pixels.Length)
    47. bmp.UnlockBits(bmpData)
    48. Dim minX As Integer = bmp.Width, minY As Integer = bmp.Height, maxX As Integer = 0, maxY As Integer = 0
    49. Dim found As Boolean = False
    50. ' **Schnelles Scannen der Pixel**
    51. For y As Integer = 0 To bmp.Height - 1
    52. Dim offset As Integer = y * stride
    53. For x As Integer = 0 To bmp.Width - 1
    54. Dim alpha As Byte = pixels(offset + x * 4 + 3) ' Alpha-Kanal auslesen
    55. If alpha > 0 Then
    56. minX = Math.Min(minX, x)
    57. minY = Math.Min(minY, y)
    58. maxX = Math.Max(maxX, x)
    59. maxY = Math.Max(maxY, y)
    60. found = True
    61. End If
    62. Next
    63. Next
    64. ' Falls kein nicht-transparenter Pixel gefunden wurde, wird das ganze Bild behalten
    65. If Not found Then Return New Rectangle(0, 0, bmp.Width, bmp.Height)
    66. Return New Rectangle(minX, minY, maxX - minX + 1, maxY - minY + 1)
    67. End Function
    68. Private Sub Bild_DrehenWinkel()
    69. If tsc_PicEditor_PictureBox.Image IsNot Nothing Then
    70. Me.Cursor = Cursors.WaitCursor
    71. tsc_PicEditor_PictureBox.Image = RotateAndCropBitmap(CType(tsc_PicEditor_PictureBox.Image.Clone(), Bitmap), CSng(sym_PicEditor_Bearbeiten_Textbox_Drehwinkel.Text))
    72. Resize_PicEditor()
    73. My.Settings.PicEditor_Drehwinkel = CSng(sym_PicEditor_Bearbeiten_Textbox_Drehwinkel.Text)
    74. Me.Cursor = Cursors.Default
    75. Editorgeändert = True
    76. End If
    77. End Sub
    Mit meiner Software sollen nicht nur Grafiken in Braille angezeigt werden können. Es soll auch möglich sein, eigene Grafiken zu erzeugen oder auch bestehende zu bearbeiten. Ich habe ein Brailledisplay mit 48x76 Punkten. Nun habe ich beispielsweise auf Wunsch eines Besuchers auf einer Messe für Hilfsmittel Animationen von einem 2Takt- 4Takt-Otto- und 4-Takt-Diesel-Motor erzeugt. Diese Animationen können automatisch ablaufen, sodas eine blinde Person mit beiden Händen die sich bewegende Grafik abtasten kann. Auf die gleiche Weise habe ich auch eine Animation eines galoppierenden Pferdes erstellt. Diese und auch viele andere Bewegungsabläufe sind für die meisten Blinden nicht oder sehr schwer ertastbar. Auf diese Weise können ebenfalls Animationen erstellt werden, die einer blinden Person das 3D-Sehen etwas verständlicher machen sollen. Wenn eine normal sehende Person beispielsweise einen Linienbus schräg von vorne ansieht, wundert er sich nicht, dass das Hinterrad kleiner erscheint, als das Vorderrad. Gibt manh einem Blinden ein Modell eines Busses in die Hand, sind für ihn in jeder Position alle Räder exakt rund und gleichgroß. Erzeug man beispielsweise eine Grafik von einem sich drehenden Buss, kann durch die Bewegung dieser vermeidliche Fehler nachvollzogen werden.

    Die Rotation wird dann wichtig, wenn man beispielsweise eine Stadtkarte erzeugt und die Straßen mit Namen beschriftet, die im gleichen Winkel ausgerichtet sind.

    Ein weiteres Beispiel sind die Motoren. Mir fehlt noch der Wankelmotor, der einen sich drehenden Kolben hat. Um die einzelnen Bilder der Animation zeichnen zu können, ist eine Rotationsfunktion sehr hilfreich. Die entsprechenden Funktionen habe ich zwar schon auf die alte Weise mit Tabellen realisiert, aber das Rotieren von Bitmaps und das darauffolgende Übertragen der Bits auf die Arbeitsfläche dürfte schneller ablaufen, als die einzelnen Koordinaten aus den Tabellen händisch neu zu berechnen.

    Ich hoffe, dass meine Ausführungen nicht zu lang und kompliziert geworden sind.

    Da hätte ich auch schon die nächste Frage:

    VB.NET-Quellcode

    1. PufferBitmap.SetPixel(X1, Y1, System.Drawing.Color.Gray)


    Gibt es die Möglichkeit "Grau" als Grundton beizubehalten, aber unterschiedliche Helligkeitswerte zu bestimmen?

    Beim Übersetzen einer optischen Grafik werden die Durchschnittshelligkeiten der einzelnen Braillepunkte ermittelt, sodass mit einem Helligkeitsschieber das Braillebild genauer bzw. Heller gemacht werden kann. Auf diese weise kann ein Bild, je nach gewünschtem Detail, voreingestellt, bevor es weiter bearbeitet werden kann.


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

    tron25 schrieb:

    Gibt es die Möglichkeit "Grau" als Grundton beizubehalten, aber unterschiedliche Helligkeitswerte zu bestimmen?

    VB.NET-Quellcode

    1. For i = 0 To 255
    2. Dim col = Color.FromArgb(i, i, i)
    3. ' mit col etwas tun
    4. Next
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!