0

現在、一連の画像をダウンロードし、ピクセルの色を赤、黄などに変更したい部分に取り組んでいます。調査を行ったところ、WriteableBitmapExというサードパーティ API に出会いました。次のコードを使用すると、完璧な結果が得られますが、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;
    }

コードを最適化してパフォーマンスを向上させ、画像のピクセル色を変換する時間を短縮する方法を誰かが提案できますか?

4

2 に答える 2

0

このスレッドから、

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 秒に減少しました。

于 2016-05-19T08:48:47.470 に答える