Bytearray zu Bitmap

  • C#

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

    Bytearray zu Bitmap

    Ich lese eine Bitmap über das LockBit-Verfahren ([VB 2010] Tutorial: LockBits) zu einer Auflistung von Bytes ein. Diese konvertiere ich in eine List<Color> um sie besser handhaben zu können. (Konvertierungsmethoden weiter unten)
    Anschließend filtere ich die Liste etwas (prüfe auf Farbvorkommen ...). Ich habe also nun eine gekürzte Liste.
    Diese möchte ich wieder zu einer Bitmap zusammenfügen. (bzw. ich will was sehen :S )
    Ich habe es auf zwei Arten probiert.
    1. UnlockBits. Dabei habe ich die Ursprungswerte beim LockBits gelassen und nur die anderen Bytes übergeben. Fehler: "Allgemeiner Fehler in GDI"

      C#-Quellcode

      1. /// <summary>Convert an array of bytes to a bitmap</summary>
      2. /// <param name="bytes">The input bytearray</param>
      3. /// <returns></returns>
      4. public Bitmap UnlockBits(byte[] bytes)
      5. {
      6. System.Runtime.InteropServices.Marshal.Copy(bytes, 0, this.ptr, bytes.Count());
      7. bmp.UnlockBits(bmpData); // Hier tritt der Fehler auf
      8. return bmp;
      9. }

    2. TypeConverter.ConvertFrom() msdn.microsoft.com/de-de/library/ef83h055.aspx Fehler: "Ungültiger Parameter."

      C#-Quellcode

      1. TypeConverter tc = TypeDescriptor.GetConverter(typeof(Bitmap));
      2. Bitmap bitmap1 = (Bitmap)tc.ConvertFrom(bytes.ToArray());
      3. return bitmap1;

    Konvertierungsextensions

    C#-Quellcode

    1. /// <summary>
    2. /// Represents some useful extensions
    3. /// </summary>
    4. static class Extensions
    5. {
    6. /// <summary>Convert an list of bytes into a list of colors</summary>
    7. /// <param name="bytes">The list of bytes</param>
    8. /// <param name="length">Bytes per pixel</param>
    9. /// <returns>List of the colors</returns>
    10. public static List<Color> ToColorList(this List<byte> bytes, int length)
    11. {
    12. if (bytes.Count > 0 && (length == 3 || length == 4))
    13. {
    14. List<Color> lOutput = new List<Color>();
    15. // Fill with colorvalues
    16. for (int counter = 0; counter < bytes.Count(); counter += length)
    17. {
    18. // with alphachannel or
    19. if (length == 3)
    20. lOutput.Add(Color.FromArgb(bytes[counter + 2], bytes[counter + 1], bytes[counter]));
    21. else if (length == 4)
    22. lOutput.Add(Color.FromArgb(bytes[counter + 3], bytes[counter + 2], bytes[counter + 1], bytes[counter]));
    23. }
    24. return lOutput;
    25. }
    26. else
    27. throw new ArgumentException();
    28. }
    29. /// <summary>Convert a list of color into a list of byte</summary>
    30. /// <param name="colors">The input list of color</param>
    31. /// <param name="length">Bytes per pixel</param>
    32. /// <returns>List of byte</returns>
    33. public static List<byte> ToByteList(this List<Color> colors, int length)
    34. {
    35. if (colors.Count > 0 && (length == 3 || length == 4))
    36. {
    37. List<byte> lOutput = new List<byte>();
    38. for (int i = 0; i < colors.Count; i += length)
    39. {
    40. lOutput.Add(colors[i].B);
    41. lOutput.Add(colors[i + 1].G);
    42. lOutput.Add(colors[i + 2].R);
    43. if (length == 4)
    44. lOutput.Add(colors[i + 3].A);
    45. }
    46. return lOutput;
    47. }
    48. else
    49. throw new ArgumentException();
    50. }
    51. }

    Hier auch mal die Methode zum Filtern. Ihre Funktion ist es, zu schauen wo komplett weiße "Streifen" sind. Danach holt sie sich die Farbwerte die zwischen zwei weißen Streifen liegen die nicht direkt untereinander liegen. (Bild im Anhang)
    Methode

    C#-Quellcode

    1. /// <summary>
    2. /// Search for strings in the given color array
    3. /// </summary>
    4. /// <param name="bytes">The given colors of the bitmap</param>
    5. /// <param name="width">Length of one strip</param>
    6. /// <returns></returns>
    7. public List<Color> SearchStrings(IEnumerable<Color> color, long width)
    8. {
    9. List<Color> lColors = (List<Color>) color;
    10. List<Stop> lWhiteLines = new List<Stop>();
    11. List<Color> lOutput = new List<Color>();
    12. long counter = 0;
    13. bool newLine = false;
    14. long start = 0;
    15. for (int i = 0; i < lColors.Count(); i ++)
    16. {
    17. // If new line
    18. if ((i == 0) || (i % width == 0))
    19. {
    20. newLine = true;
    21. start = i;
    22. }
    23. // If a completely white and new line exists
    24. if (newLine == true)
    25. {
    26. // If the actual field is white too
    27. if (lColors[i].A == 255 && lColors[i].R == 255 && lColors[i].G == 255 && lColors[i].B == 255)
    28. {
    29. counter++;
    30. // If there is a complete white strip
    31. if (counter == width)
    32. {
    33. // Add strip to the list and restart the procedurce
    34. lWhiteLines.Add(new Stop(start, i - start));
    35. counter = 0;
    36. newLine = false;
    37. }
    38. }
    39. else
    40. {
    41. newLine = false;
    42. counter = 0;
    43. }
    44. }
    45. }
    46. for (int i = 1; i < lWhiteLines.Count - 1; i++)
    47. {
    48. // If from the last line to the next is no whitespace
    49. if (lWhiteLines[i - 1].Position + lWhiteLines[i - 1].Count + 1 != lWhiteLines[i].Position)
    50. {
    51. // Here must be something between whiteLines[i] and whitelines[i - 1]
    52. for (int n = (int) (lWhiteLines[i - 1].Position + lWhiteLines[i - 1].Count); n < lWhiteLines[i].Position + lWhiteLines[i].Count - 1; n++)
    53. lOutput.Add(lColors[n]);
    54. }
    55. }
    56. return lOutput;
    57. }


    Hat jemand vielleicht eine Idee wie man die Bytes zu einer Bitmap zusammenfügen kann, oder auch wo sonst ein Fehler liegen könnte ?
    Für jegliche Art von Hilfe, auch Anmerkungen für schlechten Codestil(!) ist erwünscht.

    Viele Grüße
    LaMiy
    Bilder
    • unlockbits.png

      36,46 kB, 1.017×373, 129 mal angesehen
    • skitze.png

      7,56 kB, 884×252, 124 mal angesehen

    ThuCommix schrieb:

    Les die Bytes doch in einen Stream, und benutzt Bitmap.FromStream?
    Gute Idee !, aber da bekomm' ich leider den selben Fehler. Vielleicht liegt es daran, dass ich die Bytes irgendwie falsch herausschneide, sodass sie nicht weiter verarbeitet werden können.

    C#-Quellcode

    1. using (MemoryStream mStream = new MemoryStream())
    2. {
    3. mStream.Write(bytes.ToArray(), 0, bytes.ToArray().Length);
    4. mStream.Seek(0, SeekOrigin.Begin);
    5. Bitmap bm = new Bitmap(mStream);
    6. return bm;
    7. }


    sonne75 schrieb:

    Und hier steht nichts Sinnvolles drin?
    Sinnvolles schon, aber leider kann ich damit nichts anfangen, da ich, wie gesagt, das LockBits Verfahren schon verwende. Ich brauche ja meine Infos aus der LockBit()- Methode um UnLockBits() korrekt aufzurufen.

    Trotzdem Danke an euch beide ;)
    Bilder
    • parameter.png

      45,93 kB, 993×537, 139 mal angesehen
    Was ist denn, wenn du deine Bits nicht veränderst, sondern sie mit UnlockBits wieder reinschreibst? Generell muss es ja funktionieren.

    Wenn es dann läuft, musst du nach und nach einige Veränderungen an den Bits machen, bis du einen Fehler bekommst, dann siehst du, wo er herkommt.

    LaMiy schrieb:

    Diese konvertiere ich in eine List<Color> um sie besser handhaben zu können. (Konvertierungsmethoden weiter unten)

    Das ist nicht nötig.
    Man kann als Array gleich ein Color-Array verwenden, denn den IntPtr interesseirts nicht, ob er auf ein Color-Array zeigt, oder auf ein Byte-Array.
    Ich habe nun einen großen Fehler bemerkt. Meine Konvertierung war vollkommener Müll.
    So sollte es richtig sein.

    C#-Quellcode

    1. /// <summary>Convert a list of color into a list of byte</summary>
    2. /// <param name="colors">The input list of color</param>
    3. /// <param name="length">Bytes per pixel</param>
    4. /// <returns>List of byte</returns>
    5. public static List<byte> ToByteList(this List<Color> colors, int length)
    6. {
    7. if (colors.Count > 0 && (length == 3 || length == 4))
    8. {
    9. List<byte> lOutput = new List<byte>();
    10. for (int i = 0; i <= colors.Count - 1; i ++)
    11. {
    12. lOutput.Add(colors[i].B);
    13. lOutput.Add(colors[i].G);
    14. lOutput.Add(colors[i].R);
    15. if (length == 4)
    16. lOutput.Add(colors[i].A);
    17. }
    18. return lOutput;
    19. }
    20. else
    21. throw new ArgumentException();
    22. }

    Die Fehler bleiben allerdings. Da werde ich jetzt gleich nochmal genauer nach schauen.

    ThuCommix schrieb:

    Wieso Seekst du den Stream wieder auf 0 ?
    Ich habe das Codesnippet mit dem MemoryStream und der Bitmap so kopiert und mich darauf verlassen dass es so richtig ist. (Wenn ich das Seek weglasse kommt der selbe Fehler)

    ErfinderDesRades schrieb:

    Man kann als Array gleich ein Color-Array verwenden, denn den IntPtr interesseirts nicht, ob er auf ein Color-Array zeigt, oder auf ein Byte-Array.
    Wie meinst du das ?
    Problem ist ja das mein Bytearray so aufgebaut ist.
    255 (B)
    255 (G)
    255 (R)
    255 (A - falls vorhanden)
    Meine Liste von Colorobjekten soll allerdings ja 3 (wenn Aplhakanal, dann 4) Bytes darstellen.
    Also Color(255, 255, 255) oder Color(liste[0], liste[1], liste[2])
    deine Sorge mag dieser Test zerstreuen:

    VB.NET-Quellcode

    1. <StructLayout(LayoutKind.Explicit, Pack:=1)> _
    2. Public Structure ColorConverter
    3. <FieldOffset(0)> Public Color As Color
    4. <FieldOffset(0)> Public ByteB As Byte
    5. <FieldOffset(1)> Public ByteG As Byte
    6. <FieldOffset(2)> Public ByteR As Byte
    7. <FieldOffset(3)> Public ByteA As Byte
    8. Public Shared Instance As ColorConverter
    9. End Structure
    10. Private Sub TestColorConverter()
    11. Dim col = Color.FromArgb(1, 2, 3, 4)
    12. With ColorConverter.Instance
    13. .Color = col
    14. MessageBox.Show(String.Join(" ", .ByteA, .ByteR, .ByteG, .ByteB))
    15. End With
    16. End Sub
    Er zeigt, dass die Lage der Bytes in der Color-Struktur identisch ist, mit der Reihenfolge, wie sie im Byte-Array liegen, nämlich BGRA

    ErfinderDesRades schrieb:

    deine Sorge mag dieser Test zerstreuen:
    Das war auf jeden Fall schon mal sehr hilfreich, aber ich verstehe noch nicht so ganz wie ich mir das zu nutzen machen soll wenn keine Konvertierung nötig sein muss, deiner Meinung.
    Dieser Code hier klappt (natürlich) nicht.

    C#-Quellcode

    1. // Declare an array to hold the bytes of the bitmap.
    2. int bytes = Math.Abs(bmpData.Stride) * b.Height;
    3. ArgbColor[] rgbValues = new ArgbColor[bytes];
    4. // Copy the RGB values into the array.
    5. Marshal.Copy(ptr, rgbValues, 0, bytes);
    Bilder
    • das.png

      21,54 kB, 1.252×272, 118 mal angesehen