WinRT-Image-Handling

Ein Freund und ich verbrachten den größten Teil der letzten Nacht damit, uns fast die Haare auszureißen und zu versuchen, mit einigen Bildern in einer Metro-App zu arbeiten. Wir haben Bilder mit dem Freigabecharme in die App aufgenommen, und dann wollte ich mit ihnen eine andere Arbeit ausführen, die Bilder zuschneiden und sie wieder im AppData-Ordner speichern. Dies erwies sich als äußerst frustrierend.

Am Ende lautet meine Frage: "Was ist die richtige Vorgehensweise, ohne das Gefühl zu haben, dass ich ein paar nicht übereinstimmende Puzzleteile zusammenhämmere?"

Wenn Sie mehrere Bilder mit der App teilen, werden diese als Liste von angezeigtWindows.Storage.StorageFiles. Hier ist ein Code, der dafür verwendet wird.

<code>var storageItems = await _shareOperation.Data.GetStorageItemsAsync();

foreach (StorageFile item in storageItems)
{
    var stream = await item.OpenReadAsync();
    var properties = await item.Properties.GetImagePropertiesAsync();

    var image = new WriteableBitmap((Int32)properties.Width, (Int32)properties.Height);
    image.SetSource(stream);

    images.Add(image);
}
</code>

Einige Online-Suchanfragen haben ergeben, dass derzeit aWindows.UI.Xaml.Media.Imaging.WriteableBitmap Nur so können Sie auf die Pixeldaten im Bild zugreifen.Diese Frage Enthält eine hilfreiche Antwort voller Erweiterungsmethoden zum Speichern von Bildern in einer Datei. Daher haben wir diese verwendet.

Unsere Probleme waren die schlimmsten, als ich später versuchte, die Dateien erneut zu öffnen. Ich habe etwas Ähnliches gemacht wie vorher:

<code>var files = await ApplicationData.Current.LocalFolder.GetFilesAsync();

foreach (var file in files)
{
    var fileStream = await file.OpenReadAsync();
    var properties = await file.Properties.GetImagePropertiesAsync();
    var bitmap = new WriteableBitmap((Int32)properties.Width, (Int32)properties.Height);
    bitmap.SetSource(fileStream);

    System.IO.Stream stream = bitmap.PixelBuffer.AsStream();
</code>

Hier kommt ein Problem. Wie lang ist dieser Stream, wenn ich die Bytes raus haben will?

<code>    // CRASH! Length isn't supported on an IRandomAccessStream.
    var pixels = new byte[fileStream.Length];
</code>

OK, versuch es nochmal.

<code>    var pixels = new byte[stream.Length];
</code>

Dies funktioniert, außer ... wenn das Bild komprimiert ist, ist der Stream kürzer als erwartet, sodass Sie möglicherweise eine Ausnahme außerhalb der Grenzen erhalten. Vorerst soll es sich um eine unkomprimierte Bitmap handeln.

<code>    await _stream.ReadAsync(pixels, 0, pixels.Length);
</code>

Rate mal. Obwohl ich sagtebitmap.SetSource(fileStream); Um die Daten einzulesen, ist mein Bytearray immer noch voller Nullen. Ich habe keine Idee warum. Wenn ich das gleich übergebebitmap In einer Benutzeroberfläche über die Beispieldatengruppe wird das Bild einwandfrei angezeigt. Die Pixeldaten in dieser Bitmap sind also eindeutig irgendwo vorhanden, aber ich kann sie nicht auslesenbitmap.PixelBuffer? Warum nicht?

Schließlich ist hier, was tatsächlich funktioniert hat.

<code>    var decoder = await BitmapDecoder.CreateAsync(BitmapDecoder.PngDecoderId, fileStream);
    var data = await decoder.GetPixelDataAsync();
    var bytes = data.DetachPixelData();

    /* process my data, finally */

} // end of that foreach I started a while ago
</code>

