Bild beschneiden, in der Größe verkleinern und aufhellen

  • VB.NET
  • .NET 4.5

Es gibt 10 Antworten in diesem Thema. Der letzte Beitrag () ist von EaranMaleasi.

    Bild beschneiden, in der Größe verkleinern und aufhellen

    Hallo Leute,

    ich stehe vor der Herausforderung einer Mini-Produktbildbearbeitung und würde mich da über Ideen und Anregungen für die Umsetzung sehr freuen.

    Was ich vor habe:
    * Kunden sollen Produktbilder in einem Lichtzelt aufnehmen (Produkt auf weißem Hingergrund) (FUNKTIONIERT BEREITS)
    * Rohbilder (JPG) per Drag and Drop in meine Anwendung ziehen (FUNKTIONIERT BEREITS)
    * Bilder sollen in eine Datei gespeichert werden (FUNKTIONIERT BEREITS)
    * Bilder sollen nun folgendermaßen bearbeitet werden:
    - Größe der Bilder (welche meist 3000 Pixel breit sind) auf 800 Pixel Breite bringen
    - weiße Flächen rund um die Objekte automatisch entfernen (siehe Bild anbei)
    - das Bild insgesamt aufhellen (mehr Belichtung)

    * Bilder sollen in den Webshop hochgeladen werden (FUNKTIONIERT BEREITS)

    Legende:
    Schwarz = hab ich schon lauffähig und funktioniert bereits
    Blau = wäre ein Zusatzgoodie, welches ich gerne umsetzen würde.

    Nun stellt sich die Frage wie ich an diese Aufgabenstellung (blau) am Besten herangehe.
    Bilder verkleinern hatte ich schon mal - das wäre wohl noch die einfachere Aufgabe.
    Beim Zuschneiden des weißen Bereiches hätte ich mir vorgestellt von außen nach innen Reihen und Spalten von Pixeln auszulesen und sobald man auf einen dunklen Bereich kommt dort dann aufhört und zuschneidet. Aber wie ich das mache, habe ich keine Ahnung. Das Bild insgesamt aufhellen - keine Ahnung wie.

    Habt Ihr dafür Lösungen oder Ideen?

    LG Roland

    *Topic verschoben*
    Bilder
    • produktfoto_image.jpg

      62,63 kB, 1.000×750, 30 mal angesehen
    Liebe Grüße
    Roland Berghöfer

    Bei der Entwicklung meiner Anwendung(en) steht nicht "Code nach .NET Lehrbuch" im Vordergrund, sondern eine stabile und brauchbare Anwendung die der Anwender ordentlich verwenden kann. Usability für den Kunden und Supportbarkeit beim Kunden stehen an oberster Stelle. Das spiegelt sich auch in meinen Fragen und Antworten wider. Bitte verzeiht, dass meine VB.NET Quellcodes etwas VB6-lastig sind aber das ist für das funktionierende Endergebnis nicht wirklich relevant.

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „Marcus Gräfe“ ()

    Hallo Leute,

    da heute wohl alle Fasching feiern und ich scheinbar als einziger am Schreibtisch sitze, habe ich meine grauen Zellen selbst mal angestrengt und zwei von drei Punkten erledigt.

    Wie Ihr am Screenshot anbei erkennen könnt, kann ich die Helligkeit schon einmal stufenlos ändern und auch die Bildgröße auf ein gewünschtes Maximalmaß bringen.
    Hier der Hauptkern der Funktion:

    VB.NET-Quellcode

    1. ''' <summary>
    2. ''' Kopiert das Bild einer Picturebox in eine andere und passt die Helligkeit (Werte -1 bis +1) an und bringt die Bildgröße auf eine Maximalgröße (MaxBreite)
    3. ''' Das Bildverhältnis wird dabei beibehalten.
    4. ''' </summary>
    5. ''' <param name="Brightness"></param>
    6. ''' <param name="OriginalBildBox"></param>
    7. ''' <param name="ZielBildBox"></param>
    8. ''' <param name="MaxBreite"></param>
    9. ''' <remarks></remarks>
    10. Sub ModifyPicture(ByVal Brightness As Single, OriginalBildBox As PictureBox, ZielBildBox As PictureBox, MaxBreite As Integer)
    11. Dim g As Graphics
    12. Dim img As Image
    13. Dim r As Rectangle
    14. Dim Ratio As Double = 1
    15. Dim Breite As Integer = OriginalBildBox.Image.Width
    16. Dim Hoehe As Integer = OriginalBildBox.Image.Height
    17. 'Bildverhältnis beibehalten und Bild verkleinern, falls größer als Maximalbreite
    18. If breite > MaxBreite Then
    19. Ratio = 1 / breite * MaxBreite 'ergibt eine 0,irgendwas Zahl
    20. breite = MaxBreite
    21. hoehe = CInt(hoehe * Ratio)
    22. End If
    23. img = OriginalBildBox.Image
    24. ZielBildBox.Image = New Bitmap(breite, hoehe, PixelFormat.Format32bppArgb)
    25. g = Graphics.FromImage(ZielBildBox.Image)
    26. r = New Rectangle(0, 0, breite, hoehe)
    27. g.DrawImage(img, r)
    28. Dim colorMatrixVal As Single()() = { _
    29. New Single() {1, 0, 0, 0, 0}, _
    30. New Single() {0, 1, 0, 0, 0}, _
    31. New Single() {0, 0, 1, 0, 0}, _
    32. New Single() {0, 0, 0, 1, 0}, _
    33. New Single() {Brightness, Brightness, Brightness, 0, 1}}
    34. Dim colorMatrix As New ColorMatrix(colorMatrixVal)
    35. Dim ia As New ImageAttributes
    36. ia.SetColorMatrix(colorMatrix, ColorMatrixFlag.Default, ColorAdjustType.Bitmap)
    37. g.DrawImage(img, r, 0, 0, img.Width, img.Height, GraphicsUnit.Pixel, ia)
    38. ZielBildBox.Refresh()
    39. End Sub


    Nun werde ich mich daran machen, das Problem mit dem automatischen Beschneiden zu lösen ;)

    LG Roland
    Bilder
    • 05032019142020.jpg

      181,62 kB, 813×569, 31 mal angesehen
    Liebe Grüße
    Roland Berghöfer

    Bei der Entwicklung meiner Anwendung(en) steht nicht "Code nach .NET Lehrbuch" im Vordergrund, sondern eine stabile und brauchbare Anwendung die der Anwender ordentlich verwenden kann. Usability für den Kunden und Supportbarkeit beim Kunden stehen an oberster Stelle. Das spiegelt sich auch in meinen Fragen und Antworten wider. Bitte verzeiht, dass meine VB.NET Quellcodes etwas VB6-lastig sind aber das ist für das funktionierende Endergebnis nicht wirklich relevant.
    @dive26 Definiere Beschneiden.
    Das Bild insgasamt verkleinern / vergrößern geht so:

    VB.NET-Quellcode

    1. ' neue Bröße
    2. Dim imgWidth = 1278
    3. Dim imgHeight = 958
    4. ' Bitmap-Objekt in der neuen Größe erstellen
    5. Dim imgDest As New Bitmap(imgWidth, imgHeight)
    6. ' Bild interpolieren, damit die Qualität erhalten bleibt
    7. Using g As Graphics = Graphics.FromImage(imgDest)
    8. g.InterpolationMode = Drawing2D.InterpolationMode.HighQualityBicubic
    9. g.DrawImage(MyImgSource, New Rectangle(0, 0, imgWidth, imgHeight))
    10. End Using
    11. ' imgDest verwenden
    Wenn Du vom Bild einen (halbwegs) einfarbigen Rand abschneiden willst, musst Du alle paar Zeilen / Spalten von außen nach innen die erste relevante Farbveränderung suchen.
    Aus diese Koordinaten für jede Seite generierst Du Dir den Schneiderand und los.
    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).
    VB-Fragen über PN / Konversation werden ignoriert!
    @RodFromGermany
    Bild "kleiner" machen habe ich ja oben im Code schon. Was ich jetzt (schon gemacht habe), ist, automatisch alle weißen Flächen rund um das Bild entfernen - also das Produkt automatisch "freistellen". Das hab ich nun auch fertig. Das Ergebnis kannst im Screenshot unten sehen. Aus einem sehr dunklen Produktfoto wurde ein helles freigestelltes Produktfoto.

    Beim Freistellen bin ich den Weg gegangen alle Pixel von oben nach unten durchzulaufen und deren Helligkeitswert zu prüfen. Kommt ein dunklerer Pixel, dann wird zur Sicherheit noch der nächste Pixel abgefragt. Ist dieser auch dunkler, dann beginnt da wohl das Objekt und ich schneide ein paar Pixel vorher ab.
    Dies mache ich von allen vier Himmelsrichtungen und habe somit ein freigestelltes Objekt.
    Natürlich geht das Freistellen nur gut, wenn der Helligkeitswert des Hintergrundes stimmt - also annähernd weiß ist (90% Helligkeit).

    Hier der Code, der sicher noch optimiert werden könnte, aber vorerst einmal so funktioniert:
    (Der Parameter Schwellwert wurde noch nicht implementiert - vielleicht programmiere ich da noch einen Schwellwertregler der die 90% Helligkeit variabel regeln lässt.)

    VB.NET-Quellcode

    1. ''' <summary>
    2. ''' Schneidet helle Bereiche um das Bild automatisch ab
    3. ''' </summary>
    4. ''' <param name="BildBox"></param>
    5. ''' <param name="Schwellwert"></param>
    6. ''' <remarks></remarks>
    7. Public Sub Beschneidebild(BildBox As PictureBox, Schwellwert As Integer)
    8. Dim BildZeilen As Integer = BildBox.Image.Height
    9. Dim BildSpalten As Integer = BildBox.Image.Width
    10. Dim BeschneideOben As Integer = 0
    11. Dim BeschneideUnten As Integer = BildZeilen
    12. Dim BeschneideLinks As Integer = 0
    13. Dim BeschneideRechts As Integer = BildSpalten
    14. 'von oben nach unten alle Pixel kontrollieren
    15. Dim MyBitmap As New Bitmap(BildBox.Image)
    16. Dim curPixColor As Color
    17. Dim PixelHelligkeit As Single = 1
    18. Dim ObjektGefunden As Boolean = False
    19. 'Von oben nach unten "scannen"
    20. ObjektGefunden = False
    21. For zeile As Integer = 1 To BildZeilen - 1
    22. If ObjektGefunden = True Then Exit For
    23. For spalte As Integer = 1 To BildSpalten - 1
    24. curPixColor = MyBitmap.GetPixel(spalte, zeile)
    25. PixelHelligkeit = curPixColor.GetBrightness()
    26. If PixelHelligkeit < 0.9 Then
    27. 'Prüfen ob das nächste Pixel auch dunkel ist, wenn ja, dann erst als Objekt identifizieren
    28. curPixColor = MyBitmap.GetPixel(spalte + 1, zeile)
    29. PixelHelligkeit = curPixColor.GetBrightness()
    30. If PixelHelligkeit < 0.9 Then
    31. ObjektGefunden = True
    32. BeschneideOben = zeile - 3
    33. If BeschneideOben < 0 Then BeschneideOben = 0
    34. Exit For
    35. End If
    36. End If
    37. Next spalte
    38. Next zeile
    39. 'Von links nach rechts "scannen"
    40. ObjektGefunden = False
    41. For spalte As Integer = 1 To BildSpalten - 1
    42. If ObjektGefunden = True Then Exit For
    43. For zeile As Integer = 1 To BildZeilen - 1
    44. curPixColor = MyBitmap.GetPixel(spalte, zeile)
    45. PixelHelligkeit = curPixColor.GetBrightness()
    46. If PixelHelligkeit < 0.9 Then
    47. 'Prüfen ob das nächste Pixel auch dunkel ist, wenn ja, dann erst als Objekt identifizieren
    48. curPixColor = MyBitmap.GetPixel(spalte, zeile + 1)
    49. PixelHelligkeit = curPixColor.GetBrightness()
    50. If PixelHelligkeit < 0.9 Then
    51. ObjektGefunden = True
    52. BeschneideLinks = spalte - 3
    53. If BeschneideLinks < 0 Then BeschneideLinks = 0
    54. Exit For
    55. End If
    56. End If
    57. Next zeile
    58. Next spalte
    59. 'Von unten nach oben "scannen"
    60. ObjektGefunden = False
    61. For zeile As Integer = BildZeilen - 1 To 1 Step -1
    62. If ObjektGefunden = True Then Exit For
    63. For spalte As Integer = 1 To BildSpalten - 1
    64. curPixColor = MyBitmap.GetPixel(spalte, zeile)
    65. PixelHelligkeit = curPixColor.GetBrightness()
    66. If PixelHelligkeit < 0.9 Then
    67. 'Prüfen ob das nächste Pixel auch dunkel ist, wenn ja, dann erst als Objekt identifizieren
    68. curPixColor = MyBitmap.GetPixel(spalte, zeile - 1)
    69. PixelHelligkeit = curPixColor.GetBrightness()
    70. If PixelHelligkeit < 0.9 Then
    71. ObjektGefunden = True
    72. BeschneideUnten = zeile + 3
    73. If BeschneideUnten > BildZeilen Then BeschneideUnten = BildZeilen - 1
    74. Exit For
    75. End If
    76. End If
    77. Next spalte
    78. Next zeile
    79. 'Von rechts nach links "scannen"
    80. ObjektGefunden = False
    81. For spalte As Integer = BildSpalten - 1 To BeschneideLinks + 1 Step -1
    82. If ObjektGefunden = True Then Exit For
    83. For zeile As Integer = 1 To BildZeilen - 1
    84. curPixColor = MyBitmap.GetPixel(spalte, zeile)
    85. PixelHelligkeit = curPixColor.GetBrightness()
    86. If PixelHelligkeit < 0.9 Then
    87. 'Prüfen ob das nächste Pixel auch dunkel ist, wenn ja, dann erst als Objekt identifizieren
    88. curPixColor = MyBitmap.GetPixel(spalte, zeile + 1)
    89. PixelHelligkeit = curPixColor.GetBrightness()
    90. If PixelHelligkeit < 0.9 Then
    91. ObjektGefunden = True
    92. BeschneideRechts = spalte + 3
    93. If BeschneideRechts > BildSpalten - 1 Then BeschneideRechts = BildSpalten - 1
    94. Exit For
    95. End If
    96. End If
    97. Next zeile
    98. Next spalte
    99. 'Bildmaße entsprechend anpassen
    100. BeschneideUnten -= BeschneideOben
    101. BeschneideRechts -= BeschneideLinks
    102. 'Hier Beschneiden
    103. Dim CropRect As New Rectangle(BeschneideLinks, BeschneideOben, BeschneideRechts, BeschneideUnten)
    104. Dim OriginalImage = BildBox.Image
    105. Dim CropImage = New Bitmap(CropRect.Width, CropRect.Height)
    106. Using grp = Graphics.FromImage(CropImage)
    107. grp.DrawImage(OriginalImage, New Rectangle(0, 0, CropRect.Width, CropRect.Height), CropRect, GraphicsUnit.Pixel)
    108. OriginalImage.Dispose()
    109. BildBox.Image = New Bitmap(CropRect.Width, CropRect.Height, PixelFormat.Format32bppArgb)
    110. BildBox.Image = CropImage
    111. BildBox.Refresh()
    112. End Using
    113. End Sub

    Bilder
    • 05032019171153.jpg

      186,64 kB, 691×571, 25 mal angesehen
    • 05032019171313.jpg

      168,18 kB, 689×573, 23 mal angesehen
    • 05032019171410.jpg

      192,78 kB, 687×570, 21 mal angesehen
    Liebe Grüße
    Roland Berghöfer

    Bei der Entwicklung meiner Anwendung(en) steht nicht "Code nach .NET Lehrbuch" im Vordergrund, sondern eine stabile und brauchbare Anwendung die der Anwender ordentlich verwenden kann. Usability für den Kunden und Supportbarkeit beim Kunden stehen an oberster Stelle. Das spiegelt sich auch in meinen Fragen und Antworten wider. Bitte verzeiht, dass meine VB.NET Quellcodes etwas VB6-lastig sind aber das ist für das funktionierende Endergebnis nicht wirklich relevant.
    @RodFromGermany
    Danke, alles erledigt. Manchmal frage ich einfach zu früh und kanns dann doch irgendwie selbst.
    Möglicherweise möchte ein Admin dies in Tips & Tricks kopieren. Vielleicht sucht mal jemand sowas.
    Liebe Grüße
    Roland Berghöfer

    Bei der Entwicklung meiner Anwendung(en) steht nicht "Code nach .NET Lehrbuch" im Vordergrund, sondern eine stabile und brauchbare Anwendung die der Anwender ordentlich verwenden kann. Usability für den Kunden und Supportbarkeit beim Kunden stehen an oberster Stelle. Das spiegelt sich auch in meinen Fragen und Antworten wider. Bitte verzeiht, dass meine VB.NET Quellcodes etwas VB6-lastig sind aber das ist für das funktionierende Endergebnis nicht wirklich relevant.

    dive26 schrieb:

    Möglicherweise möchte ein Admin dies in Tips & Tricks kopieren.
    Du kannst dort selbst einen Thread erstellen, der muss dann allerdings von einem Moderator freigeschaltet werden..
    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).
    VB-Fragen über PN / Konversation werden ignoriert!
    Ich möchte mich nochmals für die vielen Hilfestellungen hier im Forum bedanken. Die Software ist so gut wie fertig und aktuell schon in der öffentlichen BETA. Wer möchte kanns auch mal ansehen, ausprobieren und sogar bis 30. Mai 2019 kostenfrei verwenden:

    Download: bonit.at/download/BONit ERP2 Business-Software Setup.exe
    Handbuch: bonit.at/handbuch.asp?handbuch=erp2

    Für Kritik und Anregungen bin ich immer offen.
    PS: Die Funktion aus diesem Beitrag wurde hier eingebaut: bonit.at/handbuch.asp?Handbuch=erp2&id=208

    Liebe Grüße
    Roland Berghöfer

    Bei der Entwicklung meiner Anwendung(en) steht nicht "Code nach .NET Lehrbuch" im Vordergrund, sondern eine stabile und brauchbare Anwendung die der Anwender ordentlich verwenden kann. Usability für den Kunden und Supportbarkeit beim Kunden stehen an oberster Stelle. Das spiegelt sich auch in meinen Fragen und Antworten wider. Bitte verzeiht, dass meine VB.NET Quellcodes etwas VB6-lastig sind aber das ist für das funktionierende Endergebnis nicht wirklich relevant.
    Servus, habs mir mal runtergeladen und ganz kurz angesehen.

    Windows 10 meinte natürlich sofort, dass es sich um ein schädliches Programm handelt, daher wäre es schön, gerade da es sich um eine ERP Software handelt, wenn das Setup und Anwendung (sowie die verwendeten DLLs) signiert wären. Also mit nem richtigen Codesigning Zertifikat via signtool.exe. Könnte aber natürlich auch einfach daran liegen, dass es eine Beta ist und du/ihr das Programm deswegen noch nicht signiert.

    Ein Setup zu haben ist schon gut, eine Deinstallationsroutine die nicht nur den Eintrag unter "Programme und Features" löscht, sondern auch den Ordner, wäre noch besser.

    Ansonsten ist das ein echt schickes Programm, wo ich jedoch direkt nen Feature Request hätte, Dark Mode! :D
    Mit so viel Weiß kann ich echt nicht umgehen, ist jedoch ne rein subjektive Sache von mir.

    So rein aus interesse, hast du das nun in Eigenregie entwickelt? Oder waren da noch mehr Leute am Werk?
    Post-AGB:
    §1 Mit dem Lesen dieses Posts stimmst du den AGB unverzüglich zu
    §2 Ein Widerruf muss innerhalb von 3 Sekunden nach Lesen des Hauptbestandteil des ersten jemals gelesenen Posts erfolgen
    Abs.1 Die Signatur zählt nicht zum Hauptbestandteil des Posts
    §3 Ein erfolgreicher Widerruf zwingt zu einem Besuch bei einem Hypnotiseur oder Neurochirurg, sodass der gelesene Text aus den Erinnerungen entfernt werden kann
    Abs.1 Die Kosten und Risiken sind jeweils selbst zu tragen
    @EaranMaleasi
    Vielen Dank für Dein Lob. Freut mich natürlich, wenn man von "Kollegen" gelobt wird.

    Thema Installationsordner löschen:
    Da es sich um eine fiskaltechnische Software und daher eine heikle Angelegenheit handelt, darf bei der Deinstallation der Ordner mit den Kundendaten NICHT angetastet werden. Daher löscht die Deinstallation keine Daten im Installationsordner sondern nur die Daten die die Installationsroutine selbst reinkopiert hat. Update geschieht auch mit der Installationsdatei (welche vorher automatisch eine Deinstallation durchführt). Somit löscht sich ein Kunde keine Daten, wenn er die Installationsroutine mehrmals ausführt (warum auch immer).

    Dark Mode (ich hoffe Du meinst die Oberflächenfarbe und nicht die Finanz ;) ) ist nicht geplant, da dies ehrlich gesagt zu viel Aufwand ist. Die Anwendung ist in Winforms programmiert und nicht in WPF, daher sind die Grafikelemente alle "von Hand" gemacht.

    Signatur
    Habe ich ehrlich gesagt bis jetzt noch bei keiner Software integriert. Von meinen anderen beiden Produkten, wo ich jeweils schon ca. 4000 Lizenzen draußen habe, hat das bisher noch keinen Endkunden gestört. Zugegeben wäre es "schöner" wenn da eine Signatur drin ist. Aber ich denke es ist sehr viel Aufwand diese Signatur für jede Release neu zu machen. Updates gibt es fast jede Woche. Habe mich ehrlich gesagt damit noch nicht wirklich beschäftigt. Danke aber für den Hinweis, werde mir das mal ansehen wie das so abläuft.

    100% Eigenregie
    Ja, ich habe alles 100% selbst programmiert. Wir sind zwar in der Firma zu dritt. Ein Techniker der auch Kundensupport macht, mein Geschäftspartner für Buchhaltung und Kundensupport und ich als Eigentümer mache Werbung, Programmierung, Kundensupport und alles was sonst noch so anfällt. Ich habe im September 2018 mit der Programmierung begonnen und jetzt im März wurden alle Arbeiten inkl. Handbuch, Shopsystem (bonit.at/shop.asp), Werbung und Kopierschutz abgeschlossen. Wenn man nichts anderes machen würde als Programmieren, dann wäre das wohl in der Hälfte der Zeit gegangen. Aber da das Tagesgeschäft weiterläuft, konnte ich nur zwischendurch oder Abends und am Wochenende so richtig in Ruhe programmieren.
    Liebe Grüße
    Roland Berghöfer

    Bei der Entwicklung meiner Anwendung(en) steht nicht "Code nach .NET Lehrbuch" im Vordergrund, sondern eine stabile und brauchbare Anwendung die der Anwender ordentlich verwenden kann. Usability für den Kunden und Supportbarkeit beim Kunden stehen an oberster Stelle. Das spiegelt sich auch in meinen Fragen und Antworten wider. Bitte verzeiht, dass meine VB.NET Quellcodes etwas VB6-lastig sind aber das ist für das funktionierende Endergebnis nicht wirklich relevant.

    dive26 schrieb:

    Aber ich denke es ist sehr viel Aufwand diese Signatur für jede Release neu zu machen.
    Ganz und garnicht, das lässt sich per PostBuild in VisualStudio erledigen, oder beim erstellen des Setups (sofern das keine Eigenentwicklung ist). Die einzige Entscheidung die du dabei treffen musst it, ob es dir das Geld wert ist, denn die "Code Signing Zertifikate" sind nicht gerade günstig.

    Vorteil ist, dass zum einen SmartScreen und UAC freundlicher sind, dass unter Eigenschaften der Punkt "Digitale Signaturen" mitsamt der Signatur auftaucht, und dass niemand ohne weiteres per Hex-Editor an deinen Binaries rumfuchteln kann, da Windows afaik sonst die Ausführung verweigert.
    Nachteil ist, du lässt für etwas Geld liegen, das vermutlich noch weniger Leuten auffallen wird, als jetzt die fehlende Signatur.

    dive26 schrieb:

    ich hoffe Du meinst die Oberflächenfarbe und nicht die Finanz
    Ich bin für jeden DarkMode zu haben :P

    dive26 schrieb:

    Somit löscht sich ein Kunde keine Daten, wenn er die Installationsroutine mehrmals ausführt (warum auch immer)
    Davon kann ich leider ein Lied singen. Oder zwei.
    Post-AGB:
    §1 Mit dem Lesen dieses Posts stimmst du den AGB unverzüglich zu
    §2 Ein Widerruf muss innerhalb von 3 Sekunden nach Lesen des Hauptbestandteil des ersten jemals gelesenen Posts erfolgen
    Abs.1 Die Signatur zählt nicht zum Hauptbestandteil des Posts
    §3 Ein erfolgreicher Widerruf zwingt zu einem Besuch bei einem Hypnotiseur oder Neurochirurg, sodass der gelesene Text aus den Erinnerungen entfernt werden kann
    Abs.1 Die Kosten und Risiken sind jeweils selbst zu tragen