8

Вопрос по windows-store-apps, image, windows-runtime, c# – Обработка изображений WinRT

Мы с другом провели большую часть прошлой ночи, чуть не порвав свои волосы, пытаясь поработать с некоторыми изображениями в приложении метро. Мы добавили изображения в приложение с общим очарованием, а затем я хотел поработать с ними, обрезать изображения и сохранить их обратно в папку appdata. Это оказалось крайне разочаровывающим.

Мой вопрос, в конце всего этого, будет следующим: «Каков правильный способ сделать это, не чувствуя, что я собираю кучу несоответствующих кусочков мозаики?»

При совместном использовании нескольких изображений с приложением они отображаются в виде спискаWindows.Storage.StorageFiles. Вот некоторый код, используемый для этого.

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);
}

Некоторые поиски в Интернете показали, что в настоящее времяWindows.UI.Xaml.Media.Imaging.WriteableBitmap это единственное, что позволяет вам получить доступ к пиксельным данным на изображении.Этот вопрос содержит полезный ответ, полный методов расширения для сохранения изображений в файл, поэтому мы использовали их.

Наши проблемы были худшими, когда я попытался открыть файлы позже. Я сделал что-то похожее до:

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();

Здесь возникает проблема. Как долго этот поток, если я хочу байты из него?

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

Хорошо, попробуйте еще раз.

    var pixels = new byte[stream.Length];

Это работает, за исключением ... если изображение сжато, поток будет короче, чем вы ожидаете, поэтому вы в конечном итоге получите исключение за пределами границ. А пока представьте, что это несжатый битовый массив.

    await _stream.ReadAsync(pixels, 0, pixels.Length);

Ну угадай что. Хотя я сказалbitmap.SetSource(fileStream); чтобы прочитать данные, мой байтовый массив все еще полон нулей. Понятия не имею почему. Если я пройду это жеbitmap в мой пользовательский интерфейс через образец группы данных, изображение отображается просто отлично. Таким образом, он явно содержит данные пикселей в этом растровом изображении, но я не могу прочитать их изbitmap.PixelBuffer? Почему бы и нет?

Наконец, вот что в итоге получилось.

    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

Так что теперь у меня есть данные изображения, но у меня все еще есть большая проблема. Чтобы что-то с этим сделать, я должен сделать предположения о его формате. Я понятия не имею, являются ли это rgba, rgb, abgr, bgra, какими бы они ни были. Если я угадаю неправильно, моя обработка просто терпит неудачу. У меня были десятки тестовых прогонов, в которых выпадали нулевые байты и испорченные изображения, перевернутые изображения (???), неправильные цвета и т. Д. Я ожидал бы найти эту информацию вproperties что я получил от звонкаawait file.Properties.GetImagePropertiesAsync();, но не повезло. Это содержит только ширину и высоту изображения, а также некоторые другие бесполезные вещи. Минимальная документацияВот.

Итак, почему этот процесс настолько болезненный? Отражает ли это незрелость библиотек прямо сейчас, и можно ли ожидать, что она станет лучше? Или уже есть какой-то стандартный способ сделать это? Я хотел бы, чтобы это было так же легко, как вSystem.Drawing, Это дало вам все данные, которые вам когда-либо были нужны, и успешно загрузило любой тип изображения, не заставляя вас самостоятельно работать с потоками.

  • Ах, мне не нужен декодер, мне просто нужно искать начало. Спасибо! Я сделаю это. И я проверю вашу библиотеку!

    от Tesserex
  • Возможно, вам нужно подождать, пока изображение загрузится, прежде чем вы сможете получить доступ к пиксельному буферу? Я хотел бы начать с ожидания Task.Delay (1000), прежде чем получить доступ к PixelBuffer. Если это помогает - возможно, есть событие, которое нужно ждать, как bitmap.Loaded.

    от
  • Поиск потока не работал, все равно было все нулевые байты. Сожалею.

    от Tesserex
  • ... также все плохо реализуют библиотеки изображений, хотя BitmapEncoder / BitmapDecoder довольно хороши. stb_image.c в большинстве случаев и на большинстве платформ работает намного лучше, чем любые доступные нативные библиотеки, поскольку он обеспечивает минимальный и правильный интерфейс. (байты, размеры, форматы, без хрома и шаблонов).

    от jheriko
  • «Я понятия не имею, являются ли это rgba, rgb, abgr, bgra, какими бы они ни были». На самом деле вы точно знаете, что это за ваш выбор формата. Поработайте один раз и для PNG, и вы сможете применить логику ко всем PNG.

    от jheriko
  • 2

    You might need to seek to the beginning of the stream to get the pixel

    From what I have seen - when you are planning on loading the WriteableBitmap with a stream - you don't need to check the image dimensions - just do new WriteableBitmap(1,1), then call SetSource(). Not sure why you were thinking var pixels = new byte[fileStream.Length]; would work, since the fileStream has the compressed image bytes and not a pixel array.

    s array:

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

    I had started working on a WinRT port of WriteableBitmapEx - maybe it could help you: http://bit.ly/WriteableBitmapExWinRT. I have not tested it well and it is based on an older version of WBX, but it is fairly complete in terms of feature support. Might be a tad slower than it is possible too.