Image to Binary Image

  • C#
  • .NET (FX) 4.5–4.8

Es gibt 7 Antworten in diesem Thema. Der letzte Beitrag () ist von Petersilie.

    Image to Binary Image

    Im Spoiler die original Frage.
    Spoiler anzeigen

    Hallo Leute,

    ich versuche momentan Bilder in "binär" umzuwandeln.
    Versteht mich hier bitte nicht falsch, aus einem jpeg o. png eine Textdatei zu erstellen die aus 1 und 0 besteht ist kein Problem.

    1 und 0 sind in meinem Fall Bitmaps (8x8 Pixel).
    Bilder im Anhang als bin0.png und bin1.png.
    Ein einzelner Pixel wird also in eine 8x8 große Bitmap übersetzt.

    An und für sich funktioniert das auch... nur ist das Resultat ist schräg und versetzt (siehe Anhänge).
    Ich komme hier momentan nicht weiter. Ich sehe den Wald vor lauter Bäumen nicht...

    Code für das konvertieren von Pixel in 1 und 0 Bitmap
    Spoiler anzeigen

    C#-Quellcode

    1. private static Bitmap CreateBinaryImage(Bitmap zero, Bitmap one, Bitmap source)
    2. {
    3. // store all results and their location here
    4. var imageList = new List<KeyValuePair<Point, Bitmap>>();
    5. // new width and height
    6. int w = source.Width * zero.Width;
    7. int h = source.Height * zero.Height;
    8. // width and height of binary image
    9. int hbin = zero.Height;
    10. int wbin = zero.Width;
    11. // image position indexer
    12. int posX = 0;
    13. int posY = 0;
    14. // bitmap to draw on
    15. var canvas = new Bitmap(w, h, source.PixelFormat);
    16. using (var g = Graphics.FromImage(canvas))
    17. {
    18. // set background
    19. g.FillRectangle(Brushes.Black, 0, 0, canvas.Width, canvas.Height);
    20. unsafe
    21. {
    22. BitmapData lockBits = source.LockBits(
    23. new Rectangle(0, 0, source.Width, source.Height),
    24. ImageLockMode.ReadWrite,
    25. source.PixelFormat);
    26. try
    27. {
    28. // bits per pixel
    29. int bpp = Bitmap.GetPixelFormatSize(source.PixelFormat) / 8;
    30. int hip = lockBits.Height;
    31. int wib = lockBits.Width;
    32. // pointer to first pixel
    33. byte* ptrPx = (byte*)lockBits.Scan0;
    34. // length of stride
    35. int bytes = Math.Abs(lockBits.Stride);
    36. for (int y = 0; y < hip; y++)
    37. {
    38. // get pointer to current scanline
    39. byte* scanLine = ptrPx + (y * lockBits.Stride);
    40. // iterate through pixels
    41. for (int x = 0; x < bytes; x+=bpp)
    42. {
    43. // calculate average to determine wether pixel will be 1 or 0
    44. float avg = ((scanLine[x + 2] & 0xff) / 255f +
    45. (scanLine[x + 1] & 0xff) / 255f +
    46. (scanLine[x] & 0xff) / 255f) / 3;
    47. avg = Math.Min(1f, .35f + .65f * avg);
    48. // set location and bitmap which will later be drawn
    49. imageList.Add(new KeyValuePair<Point, Bitmap>(
    50. new Point(posX, posY),
    51. avg < 0.5f ? one : zero));
    52. // increment or decrement the binary image location variables
    53. posX = posX >= w ? 0 : posX + wbin;
    54. posY = posX >= w ? posY + hbin : posY;
    55. }
    56. }
    57. // draw images on canvas bitmap
    58. for (int i=0; i<imageList.Count; i++) {
    59. g.DrawImage(imageList[i].Value, imageList[i].Key);
    60. }
    61. }
    62. finally
    63. {
    64. // release locked bitmap bits
    65. if (source != null) {
    66. source.UnlockBits(lockBits);
    67. }
    68. }
    69. return canvas;
    70. }
    71. }
    72. }



    Aufruf von CreateBinaryImage:
    Spoiler anzeigen

    C#-Quellcode

    1. static void Main(string[] args)
    2. {
    3. var source = (Bitmap)Load.FromFile(@"C:\Users\...\Pictures\240px-C_Sharp_wordmark.svg.png");
    4. var zero = (Bitmap)Load.FromFile("c:\\users\\...\\desktop\\bin0.jpeg");
    5. var one = (Bitmap)Load.FromFile("c:\\users\\...\\desktop\\bin1.jpeg");
    6. var binaryImage = CreateBinaryImage(zero, one, source);
    7. binaryImage.Save("c:\\users\\...\\desktop\\binaryImage_test.jpeg");
    8. Console.Write("Done... Press any key to exit");
    9. Console.ReadKey();
    10. }


    @RodFromGermany
    @Morrison
    @exc-jdbi
    @Mircosofter2206

    Problem war lediglich die Indizierung der zu setzenden Bilder.

    Weiter unten der neue Code der Funktioniert.
    An alle die vielleicht sagen "Warum nicht einfach GetPixel() und SetPixel() verwenden?": Weil es unfassbar langsam ist!

    ImageData Class:
    Spoiler anzeigen

    C#-Quellcode

    1. using System;
    2. using System.Drawing;
    3. using System.Drawing.Imaging;
    4. namespace ImageConverter
    5. {
    6. public unsafe class ImageData : IDisposable
    7. {
    8. public Bitmap Bitmap { get; }
    9. public BitmapData Bits { get; }
    10. /// <summary>
    11. /// Width in Pixel
    12. /// </summary>
    13. public int Wip { get; }
    14. /// <summary>
    15. /// Height in Pixel
    16. /// </summary>
    17. public int Hip { get; }
    18. /// <summary>
    19. /// Byte per Pixel
    20. /// </summary>
    21. public int Bpp { get; }
    22. /// <summary>
    23. /// Pointer to first pixel of Image
    24. /// </summary>
    25. public unsafe byte* FirstPixel { get; }
    26. /// <summary>
    27. /// Stride of Image
    28. /// </summary>
    29. public int Stride { get; }
    30. /// <summary>
    31. /// Amount of Bytes per Pixel row (Math.Abs(Stride))
    32. /// </summary>
    33. public int Bytes { get; }
    34. public ImageData(Bitmap bitmap, ImageLockMode lockMode)
    35. {
    36. Bitmap = bitmap;
    37. Bits = bitmap.LockBits(
    38. new Rectangle(0, 0, bitmap.Width, bitmap.Height),
    39. lockMode,
    40. bitmap.PixelFormat);
    41. Bpp = Bitmap.GetPixelFormatSize(bitmap.PixelFormat) / 8;
    42. Hip = Bits.Height;
    43. Wip = Bits.Width;
    44. FirstPixel = (byte*)Bits.Scan0;
    45. Stride = Bits.Stride;
    46. Bytes = Math.Abs(Stride);
    47. }
    48. public Color GetPixel(int x, int y)
    49. {
    50. int px = Bpp * x + Stride * y;
    51. if (Bpp % 3 == 0)
    52. {
    53. return Color.FromArgb(
    54. FirstPixel[px + 2],
    55. FirstPixel[px + 1],
    56. FirstPixel[px + 0]);
    57. }
    58. else if (Bpp % 4 == 0)
    59. {
    60. return Color.FromArgb(
    61. FirstPixel[px + 3],
    62. FirstPixel[px + 2],
    63. FirstPixel[px + 1],
    64. FirstPixel[px + 0]);
    65. }
    66. return Color.Empty;
    67. }
    68. public void SetPixel(int x, int y, Color c)
    69. {
    70. int px = Bpp * x + Stride * y;
    71. if (Bpp % 3 == 0)
    72. {
    73. FirstPixel[px + 0] = c.B;
    74. FirstPixel[px + 1] = c.G;
    75. FirstPixel[px + 2] = c.R;
    76. }
    77. else if (Bpp % 4 == 0)
    78. {
    79. FirstPixel[px + 0] = c.B;
    80. FirstPixel[px + 1] = c.G;
    81. FirstPixel[px + 2] = c.R;
    82. FirstPixel[px + 3] = c.A;
    83. }
    84. }
    85. ~ImageData() { Dispose(false); }
    86. public void Dispose() { Dispose(true); }
    87. private void Dispose(bool disposing)
    88. {
    89. if (disposing)
    90. {
    91. GC.SuppressFinalize(this);
    92. }
    93. try { Bitmap.UnlockBits(Bits); }
    94. catch { }
    95. }
    96. }
    97. }




    Test Class mit CreateBinaryImage() Implementation (Methode zum erstellen eines "Binär" Bildes)
    Spoiler anzeigen

    C#-Quellcode

    1. using System;
    2. using System.Collections.Generic;
    3. using System.Drawing;
    4. using System.Drawing.Imaging;
    5. namespace ImageConverter
    6. {
    7. class Program
    8. {
    9. static void Main(string[] args)
    10. {
    11. Bitmap source = (Bitmap)Image.FromFile(@"C:\data\resources\images\image.jpg");
    12. Bitmap zero = (Bitmap)Image.FromFile(@"c:\data\resources\images\bin_0_1024px_w.png");
    13. Bitmap one = (Bitmap)Image.FromFile(@"c:\data\resources\images\bin_1_1024px_w.png");
    14. zero = Resize(zero, 16, 16);
    15. one = Resize(one, 16, 16);
    16. var img = CreateBinaryImage(source, zero, one);
    17. img.Save("C:\\users\\[user]\\desktop\\test.png");
    18. Console.Write("Done... Press any key to exit");
    19. Console.ReadKey();
    20. }
    21. private static Bitmap Resize(Image source, int width, int height)
    22. {
    23. var destRect = new Rectangle(0, 0, width, height);
    24. var destImage = new Bitmap(width, height, source.PixelFormat);
    25. destImage.SetResolution(source.HorizontalResolution, source.VerticalResolution);
    26. using (var g = Graphics.FromImage(destImage))
    27. {
    28. g.CompositingMode = System.Drawing.Drawing2D.CompositingMode.SourceCopy;
    29. g.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality;
    30. g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
    31. g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
    32. g.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.HighQuality;
    33. using (var wrap = new ImageAttributes()) {
    34. wrap.SetWrapMode(System.Drawing.Drawing2D.WrapMode.TileFlipXY);
    35. g.DrawImage(source, destRect, 0, 0, source.Width, source.Height, GraphicsUnit.Pixel, wrap);
    36. }
    37. }
    38. return destImage;
    39. }
    40. private static Bitmap CreateBinaryImage(Bitmap source, Bitmap zero, Bitmap one, float threshold = 0.53f)
    41. {
    42. var canvas = new Bitmap(
    43. source.Width * zero.Width,
    44. source.Height * zero.Height,
    45. PixelFormat.Format24bppRgb);
    46. unsafe
    47. {
    48. var srcData = new ImageData(source, ImageLockMode.ReadOnly);
    49. var zData = new ImageData(zero, ImageLockMode.ReadOnly);
    50. var oData = new ImageData(one, ImageLockMode.ReadOnly);
    51. var cnvData = new ImageData(canvas, ImageLockMode.ReadWrite);
    52. try
    53. {
    54. /* Create lists of color arrays of the replacement images.
    55. ** Use the bpp property for incrementation to step through the individual pixel values.
    56. ** Stores each pixels RGB values as bytes. */
    57. var zPixel = new List<Color[]>();
    58. var oPixel = new List<Color[]>();
    59. for (int y=0; y<zData.Hip; y++)
    60. {
    61. /* Pointer to the desired row of pixels */
    62. byte* zScan = zData.FirstPixel + y * zData.Bytes;
    63. byte* oScan = oData.FirstPixel + y * oData.Bytes;
    64. /* We only want to store the Pixels color which is why we need to devide by Byte per pixel */
    65. Color[] z = new Color[zData.Bytes / zData.Bpp];
    66. Color[] o = new Color[oData.Bytes / oData.Bpp];
    67. int index = 0;
    68. for (int x=0; x<zData.Bytes; x+=zData.Bpp)
    69. {
    70. z[index] = Color.FromArgb(zScan[x + 2], zScan[x + 1], zScan[x + 0]);
    71. o[index] = Color.FromArgb(oScan[x + 2], oScan[x + 1], oScan[x + 0]);
    72. index++;
    73. }
    74. zPixel.Add(z);
    75. oPixel.Add(o);
    76. }
    77. int cx = 0; /* X-position on canvas */
    78. int cy = 0; /* Y-position on canvas */
    79. for (int y0=0; y0<srcData.Hip; y0++)
    80. {
    81. byte* srcScan = srcData.FirstPixel + y0 * srcData.Bytes;
    82. for (int x0=0; x0<srcData.Bytes; x0+=srcData.Bpp)
    83. {
    84. // calculate average to determine if pixel will be 1 or 0
    85. float avg = ((srcScan[x0 + 2] & 0xff) / 255f +
    86. (srcScan[x0 + 1] & 0xff) / 255f +
    87. (srcScan[x0 + 0] & 0xff) / 255f) / 3;
    88. avg = Math.Min(1f, .35f + .65f * avg);
    89. /* Determine which colors/pixels to use to reduce length of code */
    90. List<Color[]> pixels = avg < threshold ? oPixel : zPixel;
    91. /* Calculate start indizes on canvas */
    92. cx = (x0 / srcData.Bpp) * pixels[0].Length;
    93. cy = y0 * pixels.Count;
    94. int y1 = 0;
    95. while (y1 < pixels.Count && cy < canvas.Height)
    96. {
    97. int x1 = 0;
    98. while (x1 + zData.Bpp < pixels[y1].Length && cx < canvas.Width)
    99. {
    100. /* Increment the start indizes of the canvas with the current
    101. ** Pixel position of the replacement image. */
    102. cnvData.SetPixel(cx + x1, cy + y1, pixels[y1][x1]);
    103. x1++;
    104. }
    105. y1++;
    106. }
    107. }
    108. }
    109. }
    110. finally
    111. {
    112. srcData.Dispose();
    113. zData.Dispose();
    114. oData.Dispose();
    115. cnvData.Dispose();
    116. }
    117. return canvas;
    118. }
    119. }
    120. }
    121. }



    Hier ein Code, der das Prinzip des vorherigen Codes, etwas simpler darstellt und für manche evtl etwas einfacher zu verstehen ist:
    Spoiler anzeigen

    C#-Quellcode

    1. using System;
    2. using System.Collections.Generic;
    3. using System.Drawing;
    4. using System.Drawing.Imaging;
    5. namespace ImageConverter
    6. {
    7. class Program
    8. {
    9. static void Main(string[] args)
    10. {
    11. Bitmap source = (Bitmap)Image.FromFile(@"C:\data\resources\images\image.jpg");
    12. Bitmap zero = (Bitmap)Image.FromFile(@"c:\data\resources\images\bin_0_1024px_w.png");
    13. Bitmap one = (Bitmap)Image.FromFile(@"c:\data\resources\images\bin_1_1024px_w.png");
    14. zero = Resize(zero, 16, 16);
    15. one = Resize(one, 16, 16);
    16. var sourceMap = GetBinaryMap(source, .625f);
    17. var testImage = GetParsedMap(sourceMap, zero, one);
    18. testImage.Save("c:\\users\\[user]\\desktop\\test.png");
    19. Console.Write("Done... Press any key to exit");
    20. Console.ReadKey();
    21. }
    22. private static Bitmap Resize(Image source, int width, int height)
    23. {
    24. var destRect = new Rectangle(0, 0, width, height);
    25. var destImage = new Bitmap(width, height, source.PixelFormat);
    26. destImage.SetResolution(source.HorizontalResolution, source.VerticalResolution);
    27. using (var g = Graphics.FromImage(destImage))
    28. {
    29. g.CompositingMode = System.Drawing.Drawing2D.CompositingMode.SourceCopy;
    30. g.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality;
    31. g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
    32. g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
    33. g.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.HighQuality;
    34. using (var wrap = new ImageAttributes())
    35. {
    36. wrap.SetWrapMode(System.Drawing.Drawing2D.WrapMode.TileFlipXY);
    37. g.DrawImage(source, destRect, 0, 0, source.Width, source.Height, GraphicsUnit.Pixel, wrap);
    38. }
    39. }
    40. return destImage;
    41. }
    42. private static Bitmap GetParsedMap(int[,] map, Bitmap z, Bitmap o)
    43. {
    44. var zMap = GetImageMap(z);
    45. var oMap = GetImageMap(o);
    46. Bitmap canvas = new Bitmap(map.GetLength(0) * z.Width, map.GetLength(1) * z.Width);
    47. int cx = 0;
    48. int cy = 0;
    49. for (int y0 = 0; y0 < map.GetLength(0); y0++)
    50. {
    51. for (int x0 = 0; x0 < map.GetLength(1); x0++)
    52. {
    53. List<Color[]> cMap = map[y0, x0] == 0 ? zMap : oMap;
    54. cx = x0 * cMap[0].Length;
    55. cy = y0 * cMap.Count;
    56. int y1 = 0;
    57. while (y1 < cMap.Count && cy < canvas.Height)
    58. {
    59. int x1 = 0;
    60. while (x1 < cMap[y1].Length && cx < canvas.Width)
    61. {
    62. canvas.SetPixel(cx + x1, cy + y1, cMap[y1][x1]);
    63. x1++;
    64. }
    65. y1++;
    66. }
    67. }
    68. }
    69. return canvas;
    70. }
    71. private static List<Color[]> GetImageMap(Bitmap map)
    72. {
    73. var bList = new List<Color[]>();
    74. unsafe
    75. {
    76. BitmapData lockBits = map.LockBits(
    77. new Rectangle(0, 0, map.Width, map.Height),
    78. ImageLockMode.ReadOnly,
    79. map.PixelFormat);
    80. try
    81. {
    82. int bpp = Bitmap.GetPixelFormatSize(map.PixelFormat) / 8;
    83. int hip = lockBits.Height;
    84. int wib = lockBits.Width;
    85. byte* ptrPx = (byte*)lockBits.Scan0;
    86. int bytes = Math.Abs(lockBits.Stride);
    87. for (int y = 0; y < hip; y++)
    88. {
    89. byte* scanLine = ptrPx + y * bytes;
    90. Color[] rowColors = new Color[bytes / bpp];
    91. int index = 0;
    92. for (int x = 0; x < bytes; x += bpp)
    93. {
    94. Color px = Color.FromArgb(
    95. scanLine[x + 2],
    96. scanLine[x + 1],
    97. scanLine[x]);
    98. rowColors[index] = px;
    99. index++;
    100. }
    101. bList.Add(rowColors);
    102. }
    103. return bList;
    104. }
    105. finally
    106. {
    107. map.UnlockBits(lockBits);
    108. }
    109. }
    110. }
    111. private static int[,] GetBinaryMap(Bitmap source, float threshold = 0.53f)
    112. {
    113. int[,] map;
    114. unsafe
    115. {
    116. BitmapData lockBits = source.LockBits(
    117. new Rectangle(0, 0, source.Width, source.Height),
    118. ImageLockMode.ReadWrite,
    119. source.PixelFormat);
    120. try
    121. {
    122. // byte per pixel
    123. int bpp = Bitmap.GetPixelFormatSize(source.PixelFormat) / 8;
    124. int hip = lockBits.Height;
    125. int wib = lockBits.Width;
    126. // pointer to first pixel
    127. byte* ptrPx = (byte*)lockBits.Scan0;
    128. // length of stride
    129. int bytes = Math.Abs(lockBits.Stride);
    130. map = new int[hip + 1, wib + 1];
    131. for (int y = 0; y < hip; y++)
    132. {
    133. int x0 = 0;
    134. // get pointer to current scanline
    135. byte* scanLine = ptrPx + y * bytes;
    136. // iterate through pixels
    137. for (int x = 0; x < bytes; x += bpp)
    138. {
    139. // calculate average to determine wether pixel will be 1 or 0
    140. float avg = ((scanLine[x + 2] & 0xff) / 255f +
    141. (scanLine[x + 1] & 0xff) / 255f +
    142. (scanLine[x] & 0xff) / 255f) / 3;
    143. avg = Math.Min(1f, .35f + .65f * avg);
    144. map[y, x0] = avg < threshold ? 0 : 1;
    145. x0++;
    146. }
    147. }
    148. }
    149. finally
    150. {
    151. // release locked bitmap bits
    152. if (source != null)
    153. {
    154. source.UnlockBits(lockBits);
    155. }
    156. }
    157. return map;
    158. }
    159. }

    Bilder
    • binary_image_warp0.png

      1,03 MB, 1.612×620, 178 mal angesehen
    • bin_0_1024px_w.png

      28,33 kB, 1.024×1.024, 100 mal angesehen
    • bin_1_1024px_w.png

      15,21 kB, 1.024×1.024, 98 mal angesehen
    • image.jpg

      49,28 kB, 970×575, 97 mal angesehen
    Dateien
    • test.png

      (17,31 MB, 203 mal heruntergeladen, zuletzt: )

    Dieser Beitrag wurde bereits 3 mal editiert, zuletzt von „Petersilie“ ()

    Petersilie schrieb:

    schräg und versetzt
    sieht aus, als ob die Zeilenlänge nicht stimmt.
    Mach die mal Mod 4 = 0.
    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!
    @RodFromGermany Hi, Danke für deine Antwort.

    Das ist leider nicht das Problem.
    Das erste Bild hat eine Abmessung von 1920x1080 und das zweite 240x240.
    Beides also ein vielfaches von 4.
    Die Abmessung der Bitmap 'canvas' ist ebenfalls ein vielfaches von 4 da die original Abmessung mit 8 multipliziert wurde.


    Ich glaube es hat etwas mit Graphics.DrawImage() zutun.
    Ändert man die Größe der 8x8 Bitmaps auf 1x1 ab, erhält man eine perfekte schwarz weiß Kopie des Bildes.
    Ändert man den Code um, so dass keine Bitmaps gesetzt werden, sondern einfach nur die einzelnen Pixel in schwarz oder weiß
    abgeändert werden, erhält man ebenfalls eine perfekte schwarz weiß Kopie.

    Dieser Beitrag wurde bereits 3 mal editiert, zuletzt von „Petersilie“ ()

    Morrison schrieb:

    der berücksichtigt werden muss!
    Macht er doch:

    Petersilie schrieb:

    C#-Quellcode

    1. int bpp = Bitmap.GetPixelFormatSize(source.PixelFormat) / 8;
    2. // ...
    3. // iterate through pixels
    4. for (int x = 0; x < bytes; x += bpp) { ... }
    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!
    Guten Morgen

    In Zeilen 57 und 58 ist der Code doch NULL-basieren, aber du gehst bis auf posX >= w.
    Ist das nicht einmal zu viel?

    Zeilen 57 und 58

    C#-Quellcode

    1. posX = posX + wbin >= w ? 0 : posX + wbin;
    2. posY = posX + wbin >= w ? posY + hbin : posY;


    ODER

    so sein lassen und
    zwischen Zeile 58 und 59

    C#-Quellcode

    1. if (posX == w) posX = 0;



    Freundliche Grüsse

    exc-jdbi

    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „exc-jdbi“ ()

    @RodFromGermany
    @Morrison
    @exc-jdbi
    @Mircosofter2206

    Hallo Leute,
    erstmal vielen Dank für eure Antworten!

    Da ich momentan mit einem anderen Projekt vollkommen ausgelastet bin und das ganze hier nur ein privates Projekt ist,
    kann ich gerade leider nicht ernsthaft daran weiterarbeiten (besser gesagt fehlt mir gerade die Lust nach 10 bis 12 Stunden noch weiter zu hacken).

    Ich melde mich wenn ich wieder etwas mehr Luft haben!