Как повысить производительность изменения цвета пикселя изображения с помощью WriteableBitmapEx в Windows 10?

В настоящее время я работаю над той частью, где я хочу загрузить набор изображений и изменить их цвет пикселя, например, на красный, желтый и т. Д. Я провел некоторое исследование и наткнулся на сторонний API под названием WriteableBitmapEx. Я использовал следующий код, и он дает идеальный результат, но для 10 изображений это занимает более 4-5 минут.

public class ChangeImageColor
{
    public async Task<WriteableBitmap> downloadImageandChangeColor(string image_url, string hex_color)
    {
        Uri uri = new Uri(image_url);
        var fileName = getname(image_url);
        var bitmapImage = new BitmapImage();
        var httpClient = new HttpClient();
        var httpResponse = await httpClient.GetAsync(uri);
        byte[] b = await httpResponse.Content.ReadAsByteArrayAsync();

        // create a new in memory stream and datawriter
        var stream = new InMemoryRandomAccessStream();

        DataWriter dw = new DataWriter(stream);

        // write the raw bytes and store
        dw.WriteBytes(b);
        await dw.StoreAsync();

        // set the image source
        stream.Seek(0);
        bitmapImage.SetSource(stream);



        // read from pictures library
        stream.Seek(0);
        WriteableBitmap bitMap = await GetFileFromStorageandChangeColor(fileName, stream,hex_color);

        //StorageFile file = await WriteableBitmapToStorageFile(bitMap, FileFormat.Png, fileName);

        return bitMap;
    }


    public async Task<WriteableBitmap> GetFileFromStorageandChangeColor(string fileName, InMemoryRandomAccessStream pictureStream,string hex_color)
    {
        //var pictureFile = await KnownFolders.PicturesLibrary.GetFileAsync(fileName);
        WriteableBitmap writeableBitmap = null;
        //using (var pictureStream = await pictureFile.OpenAsync(FileAccessMode.Read))
        //{
        BitmapImage bmp = new BitmapImage();
        bmp.SetSource(pictureStream);
        // Load the picture in a WriteableBitmap
        writeableBitmap = new WriteableBitmap(bmp.PixelWidth, bmp.PixelHeight);
        pictureStream.Seek(0);
        writeableBitmap.SetSource(pictureStream);

        // Now we have to extract the pixels from the writeablebitmap
        // Get all pixel colors from the buffer
        byte[] pixelColors = writeableBitmap.PixelBuffer.ToArray();

        // Execute the filter on the color array
        //ApplyFilter(pixelColors);
        writeableBitmap = ChangeColor(writeableBitmap, hex_color);

        // Tell the image it needs a redraw
        writeableBitmap.Invalidate();
        // }
        return writeableBitmap;
    }

    public WriteableBitmap ChangeColor(WriteableBitmap scrBitmap, string hex_value)
    {
        //You can change your new colour here.
        Color newColor = MCSExtensions.GetColorFromHex(hex_value).Color;
        Color actualColor;

        //WriteableBitmap newBitmap = BitmapFactory.New(scrBitmap.PixelWidth, scrBitmap.PixelHeight);
        //newBitmap.ForEach((x, y, srcColor) => srcColor.A > 150 ? newColor : srcColor);
        //newBitmap.Invalidate();
        //make an empty bitmap the same size as scrBitmap
        WriteableBitmap newBitmap = new WriteableBitmap(scrBitmap.PixelWidth, scrBitmap.PixelHeight);
        for (int i = 0; i < scrBitmap.PixelWidth; i++)
        {
            for (int j = 0; j < scrBitmap.PixelHeight; j++)
            {
                //get the pixel from the scrBitmap image
                actualColor = scrBitmap.GetPixel(i, j);
                // > 150 because.. Images edges can be of low pixel colr. if we set all pixel color to new then there will be no smoothness left.
                if (actualColor.A > 0)
                    newBitmap.SetPixel(i, j, (Color)newColor);
                else
                    newBitmap.SetPixel(i, j, actualColor);
            }
        }

        return newBitmap;

    }

