如何在Windows 10中使用WriteableBitmapEx提高更改图像像素颜色的性能?

时间:2016-05-09 06:22:45

标签: c# win-universal-app windows-10-universal writeablebitmapex

我目前正在处理我想要下载一组图像并将其像素颜色更改为红色,黄色等的部分。我做了一些研究并遇到了名为 WriteableBitmapEx <的第三方API / strong>即可。我使用了以下代码,它给出了完美的结果,但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 个答案:

答案 0 :(得分:0)

来自this thread

  

GetPixel和SetPixel扩展方法对于多次迭代更改非常昂贵,因为它们提取BitmapContext(WriteableBitmap的PixelBuffer),进行更改,然后在使用BitmapContext完成调用时写回更新的PixelBuffer。

要改善这一点,请使用WriteableBitmapEx的BitmapContext对象提取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秒。

答案 1 :(得分:0)

在做了这么多试验和错误的方法之后,我找到了适用于Windows Phone 8.1 / Windows 10的BitmapIcon控件。它有一个前景属性,可用于更改图标颜色。我用它来表示以下类型的图标,性能非常好,并且所需的代码不多。最好的部分是不需要第三方API。

Icon

BitmapIcon参考 BitmapIcon