So, jetzt habe ich Bilddaten, aber ich habe immer noch ein großes Problem. Um etwas damit anfangen zu können, muss ich Annahmen über das Format treffen. Ich habe keine Ahnung, ob es rgba, rgb, abgr, bgra ist, was auch immer sie sein können. Wenn ich falsch denke, schlägt meine Verarbeitung fehl. Ich hatte Dutzende von Testläufen, in denen Null-Byte-Bilder und beschädigte Bilder, verkehrte Bilder (???), falsche Farben usw. ausgespuckt wurden. Ich hätte erwartet, dass ich einige dieser Informationen in der finden würdeproperties das habe ich von anrufen bekommenawait file.Properties.GetImagePropertiesAsync();, aber kein Glück. Das enthält nur die Bildbreite und -höhe sowie einige andere nutzlose Dinge. Minimale DokumentationHier.

Warum ist dieser Prozess so schmerzhaft? Spiegelt dies nur die Unreife der Bibliotheken wider, und kann ich damit rechnen, dass es besser wird? Oder gibt es dafür schon einen Standard? Ich wünschte, es wäre so einfach wie inSystem.Drawing. Das gab Ihnen alle Daten, die Sie jemals brauchten, und hat jeden Bildtyp korrekt geladen, ohne dass Sie sich selbst mit Streams befassen mussten.

 jheriko19. Mai 2012, 16:57
"Ich habe keine Ahnung, ob es rgba, rgb, abgr, bgra ist, was auch immer sie sein können." Tatsächlich wissen Sie anhand des Formats Ihrer Wahl genau, worum es sich handelt. Wenn Sie es einmal für PNG ausarbeiten, können Sie die Logik auf alle PNGs anwenden.
 jheriko19. Mai 2012, 16:58
... außerdem implementiert jeder Bildbibliotheken ziemlich schlecht, obwohl BitmapEncoder / BitmapDecoder recht nett sind. stb_image.c macht in den meisten Fällen und auf den meisten Plattformen einen viel besseren Job als die verfügbaren nativen Bibliotheken, da es eine minimale und korrekte Schnittstelle bietet. (Bytes, Maße, Formate, kein Chrom und Kesselschild).

Antworten auf die Frage(1)

Nach allem, was ich gesehen habe, müssen Sie beim Laden der WriteableBitmap mit einem Stream die Bildabmessungen nicht überprüfen. Führen Sie einfach eine neue WriteableBitmap (1,1) aus und rufen Sie dann SetSource () auf.Ich bin mir nicht sicher, warum du var pixels = new byte [fileStream.Length] gedacht hast. würde funktionieren, da der fileStream die komprimierten Bildbytes und kein Pixelarray hat.

um das Pixel-Array zu erhalten:

<code>var pixelStream = pixelBuffer.AsStream();
var bytes = new byte[this.pixelStream.Length];
this.pixelStream.Seek(0, SeekOrigin.Begin);
this.pixelStream.Read(bytes, 0, Bytes.Length);
</code>

Ich hatte angefangen, an einem WinRT-Port von zu arbeitenWriteableBitmapEx - Vielleicht könnte es Ihnen helfen:http://bit.ly/WriteableBitmapExWinRT. Ich habe es nicht gut getestet und es basiert auf einer älteren Version von WBX, aber es ist ziemlich vollständig in Bezug auf die Funktionsunterstützung. Könnte etwas langsamer sein, als es auch möglich ist.

 Tesserex05. Apr. 2012, 05:05
Ah, also brauche ich den Decoder nicht, ich muss nur zum Anfang suchen. Vielen Dank! Ich werde es versuchen. Und ich werde Ihre Bibliothek überprüfen!
 Tesserex05. Apr. 2012, 05:21
Die Suche nach dem Stream hat nicht funktioniert, hatte immer noch alle Null-Bytes. Es tut uns leid.
 Filip Skakun05. Apr. 2012, 18:07
Vielleicht müssen Sie warten, bis das Bild geladen ist, bevor Sie auf den Pixelpuffer zugreifen können? Ich würde damit beginnen, einen wait-Task.Delay (1000) auszuführen, bevor ich auf den PixelBuffer zugreife. Wenn das hilft - vielleicht gibt es ein Ereignis wie bitmap.Loaded zu warten.

Ihre Antwort auf die Frage