    private async Task<StorageFile> WriteableBitmapToStorageFile(WriteableBitmap WB, FileFormat fileFormat, string fileName)
    {
        string FileName = fileName.Replace(".png", "") + ".";
        Guid BitmapEncoderGuid = BitmapEncoder.JpegEncoderId;
        switch (fileFormat)
        {
            case FileFormat.Jpeg:
                FileName += "jpeg";
                BitmapEncoderGuid = BitmapEncoder.JpegEncoderId;
                break;
            case FileFormat.Png:
                FileName += "png";
                BitmapEncoderGuid = BitmapEncoder.PngEncoderId;
                break;
            case FileFormat.Bmp:
                FileName += "bmp";
                BitmapEncoderGuid = BitmapEncoder.BmpEncoderId;
                break;
            case FileFormat.Tiff:
                FileName += "tiff";
                BitmapEncoderGuid = BitmapEncoder.TiffEncoderId;
                break;
            case FileFormat.Gif:
                FileName += "gif";
                BitmapEncoderGuid = BitmapEncoder.GifEncoderId;
                break;
        }
        var file = await KnownFolders.PicturesLibrary.CreateFileAsync(
                    FileName,
                    CreationCollisionOption.ReplaceExisting);
        using (IRandomAccessStream stream = await file.OpenAsync(FileAccessMode.ReadWrite))
        {
            BitmapEncoder encoder = await BitmapEncoder.CreateAsync(BitmapEncoderGuid, stream);
            Stream pixelStream = WB.PixelBuffer.AsStream();
            byte[] pixels = new byte[pixelStream.Length];
            await pixelStream.ReadAsync(pixels, 0, pixels.Length);
            encoder.SetPixelData(BitmapPixelFormat.Bgra8, BitmapAlphaMode.Straight,
                      (uint)WB.PixelWidth,
                      (uint)WB.PixelHeight,
                      96.0,
                      96.0,
                      pixels);
            await encoder.FlushAsync();
        }
        return file;
    }
    private enum FileFormat
    {
        Jpeg,
        Png,
        Bmp,
        Tiff,
        Gif
    }

    public static string getname(string name)
    {
        string image_name = string.Empty;
        image_name = (name).Substring(Math.Max(0, (name).Length - 20)).Replace(@"/", "_");
        return image_name;
    }

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

2 ответа

Решение

После стольких проб и ошибок я нашел элемент управления BitmapIcon, который доступен для Windows Phone 8.1/Windows 10. Он имеет свойство переднего плана, которое можно использовать для изменения цвета значка. Я использовал его для пиктограмм следующих типов, и производительность была действительно хорошей, и не нужно много кода. Лучшая часть не требует стороннего API.

Значок

Ссылка на BitmapIcon BitmapIcon

Из этой темы

Методы расширения GetPixel и SetPixel очень дороги для множественных итеративных изменений, поскольку они извлекают BitmapContext (PixelBuffer объекта WriteableBitmap), вносят изменения, а затем записывают обратно обновленный PixelBuffer, когда вызов выполняется с помощью BitmapContext.

Чтобы улучшить это, используйте объект BitmapContext объекта WriteableBitmapEx для извлечения PixelBuffer, затем вызывайте Get и SetPixel так часто, как это необходимо для контекста растрового изображения. Когда сделано с setPixel, удалите BitmapContext.

Итак, обновите ваш ChangeColor метод добавить using(){} следующее:

   using (newBitmap.GetBitmapContext())
   {
       for (int i = 0; i < scrBitmap.PixelWidth; i++)
       {
           for (int j = 0; j < scrBitmap.PixelHeight; j++)
           {

               actualColor = scrBitmap.GetPixel(i, j);
               // > 150 because.. Images edges can be of low pixel col
               if (actualColor.A > 0)
                   newBitmap.SetPixel(i, j, (Color)newColor);
               else
                   newBitmap.SetPixel(i, j, actualColor);

               //get the pixel from the scrBitmap image             
           }
       }
   }

Я проверил ваш код с размером изображения 238px*220px. Я потратил около 40 секунд, чтобы установить все пиксели раньше, после обновления он уменьшился примерно до 20 секунд.

Другие вопросы по тегам